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

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

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

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

Q&A

解決済

1回答

1122閲覧

レーザーを真っ直ぐ上方に飛ばしたい。

sakuramochiIII

総合スコア8

Python

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

0グッド

0クリップ

投稿2021/06/04 06:10

前提・実現したいこと

Python(pyxel)でインベーダーゲーム(シューティングゲーム)を作っています。
スペースキーを押すことでレーザーが発生し、画面上方にまっすぐ飛んでいく機能を実装中なのですが、発生したレーザーがその場ですぐに消えてしまいます。ちなみにエラーメッセージは出ていません。
laserに関する記述を色々試行錯誤しているのですが、どうしても「上方に飛ばす」という動作になりません。アドバイスを頂きたいです。

発生している問題・エラーメッセージ

エラーメッセージ(無し)

該当のソースコード

Python

1import pyxel 2 3WINDOW_H = 120 4WINDOW_W = 160 5PLAYER_H = 20 6PLAYER_W = 19 7 8class Vec2: 9 def __init__(self, x, y): 10 self.x = x 11 self.y = y 12 13class player: 14 def __init__(self, img_id): 15 self.pos = Vec2(0, 0) 16 self.vec = 0 17 self.img_player = img_id 18 19 def update(self, x, y, dy): 20 self.pos.x = x 21 self.pos.y = y 22 self.vec = dy 23 24class kadai: 25 def __init__(self,img_id): 26 self.pos = Vec2(0, 0) 27 self.img_player = img_id 28 29 def update(self, x, y): 30 self.pos.x = x 31 self.pos.y = y 32 33class test: 34 def __init__(self,img_id): 35 self.pos = Vec2(0, 0) 36 self.img_player = img_id 37 38 def update(self, x, y): 39 self.pos.x = x 40 self.pos.y = y 41 42class Laser: 43 def __init__(self): 44 self.pos = Vec2(0, 0) 45 self.vec = 0 46 self.size = 2 47 self.speed = 3 48 self.color = 10 # 0~15 49 50 def update(self, x, y, dy, size, color): 51 self.pos.x = x 52 self.pos.y = y 53 self.vec = dy 54 self.size = size 55 self.color = color 56 57class App: 58 def __init__(self): 59 self.IMG_ID0 = 0 60 self.IMG_ID1 = 1 61 # self.IMG_ID2 = 2 62 63 pyxel.init(160, 120, caption="Students Wars") 64 pyxel.image(0).load(0,0,"./player_a-2.png") 65 66 #Starting Point 67 self.player_x = 70 68 self.player_y = 101 69 70 # make instance 71 self.mplayer = player(self.IMG_ID1) 72 self.Lasers = [] 73 74 pyxel.run(self.update, self.draw) 75 76 def update(self): 77 if pyxel.btnp(pyxel.KEY_Q): 78 pyxel.quit() 79 self.update_player() 80 81 # ====== ctrl Laser ====== 82 if pyxel.btnp(pyxel.KEY_SPACE): 83 new_laser = Laser() 84 new_laser.update(self.mplayer.pos.x + PLAYER_W/2 , 85 self.mplayer.pos.y + PLAYER_H/2 , 86 self.mplayer.vec, new_laser.size, new_laser.color) 87 self.Lasers.append(new_laser) 88 89 laser_count = len(self.Lasers) 90 for i in range(laser_count): 91 if 0 < self.Lasers[i].pos.x and self.Lasers[i].pos.x < WINDOW_W: 92 # Laser update 93 self.Lasers[i].update(self.Lasers[i].pos.x, 94 self.Lasers[i].pos.y - self.Lasers[i].speed, self.Lasers[i].vec, self.Lasers[i].size, self.Lasers[i].color) 95 else: 96 del self.Lasers[i] 97 break 98 99 def update_player(self): 100 if pyxel.btn(pyxel.KEY_LEFT) or pyxel.btn(pyxel.GAMEPAD_1_LEFT): 101 self.player_x = max(self.player_x - 2, 0) 102 103 if pyxel.btn(pyxel.KEY_RIGHT) or pyxel.btn(pyxel.GAMEPAD_1_RIGHT): 104 self.player_x = min(self.player_x + 2, pyxel.width - 20) 105 106 if pyxel.btn(pyxel.KEY_UP) or pyxel.btn(pyxel.GAMEPAD_1_UP): 107 self.player_y = max(self.player_y - 2, 0) 108 109 if pyxel.btn(pyxel.KEY_DOWN) or pyxel.btn(pyxel.GAMEPAD_1_DOWN): 110 self.player_y = min(self.player_y + 2, pyxel.height - 19 ) 111 112 def draw(self): 113 pyxel.cls(0) 114 pyxel.blt(self.player_x, self.player_y, 0, 0, 0,128,128,0) 115 116 117 # ======= draw player ======== 118 if self.mplayer.vec > 0: 119 pyxel.blt(self.mplayer.pos.x, self.mplayer.pos.y, self.IMG_ID1, 0, 0, -PLAYER_W, PLAYER_H, 5) 120 else: 121 pyxel.blt(self.mplayer.pos.x, self.mplayer.pos.y, self.IMG_ID1, 0, 0, PLAYER_W, PLAYER_H, 5) 122 123 # ====== draw Lasers ====== 124 for laser in self.Lasers: 125 pyxel.rect(self.player_x+9, self.player_y-2, laser.pos.x-7, laser.pos.y-5,10) 126 127App()

試したこと

class Laserのdef update()の中身のself.pos.yを、self.pos.y - self.Lasers[i].speedと置いてみましたが、[i]が使えないとエラーが出ました。(下記)

Python

1class Laser: 2 def __init__(self): 3 self.pos = Vec2(0, 0) 4 self.vec = 0 5 self.size = 2 6 self.speed = 3 7 self.color = 10 # 0~15 8 9 def update(self, x, y, dy, size, color): 10 self.pos.x = x 11 self.pos.y = y 12 self.vec = dy 13 self.size = size 14 self.color = color

自分の中では、おそらく訂正すべき箇所はdef updateの中身だろうと予想しておりますが、能力不足がゆえ成功できませんでしたので、皆様のご意見を頂きたいです。

補足情報(FW/ツールのバージョンなど)

ちなみにまだkadaiやtestに関する記述は未完成ですので、ご了承ください。
ここにより詳細な情報を記載してください。

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

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

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

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

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

guest

回答1

0

ベストアンサー

[i]が使えないとエラーが出ました。(下記)

エラー内容は IndexError ですか?

for 文内での del が問題で、
for ループ中にコンテナであるリストの長さが変わってしまいます。

i = 0,1,2,3,4 と繰り返しているのに、途中で3番目の要素が del されると
4 番目の要素が ​3 番目に繰り上がり、self.Lasers[4] はエラーになります。

簡単な対策は、コピーを繰り返す事です。

python

1for laser in self.Lasers[:]: # コピーを繰り返す 2 if 0 < laser.y < WINDOW_H: 3 laser.update(...) 4 else: 5 self.Lasers.remove(laser)

事前に省いておく方法もあります

python

1self.Lasers = [laser for laser in self.Lasers if 0 < laser.y < WINDOW_H] 2 3for laser in self.Lasers: 4 laser.update(...)

python

1if 0 < self.Lasers[i].pos.x and self.Lasers[i].pos.x < WINDOW_W:

縦方向なのに、x軸(横方向)?で判定してるのが気になる点。

Python では、条件式 if A < B and B < C:if A < B < C: と書けます。


  • self.mplayer.pos が更新されていない → 移動時は self.player_x, self.player_y が変化

======= draw player ======== の部分のコードが機能してない。
pyxel.blt(self.player_x, self.player_y, 0, 0, 0,128,128,0) が、
カーソルで動かせる自機画像になってます。

  • Laser の pos の初期値が常に 0, 0
  • pyxel.rect(x, y, width, height) です。

 第3,4 を終点の座標と勘違いしていないか確認。
少なくともレーザーの幅がレーザーの位置で変わるのはおかしいはずです。

width, height は負の値は取れないようなので、
始点の y 座標を時間経過で変化するようにします。

投稿2021/06/04 08:25

teamikl

総合スコア8760

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

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

sakuramochiIII

2021/06/04 12:30

コメントありがとうございます。 ご指摘の箇所、訂正させていただきました。 そこでさらに質問なのですが、時間経過によりy座標を変化させるという部分で、どのように記述すれば動くかが自分で試行錯誤しても検討つきませんでした。こちらの能力不足で大変申し訳ありませんが、例えばsどのような記述ができるかご教授いただけないでしょうか。
teamikl

2021/06/04 16:02

pyxel.rect(x, y, width, height) に渡す引数を列挙してみましょう (画面に表示や、ファイルに書き出して確認) # rect(x, y, width, height) 始点~終点 (speed 1, 時機を 10,10 固定とした時) 01: rect(10, 9, 1, 1) # 10, 9 ~ 10, 10 02: rect(10, 8, 1, 2) # 10, 8 ~ 10, 10 03: rect(10, 7, 1, 3) # 10, 7 ~ 10, 10 04: rect(10, 6, 1, 4) # 10, 6 ~ 10, 10 : 解りにくければ、rect() に渡す変数を始点・終点で指摘できるように x1, y1, x2, y2 -> x, y, width, height に変換する関数を作ると良いです。 # 始点を (10, 10) 固定とするとレーザーの描画される範囲が # 解りやすくなるはず。(※ 線の太さは計上してません) # 始点 x1, y1 終点 x2, y2 01: (10, 10, 10, 9) 02: (10, 10, 10, 8) 03: (10, 10, 10, 7) 04: (10, 10, 10, 6) 実際のコードをどのようにするかは - レーザーの長さ - レーザー発射中に時期を動かした時の x 座標 (発射時の座標に固定 or 時機に追従) 等、実装したい挙動・仕様をどうするかにより変わってきます。 laser.pos.y が時間経過により変化しますが、 実装したい挙動次第では、発射時点の y 座標の情報も必要です。 # 例 pyxel.rect(発射時のx座標, laser.pos.y, 1, (laser.pos.y + 発射時のy座標)) pyxel.rect(player_x, laser.pos.y, 1, (laser.pos.y + player_y)) # 追従する場合
sakuramochiIII

2021/06/04 16:31

コメントありがとうございます。おっしゃる通りに記述したところ、playerの頭上から画面の上までレーザーが伸びました。ですが、頭上からレーザーが消えなかったのが気になって自分で試行錯誤しましたが、どうすることもできませんでした。 例えば、一直線のレーザーではなく、サイコロのように四角形のレーザーを飛ばした場合、コードはどのように変わるのでしょうか。 質問ばかり連続してしまって、本当に申し訳ありません。
teamikl

2021/06/04 21:48 編集

> 頭上からレーザーが消えなかった は、始点を自機の座標固定としている為です。細かな挙動は詰めていく必要があります。 「レーザーに長さを設定する」 「スペースキーを押していた間の長さにする」 等、様々なバリエーションが考えられます。 # 始点 x1,y2 終点 x2,y2 の時の 始点の y 座標をずらす方法を考えましょう。 # 例: 長さ2 の場合 01: (10, 10, 10, 9) 02: (10, 10, 10, 8) 03: (10, 9, 10, 7) 04: (10, 8, 10, 6) 05: (10, 7, 10, 5) ※ x, y, width, height とした時は、先のコメントに書いたように 始点と終点が逆転します。 x1, y1, x2, y2 → rect(x2, y2, 1, min(レーザーの長さ, y2-y1)) 簡易的には、rect(x, y, width, height) の height を min(height, レーザーの長さ) に変更。 他に、頭上からではなく、発射時の座標にしたい場合は、 laser.pos の変化させる値とは別に、発射時の座標を記録しておく必要があります。 >サイコロのように四角形のレーザーを飛ばした場合 四角形のレーザーがどのようなものかイメージできませんでした。光線というよりは弾?
teamikl

2021/06/04 19:24

> 頭上からレーザーが消えなかったのが気になって もし、時間が長く経っても残り続ける場合 一点、気になったのは回答でも触れた点、レーザーを消すときの条件式を確認して見て下さい。 質問のコードでは x 座標をチェックしてましたが、 レーザーは y 座標を変化させているので、x座標のチェックでは消えません。
sakuramochiIII

2021/06/05 06:06

コメントありがとうございます。 簡易的には、rect(x, y, width, height) の height を min(height, レーザーの長さ) に変更。 という部分ですが、heightは2,レーザーの長さも2なので、結果的にmin()の値は2にしかならないと思ったのですが、これだとおかしいのでしょうか。
teamikl

2021/06/05 06:25

laser の y の値は減り続ける → y2(時機座標)-y1(レーザーの座標) = height の値は 時機を基準とした場合は、増え続けます。 (ステップ 01 ~ 05 の座標の範囲で説明する為に、敢えてレーザの長さを短めに説明しました) 細部については、どのような仕様のレーザーを表現したいか次第なので、 「明らかにおかしい・間違っている」という事はありません。 レーザーの長さを固定長にするなら固定でもよいです。 長いレーザーを打ちたければ、長さの制限を増やします。
teamikl

2021/06/05 06:46

見直して気付いたけど、もしかすると例として提示した height (rect への第4引き数) の計算が間違ってたかもしれません。 (今試せないので確認は取れません) (laser.pos.y + 発射時のy座標) → (-laser.pos.y + 発射時のy座標) (laser.pos.y + player_y) → (-laser.pos.y + player_y) 基準座標は固定なので、基準座標 - レーザーのy座標 でした。
sakuramochiIII

2021/06/05 13:12

コメントありがとうございます。 発射時の座標は、どのように記録すれば良いのでしょうか。 試行錯誤しましたが全くわかりませんでしたので、申し訳ありませんがご教授願います。
teamikl

2021/06/05 13:21

laser オブジェクト生成時に palyer_x, player_y を laser オブジェクト内に記録しておきます 現状でも laser.pos に保持してますが、ここの値は時間経過で y 座標が動くので、 別の変数にも記録します。
sakuramochiIII

2021/06/05 23:49

ありがとうございます。無事上手く動きました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問