質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2181閲覧

Python Tkinterでシューティングゲームを作るときのビームの増やし方がわかりません

__n.um

総合スコア1

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/07/11 06:11

前提・実現したいこと

python初心者です。
tkinterを使ってシューティングゲームを作っているのですが、ビームを何回も発射する方法とインベーダー(敵)がたくさん表れる方法が全く分からないです。
どのようなコードを書けばいいでしょうか?
とてもつたないコードだと思いますが、質問答えてくれると嬉しいです。

該当のソースコード

import tkinter as tk from PIL import Image,ImageTk import random as ra rocketX,rocketY=300,360 rocketdx=4 invaderX,invaderY=(ra.randint(0,600)),45 invaderdx=15 beamX,beamY=300,300 beamdy=-30 #ロケット def rocket_move(event=None): global rocketX,rocketY,rocketdx canvas.delete("rocket") rocketX=rocketX+rocketdx canvas.create_image(rocketX,rocketY,image=imgrct,tag="rocket") def pushLeft(event): global rocketX,rocketdx if rocketX<15: rocketdx=0 else: rocketdx=-4 root.after(20,rocket_move) def pushRight(event): global rocketX,rocketdx if rocketX>585: rocketdx=0 else: rocketdx = 4 root.after(20,rocket_move) #インベーダー def invader_move(event=None): global invaderX,invaderY,invaderdx canvas.delete("corona1") if invaderX>=canvas.winfo_width()-15 or invaderX<15: invaderdx=-invaderdx invaderY+=10 if invaderY>canvas.winfo_height()-100: root.destroy() invaderX+=invaderdx canvas.create_image(invaderX,invaderY,image=imginv,tag="corona1") root.after(20,invader_move) #ビーム def beam_move(event=None): global rocket_move,beamY,beamdy canvas.delete("beam") beamY=beamY+beamdy if beamY<=0: pass else: canvas.create_rectangle(rocketX-3,beamY-10,rocketX+3,beamY+10,fill="yellow",tag="beam") root.after(20,beam_move) def pushEnter(event): root.after(20,beam_move) root=tk.Tk() root.geometry("600x400") canvas=tk.Canvas(root,width=600,height=400,bg="black") canvas.place(x=0,y=0) root.bind("<Left>",pushLeft) root.bind("<Right>",pushRight) root.bind("<Return>",pushEnter) canvas.focus_set() imgrct = ImageTk.PhotoImage(file="rocket.png") imginv = ImageTk.PhotoImage(file="corona1.png") imgbem = ImageTk.PhotoImage(file="beam_pink.png") root.after(20,rocket_move) root.after(20,invader_move) root.mainloop()

試したこと

色々調べた見ましたが結局何もわからないままです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

画像ファイルが無くてコードが実行できないので、方針のみの回答です。

  • 現状一つのモノを複数にするには、リストや辞書、集合(set)等のコンテナデータ型を使います。
  • 現状では全てグローバル変数なので、管理しやすくするためにクラスに纏めると良いです。
  • それぞれ個別にタイマーを廻してますが、フレーム更新周期でタイマーを実行し

 その中で、「描画処理」 rocket,invader,beamの移動と、ユーザ入力の「イベント処理」を行います。
現状のenterキー入力では、タイマーが不必要に多重に実行されてる状態です。

class役割属性補足
Rocket自機x, y
Invader敵機x, y
Beamビームx, yrocket の座標を結ぶ直線

Beam については2点の座標を扱うので検討の余地がありますが、
とりあえず、共通する属性を基底クラスに纏めます。
各クラスには、フレーム更新周期毎に呼び出す共通のメソッドを定義する。
例えば、フレーム毎の x, y 座標の遷移をその中で実装。

python

1# XXX: このコードは不完全です。実装イメージ。 2 3@dataclass 4class Unit: 5 x: int 6 y: int 7 item: str 8 9class Rocket(Unit): 10 11 v = 4 # 移動量 12 13 def update(self): 14 # NOTE: canvas.create_xxx の戻り値を item として控えておくと 15 # 再度 delete/create_xxx は不要で、coords で座標のみ移動できます。 16 canvas.coords(self.item, self.x, self.y) 17 18 def moveLeft(self): 19 self.x = max(0, self.x - self.v) 20 21 def moveRight(self): 22 # NOTE: WIDTH はキャンバスの幅、外部で定義しておく 23 self.x = min(WIDTH, self.x + self.v) 24 25# TODO: class Invader 26# TODO: class Beam 27 28 29rocket = Rocket(300, 300, canvas.create_image(300, 300, image=...) 30all_units = [rocket, Invader(...), Invader(...), Invader(...)] 31 32def tick(): 33 34 # Rocket クラスと Invader クラスに update() メソッドを実装すると、 35 # 以下のようなコードで、纏めて呼び出す事が出来ます。 36 37 for unit in all_units: 38 unit.update() 39 40 ... # 略、ビーム等も同様に処理する。 41 42 root.after(20, tick) # FPS=50 フレーム更新周期(20ms)毎に呼び出し 43 44 45root.after_idle(tick) 46root.mainloop()

ビームも同様に、複数同時に射出したいならリスト等で管理します。
但し、ビームの始点がロケットの現在座標になってる為、
ビーム発射時の座標に変更したい場合は、始点終点(x1,y1,x2,y2)座標をする等の対応が必要です。


追記: コード実行するまで解らなかった部分ですが、
ビームは一度しか打てないようになってたのですね。
ここは比較的容易に対応できます。ロケットが縦(y軸)方向に動かない前提で

python

1# beam_move関数内 2 3 if beamY<=0: 4 beamY = 300 5 else: 6 canvas.create_rectangle(rocketX-3,beamY-10,rocketX+3,beamY+10,fill="yellow",tag="beam") 7 root.after(20,beam_move)
  • ビームが画面外に出た時、beamY を初期化
  • タイマーは継続しない (インデントを深くし else: ブロックの中に入れます)

2回目のビームが画面に表示されない原因は、beamY の値が 0 のままなので
描画のコードは実行され続けているが、画面に見える範囲に無い状態となっていました。

投稿2021/07/11 11:00

編集2021/07/11 21:18
teamikl

総合スコア8681

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

__n.um

2021/07/11 11:53

とても丁寧な説明ありがとうございます!! もう一つ質問があるのですが、リストの使い方がまだよくわかっていないのでどのようにしてリストを作ってビームを複数発射すればいいでしょうか?
teamikl

2021/07/11 12:23

まずは今取り組んでる問題とは別に、リストについて基本的な使い方を調べましょう。 all_units = [rocket, Invader(...), Invader(...), Invader(...)]   ... の部分は略してますが、[ ~ ] の表記でリストを作成してます。  ここでは、[自機, 敵1, 敵2, 敵3] のリストと仮定して for unit in all_units:   unit.update() でリストの内容それぞれについて update() を呼び出してます。 リストに後から要素を追加する append メソッドがあるので、 キーを押したときにビームのオブジェクトを追加し、 ビームも同様に update() を呼び出して座標を更新、という流れです。
__n.um

2021/07/11 14:33

本当に丁寧な説明ありがとうございます。 しかし、今の私の未熟な知識ではteamiklさんの説明が理解することができません。 また知識たくさん蓄えていきたいと思います。 本当に色々教えていただきありがとうございます!!!
teamikl

2021/07/12 04:16 編集

前提知識として必要な、「リスト」と「クラス」「オブジェクト」について。チュートリアルより - https://docs.python.org/ja/3/tutorial/introduction.html#lists - https://docs.python.org/ja/3/tutorial/datastructures.html#more-on-lists - https://docs.python.org/ja/3/tutorial/classes.html 現状のコードの問題点は、それぞれのデータを「グローバル変数」で管理してる為、 「変数が一つしかない」ので、2つ目の複数の敵・ビームを作りたくても、 管理する変数が増えてしまうような構造となってます。 課題は、これを複数対応にする事 ... → リストやクラスを使った複数のデータの管理 が問題解決に繋がるはずです。 手順としては、現在ひとつのオブジェクトのデータに変数を2~3使っているのを STEP1) x,y座標 移動量を持つひとつのオブジェクトとして扱えるように「クラス」として実装する。 STEP2) オブジェクトを複数作り、「リスト」で管理する。 質問のコードを元に変更する場合、プログラムの構造的な問題なので、 一部を少し変えて対応という訳にはいかず、大幅な変更が必要になってくるので 解決の方針 として回答しましたが、 より具体的な点については、サンプルコードを元に学習するのが良いかもしれません。 【Python】Tkinterを使ってシューティングゲーム制作 https://qiita.com/ell/items/f0f74865c07710f1eab8 上の私の回答で説明したものとは異なる実装ですが、変数 enemies の「リスト」で敵を管理。 弾(Bullet)はリストで管理してませんが、「オブジェクト」として生成し、 グローバル変数では管理せずに、タイマーに登録することで管理する方法を取ってます。
__n.um

2021/07/12 14:28

本当に本当にありがとうございます!!! ここまで優しく教えて下さったり、上記のチュートリアルの記事もとても分かりやすくてteamiklさんには感謝しかありません!!! teamiklさんが丁寧に教えてくださるおかげで少しリストやクラスの基礎がわかりました! 何回も試行錯誤して頑張りたいと思います! 未熟な私にわかりやすく教えて下さり、ありがとうございました!!!
smuasher

2021/07/13 03:00

仮定について話している人もいますが、こういうことを見つけた人がいるのでうまくいきました。このプログラミング言語の周りでこの種のスピンを変えることができる人はいますか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問