Compare commits
2
Commits
f3f0c080a9
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00631d94a | ||
|
|
344e5e221f |
No files matched your search
@@ -9,6 +9,11 @@
|
||||
| `46e5509` | マス選択の基本: GameMode / PlayerController / 固定カメラ、ホバーのハイライト、クリックで座標をログ出力 |
|
||||
| `3a80827` | マスの選択状態、カメラのグリッド中心への自動配置、`FGridMath`(座標計算) |
|
||||
| `b18c19a` | ユニットの配置、移動範囲の表示、移動の実行 |
|
||||
| `41709fd` | `NOTES.md` の追加 |
|
||||
| `f3f0c08` | ターン制(Player/Enemy)、2アクション制、ダッシュ、HUD |
|
||||
| `344e5e2` | 大きいグリッドでカメラが見切れる問題の修正(四隅の投影で距離を計算) |
|
||||
|
||||
リモート(`origin/main`)には `344e5e2` まで反映済み。
|
||||
|
||||
## 現在の構成
|
||||
|
||||
@@ -18,51 +23,74 @@
|
||||
- `FGridMath`: セル座標とワールド座標の変換、範囲判定、グリッド中心
|
||||
- `FGridState`: 1マスごとの歩行可否・高さ・占有ユニットID
|
||||
- `FGridPathfinder`: ダイクストラで到達可能マスを求め、経路を復元する(4方向、コストは `GetStepCost` で差し替え可)
|
||||
- `FTurnManager`: ターン数と陣営を持つ。`OnTurnStarted` / `OnTurnEnded` で通知する。状態は値型の `FTurnState`(セーブしやすい)
|
||||
- `FActionRules`: アクション種別ごとのコストと可否。Move=1消費、Dash=残り全部(満タンのときだけ可)
|
||||
- **データ層**
|
||||
- `FUnitStats`: 移動力など。`FTableRowBase` 継承で、将来DataTableに移せる
|
||||
- `FUnitStats`: 移動力(4)、1ターンの行動ポイント(2)、ダッシュ倍率(2.0)。`FTableRowBase` 継承で、将来DataTableに移せる
|
||||
- `TurnTypes.h`: `EFaction`(Player/Enemy)、`EActionType`(Move/Dash)
|
||||
- **Actor層**
|
||||
- `AGridCell`: マスの見た目。色の優先順位は 選択 > ホバー > 移動可能 > 通常
|
||||
- `AGridManager`: マス生成、`FGridState` の所有、ユニット生成、**占有情報の唯一の更新者**
|
||||
- `AUnit`: シリンダーの見た目。Tickは移動中だけ有効
|
||||
- `ATacticaCameraPawn`: グリッド中心を見る固定俯瞰カメラ(位置を自動計算)
|
||||
- `ATacticaPlayerController`: 入力と状態(`Idle` / `UnitSelected` / `Moving`)
|
||||
- `ATacticaGameMode`: `DefaultEngine.ini` の `GlobalDefaultGameMode` で指定
|
||||
- `AGridCell`: マスの見た目。色の優先順位は 選択 > ホバー > 移動可能(緑) > ダッシュ可能(橙) > 通常
|
||||
- `AGridManager`: マス生成、`FGridState` と `FTurnManager` の所有、ユニット生成、**占有情報の唯一の更新者**。ターン開始時に、その陣営の行動ポイントを回復する。敵ターンの拡張点は `RunEnemyTurn()`
|
||||
- `AUnit`: シリンダーの見た目、陣営の色(Player=青、Enemy=赤)、行動ポイント。Tickは移動中だけ有効
|
||||
- `ATacticaCameraPawn`: 固定俯瞰カメラ。グリッドの四隅が画面に収まる距離を自動計算する
|
||||
- `ATacticaPlayerController`: 入力と状態(`Idle` / `UnitSelected` / `Moving`)。左クリックで選択・移動、Enterでターン終了
|
||||
- `ATacticaHUD`: ターン数・陣営・選択中ユニットの残りアクションを描画する(状態を持たず、毎フレーム読むだけ)
|
||||
- `ATacticaGameMode`: `DefaultEngine.ini` の `GlobalDefaultGameMode` で指定。PlayerController / Pawn / HUD をコードで設定
|
||||
|
||||
## 操作とルール
|
||||
|
||||
- 左クリック: Playerのユニットを選択 → 緑(通常移動)/橙(ダッシュ)のマスをクリックで移動
|
||||
- Enter: ターン終了
|
||||
- 移動は1アクション消費。ダッシュ(移動力の2倍まで)は2アクションを全消費し、満タンのときだけ可
|
||||
- アクションが0のユニットは、選択できるが範囲は出ない
|
||||
- Enemyのユニットは、クリックしてもログが出るだけで操作できない
|
||||
- Playerの全ユニットのアクションが0になると、移動完了後に自動でターン終了
|
||||
- 敵ターンは仮実装: ログを出し、`EnemyTurnDelay`(1秒)待ってPlayerに戻る
|
||||
- 移動中と敵ターン中は、クリックとEnterを受け付けない
|
||||
- ターン数は、Playerのターンに戻るたびに+1
|
||||
|
||||
## 設計上の決定事項
|
||||
|
||||
- **マスの判定**: `WorldDynamic` のオブジェクトタイプに限定したレイ。地形(Landscape)がマスの上面と同じ高さにあり、Visibilityチャンネルだと判定が交互に切り替わったため。
|
||||
- **ハイライト**: `/Engine/BasicShapes/BasicShapeMaterial` の動的マテリアルで `Color` パラメータを変更する。新規アセットなし。
|
||||
- **入力**: `InputComponent->BindKey`。左クリックだけなので最小。操作が増えたら、C++でInputActionを実行時生成する方式への移行を検討する。
|
||||
- **ハイライトと陣営色**: `/Engine/BasicShapes/BasicShapeMaterial` の動的マテリアルで `Color` パラメータを変更する。新規アセットなし。
|
||||
- **入力**: `InputComponent->BindKey`(左クリック、Enter)。操作が増えたら、C++でInputActionを実行時生成する方式への移行を検討する。
|
||||
- **ターン管理の置き場所**: 純粋C++の `FTurnManager` を `AGridManager` が所有する。GameModeやGameStateには置かない(論理を純粋C++に寄せる方針、Actorクラスの追加を避けるため)。
|
||||
- **占有情報**: 移動の開始時に確定させる。見た目は後から追いかける。
|
||||
- **行動ポイント**: 持ち主はユニット(`AUnit::ActionPoints`)。消費は移動の開始時、ターン自動終了の判定は移動の完了後。
|
||||
- **ユニットの当たり判定**: `NoCollision`。マスのクリック判定を邪魔しないため。クリック先のユニットは占有情報から引く。
|
||||
- **歩行可否**: マス生成時に `AGridCell::bIsWalkable` を `FGridState` へコピーし、以後は `FGridState` を正とする。
|
||||
- **画面表示**: `AHUD::DrawText`。UMGやデバッグメッセージは使わない。
|
||||
|
||||
## 未確認・既知の注意点
|
||||
|
||||
- ユニット機能(`b18c19a`)は、**ビルドは通っているが、エディタでの動作確認は未実施**。
|
||||
- カメラ距離は長辺基準の近似。画面比によっては端が切れる可能性がある。
|
||||
- ターン制・ダッシュ・HUD(`f3f0c08`)は、**ビルドは通っているが、エディタでの動作確認は未実施**。
|
||||
- 大きいグリッド(8x8)でのカメラ修正(`344e5e2`)も、**エディタでの確認は未実施**。端が切れる場合は `CameraPawn` の `MarginScale` を上げる。
|
||||
- カメラ距離は `BeginPlay` で一度だけ計算する。PIE中にウィンドウの縦横比を変えても再計算されない。
|
||||
- 初期配置(`GridManager::InitialUnits`)は3x3前提の座標。グリッドを広げたら調整が必要。範囲外の座標は配置されず、警告ログが出る。
|
||||
- 地形とマスの上面が同じ高さ(Z=0)なので、見た目のz-fightingが出る場合は `GridManager` のZを少し上げる。
|
||||
- `bIsWalkable` を実行中にエディタで変えても、`FGridState` に反映されない。
|
||||
- ユニットIDは生成順の連番。セーブ/ロードやDataTable連携を入れる際に見直す。
|
||||
- `CLAUDE.md` の「現状」節は古い(次の目標が「マウスでマスを選択」のまま)。更新が必要。
|
||||
- `CLAUDE.md` の「現状」節は古い(次の目標が「マウスでマスを選択」のまま)。`CLAUDE.md` 自体が未追跡(一度もコミットされていない)。
|
||||
- リポジトリには、今回の作業と無関係な `Content/` の差分が大量に残っている。コミットには含めていない。
|
||||
- `AGridManager` に、生成・ユニット・ターンの窓口が集まってきている。肥大化したら切り出しを検討する。
|
||||
|
||||
## 残りのタスク
|
||||
|
||||
### 直近
|
||||
- [ ] ユニット機能のエディタ動作確認(選択、範囲表示、移動、移動中の入力無視、範囲外クリック)
|
||||
- [ ] `CLAUDE.md` の「現状」を更新
|
||||
- [ ] ターン制・ダッシュ・HUDのエディタ動作確認(上記「操作とルール」の各項目、特に自動終了、移動中の入力無視、敵ターン後のアクション回復)
|
||||
- [ ] 8x8グリッドでのカメラ表示の確認
|
||||
- [ ] `CLAUDE.md` の「現状」を更新し、コミットするか決める
|
||||
- [ ] `Content/` の未コミット差分の扱いを決める(コミットするか、破棄するか)
|
||||
|
||||
### 次の機能(ユーザーの要件にあるが未着手)
|
||||
- [ ] ターン制(プレイヤーターン / 敵ターンの切り替え)
|
||||
- [ ] 行動ポイント(1ターン2アクション。移動・攻撃で消費)
|
||||
- [ ] 敵ユニットの配置とAI
|
||||
### 次の機能(未着手)
|
||||
- [ ] 敵AI(`RunEnemyTurn()` に、ユニットごとの行動を差し込む)
|
||||
- [ ] 攻撃とダメージ(`EActionType` に `Attack` を追加、HPの導入)
|
||||
- [ ] 遮蔽・命中率・視線判定
|
||||
- [ ] オーバーウォッチ
|
||||
|
||||
### 将来の拡張(設計では対応可能な構造にしてある)
|
||||
- [ ] ユニットステータスのDataTable化(`FUnitStats` を行構造体として利用)
|
||||
- [ ] ユニットステータスと行動ポイントなどの設定値のDataTable/DataAsset化(`FUnitStats` を行構造体として利用)
|
||||
- [ ] 高低差と移動コスト(`FGridCellData::Height`、`GetStepCost` の拡張)
|
||||
- [ ] 遮蔽・命中率・視線判定(`FGridState` を読む純粋ロジックとして実装)
|
||||
- [ ] 攻撃とダメージ
|
||||
- [ ] セーブ/ロード(`FTurnState` + 各ユニットの(ID、座標、陣営、行動ポイント)を保存。復元時は `FTurnManager::Restore` を使い、通知を発火させない)
|
||||
- [ ] 味方ユニットを通り抜ける移動(現状は全ユニットが障害物)
|
||||
- [ ] Enhanced Inputへの移行(操作が増えた場合)
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "TacticaCameraPawn.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "GridManager.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogTacticaCamera, Log, All);
|
||||
@@ -25,12 +27,53 @@ void ATacticaCameraPawn::BeginPlay()
|
||||
return;
|
||||
}
|
||||
|
||||
// グリッドの長辺の半分(余白つき)が画角に収まる距離を求める
|
||||
const float HalfExtent = FMath::Max(Grid->GridSizeX, Grid->GridSizeY) * Grid->CellSize * 0.5f * MarginScale;
|
||||
const float HalfFovRad = FMath::DegreesToRadians(Camera->FieldOfView * 0.5f);
|
||||
const float Distance = HalfExtent / FMath::Tan(HalfFovRad);
|
||||
|
||||
const FRotator Rotation(Pitch, 0.0f, 0.0f);
|
||||
const FVector Location = Grid->GetGridCenter() - Rotation.Vector() * Distance;
|
||||
SetActorLocationAndRotation(Location, Rotation);
|
||||
const FVector Center = Grid->GetGridCenter();
|
||||
|
||||
// 画面の縦横比(取れなければカメラの既定値)
|
||||
float Aspect = Camera->AspectRatio;
|
||||
if (const UWorld* World = GetWorld())
|
||||
{
|
||||
if (const UGameViewportClient* Viewport = World->GetGameViewport())
|
||||
{
|
||||
FVector2D Size;
|
||||
Viewport->GetViewportSize(Size);
|
||||
if (Size.X > 1.0f && Size.Y > 1.0f)
|
||||
{
|
||||
Aspect = Size.X / Size.Y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 横方向・縦方向の画角の半分のtan(余白の分だけ狭めて使う)
|
||||
const float TanH = FMath::Tan(FMath::DegreesToRadians(Camera->FieldOfView * 0.5f)) / MarginScale;
|
||||
const float TanV = TanH / FMath::Max(Aspect, 0.1f);
|
||||
|
||||
// グリッドの四隅が画面に収まる最小の距離を求める。
|
||||
// 斜めに見下ろすので、手前の辺は近く、奥の辺は遠い(深さが違う)ことを考慮する
|
||||
const FRotationMatrix RotMatrix(Rotation);
|
||||
const FVector ViewDir = RotMatrix.GetUnitAxis(EAxis::X);
|
||||
const FVector Right = RotMatrix.GetUnitAxis(EAxis::Y);
|
||||
const FVector Up = RotMatrix.GetUnitAxis(EAxis::Z);
|
||||
|
||||
const float HalfX = Grid->GridSizeX * Grid->CellSize * 0.5f;
|
||||
const float HalfY = Grid->GridSizeY * Grid->CellSize * 0.5f;
|
||||
|
||||
float Distance = 0.0f;
|
||||
for (int32 SignX = -1; SignX <= 1; SignX += 2)
|
||||
{
|
||||
for (int32 SignY = -1; SignY <= 1; SignY += 2)
|
||||
{
|
||||
const FVector Offset(SignX * HalfX, SignY * HalfY, 0.0f);
|
||||
const float Depth = FVector::DotProduct(Offset, ViewDir); // 中心からの奥行きのずれ
|
||||
const float Lateral = FMath::Abs(FVector::DotProduct(Offset, Right));
|
||||
const float Vertical = FMath::Abs(FVector::DotProduct(Offset, Up));
|
||||
|
||||
// 条件: Lateral <= (Distance + Depth) * TanH, Vertical <= (Distance + Depth) * TanV
|
||||
Distance = FMath::Max(Distance, Lateral / TanH - Depth);
|
||||
Distance = FMath::Max(Distance, Vertical / TanV - Depth);
|
||||
}
|
||||
}
|
||||
|
||||
SetActorLocationAndRotation(Center - ViewDir * Distance, Rotation);
|
||||
}
|
||||
Reference in new issue
Block a user