20回目 Gpt4oを最大限活用したGODOTゲーム作り(3か月チャレンジ 3週目)

お疲れ様です。gyagyaです!
あっという間に3週が経ちました。。
今回は3か月チャレンジの進捗報告をさせていただきます。

はじめに

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

進捗状況

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


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


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

2.調査してみた!


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

https://godotengine.org/asset-library/asset/2729

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

Combatノード

3.コードを確認する

次に具体的にコード内容を確認していきます。
※MITライセンス

extends Node


signal combat_finished(winner, loser)


func initialize(combat_combatants):
	for combatant in combat_combatants:
		combatant = combatant.instantiate()
		if combatant is Combatant:
			$Combatants.add_combatant(combatant)
			combatant.get_node("Health").dead.connect(_on_combatant_death.bind(combatant))
		else:
			combatant.queue_free()
	$UI.initialize()
	$TurnQueue.initialize()


func clear_combat():
	for n in $Combatants.get_children():
		n.queue_free()
	for n in $UI/Combatants.get_children():
		n.queue_free()


func finish_combat(winner, loser):
	combat_finished.emit(winner, loser)


func _on_combatant_death(combatant):
	var winner
	if not combatant.name == "Player":
		winner = $Combatants/Player
	else:
		for n in $Combatants.get_children():
			if not n.name == "Player":
				winner = n
				break
	finish_combat(winner, combatant)

3-1.初期化処理 func initialize(combat_combatants)

まずGPT4oに聞いてみます。
※途中で途切れちゃってますが。。

了解しました。この `combat.gd` スクリプトは、Godotエンジンを使用したターン制バトルシステムの管理を行うものです。各関数が何をしているのか、順を追って詳しく解説します。

### スクリプト全体の概要

このスクリプトは以下の機能を持っています:
1. 戦闘の初期化
2. 戦闘キャラクターの管理
3. 戦闘の終了処理
4. キャラクターの死亡処理

### 各部分の詳細解説

#### extends Node

```gdscript
extends Node
```

- この行は、このスクリプトが `Node` クラスを拡張していることを示します。`Node` はGodotの基本的な要素であり、シーンツリーの一部として機能します。

#### signal combat_finished(winner, loser)

```gdscript
signal combat_finished(winner, loser)
```

- これはシグナルを定義しています。シグナルは特定のイベントが発生したときに他のノードに通知するための仕組みです。
- `combat_finished` シグナルは、戦闘が終了したときに `winner`(勝者)と `loser`(敗者)を引数として発信されます。

#### initialize(combat_combatants)

```gdscript
func initialize(combat_combatants):
for combatant in combat_combatants:
combatant = combatant.instantiate()
if combatant is Combatant:
$Combatants.add_combatant(combatant)
combatant.get_node("Health").dead.connect(_on_combatant_death.bind(combatant))
else:
combatant.queue_free()
$UI.initialize()
$TurnQueue.initialize()
```

- この関数は、戦闘を初期化するためのものです。`combat_combatants` は戦闘に参加するキャラクターのリストです。
- `for` ループで各キャラクターをインスタンス化し、`Combatant` クラスのインスタンスであれば `Combatants` ノードに追加します。
- 各キャラクターの `Health` ノードが持つ `dead` シグナルが `_on_combatant_death` 関数に接続されます。
- 最後に、`UI` ノードと `TurnQueue` ノードの `initialize` メソッドを呼び出します。

##### 詳細なステップ

1. **ループ処理 (`for combatant in combat_combatants`)**
   - `combat_combatants` リスト内の各キャラクターを順に処理します。

2. **インスタンス化 (`combatant = combatant.instantiate()`)**
   - 各キャラクターをインスタンス化します。この操作により、キャラクターのコピーが生成されます。

3. **タイプチェック (`if combatant is Combatant`)**
   - キャラクターが `Combatant` クラスのインスタンスであるかを確認します。
   - もし `Combatant` クラスのインスタンスであれば、`Combatants` ノードに追加します。

4. **シグナル接続 (`combatant.get_node("Health").dead.connect(_on_combatant_death.bind(combatant))`)**
   - 各キャラクターの `Health` ノードが持つ `dead` シグナルを `_on_combatant_death` 関数に接続します。
   - `bind(combatant)` により、このキャラクターが死亡したときに特定のキャラクター情報を持った状態で `_on_combatant_death` 関数が呼び出されます。

5. **インスタンスの解放 (`combatant.queue_free()`)**
   - インスタンスが `Combatant` クラスのインスタンスでない場合、そのインスタンスを解放します。

6. **UIとターンキューの初期化**
   - `$UI.initialize()` と `$TurnQueue.initialize()` により、UIとターンキューの初期化を行います。

#### clear_combat()

```gdscript
func clear_combat():
for n in $Combatants.get_children():
n.queue_free()
for n in $UI/Combatants.get_children():
n.queue_free()
```

- この関数は、戦闘が終了した後に呼び出され、戦闘に関する全てのノードを解放します。
- `Combatants` ノード

GPT4o

これを参考に、でも誤っていそうなところは無視します。

①combatantのリストを作成します。
for combatant in combat_combatants:
ここで、combat_combatantsとは初期処理の引数にもたせています。
func initialize(combat_combatants):
※ちなみに呼び出し元はgame.tscnのfunc start_combat(combat_actors):でした。これは詳細は長くなるので省略しますが関数内にcombat_screen.initialize(combat_actors)があるのですがこれによってcombat.gdの初期処理が呼び出されています。また、combat_screenには以下のようにCombatノードが割り当てられていました。
@export var combat_screen: Nodeように定義すると以下が追加されて、割り当て可能状態になります。

combat_screen: Nodeの割り当て

②インスタンス化します。
combatant = combatant.instantiate()

③クラスの確認を行います。仮に、正しくインスタンス化されていない、またはcombatantがNullなどの場合はelseの処理に移ります。
if combatant is Combatant:

④戦闘員の追加を行います。
$Combatants.add_combatant(combatant)
add_combatantは以下のようになっています。
これにより実際のキャラクターを表示させることができます。
位置を決定していますね。

extends Node2D

func add_combatant(new_combatant):
new_combatant.position.x += 200 * get_child_count()
add_child(new_combatant)

new_combatant.position.x += 200 * get_child_count()により位置を定め、
add_child(new_combatant)によりCombatantsの子ノードとして戦闘員(PlayerとEnemy)を追加します。

⑤体力の取得と管理
combatant.get_node("Health").dead.connect(_on_combatant_death.bind(combatant))
これは初学者には分かりにくい、、が、GPT4oの力により解明しました。
combatant.get_node("Health")はcombatant.tscnのHealthノードの情報を取得しているっぽいです。combat.tscnにはそのノードがないので分かりにくかったです。

combatant.tscn

さらにHelth.gdではsignal deadを取得していて〜っと、、
かなり長くなりましたので続きは次回以降で!
※書いてる途中で体調を崩してしまいました、、、

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