プレイヤーが動くようになったので、今度は敵を作ります。
- シンプルな敵(上から出てきて画面下方向に移動するだけ)のクラスを作る
- 下方向に行って、画面外に出たら消える
- 一定時間ごとに敵を生成する
このくらいを目標に作業しましょう。
まずはシンプルな敵は簡単ですね。この前作った GameObject を使って作れば何も問題ありません。
- pygletは画面の下が y=0 なので、下に移動させるときは self.y を減らす
ことに注意です(忘れてて逆に移動してしまった…)。
class Enemy0(GameObject):
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
self.speed = 80
# 三角
lines = (
((-16, 16), (16, 16)), ((16, 16), (0, -16)), ((0, -16), (-16, 16)),
)
color = (230, 230, 0)
self.create_lines(lines, color)
def on_update(self, dt):
d = dt * self.speed
self.y -= d
super().on_update(dt)
さて、画面外に出たら消える、を書かないとですね。pyglet のサンプルを真似して書いてしまうのが良さそうです。つまり…
- GameObject クラスに「死んだかどうか(is_dead)」を用意する
- GameObject の on_update の後で、is_dead になったオブジェクトを消す
とすればシンプルに書けそうです。
敵が画面外(敵の大きさは中心から縦横16ピクセルで作っているから、32ピクセルです。余裕を持って32で画面外)に出たら is_dead を Trueに。
def on_update(self, dt):
d = dt * self.speed
self.y -= d
if self.y < -32:
self.is_dead = True
super().on_update(dt)
GameObject の更新ループの後、is_dead を見て削除。リストの内包表記ありましたねー。久しぶりに書きました。
for game_object in game_objects:
game_object.on_update(dt)
for dead_object in [obj for obj in game_objects if obj.is_dead]:
game_objects.remove(dead_object)
あとは、「一定時間ごとに敵を生成する」ですね。ゲーム全体の時間を管理する何かが居てくれたらいいのかな。安直に Game クラスというのを作って、on_update で更新してもらいましょう。
class Game():
def __init__(self):
self.enemy_timer = 0
def on_update(self, dt):
self.enemy_timer += dt
if self.enemy_timer > 1.0:
self.enemy_timer = 0
enemy_size = 32
enemy_x = random.randint(enemy_size, WINDOW_WIDTH - enemy_size)
enemy_y = WINDOW_HEIGHT + enemy_size
enemy = Enemy0(enemy_x, enemy_y)
game_objects.append(enemy)
dt に今回のデルタタイムが「秒」単位で入ってくるので、加算していきます。経過時間が1秒を超えたら、画面上部の見えないところに敵を生成するように書きました。
下に移動する敵なので、いい感じに画面の上から出てくるようになります。
まとめるとこんな感じです! だいぶ長くなりましたけれど、まだ 150 行程度だからなんとかなるでしょう。
import pyglet
from pyglet import shapes
from pyglet.window import key
from collections import defaultdict
import random
title = "Pyglet Shooting Game"
WINDOW_WIDTH = 640
WINDOW_HEIGHT = 480
window = pyglet.window.Window(WINDOW_WIDTH, WINDOW_HEIGHT, title)
batch = pyglet.graphics.Batch()
LINE_WIDTH = 2
PLAYER_SPEED = 120
@window.event
def on_draw():
window.clear()
batch.draw()
key_status = defaultdict(bool)
@window.event
def on_key_press(symbol, modifiers):
key_status[symbol] = True
@window.event
def on_key_release(symbol, modifiers):
key_status[symbol] = False
game = None
game_objects = []
class GameObject():
def __init__(self):
self.is_dead = False
self.line_shapes = []
self.lines = None
self.x = 0
self.y = 0
def create_lines(self, lines, color):
self.lines = lines
for line in lines:
x1 = self.x + line[0][0]
y1 = self.y + line[0][1]
x2 = self.x + line[1][0]
y2 = self.y + line[1][1]
self.line_shapes.append(shapes.Line(x1, y1, x2, y2, width=LINE_WIDTH, color=color, batch=batch))
def on_update(self, dt):
for (line, line_shape) in zip(self.lines, self.line_shapes):
line_shape.x = self.x + line[0][0]
line_shape.y = self.y + line[0][1]
line_shape.x2 = self.x + line[1][0]
line_shape.y2 = self.y + line[1][1]
def on_update(dt):
game.on_update(dt)
for game_object in game_objects:
game_object.on_update(dt)
for dead_object in [obj for obj in game_objects if obj.is_dead]:
game_objects.remove(dead_object)
pyglet.clock.schedule_interval(on_update, 1 / 120.0)
class Player(GameObject):
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
# 菱形
lines = (
((0, 16), (8, 0)), ((8, 0), (0, -16)),
((0, -16), (-8, 0)), ((-8, 0), (0, 16))
)
color = (0, 230, 0)
self.create_lines(lines, color)
def on_update(self, dt):
d = dt * PLAYER_SPEED
dx = 0
dy = 0
if key_status[key.W]: # up
dy = d
elif key_status[key.S]: # down
dy = -d
if key_status[key.A]: # left
dx = -d
elif key_status[key.D]: # right
dx = d
self.x += dx
self.y += dy
super().on_update(dt)
class Enemy0(GameObject):
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
self.speed = 80
# 三角
lines = (
((-16, 16), (16, 16)), ((16, 16), (0, -16)), ((0, -16), (-16, 16)),
)
color = (230, 230, 0)
self.create_lines(lines, color)
def on_update(self, dt):
d = dt * self.speed
self.y -= d
if self.y < -32:
self.is_dead = True
super().on_update(dt)
class Game():
def __init__(self):
self.enemy_timer = 0
def on_update(self, dt):
self.enemy_timer += dt
if self.enemy_timer > 1.0:
self.enemy_timer = 0
enemy_size = 32
enemy_x = random.randint(enemy_size, WINDOW_WIDTH - enemy_size)
enemy_y = WINDOW_HEIGHT + enemy_size
enemy = Enemy0(enemy_x, enemy_y)
game_objects.append(enemy)
def create_player():
player = Player(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 3)
game_objects.append(player)
def create_game():
global game
game = Game()
create_game()
create_player()
pyglet.app.run()