SRPGタクティカルゲーム開発チュートリアル
Part 2: ユニットの作成
Godot Engine バージョン4.4
このチュートリアルでは、Godot 4.4を使用してファイアーエムブレム風の SRPG(シミュレーションRPG)を作成する方法を学びます。キャラクターユニットの基礎構造を構築し、プレイヤー・敵ユニットの情報と動作を定義します。
デモプレイはこちらから!
アセットのダウンロードはこちらから!
このパートで学ぶこと
Resource
を使ったユニットデータ(名前・HP・攻撃など)の定義
Node2D
ベースのキャラクターユニットシーンの作成
- スプライト表示、マウス選択、移動アニメーションの制御
- 行動済み・未行動の管理と表示
始める前にはグローバルにシグナルを送受信する仕組み
始める前にグローバルにシグナルを送受信する仕組みを作成します。
signal_bus.gdというスクリプトを作成します。下記のコードを追加します。
extends Node
# ユニットが選択されたときに発信されるシグナルsignal unit_selected(unit)
# ユニットが移動したときに発信されるシグナルsignal unit_moved(is_move)
次に Project SettingsのGlobalsに先ほど作成したスクリプトをSignalBusという名前で追加します。
1. UnitDetail
リソースの作成
まず、各ユニットの基本情報を定義するための
Resource
スクリプト unit_detail.gd
を作成します。extends Resourceclass_name UnitDetail
# ユニットの種類を定義(プレイヤー or エネミー)enum UnitType { PLAYER, ENEMY,}
# ユニット名@export var unit_name: String# ユニットのタイプ(プレイヤー or エネミー)@export var unit_type: UnitType# 攻撃力@export var unit_attack: int# 防御力@export var unit_defense: int# 最大HP@export var unit_max_health: int# 現在のHP@export var unit_health: int# 移動範囲@export var unit_move_range: int# 攻撃範囲@export var unit_attack_range: int# クリティカル発生率@export var unit_critical_rate: int# クリティカル時のダメージ倍率@export var unit_critical_damage: int# スプライトアニメーション@export var unit_sprite: SpriteFrames
この
Resource
を使って、プレイヤーや敵のステータスを個別に管理します。
2. キャラクターユニットシーンの構成
画像の通り、
CharacterUnit
シーンは以下のノード構成になっています:
CharacterUnit (Node2D)├─ AnimatedSprite2D├─ Area2D └─ CollisionShape2D
AnimatedSprite2D
:移動・待機などのアニメーション表示用 Area2D
+ CollisionShape2D
:マウス入力(クリック・ホバー)の検出用
3. CharacterUnit
スクリプトの内容
character_unit.gd
では、ユニットの表示や状態管理を実装します。
@toolextends Node2Dclass_name CharacterUnit
# ユニットの詳細情報(名前・ステータス・スプライト等)@export var unit_detail: UnitDetail: set(value): unit_detail = value # スプライト情報があればAnimatedSprite2Dに反映 if value && value.unit_sprite && $AnimatedSprite2D: $AnimatedSprite2D.frames = value.unit_sprite # nullの場合はスプライトをクリア if value == null: $AnimatedSprite2D.frames = null notify_property_list_changed() @export var move_range = 3 # 移動可能範囲@export var move_speed = 0.2 # 移動速度@export var tile_size = Vector2i(16, 16) # タイルサイズ
@onready var animation: AnimatedSprite2D = $AnimatedSprite2D
var tween: Tweenvar is_selected = false # 選択状態var is_action_done = false # 行動済みフラグvar previous_position: Vector2 # 直前の位置
func _ready(): # Area2Dのinput_eventシグナルに接続 $Area2D.input_event.connect(_on_input_event) # エネミーユニットの場合はスプライトを左右反転 if unit_detail && unit_detail.unit_type == UnitDetail.UnitType.ENEMY: animation.flip_h = true
# ユニット選択時の処理func _on_input_event(viewport, event, shape_idx): # 行動済みまたはHP0の場合は何もしない if unit_detail.unit_health <= 0 or is_action_done: return # マウスボタン押下で選択状態を切り替え、シグナル発行 if event is InputEventMouseButton and event.pressed: is_selected = !is_selected if is_selected: SignalBus.unit_selected.emit(self)
# 指定パスに沿ってユニットを移動func move_path(path: Array[Vector2i]): previous_position = position # プレイヤーユニットの場合、移動開始シグナル発行 if unit_detail.unit_type == UnitDetail.UnitType.PLAYER: SignalBus.unit_moved.emit(true) for i in path.size(): # 既存のTweenが動作中なら停止 if tween && tween.is_running(): tween.kill() tween = get_tree().create_tween() var pos = path[i] * tile_size pos = pos + (tile_size / 2) pos = Vector2(pos.x, pos.y) tween.tween_property(self, "position", pos, move_speed) animation.play("move") await tween.finished animation.play("default") # プレイヤーユニットの場合、移動終了シグナル発行 if unit_detail.unit_type == UnitDetail.UnitType.PLAYER: SignalBus.unit_moved.emit(false)
# 行動状態をリセットfunc reset_action(): is_action_done = false
# 行動済み状態にし、HPが残っていればスプライトを暗くするfunc action_done(): is_action_done = true if unit_detail.unit_health != 0: $AnimatedSprite2D.self_modulate = Color(0.7, 0.7, 0.7, 1)
# スプライトの色を元に戻すfunc clear_action(): $AnimatedSprite2D.self_modulate = Color(1, 1, 1, 1)
✅ スクリプトのポイント
- スプライトアニメーションの読み込み(@tool対応)
@export var unit_detail: UnitDetail: set(value): unit_detail = value if value && value.unit_sprite && $AnimatedSprite2D: $AnimatedSprite2D.frames = value.unit_sprite if value == null: $AnimatedSprite2D.frames = null notify_property_list_changed()
- ユニットの選択処理(マウスクリックで選択状態を切り替え)
func _on_input_event(viewport, event, shape_idx): if unit_detail.unit_health <= 0 or is_action_done: return if event is InputEventMouseButton and event.pressed: is_selected = !is_selected if is_selected: SignalBus.unit_selected.emit(self)
- パスに沿った移動処理
func move_path(path: Array[Vector2i]): ... for i in path.size(): if tween && tween.is_running(): tween.kill() tween = get_tree().create_tween() var pos = path[i] * tile_size + (tile_size / 2) tween.tween_property(self, "position", pos, move_speed) animation.play("move") await tween.finished animation.play("default")
- 行動済みフラグの管理とスプライトの暗転
func action_done(): is_action_done = true if unit_detail.unit_health != 0: $AnimatedSprite2D.self_modulate = Color(0.7, 0.7, 0.7, 1)
func clear_action(): $AnimatedSprite2D.self_modulate = Color(1, 1, 1, 1)
✅ 補足ヒント
@tool
を使うことで、エディタ上で unit_detail
を設定すると自動的にスプライトが反映されます。 SignalBus
はグローバルにシグナルを送受信する仕組みで、他ユニットやUIとの連携に便利です。
次回は下記を実装します!
「ユニット配置と選択状態の管理」
マップにユニットを配置し、マウスで選んで移動させたり行動させたりするロジックを構築します!
1
100%