![インベーダー](https://assets.st-note.com/production/uploads/images/10341816/rectangle_large_type_2_b00ba61bda5ecd342fb66404512d4b60.png?width=1200)
【python】Pyxelでシューティングゲームを作る(続)
前回ソースをとりあえず丸ごと乗っけてみたので、今回からは各部分に分けて内容をメモしていこうと思います。
from random import randint
import pyxel
モジュールをインポートします。pyxelを使うのでpyxelは当然ですね。あとはランダム関数を使いたいのでrandamからrandintをインポートします。
WINDOW_H = 120
WINDOW_W = 160
SHIP_H = 16
SHIP_W = 16
これは…ウィンドウサイズと宇宙船のサイズを決めたんですよね、たぶん。
class APP:
def __init__(self):
self.IMG_ID0 = 0
self.IMG_ID1 = 1
self.IMG_ID0_X = 60
self.IMG_ID0_Y = 65
self.game_over = False
self.game_end = False
self.boss_flug = False
self.boss_count = 1
self.score = 0
self.shots = []
self.enemys = []
self.boss_hp = 500
self.bombs = []
self.p_ship = Ship()
メインのクラスとなるAPPクラスです。最初はいろいろな変数を定義します。[]になっているところはリストです。shots(自機弾)、enemys(敵)、bombs(爆発エフェクト)はいっぱい出てくるので、リストで管理します。
pyxel.init(WINDOW_W, WINDOW_H, caption="Hello Pyxel")
# ドット絵を読み込む
pyxel.load("/Users/xxxxxxxx/pyxel/image/sample.pyxel")
pyxel.mouse(False)
pyxel.run(self.update, self.draw)
pyxel.initでメインウィンドウを作成します。その後、付属のpyxeleditorで作ったドット絵を読み込んでいます。ファイルパスは該当の.pyxelファイルを直接指定しています。pyxeleditorについては別の機会にまとめてみたいと思います。mouseをfalseにしてカーソルを非表示、runでゲームが開始されます。ゲームはupdateとdrawの繰り返しで進行していきます。
(例)自機が移動する:位置情報を更新(update)→描画(draw)
def update(self):
if pyxel.btnp(pyxel.KEY_Q):
pyxel.quit()
#自機の更新
if self.game_over == False:
self.p_ship.update(pyxel.mouse_x, pyxel.mouse_y)
shot_count = len(self.shots)
for j in range (shot_count):
if self.shots[j].pos_y > 10:
self.shots[j].pos_y = self.shots[j].pos_y - 3
else:
del self.shots[j]
break
update関数に入ります。最初のif文ではQが押された場合にゲームを終了できるようにしています。その後、自機の位置をマウス座標で更新して、自機弾の更新に移ります。
弾の更新はfor文で行っています。y座標は下に行くほど大きくなるので、上に進ませたい場合はマイナスする必要があります。画面内のすべての弾に対して-3をして、もしもy座標が10より小さくなったら、弾のオブジェクトを破棄しています。ウィンドウの天井に近づいたら消える処理です。
#当たり判定
#敵と弾
shot_hit = len(self.shots)
for h in range (shot_hit):
enemy_hit = len(self.enemys)
for e in range (enemy_hit):
if ((self.enemys[e].ene_x <= self.shots[h].pos_x
<= self.enemys[e].ene_x + 20)and(self.enemys[e].ene_y
<= self.shots[h].pos_y <= self.enemys[e].ene_y + 20)):
#敵に当たったらその座標に爆発を乗せる
new_bomb = Bomb(self.enemys[e].ene_x,
self.enemys[e].ene_y)
self.bombs.append(new_bomb)
del self.enemys[e]
if self.boss_flug == False:
self.score = self.score + 100
break#敵に当たったらbreak
else:
continue
break#敵に当たったらbreak
#敵と自機
enemy_atk = len(self.enemys)
for e in range (enemy_atk):
if (((self.enemys[e].ene_x >= self.p_ship.ship_x) and
(self.enemys[e].ene_x <= self.p_ship.ship_x + 15) and
(self.enemys[e].ene_y >= self.p_ship.ship_y) and
(self.enemys[e].ene_y <= self.p_ship.ship_y + 15))or
((self.enemys[e].ene_x + 15 >= self.p_ship.ship_x) and
(self.enemys[e].ene_x + 15 <= self.p_ship.ship_x + 15) and
(self.enemys[e].ene_y + 15 >= self.p_ship.ship_y) and
(self.enemys[e].ene_y + 15 <= self.p_ship.ship_y + 15))):
self.game_over = True
new_bomb = Bomb(self.p_ship.ship_x, self.p_ship.ship_y)
self.bombs.append(new_bomb)
当たり判定です。ごちゃごちゃになってしまいました…。基本的には弾と敵でも、敵と自機でも、x座標とy座標が対象の領域に入ったかを見ています。もっといい書き方がある気がするのですが…難しいです。当たった場合には爆発を乗っけています。敵は倒すと100点加算です。
#enemy update
if self.game_end == False:
if self.boss_flug == False:
if pyxel.frame_count % 20 == 0:
new_enemy = Enemy()
self.enemys.append(new_enemy)
else:
if pyxel.frame_count % 8 == 0:
new_enemy = Enemy()
self.enemys.append(new_enemy)
enemy_count = len(self.enemys)
for e in range (enemy_count):
enemy_vec1 = randint(0, 7)
enemy_vec2 = enemy_vec1 % 2
if self.enemys[e].ene_y < 115:
if enemy_vec2 > 0:
self.enemys[e].ene_x = self.enemys[e].ene_x + 4
self.enemys[e].ene_y = self.enemys[e].ene_y + 1.5
else:
self.enemys[e].ene_x = self.enemys[e].ene_x - 4
self.enemys[e].ene_y = self.enemys[e].ene_y + 1.5
else:
del self.enemys[e]
break
#画面の爆発が3以上になったら古いものから消していく
if len(self.bombs) > 3:
del self.bombs[0]
敵の更新です。frame.countが20で割り切れる場合に敵を追加で降らせます。20フレームごとに敵が出てくる感じですね。敵は左右にランダムに揺れながら下に降りてきます。ウィンドウの底に近づくと消えます。
boss_flugがTrue(ボス出現時)になると敵はそのままボスの弾になり、発生間隔が8フレームごとになります。ボスの弾は敵とは別に作ってみたいですね。
#ボス出現フラグ
if self.boss_flug == False: #ボス未出現の状態で
if self.score != 0: #ゲーム開始直後ではなく
if self.score % 2000 == 0: #スコアxxxx点に達したら
if self.game_end == False: #ゲームクリアフラグがない場合にボス発生
self.boss_flug = True
self.boss_hp = 200 * self.boss_count
#ボスの当たり判定
if self.boss_flug == True:
shot_hit = len(self.shots)
for h in range (shot_hit):
if ((70 <= self.shots[h].pos_x <= 85)and
(10 <= self.shots[h].pos_y <= 20)):
self.boss_hp = self.boss_hp - 1
new_bomb = Bomb(self.shots[h].pos_x, self.shots[h].pos_y)
self.bombs.append(new_bomb)
#ボス消滅
if self.boss_hp <= 0:
if self.boss_flug == True:
self.score = self.score + 5000
pyxel.cls(0)
self.boss_flug = False
#self.game_end = True
self.boss_count = self.boss_count + 1
ボス関連の部分です。2000点ごとにボスが出現します。インベーダーは1体100点なので、20体倒すごとですね。自機弾をfor文で処理してボスの弱点に当たったか判定しています。ちょっとここは手抜きになってしまいましたね…。座標も固定ですし。
ボスを倒したら、5000点加算して、フラグをfalseに戻します。また、boss_countを+1します。ボスの体力は200×boss_countなので、だんだんタフになる仕組みです。
ちょっと長くなりましたので、次のnoteに分けます。次は描画のdraw関数と、各オブジェクトについて書きたいと思います。
ここまで読んでいただきありがとうございます!