23回目 Gpt4oを最大限活用でポケモン風RPGを作るチャレンジ(3か月チャレンジ 4週目)

お疲れ様です。gyagyaです!
基本的に週末しか時間とれていないです、、くそう、、
今回は3か月チャレンジの進捗報告をさせていただきます。

はじめに

現在バトルシーンを作成中です!
ただ、現状簡単なUIを作成しただけでまだボタン処理がなにもありません。

今日の目的
★前回同様、ボタン押下時の処理について理解すること
としています。
分からないことはとにかくGPT4oに聞きまくります。


1.ボタン押下時の処理実装方法が分からない!!


ボタンを押下した際の処理をどのようにして作成すべきか悩んでいます。
バトルシーンにまとめて書くべきか、ボタンそれぞれに書くべきか。。
SOLID原則から考えるとどのように書くのが適切か。
そもそもどんな風に記述すればいいのか、、(笑)

2.調査してみた!


今回はGODOTアセットから参考になりそうなものを取得しました。

戦闘に関するノードを確認してみましょう。
これを見る限りでは目的は比較的はっきり分けられているように見えます。
つまりCombatノードで全て記述するのではないということですね。
※とはいってもCombatノードには色々組み込まれているのでそれは分けた方が良い可能性があります。
・Combatノード   :戦闘の初期化、戦闘の勝利/敗北などを管理する
・UIノード       :ボタン押下時の処理を管理する
・Combatantsnノード  :戦闘員を表示させる


さて、ここまでは前回の復習でした。
以下、続きになります。
結局、戦闘開始→自分のターン開始→例えば攻撃ボタン押下→相手にダメージ→ターン終了→相手の攻撃→自分にダメージ→ターン終了までの一連の流れが知りたいので、次に攻撃ボタン押下時の処理に進みます。

まず、CombatノードにはUIノードがありました。

UIノード

ここにはAttackボタンがあり、シグナルが接続されているそうです。(Wifiみたいなマーク)
UIのスクリプトを抜粋します。

①宣言

extends Control


@export var combatants_node: Node
@export var info_scene: PackedScene

例のごとくですがUIにもノードとシーンがアタッチされていました。これらを利用するための宣言です。

ノードとシーンがアタッチ

②初期化

func initialize():
	for combatant in combatants_node.get_children():
		var health = combatant.get_node("Health")
		var info = info_scene.instantiate()
		var health_info = info.get_node("VBoxContainer/HealthContainer/Health")
		health_info.value = health.life
		health_info.max_value = health.max_life
		info.get_node("VBoxContainer/NameContainer/Name").text = combatant.name
		health.health_changed.connect(health_info.set_value)
		$Combatants.add_child(info)
	$Buttons/GridContainer/Attack.grab_focus()

ここで体力やキャラクターの情報を取得しています。
毎回呼ばれているのかな?と推測しています。

③攻撃ボタン押下時

func _on_Attack_button_up():
	if not combatants_node.get_node("Player").active:
		return
	combatants_node.get_node("Player").attack(combatants_node.get_node("Opponent"))

これがまさに攻撃ボタンになります。今回はここを掘り下げます。
といっても実際はわずか数行しかありません。
if not combatants_node.get_node("Player").active:は単にプレイヤーがアクティブ状態かどうかを判断し、異なればリターンしているだけです。
重要なのはcombatants_node.get_node("Player").attack(combatants_node.get_node("Opponent"))になります。

・combatants_nodeノードからPlayerノードを再び取得し、そのattackメソッドを呼び出します。
・attackメソッドの引数として、combatants_nodeノードから取得したOpponentノードを渡します。

GPT4o

つまり、こーいうことです。
Combatantsノード、すなわちキャラクター取得ノード(今回はPlayerノードを取得しています。)を呼び出します。
呼び出されたPlayerノードによって、以下のattack(target):メソッドを実行します。初期値ではdamageが1で固定されていました。

class_name Combatant
extends Node


signal turn_finished

@export var damage: int = 1
@export var defense: int = 1

var active = false: set = set_active



func set_active(value):
	active = value
	set_process(value)
	set_process_input(value)

	if not active:
		return
	if $Health.armor >= $Health.base_armor + defense:
		$Health.armor = $Health.base_armor


func attack(target):
	target.take_damage(damage)
	turn_finished.emit()


func consume(item):
	item.use(self)
	turn_finished.emit()


func defend():
	$Health.armor += defense
	turn_finished.emit()


func flee():
	turn_finished.emit()


func take_damage(damage_to_take):
	$Health.take_damage(damage_to_take)
	$Sprite2D/AnimationPlayer.play("take_damage")

ここまででかなり理解が深まってきました。
このように見ると、思ってた以上に役割分担がしっかりされていてとてもいいコード、SOLIDの原則従ったコードに見えました。

おまけ

最後にちょっとパラメータをいじってみましょう。
healthノードのInspecterを見るとMaxLifeをいじれるのでいじります。
100を500とかにしときます。

すると以下のようになります。

実際のゲーム画面

あれれ?ゲーム開始時点で体力がねえ、、
と思いきや、この500とはあくまでも体力ゲージが500まであるというだけで初期の体力が少ないためでした。
実際には以下のPlayer.gdに記載されています。
ここでmax_lifeを500にすると自動的にInspecterの体力が変わります。
これが@exportで定義するメリットなのですね

@exportについて

次こそ!!


ゲーム画面

おやおや?!変わってない?!
でも相手から攻撃を受けた瞬間に表示されました。

ゲーム画面

原因は初期化時に防御力しか受け取ってなかったからでした。

armorしかない

そこで以下のように初期体力と体力を分けて定義し、初期処理で初期体力を取得します。

initial_life=500を取得。

これでどうだ?!

ゲーム画面

とほほ、、、だが諦めない。
そもそもこの体力ゲージはどこから来るのか探ります。
まず、UIが確実に怪しいのでUIから調査。GPT4oに尋ねると
「var info = info_scene.instantiate()」にあるんじゃね?って教えてくれました。めっちゃ便利。。

var info = info_scene.instantiate()にありそう

info_scene: PackedSceneとされていて、Info.tscnに移動。

Info.tscnを発見!

ここでMaxValueを500、Valueを500に変更してみます。


MaxValueを500、Valueを500

すると、、、

ゲーム画面

できた!!やったー!
ということでひとまず終わります。
次回はこの技術を参考に実装に進んでいこうと思います。
結論:詰まったらGPT4oが最強。

この記事が気に入ったらサポートをしてみませんか?