敵を追加して、弾との当たり判定を追加

弾を撃てたら、次は何か弾に当たるものが欲しいです。

早速やってみました。

テキストで頑張るのも限界なのでしょうか。スクショにするとさっぱりわからないですね。

まずは敵らしきオブジェクトを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()

タイトルとURLをコピーしました