Pyxel 敵キャラクターを動かす

ゲームAIにチャレンジする

 Python向けレトロゲームエンジン「Pyxel(ピクセル)」で,敵キャラクターが自動的に動く処理を作りたいと思います。作業記録の意味合いが強く学習者向けの記事ではありませんが,ゲーム作成に役立ちそうな処理については紹介したいと思います。

想定しているゲーム

 上から見たタイプのアクションアドベンチャーゲームで,上下左右の方向にキャラクターが移動するものです。以前紹介したシューティングゲームPyxel シューティングゲーム(前編) - 勉強ボックス管理者ブログ」の作りが基本になっています。
 シューティングゲームでは,時間経過で次々と敵機が出現して決められた移動を行いました。今度のゲームでは敵キャラクターがプレイヤーの方向へ進むなど自動的に動く方法を考えます。

プレイヤーキャラクターのいる方向を取得する

 Pyxelのatan2()命令を使うと,敵キャラクターから見たプレイヤーキャラクターの方向(角度)が取得できます。atan2はアークタンジェント2とよみます)

pyxel.atan2(y, x) # y, x の引数の順序に注意

 
 atan2()命令で取得できる角度は,画面下側がプラスの角度,画面上側がマイナスの値になります。
 


マウス位置の方向を向くサンプルコード

import pyxel
pyxel.init(128,128)

player_x = 0
player_y = 0
enemy_x = 64
enemy_y = 64
deg = 0

def update():
    global player_x,player_y,deg
    player_x = pyxel.mouse_x
    player_y = pyxel.mouse_y
    x = player_x - enemy_x
    y = player_y - enemy_y
    deg = pyxel.atan2(y,x)
    return

def draw():
    pyxel.cls(0)
    pyxel.text(player_x,player_y, "P", 7)

    r = 16
    pyxel.circb(enemy_x,enemy_y, r, 10)
    x = pyxel.cos(deg)*r
    y = pyxel.sin(deg)*r
    pyxel.circ(x+enemy_x,y+enemy_y, 2, 3)

    pyxel.text(1,1, str(deg), 7)
    return

pyxel.run(update,draw)

実行結果

x = pyxel.cos(deg)*r
y = pyxel.sin(deg)*r

atan2()命令で取得した角度を使って,敵キャラクターの位置からプレイヤーキャラクターの方向へ進むには,cos()の値でx座標,sin()の値でy座標を取得します。(例での変数 r の代わりに進みたい距離をかけると移動量を算出できます)

 

ゲームAIについて

 AIというと,自然な文章でチャットに答えてくれたり,プロのような絵を描いてくれたりするAIが話題ですが,ここでのゲームAIは「敵キャラクターの意思決定処理」です。ゲーム内のキャラクターの行動をプログラミングで作成します。

【参考サイト】
 ・【記事更新】教養知識としてのAI 〔第3回〕ゲームとAI – 人工知能学会 (The Japanese Society for Artificial Intelligence)
  連載:「教養知識としてのAI」〔第3 回〕ゲームとAI
(マンガ部分を大きく読めるpdfファイルがダウンロードできます)

 ・アーティクル ゲームAI の原点『パックマン』はいかにして生み出されたのか?:岩谷 徹インタビューパックマンの仕様書も掲載されています)

 ・ゲームAI -基礎編- 『知識表現と影響マップ』 | Cygames Engineers' Blog (画面のアニメーションでゲームAIのイメージがつかみやすいと思います)

 ・ディジタルゲームにおける人工知能技術の応用の現在(<特集>エンターテイメントにおけるAI)

 ・ゲームAI入門(前半)

 <2023-04-24追記>
 下記ページは三宅陽一郎さんがゲームAIの発展の歴史がわかるようにブックマークを並べたもので,2022年11月の記事です。興味を持たれた方はこちらを参考にされると良いかと思います。

 ・【記事更新】私のブックマーク「ディジタルゲームの人工知能の歴史的変遷─ルールベースからディープラーニングまで」 – 人工知能学会 (The Japanese Society for Artificial Intelligence)

 
【参考文献】
 三宅 陽一郎(2012)「はじめてのゲームAI 」( WEB+DB PRESS Vol.68 特集3 )技術評論社

ルールベースAIの実装

 敵キャラクターの行動をif文で表せるルールで決定する処理で,「プレイヤーキャラクターが目の前に来たら,その方向に移動する」敵キャラクターの処理を作成してみます。

・敵キャラクターには上下左右の現在の向きを持たせ,一部の敵には各方向のドット絵も用意しました。

 【ソースコードgame/pyxel/gameAI_01 at main · benkyoubox/game · GitHub
  フォルダ内の enemy.py で EnemyWonderingクラスの ai()メソッドが移動先決定の処理です。

enemy.py 抜粋
※ai()の引数 px py はプレイヤーキャラクターの中心座標です。

def ai(self,px,py):
    # ルールベースAI

    dx = dy = 0
    if self.cnt % 4 != 0 :
        return dx,dy

    speed = self.speed + pyxel.rndi(0,2)
    u,v,w,h,col = self.getdata()
    x = self.x + abs(w)//2
    y = self.y + abs(h)//2
    pdir,deg = self.getdir(px-x,py-y)

    # 攻撃されていたらプレイヤーの方向を向いてデータ再取得
    if self.life < self.life_last :
        self.life_last = self.life
        self.dir = pdir
        u,v,w,h,col = self.getdata()
        x = self.x + abs(w)//2
        y = self.y + abs(h)//2

    # 範囲内にプレイヤーがいれば近づく
    dist = self.sensor
    if self.dir == DIR_UP :
        cx = x - dist // 2
        cy = y - dist
    elif self.dir == DIR_DOWN:
        cx = x - dist // 2
        cy = y
    elif self.dir == DIR_LEFT:
        cx = x - dist
        cy = y - dist // 2
    elif self.dir == DIR_RIGHT:
        cx = x
        cy = y - dist // 2

    if cx <= px <= cx + dist and cy <= py <= cy + dist:
        self.dir = pdir
        dx = pyxel.cos(deg)*speed
        dy = pyxel.sin(deg)*speed

    # プレイヤーが近くにいない場合前進
    if dx == 0 and dy == 0 and self.cnt % 8 == 0:
        r = pyxel.rndf(0,1.0)
        if 0 < r < 0.6:
            if self.dir == DIR_UP :
                dy = -speed
            elif self.dir == DIR_DOWN :
                dy = speed
            elif self.dir == DIR_LEFT :
                dx = -speed
            elif self.dir == DIR_RIGHT :
                dx = speed
        elif 0.8 < r:
            # 一定の割合で方向転換
            self.dir = pyxel.rndi(0,3)
    
    chk = 0
    if self.chkwall(dx,0) :
        dx = 0
        chk += 1
    if self.chkwall(0,dy) :
        dy = 0
        chk += 1
    if 1 < chk:
        self.dir = pyxel.rndi(0,3)

    return dx,dy

・敵キャラクターの向いている方向に矩形の範囲を設定して,その中にプレイヤーキャラクターの座標が入ったかどうかを判定します。

・範囲内にいる場合は,プレイヤーの方向への移動量を算出します。

 【Webブラウザでのデモ】
 http://benkyoubox.starfree.jp/100_game/10_pyxel/actadv/actadv02_enemy.html
 上下左右方向キーで移動してスペースキーで攻撃。



関連記事

kinutani.hateblo.jp