ゲーム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
ゲームAIについて
AIというと,自然な文章でチャットに答えてくれたり,プロのような絵を描いてくれたりするAIが話題ですが,ここでのゲームAIは「敵キャラクターの意思決定処理」です。ゲーム内のキャラクターの行動をプログラミングで作成します。
【参考サイト】
・【記事更新】教養知識としてのAI 〔第3回〕ゲームとAI – 人工知能学会 (The Japanese Society for Artificial Intelligence)
連載:「教養知識としてのAI」〔第3 回〕ゲームとAI(マンガ部分を大きく読めるpdfファイルがダウンロードできます)
・アーティクル ゲームAI の原点『パックマン』はいかにして生み出されたのか?:岩谷 徹インタビュー (パックマンの仕様書も掲載されています)
・ゲームAI -基礎編- 『知識表現と影響マップ』 | Cygames Engineers' Blog (画面のアニメーションでゲーム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
上下左右方向キーで移動してスペースキーで攻撃。