プレイヤーから弾を撃つ処理を追加してみましょう。
画面上のオブジェクトは GameObject クラスで表現することにしたので、弾も同様に GameObject を継承して作ってみます。
追加が必要なポイントは
- プレイヤーが弾を撃ったら新しい GameObject (弾) を生成したい
- 「弾を撃つ」ために、スペースキーが「押された」判定を取得したい
の2点ですね。
まず、「弾」ですが、Shot クラスを作ります。
on_update で、初期状態で決めておいた self.speed に従って上方向に進むようにして、画面外に出たら削除します。Shot はこれだけ。簡単ですね。
class Shot(GameObject):
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
lines = (
((0, 0), (0, 16)), # 縦に1本、16ピクセルで弾を表示
)
color = (0, 230, 0)
self.create_lines(lines, color)
self.speed = 160
def on_update(self, dt):
d = dt * self.speed
self.y += d
# 画面外判定
if self.y >= WINDOW_HEIGHT + 16:
self.is_dead = True
super().on_update(dt)
次は、プレイヤーの on_update で
- スペースキーが押された判定を取得する
- スペースキーが押されたら Shot クラスを作る
を書きます。肝になる部分は以下のところです。
space_key_status = key_status[key.SPACE]
if space_key_status and not self.prev_space_key_status:
new_game_objects.append(Shot(self.x, self.y + 16))
self.prev_space_key_status = space_key_status
キーが「押されている」判定は key_status[] に入っています。「前回の on_update では押されていなくて、今回の on_update で押されている」ことがわかれば入力判定が取れますね。本当は何か別のところで Press 判定取るようにした方が良いのでしょう…。とりあえずプレイヤーの中でやってしまいました。
次に、新しく作った GameObject の処理についてです。いったん new_game_objects に積んで、次のフレームから on_update が回るようにしてみました。
久々に画面写真を載せてみますが、こんな感じです。菱形がプレイヤー、棒が弾、三角が敵…となっています。デザインがいまいちなのが盛り上がりませんが、なんとなくゲームっぽい?
import pyglet
from pyglet import shapes
from pyglet.window import key
from collections import defaultdict
import random
import copy
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 = []
new_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)
game_objects.extend(new_game_objects)
new_game_objects.clear()
pyglet.clock.schedule_interval(on_update, 1 / 120.0)
class Shot(GameObject):
def __init__(self, x, y):
super().__init__()
self.x = x
self.y = y
lines = (
((0, 0), (0, 16)), # 縦に1本、16ピクセルで弾を表示
)
color = (0, 230, 0)
self.create_lines(lines, color)
self.speed = 160
def on_update(self, dt):
d = dt * self.speed
self.y += d
# 画面外判定
if self.y >= WINDOW_HEIGHT + 16:
self.is_dead = True
super().on_update(dt)
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)
self.prev_space_key_status = False
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
space_key_status = key_status[key.SPACE]
if space_key_status and not self.prev_space_key_status:
new_game_objects.append(Shot(self.x, self.y + 16))
self.prev_space_key_status = space_key_status
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()