
ジャンケン NPC同士を戦わせる
これまで、何体かNPCを作りましたが、結局、どういう思考パターンがジャンケンで一番強いのか検証したいと思います。
まずは、NPC同士で戦えるようにします。
今日のハイライト

プレイヤーとCPU ➡ 1Pと2Pに変更
シーンの構造を変更
これまで、プレイヤーはキー入力、CPUは思考パターンによる乱数で1~3までの数字を出力してきましたが、NPC同士で戦わせる場合は、両方が、それぞれの思考パターンによる乱数を出力します。
プレイヤーとCPUという分け方にはならなくなるため、1Pと2Pにし、分かれていた処理を、同じスクリプトの「Characterシーン」で処理を行うようにしました。

Characterシーンのスクリプト
初期設定
・1Pと2P両方で使うCPUキャラクターのパスを設定
・1Pと2Pの表示のパスを設定
・必要な変数の定義
extends Node
# 初期設定
# 親ノード
@onready var main = get_node("../")
# charのパス
@onready var chr0 = $Human # プレイヤー
@onready var chr1 = $CPU_Boy
@onready var chr2 = $CPU_Gu
@onready var chr3 = $CPU_Choki
@onready var chr4 = $CPU_Pa
@onready var chr5 = $CPU_History
@onready var chr6 = $CPU_MindHack
@onready var chr7 = $CPU_Fortune
# 1p,2pのパス
@onready var name1 = $"HUD/1PName"
@onready var name2 = $"HUD/2PName"
@onready var hand1 = $"HUD/1PHand"
@onready var hand2 = $"HUD/2PHand"
var p1 = "" # 1Pキャラのパスを入れる変数
var p2 = "" # 2Pキャラのパスを入れる変数
var selected_1p = false # 1Pが選択したか?フラグ
var selected_2p = false # 2Pが選択したか?フラグ
キャラを設定
・1P、2Pのキャラを設定し、絵と名前を表示する
# キャラ選択
func _ready() -> void:
# 1Pと2Pのキャラ選択
p1 = chr1
p2 = chr2
# chr0だったら1Pはプレイヤー、それ以外はCPU
if p1 == chr0:
Global.ai_on = false
else:
Global.ai_on = true
# 1pと2pのキャラ表示
p1.show()
p1.position = Vector2(90,150)
p2.show()
p2.position = Vector2(550,150)
name1.text = "1P:" + p1.chr_name
name2.text = "2P:" + p2.chr_name
プレイヤーのキー入力
・キー入力を数字に変換し、1P入力フラグを立てて、2Pの手の選択へ
# プレイヤーの手をキー入力で取得
func _process(delta: float) -> void:
if not Global.game_set and not Global.ai_on:
if Input.is_action_just_pressed("gu"):
Global.player_hand = 1 # グーは1
elif Input.is_action_just_pressed("choki"):
Global.player_hand = 2 # チョキは2
elif Input.is_action_just_pressed("pa"):
Global.player_hand = 3 # パーは3
else:
return # どのキーも押されていない場合は何もしない
Global.input_janken_key = true
selected_1p = true
select_2p_hand()
1PがNPCの場合の手を選択
・Mainシーンからの指示で、1PのCPUへ手をリクエストし2Pの手の選択へ
# 1PがNPCの場合の手をリクエスト
func select_1p_hand():
if not selected_1p and Global.ai_on == true: # ai_onの時はCPUへリクエスト
Global.whose_request = 1 # 1Pからのリクエストであると設定
p1.choose_number() # 1Pに設定されたCPUに1Pの手をリクエスト
selected_1p = true # 1Pの手を選択済みにする
select_2p_hand() # 2Pの手の選択へ
2Pの手を選択
・2PのCPUへリクエスト
# 2Pの手をリクエスト
func select_2p_hand():
if not selected_2p:
Global.whose_request = 2
p2.choose_number()
selected_2p = true
CPUから手を受け取る
・リクエストを出していたCPUから手を受け取る
・1Pと2Pの手の表示を指示し、MainシーンのJudgeへ手を返す
# CPUから手を受け取る
func hand_count():
print("hand count")
update_hand() # 1Pと2Pの手を表示
main.judge() # メインで勝敗判断
selected_2p = true # 選択済みフラグをON
プレイヤーとCPUの手を表示
・手を表示する前に一瞬消すことで、同じ手を出しても分かるように変更
・1Pと2Pの手を表示
# プレイヤーとCPUの手を表示
func update_hand():
var text_direct = {1: "✊", 2: "✌", 3: "✋"} # 数字とCPUの手を定義
hand1.hide()
hand2.hide()
await get_tree().create_timer(0.05).timeout
# 1Pの手を表示
hand1.show()
hand1.text = text_direct[Global.player_hand] # YourHandラベルに表示
# 2Pの手を表示
hand2.show()
hand2.text = text_direct[Global.cpu_hand] # CPUHandラベルに表示
Mainシーンの変更
ジャンケンコールの修正
・janken_call関数の最後にプレイヤーの手をAIに任せる処理を追加
#プレイヤーの手をAIに任せる
if Global.ai_on == true:
character.select_1p_hand()
Globalシーンへ変数を追加
var ai_on = false # 手をAIに任せるかどうかのフラグ
var whose_request = 0 # 1P,2Pどちらのリクエストかのフラグ
新キャラを追加
完全にランダムの手を出すNPC

NPC同士のジャンケン対決
グーを出しがちな鉄拳グーに対して、完全な乱数で対抗するナオト。
ナオトが先行しますが途中でグーが逆転、しかし、最後はナオトが再逆転で勝利しました。

次はキャラクターセレクト画面を作ります
ジャンケンは簡単だしプログラムの練習に丁度良いですね。
誰かのお役に立てれば幸いです。