弾を撃てたら、次は何か弾に当たるものが欲しいです。
早速やってみました。
テキストで頑張るのも限界なのでしょうか。スクショにするとさっぱりわからないですね。
まずは敵らしきオブジェクトを16体作ってみます。
座標を適当にランダムで散らして…。プレイヤーと初期状態で重ならないように、y座標は画面の上半分。左右をちょっとだけ空けて。
enemies = []
for i in range(0, 16):
enemy_x = random.randint(8, window.width - 16)
enemy_y = random.randint(window.height//2, window.height - 16)
enemy = pyglet.text.Label('[x_x]', font_name='Courier New', font_size=12,
x=enemy_x, y=enemy_y,
anchor_x='center', anchor_y='center')
enemies.append(enemy)
そうしたらon_draw()にenemiesも追加してやると描画されるんですよね。
次は当たり判定! いろいろやり方はあるし、こだわりも多いところですけれど。一番単純なやり方は矩形の判定でしょうか。
# 弾と敵の当たり判定
for enemy in enemies:
if abs(bullet.x - enemy.x) < 24 and abs(bullet.y - enemy.y) < 8:
removed_enemies.append(enemy)
removed_bullets.append(bullet)
弾のループの中で、全ての敵と座標を比較します。縦横それぞれ一定の範囲内(これだと横24ピクセル・縦8ピクセル内)に居たら、当たったとして敵も弾も消します、と。
この方法だと弾や敵の移動速度がものすごく速かったり、範囲が小さかったりすると当たり抜けしますよね。後で真面目に移動量を考えるか、もっと単純に dt や当たり判定の大きさ・速度に制限を設けてしまうなどを考えることにしましょうか。処理の実験としてはまあいいでしょう。
消したい敵とか弾をいったん removed_enemies や removed_bullets に入れているのはループ中だからですね。そういえば、Python でループ中に要素消したらどういう挙動になるんだろう? 後で確認しておかないと。
だんだん長くなってきてしまいましたが、今日の全体のリストはこんな感じになりました。この辺までくると、弾や敵の処理をちょっとまとめてみたいとか、敵を動かすためには敵の動きも dt に従って update したいとか、いろいろ見えてきますね。次はその辺に手を付けるつもりです。
import pyglet
from pyglet.window import key
from collections import defaultdict
import math
import random
window = pyglet.window.Window()
player = pyglet.text.Label('<->',
font_name='Courier New',
font_size=12,
x=window.width//2, y=window.height//4,
anchor_x='center', anchor_y='center')
bullets = []
def fire():
bullet = pyglet.text.Label('I', font_name='Courier New', font_size=12,
x=window.width//2, y=window.height//2,
anchor_x='center', anchor_y='center')
bullet.x = player.x
bullet.y = player.y
bullets.append(bullet)
enemies = []
for i in range(0, 16):
enemy_x = random.randint(8, window.width - 16)
enemy_y = random.randint(window.height//2, window.height - 16)
enemy = pyglet.text.Label('[x_x]', font_name='Courier New', font_size=12,
x=enemy_x, y=enemy_y,
anchor_x='center', anchor_y='center')
enemies.append(enemy)
@window.event
def on_draw():
window.clear()
player.draw()
for bullet in bullets:
bullet.draw()
for enemy in enemies:
enemy.draw()
key_status = defaultdict(bool)
@window.event
def on_key_press(symbol, modifiers):
key_status[symbol] = True
if symbol == key.SPACE:
fire()
@window.event
def on_key_release(symbol, modifiers):
key_status[symbol] = False
# プレイヤー移動速度(1 秒あたりのピクセル数)
MOVE_SPEED = 320
# 弾の速度
BULLET_SPEED = 480
# 上下左右ボタンの定義
BUTTON_UP = 0x01
BUTTON_RIGHT = 0x02
BUTTON_LEFT = 0x04
BUTTON_DOWN = 0x08
def update(dt):
dx = 0
dy = 0
d = dt * MOVE_SPEED
bit_count = 0
button = 0
if key_status[key.W]: # up
button |= BUTTON_UP
bit_count += 1
dy = d
elif key_status[key.S]: # down
button |= BUTTON_DOWN
bit_count += 1
dy = -d
if key_status[key.A]: # left
button |= BUTTON_LEFT
bit_count += 1
dx = -d
elif key_status[key.D]: # right
button |= BUTTON_RIGHT
bit_count += 1
dx = d
# 同時にキーが複数押されているなら斜め移動なので、速度を下げる。
# Python 3.10 なら bit_count が使える
# bit_count = button.bit_count()
if bit_count == 1:
player.x += dx
player.y += dy
elif bit_count == 2:
player.x += dx / math.sqrt(2)
player.y += dy / math.sqrt(2)
# bullet 位置を更新し、範囲外のオブジェクトは削除
removed_bullets = []
removed_enemies = []
for bullet in bullets:
bullet.y += dt * BULLET_SPEED
if bullet.y > window.height:
removed_bullets.append(bullet)
continue
# 弾と敵の当たり判定
for enemy in enemies:
if abs(bullet.x - enemy.x) < 24 and abs(bullet.y - enemy.y) < 8:
removed_enemies.append(enemy)
removed_bullets.append(bullet)
for to_remove in removed_bullets:
if to_remove in bullets:
bullets.remove(to_remove)
for to_remove in removed_enemies:
if to_remove in enemies:
enemies.remove(to_remove)
pyglet.clock.schedule_interval(update, 1 / 120.0)
pyglet.app.run()