Add turn system, two-action rule with dash, and HUD
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
41709fdd65
commit
f3f0c080a9
17 files changed
+546
-31
No files matched your search
@@ -0,0 +1,35 @@
|
|||||||
|
#include "ActionRules.h"
|
||||||
|
|
||||||
|
int32 FActionRules::GetCost(EActionType Type, int32 ActionPointsLeft)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case EActionType::Dash:
|
||||||
|
return ActionPointsLeft; // 残りを全て消費
|
||||||
|
case EActionType::Move:
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FActionRules::CanPerform(EActionType Type, int32 ActionPointsLeft, int32 ActionsPerTurn)
|
||||||
|
{
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case EActionType::Dash:
|
||||||
|
return ActionPointsLeft > 0 && ActionPointsLeft >= ActionsPerTurn;
|
||||||
|
case EActionType::Move:
|
||||||
|
default:
|
||||||
|
return ActionPointsLeft >= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 FActionRules::GetDashRange(int32 MoveRange, float DashMultiplier)
|
||||||
|
{
|
||||||
|
return FMath::Max(MoveRange, FMath::FloorToInt(MoveRange * DashMultiplier));
|
||||||
|
}
|
||||||
|
|
||||||
|
EActionType FActionRules::ChooseMoveAction(int32 PathCost, int32 MoveRange)
|
||||||
|
{
|
||||||
|
return PathCost <= MoveRange ? EActionType::Move : EActionType::Dash;
|
||||||
|
}
|
||||||
@@ -54,6 +54,12 @@ void AGridCell::SetInRange(bool bInInRange)
|
|||||||
RefreshColor();
|
RefreshColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AGridCell::SetInDashRange(bool bInInDashRange)
|
||||||
|
{
|
||||||
|
bInDashRange = bInInDashRange;
|
||||||
|
RefreshColor();
|
||||||
|
}
|
||||||
|
|
||||||
void AGridCell::SetHovered(bool bInHovered)
|
void AGridCell::SetHovered(bool bInHovered)
|
||||||
{
|
{
|
||||||
bHovered = bInHovered;
|
bHovered = bInHovered;
|
||||||
@@ -70,9 +76,12 @@ void AGridCell::RefreshColor()
|
|||||||
{
|
{
|
||||||
if (DynamicMaterial)
|
if (DynamicMaterial)
|
||||||
{
|
{
|
||||||
// 優先順位: 選択 > ホバー > 移動可能 > 通常
|
// 優先順位: 選択 > ホバー > 移動可能 > ダッシュ可能 > 通常
|
||||||
const FLinearColor& Color = bSelected ? SelectedColor
|
const FLinearColor* Color = &NormalColor;
|
||||||
: (bHovered ? HoverColor : (bInRange ? RangeColor : NormalColor));
|
if (bSelected) { Color = &SelectedColor; }
|
||||||
DynamicMaterial->SetVectorParameterValue(TEXT("Color"), Color);
|
else if (bHovered) { Color = &HoverColor; }
|
||||||
|
else if (bInRange) { Color = &RangeColor; }
|
||||||
|
else if (bInDashRange) { Color = &DashRangeColor; }
|
||||||
|
DynamicMaterial->SetVectorParameterValue(TEXT("Color"), *Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,54 @@ void AGridManager::BeginPlay()
|
|||||||
GridSizeX, GridSizeY, CellSize);
|
GridSizeX, GridSizeY, CellSize);
|
||||||
|
|
||||||
GenerateGrid();
|
GenerateGrid();
|
||||||
|
|
||||||
|
// ターン開始の通知を購読してから、最初のターンを始める
|
||||||
|
TurnManager.OnTurnStarted.AddUObject(this, &AGridManager::HandleTurnStarted);
|
||||||
|
TurnManager.Start(EFaction::Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGridManager::HandleTurnStarted(int32 TurnNumber, EFaction Faction)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaGrid, Log, TEXT("ターン %d 開始: %s"), TurnNumber,
|
||||||
|
Faction == EFaction::Player ? TEXT("Player") : TEXT("Enemy"));
|
||||||
|
|
||||||
|
// その陣営の全ユニットの行動ポイントを回復する
|
||||||
|
for (AUnit* Unit : Units)
|
||||||
|
{
|
||||||
|
if (Unit && Unit->Faction == Faction)
|
||||||
|
{
|
||||||
|
Unit->RestoreActions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Faction == EFaction::Enemy)
|
||||||
|
{
|
||||||
|
RunEnemyTurn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGridManager::RunEnemyTurn()
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaGrid, Log, TEXT("敵ターン(仮): %.1f 秒後にPlayerターンへ戻ります"), EnemyTurnDelay);
|
||||||
|
GetWorldTimerManager().SetTimer(EnemyTurnTimer, this, &AGridManager::FinishEnemyTurn,
|
||||||
|
FMath::Max(EnemyTurnDelay, 0.01f), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AGridManager::FinishEnemyTurn()
|
||||||
|
{
|
||||||
|
TurnManager.EndTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AGridManager::HasActionsLeft(EFaction Faction) const
|
||||||
|
{
|
||||||
|
for (const AUnit* Unit : Units)
|
||||||
|
{
|
||||||
|
if (Unit && Unit->Faction == Faction && Unit->ActionPoints > 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AGridCell* AGridManager::GetCellAt(FIntPoint Coord) const
|
AGridCell* AGridManager::GetCellAt(FIntPoint Coord) const
|
||||||
@@ -105,8 +153,9 @@ void AGridManager::SpawnUnits()
|
|||||||
NextUnitId = 0;
|
NextUnitId = 0;
|
||||||
const FVector Origin = GetActorLocation();
|
const FVector Origin = GetActorLocation();
|
||||||
|
|
||||||
for (const FIntPoint& Coord : InitialUnitCoords)
|
for (const FInitialUnit& Entry : InitialUnits)
|
||||||
{
|
{
|
||||||
|
const FIntPoint Coord = Entry.Coord;
|
||||||
const int32 UnitId = NextUnitId;
|
const int32 UnitId = NextUnitId;
|
||||||
|
|
||||||
// 先に論理側で配置できるか確認する(範囲外・歩行不可・占有済みなら失敗)
|
// 先に論理側で配置できるか確認する(範囲外・歩行不可・占有済みなら失敗)
|
||||||
@@ -132,6 +181,8 @@ void AGridManager::SpawnUnits()
|
|||||||
GridState.PlaceUnit(UnitId, Coord);
|
GridState.PlaceUnit(UnitId, Coord);
|
||||||
Unit->UnitId = UnitId;
|
Unit->UnitId = UnitId;
|
||||||
Unit->GridCoord = Coord;
|
Unit->GridCoord = Coord;
|
||||||
|
Unit->SetFaction(Entry.Faction);
|
||||||
|
Unit->RestoreActions();
|
||||||
Units.Add(Unit);
|
Units.Add(Unit);
|
||||||
++NextUnitId;
|
++NextUnitId;
|
||||||
}
|
}
|
||||||
@@ -187,13 +238,20 @@ bool AGridManager::TryMoveUnit(AUnit* Unit, FIntPoint Dest, const FGridReachResu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AGridManager::SetRangeHighlight(const TArray<FIntPoint>& Coords, bool bInRange)
|
void AGridManager::SetRangeHighlight(const TArray<FIntPoint>& Coords, bool bInRange, bool bDash)
|
||||||
{
|
{
|
||||||
for (const FIntPoint& Coord : Coords)
|
for (const FIntPoint& Coord : Coords)
|
||||||
{
|
{
|
||||||
if (AGridCell* Cell = GetCellAt(Coord))
|
if (AGridCell* Cell = GetCellAt(Coord))
|
||||||
{
|
{
|
||||||
Cell->SetInRange(bInRange);
|
if (bDash)
|
||||||
|
{
|
||||||
|
Cell->SetInDashRange(bInRange);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Cell->SetInRange(bInRange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
#include "TacticaGameMode.h"
|
#include "TacticaGameMode.h"
|
||||||
#include "TacticaPlayerController.h"
|
#include "TacticaPlayerController.h"
|
||||||
#include "TacticaCameraPawn.h"
|
#include "TacticaCameraPawn.h"
|
||||||
|
#include "TacticaHUD.h"
|
||||||
|
|
||||||
ATacticaGameMode::ATacticaGameMode()
|
ATacticaGameMode::ATacticaGameMode()
|
||||||
{
|
{
|
||||||
DefaultPawnClass = ATacticaCameraPawn::StaticClass();
|
DefaultPawnClass = ATacticaCameraPawn::StaticClass();
|
||||||
PlayerControllerClass = ATacticaPlayerController::StaticClass();
|
PlayerControllerClass = ATacticaPlayerController::StaticClass();
|
||||||
|
HUDClass = ATacticaHUD::StaticClass();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include "TacticaHUD.h"
|
||||||
|
#include "GridManager.h"
|
||||||
|
#include "TacticaPlayerController.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
#include "Engine/Canvas.h"
|
||||||
|
#include "Engine/Engine.h"
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
|
||||||
|
void ATacticaHUD::DrawHUD()
|
||||||
|
{
|
||||||
|
Super::DrawHUD();
|
||||||
|
|
||||||
|
if (!GridManager)
|
||||||
|
{
|
||||||
|
GridManager = Cast<AGridManager>(
|
||||||
|
UGameplayStatics::GetActorOfClass(this, AGridManager::StaticClass()));
|
||||||
|
}
|
||||||
|
if (!GridManager || !GEngine)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UFont* Font = GEngine->GetMediumFont();
|
||||||
|
const FTurnState& Turn = GridManager->GetTurnManager().GetState();
|
||||||
|
const bool bPlayerTurn = Turn.CurrentFaction == EFaction::Player;
|
||||||
|
|
||||||
|
const FString TurnText = FString::Printf(TEXT("Turn %d - %s"),
|
||||||
|
Turn.TurnNumber, bPlayerTurn ? TEXT("Player") : TEXT("Enemy"));
|
||||||
|
DrawText(TurnText, bPlayerTurn ? FLinearColor(0.4f, 0.7f, 1.0f) : FLinearColor(1.0f, 0.4f, 0.4f),
|
||||||
|
20.0f, 20.0f, Font, 1.5f);
|
||||||
|
|
||||||
|
// 選択中ユニットの残りアクション
|
||||||
|
if (const ATacticaPlayerController* PC = Cast<ATacticaPlayerController>(GetOwningPlayerController()))
|
||||||
|
{
|
||||||
|
if (const AUnit* Unit = PC->GetSelectedUnit())
|
||||||
|
{
|
||||||
|
const FString UnitText = FString::Printf(TEXT("Unit %d Actions: %d / %d"),
|
||||||
|
Unit->UnitId, Unit->ActionPoints, Unit->Stats.ActionsPerTurn);
|
||||||
|
DrawText(UnitText, FLinearColor::White, 20.0f, 55.0f, Font, 1.2f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawText(TEXT("[Enter] End Turn"), FLinearColor(0.8f, 0.8f, 0.8f), 20.0f, 85.0f, Font, 1.0f);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "TacticaPlayerController.h"
|
#include "TacticaPlayerController.h"
|
||||||
|
#include "ActionRules.h"
|
||||||
#include "GridCell.h"
|
#include "GridCell.h"
|
||||||
#include "GridManager.h"
|
#include "GridManager.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
@@ -21,6 +22,8 @@ void ATacticaPlayerController::SetupInputComponent()
|
|||||||
{
|
{
|
||||||
InputComponent->BindKey(EKeys::LeftMouseButton, IE_Pressed, this,
|
InputComponent->BindKey(EKeys::LeftMouseButton, IE_Pressed, this,
|
||||||
&ATacticaPlayerController::OnClick);
|
&ATacticaPlayerController::OnClick);
|
||||||
|
InputComponent->BindKey(EKeys::Enter, IE_Pressed, this,
|
||||||
|
&ATacticaPlayerController::OnEndTurnKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,11 +73,27 @@ AGridManager* ATacticaPlayerController::GetGridManager()
|
|||||||
return GridManager;
|
return GridManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATacticaPlayerController::OnClick()
|
bool ATacticaPlayerController::CanAcceptInput()
|
||||||
{
|
{
|
||||||
if (State == EState::Moving)
|
if (State == EState::Moving)
|
||||||
{
|
{
|
||||||
UE_LOG(LogTacticaInput, Log, TEXT("移動中のため入力を無視しました"));
|
UE_LOG(LogTacticaInput, Log, TEXT("移動中のため入力を無視しました"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AGridManager* Grid = GetGridManager();
|
||||||
|
if (!Grid || Grid->GetTurnManager().GetCurrentFaction() != EFaction::Player)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("Playerのターンではないため入力を無視しました"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ATacticaPlayerController::OnClick()
|
||||||
|
{
|
||||||
|
if (!CanAcceptInput())
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,13 +109,19 @@ void ATacticaPlayerController::OnClick()
|
|||||||
|
|
||||||
if (State == EState::Idle)
|
if (State == EState::Idle)
|
||||||
{
|
{
|
||||||
if (AUnit* Unit = Grid->GetUnitAt(Coord))
|
AUnit* Unit = Grid->GetUnitAt(Coord);
|
||||||
|
if (!Unit)
|
||||||
{
|
{
|
||||||
SelectUnit(Unit, Cell);
|
UE_LOG(LogTacticaInput, Log, TEXT("(%d, %d) にユニットはいません"), Coord.X, Coord.Y);
|
||||||
|
}
|
||||||
|
else if (Unit->Faction != EFaction::Player)
|
||||||
|
{
|
||||||
|
// 敵ユニットは操作できない
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("敵ユニット %d を選択しました(操作はできません)"), Unit->UnitId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UE_LOG(LogTacticaInput, Log, TEXT("(%d, %d) にユニットはいません"), Coord.X, Coord.Y);
|
SelectUnit(Unit, Cell);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,19 +129,45 @@ void ATacticaPlayerController::OnClick()
|
|||||||
// UnitSelected: 移動可能なマス(起点を除く)だけ受け付ける
|
// UnitSelected: 移動可能なマス(起点を除く)だけ受け付ける
|
||||||
if (SelectedUnit && Coord != SelectedUnit->GridCoord && ReachResult.IsReachable(Coord))
|
if (SelectedUnit && Coord != SelectedUnit->GridCoord && ReachResult.IsReachable(Coord))
|
||||||
{
|
{
|
||||||
if (Grid->TryMoveUnit(SelectedUnit, Coord, ReachResult))
|
const int32 PathCost = ReachResult.Cost[Coord];
|
||||||
|
const EActionType Action = FActionRules::ChooseMoveAction(PathCost, SelectedUnit->Stats.MoveRange);
|
||||||
|
|
||||||
|
if (FActionRules::CanPerform(Action, SelectedUnit->ActionPoints, SelectedUnit->Stats.ActionsPerTurn))
|
||||||
{
|
{
|
||||||
State = EState::Moving;
|
const int32 Cost = FActionRules::GetCost(Action, SelectedUnit->ActionPoints);
|
||||||
SelectedUnit->OnMoveFinished.AddUObject(this, &ATacticaPlayerController::OnUnitMoveFinished);
|
if (Grid->TryMoveUnit(SelectedUnit, Coord, ReachResult))
|
||||||
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d を (%d, %d) へ移動開始"),
|
{
|
||||||
SelectedUnit->UnitId, Coord.X, Coord.Y);
|
// 行動ポイントは移動の開始時に消費する
|
||||||
return;
|
SelectedUnit->SpendActions(Cost);
|
||||||
|
State = EState::Moving;
|
||||||
|
SelectedUnit->OnMoveFinished.AddUObject(this, &ATacticaPlayerController::OnUnitMoveFinished);
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d を (%d, %d) へ移動開始 (%s, 消費 %d, 残り %d)"),
|
||||||
|
SelectedUnit->UnitId, Coord.X, Coord.Y,
|
||||||
|
Action == EActionType::Dash ? TEXT("Dash") : TEXT("Move"),
|
||||||
|
Cost, SelectedUnit->ActionPoints);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UE_LOG(LogTacticaInput, Log, TEXT("(%d, %d) には移動できません"), Coord.X, Coord.Y);
|
UE_LOG(LogTacticaInput, Log, TEXT("(%d, %d) には移動できません"), Coord.X, Coord.Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ATacticaPlayerController::OnEndTurnKey()
|
||||||
|
{
|
||||||
|
if (!CanAcceptInput())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearSelection();
|
||||||
|
if (AGridManager* Grid = GetGridManager())
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("Enterキーでターンを終了します"));
|
||||||
|
Grid->GetTurnManager().EndTurn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ATacticaPlayerController::SelectUnit(AUnit* Unit, AGridCell* Cell)
|
void ATacticaPlayerController::SelectUnit(AUnit* Unit, AGridCell* Cell)
|
||||||
{
|
{
|
||||||
AGridManager* Grid = GetGridManager();
|
AGridManager* Grid = GetGridManager();
|
||||||
@@ -128,31 +179,60 @@ void ATacticaPlayerController::SelectUnit(AUnit* Unit, AGridCell* Cell)
|
|||||||
SelectedUnit = Unit;
|
SelectedUnit = Unit;
|
||||||
SelectedCell = Cell;
|
SelectedCell = Cell;
|
||||||
SelectedCell->SetSelected(true);
|
SelectedCell->SetSelected(true);
|
||||||
|
State = EState::UnitSelected;
|
||||||
ReachResult = FGridPathfinder::FindReachable(Grid->GetGridState(), Unit->GridCoord, Unit->Stats.MoveRange);
|
|
||||||
|
|
||||||
RangeCoords.Reset();
|
RangeCoords.Reset();
|
||||||
|
DashRangeCoords.Reset();
|
||||||
|
ReachResult = FGridReachResult();
|
||||||
|
|
||||||
|
// アクションが残っていなければ、選択はできるが範囲は表示しない
|
||||||
|
if (Unit->ActionPoints <= 0)
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d を選択 (アクションが残っていないため移動できません)"), Unit->UnitId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ダッシュできる(アクションが全部残っている)ときだけ、探索範囲を広げる
|
||||||
|
const int32 MoveRange = Unit->Stats.MoveRange;
|
||||||
|
const bool bCanDash = FActionRules::CanPerform(
|
||||||
|
EActionType::Dash, Unit->ActionPoints, Unit->Stats.ActionsPerTurn);
|
||||||
|
const int32 MaxCost = bCanDash
|
||||||
|
? FActionRules::GetDashRange(MoveRange, Unit->Stats.DashMultiplier)
|
||||||
|
: MoveRange;
|
||||||
|
|
||||||
|
ReachResult = FGridPathfinder::FindReachable(Grid->GetGridState(), Unit->GridCoord, MaxCost);
|
||||||
|
|
||||||
for (const TPair<FIntPoint, int32>& Pair : ReachResult.Cost)
|
for (const TPair<FIntPoint, int32>& Pair : ReachResult.Cost)
|
||||||
{
|
{
|
||||||
if (Pair.Key != Unit->GridCoord)
|
if (Pair.Key == Unit->GridCoord)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (FActionRules::ChooseMoveAction(Pair.Value, MoveRange) == EActionType::Move)
|
||||||
{
|
{
|
||||||
RangeCoords.Add(Pair.Key);
|
RangeCoords.Add(Pair.Key);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DashRangeCoords.Add(Pair.Key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Grid->SetRangeHighlight(RangeCoords, true);
|
Grid->SetRangeHighlight(RangeCoords, true, false);
|
||||||
|
Grid->SetRangeHighlight(DashRangeCoords, true, true);
|
||||||
|
|
||||||
State = EState::UnitSelected;
|
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d を選択 (残りアクション %d, 通常 %d マス, ダッシュ %d マス)"),
|
||||||
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d を選択 (移動力 %d, 移動可能 %d マス)"),
|
Unit->UnitId, Unit->ActionPoints, RangeCoords.Num(), DashRangeCoords.Num());
|
||||||
Unit->UnitId, Unit->Stats.MoveRange, RangeCoords.Num());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATacticaPlayerController::ClearSelection()
|
void ATacticaPlayerController::ClearSelection()
|
||||||
{
|
{
|
||||||
if (AGridManager* Grid = GetGridManager())
|
if (AGridManager* Grid = GetGridManager())
|
||||||
{
|
{
|
||||||
Grid->SetRangeHighlight(RangeCoords, false);
|
Grid->SetRangeHighlight(RangeCoords, false, false);
|
||||||
|
Grid->SetRangeHighlight(DashRangeCoords, false, true);
|
||||||
}
|
}
|
||||||
RangeCoords.Reset();
|
RangeCoords.Reset();
|
||||||
|
DashRangeCoords.Reset();
|
||||||
ReachResult = FGridReachResult();
|
ReachResult = FGridReachResult();
|
||||||
|
|
||||||
if (SelectedCell)
|
if (SelectedCell)
|
||||||
@@ -172,4 +252,13 @@ void ATacticaPlayerController::OnUnitMoveFinished()
|
|||||||
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d の移動完了"), SelectedUnit->UnitId);
|
UE_LOG(LogTacticaInput, Log, TEXT("ユニット %d の移動完了"), SelectedUnit->UnitId);
|
||||||
}
|
}
|
||||||
ClearSelection();
|
ClearSelection();
|
||||||
|
|
||||||
|
// 移動が完全に終わった後で、自動終了を判定する(移動中は判定しない)
|
||||||
|
AGridManager* Grid = GetGridManager();
|
||||||
|
if (Grid && Grid->GetTurnManager().GetCurrentFaction() == EFaction::Player
|
||||||
|
&& !Grid->HasActionsLeft(EFaction::Player))
|
||||||
|
{
|
||||||
|
UE_LOG(LogTacticaInput, Log, TEXT("Playerの全ユニットのアクションが0になったためターンを終了します"));
|
||||||
|
Grid->GetTurnManager().EndTurn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#include "TurnManager.h"
|
||||||
|
|
||||||
|
void FTurnManager::Start(EFaction FirstFaction)
|
||||||
|
{
|
||||||
|
State.TurnNumber = 1;
|
||||||
|
State.CurrentFaction = FirstFaction;
|
||||||
|
OnTurnStarted.Broadcast(State.TurnNumber, State.CurrentFaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FTurnManager::EndTurn()
|
||||||
|
{
|
||||||
|
const EFaction Ended = State.CurrentFaction;
|
||||||
|
OnTurnEnded.Broadcast(Ended);
|
||||||
|
|
||||||
|
// Player → Enemy → Player ...。Playerに戻ったらターン数を進める
|
||||||
|
if (Ended == EFaction::Player)
|
||||||
|
{
|
||||||
|
State.CurrentFaction = EFaction::Enemy;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
State.CurrentFaction = EFaction::Player;
|
||||||
|
++State.TurnNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
OnTurnStarted.Broadcast(State.TurnNumber, State.CurrentFaction);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "Components/StaticMeshComponent.h"
|
#include "Components/StaticMeshComponent.h"
|
||||||
#include "Engine/StaticMesh.h"
|
#include "Engine/StaticMesh.h"
|
||||||
|
#include "Materials/MaterialInstanceDynamic.h"
|
||||||
#include "UObject/ConstructorHelpers.h"
|
#include "UObject/ConstructorHelpers.h"
|
||||||
|
|
||||||
AUnit::AUnit()
|
AUnit::AUnit()
|
||||||
@@ -26,10 +27,53 @@ AUnit::AUnit()
|
|||||||
// 底面がActorの原点(マスの上面)に来るよう、高さの半分(50cm)上げる
|
// 底面がActorの原点(マスの上面)に来るよう、高さの半分(50cm)上げる
|
||||||
MeshComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f));
|
MeshComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f));
|
||||||
|
|
||||||
|
// 色を変えられる標準マテリアル(パラメータ"Color")
|
||||||
|
static ConstructorHelpers::FObjectFinder<UMaterialInterface> BaseMaterial(
|
||||||
|
TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));
|
||||||
|
if (BaseMaterial.Succeeded())
|
||||||
|
{
|
||||||
|
MeshComponent->SetMaterial(0, BaseMaterial.Object);
|
||||||
|
}
|
||||||
|
|
||||||
// マスのクリック判定をユニットが邪魔しないよう、当たり判定は無効にする
|
// マスのクリック判定をユニットが邪魔しないよう、当たり判定は無効にする
|
||||||
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
MeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AUnit::BeginPlay()
|
||||||
|
{
|
||||||
|
Super::BeginPlay();
|
||||||
|
|
||||||
|
DynamicMaterial = MeshComponent->CreateDynamicMaterialInstance(0);
|
||||||
|
RefreshColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AUnit::SetFaction(EFaction InFaction)
|
||||||
|
{
|
||||||
|
Faction = InFaction;
|
||||||
|
RefreshColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AUnit::RefreshColor()
|
||||||
|
{
|
||||||
|
if (DynamicMaterial)
|
||||||
|
{
|
||||||
|
DynamicMaterial->SetVectorParameterValue(TEXT("Color"),
|
||||||
|
Faction == EFaction::Player ? PlayerColor : EnemyColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AUnit::SpendActions(int32 Amount)
|
||||||
|
{
|
||||||
|
ActionPoints = FMath::Max(0, ActionPoints - Amount);
|
||||||
|
OnActionPointsChanged.Broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AUnit::RestoreActions()
|
||||||
|
{
|
||||||
|
ActionPoints = Stats.ActionsPerTurn;
|
||||||
|
OnActionPointsChanged.Broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
void AUnit::MoveAlongPath(const TArray<FVector>& InWaypoints)
|
void AUnit::MoveAlongPath(const TArray<FVector>& InWaypoints)
|
||||||
{
|
{
|
||||||
Waypoints = InWaypoints;
|
Waypoints = InWaypoints;
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "TurnTypes.h"
|
||||||
|
|
||||||
|
// アクションのコストや可否のルール。UObject/Actorに依存しない純粋なロジック
|
||||||
|
// 数値(行動ポイント数、ダッシュ倍率)は呼び出し側がFUnitStats等から渡す
|
||||||
|
struct TACTICA_API FActionRules
|
||||||
|
{
|
||||||
|
// 実行時に消費する行動ポイント。Dashは残り全部
|
||||||
|
static int32 GetCost(EActionType Type, int32 ActionPointsLeft);
|
||||||
|
|
||||||
|
// 実行できるか。DashはActionsPerTurn分(=全部)残っているときだけ
|
||||||
|
static bool CanPerform(EActionType Type, int32 ActionPointsLeft, int32 ActionsPerTurn);
|
||||||
|
|
||||||
|
// ダッシュで移動できる最大マス数
|
||||||
|
static int32 GetDashRange(int32 MoveRange, float DashMultiplier);
|
||||||
|
|
||||||
|
// 移動コスト(マス数)から、必要なアクション種別を決める
|
||||||
|
static EActionType ChooseMoveAction(int32 PathCost, int32 MoveRange);
|
||||||
|
};
|
||||||
@@ -47,7 +47,12 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, Category = "Grid|Highlight")
|
UPROPERTY(EditAnywhere, Category = "Grid|Highlight")
|
||||||
FLinearColor RangeColor = FLinearColor(0.2f, 0.8f, 0.3f);
|
FLinearColor RangeColor = FLinearColor(0.2f, 0.8f, 0.3f);
|
||||||
|
|
||||||
|
// ダッシュでのみ移動できる範囲の色
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Grid|Highlight")
|
||||||
|
FLinearColor DashRangeColor = FLinearColor(1.0f, 0.45f, 0.1f);
|
||||||
|
|
||||||
void SetInRange(bool bInInRange);
|
void SetInRange(bool bInInRange);
|
||||||
|
void SetInDashRange(bool bInInDashRange);
|
||||||
void SetHovered(bool bInHovered);
|
void SetHovered(bool bInHovered);
|
||||||
void SetSelected(bool bInSelected);
|
void SetSelected(bool bInSelected);
|
||||||
|
|
||||||
@@ -59,6 +64,7 @@ private:
|
|||||||
void RefreshColor();
|
void RefreshColor();
|
||||||
|
|
||||||
bool bInRange = false;
|
bool bInRange = false;
|
||||||
|
bool bInDashRange = false;
|
||||||
bool bHovered = false;
|
bool bHovered = false;
|
||||||
bool bSelected = false;
|
bool bSelected = false;
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,30 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/Actor.h"
|
#include "GameFramework/Actor.h"
|
||||||
#include "GridState.h"
|
#include "GridState.h"
|
||||||
|
#include "TurnManager.h"
|
||||||
|
#include "TurnTypes.h"
|
||||||
#include "GridManager.generated.h"
|
#include "GridManager.generated.h"
|
||||||
|
|
||||||
class AGridCell;
|
class AGridCell;
|
||||||
class AUnit;
|
class AUnit;
|
||||||
struct FGridReachResult;
|
struct FGridReachResult;
|
||||||
|
|
||||||
|
// ユニットの初期配置(座標と陣営)
|
||||||
|
USTRUCT()
|
||||||
|
struct FInitialUnit
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Units")
|
||||||
|
FIntPoint Coord = FIntPoint::ZeroValue;
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Units")
|
||||||
|
EFaction Faction = EFaction::Player;
|
||||||
|
|
||||||
|
FInitialUnit() {}
|
||||||
|
FInitialUnit(FIntPoint InCoord, EFaction InFaction) : Coord(InCoord), Faction(InFaction) {}
|
||||||
|
};
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class TACTICA_API AGridManager : public AActor
|
class TACTICA_API AGridManager : public AActor
|
||||||
{
|
{
|
||||||
@@ -45,9 +63,17 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, Category = "Units")
|
UPROPERTY(EditAnywhere, Category = "Units")
|
||||||
TSubclassOf<AUnit> UnitClass;
|
TSubclassOf<AUnit> UnitClass;
|
||||||
|
|
||||||
// ユニットの初期配置座標(1要素につき1体)
|
// ユニットの初期配置(1要素につき1体)
|
||||||
UPROPERTY(EditAnywhere, Category = "Units")
|
UPROPERTY(EditAnywhere, Category = "Units")
|
||||||
TArray<FIntPoint> InitialUnitCoords = { FIntPoint(0, 0), FIntPoint(2, 2) };
|
TArray<FInitialUnit> InitialUnits = {
|
||||||
|
FInitialUnit(FIntPoint(0, 0), EFaction::Player),
|
||||||
|
FInitialUnit(FIntPoint(1, 0), EFaction::Player),
|
||||||
|
FInitialUnit(FIntPoint(2, 2), EFaction::Enemy),
|
||||||
|
FInitialUnit(FIntPoint(1, 2), EFaction::Enemy) };
|
||||||
|
|
||||||
|
// 敵ターンの待ち時間(秒)。仮実装
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Turn")
|
||||||
|
float EnemyTurnDelay = 1.0f;
|
||||||
|
|
||||||
// 生成したユニットの一覧
|
// 生成したユニットの一覧
|
||||||
UPROPERTY(VisibleAnywhere, Category = "Units")
|
UPROPERTY(VisibleAnywhere, Category = "Units")
|
||||||
@@ -63,8 +89,15 @@ public:
|
|||||||
// Reach は、ユニットの現在位置から求めた FindReachable の結果
|
// Reach は、ユニットの現在位置から求めた FindReachable の結果
|
||||||
bool TryMoveUnit(AUnit* Unit, FIntPoint Dest, const FGridReachResult& Reach);
|
bool TryMoveUnit(AUnit* Unit, FIntPoint Dest, const FGridReachResult& Reach);
|
||||||
|
|
||||||
// 指定したマスの「移動可能」表示をON/OFFする
|
// 指定したマスの「移動可能」表示をON/OFFする(bDash=trueならダッシュ範囲の色)
|
||||||
void SetRangeHighlight(const TArray<FIntPoint>& Coords, bool bInRange);
|
void SetRangeHighlight(const TArray<FIntPoint>& Coords, bool bInRange, bool bDash = false);
|
||||||
|
|
||||||
|
// ターン管理(通知を購読するときはここから取る)
|
||||||
|
FTurnManager& GetTurnManager() { return TurnManager; }
|
||||||
|
const FTurnManager& GetTurnManager() const { return TurnManager; }
|
||||||
|
|
||||||
|
// 指定した陣営に、行動ポイントが残っているユニットがいるか
|
||||||
|
bool HasActionsLeft(EFaction Faction) const;
|
||||||
|
|
||||||
// 座標のマスを返す(範囲外はnullptr)
|
// 座標のマスを返す(範囲外はnullptr)
|
||||||
AGridCell* GetCellAt(FIntPoint Coord) const;
|
AGridCell* GetCellAt(FIntPoint Coord) const;
|
||||||
@@ -72,10 +105,19 @@ public:
|
|||||||
// グリッド全体の中心(ワールド)
|
// グリッド全体の中心(ワールド)
|
||||||
FVector GetGridCenter() const;
|
FVector GetGridCenter() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// 敵ターンの拡張点。将来の敵AIはここでユニットごとの行動を実行し、
|
||||||
|
// 終わったら TurnManager.EndTurn() を呼ぶ。今は待ってから終了するだけ
|
||||||
|
virtual void RunEnemyTurn();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GenerateGrid();
|
void GenerateGrid();
|
||||||
void SpawnUnits();
|
void SpawnUnits();
|
||||||
|
void HandleTurnStarted(int32 TurnNumber, EFaction Faction);
|
||||||
|
void FinishEnemyTurn();
|
||||||
|
|
||||||
FGridState GridState;
|
FGridState GridState;
|
||||||
|
FTurnManager TurnManager;
|
||||||
|
FTimerHandle EnemyTurnTimer;
|
||||||
int32 NextUnitId = 0;
|
int32 NextUnitId = 0;
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "GameFramework/HUD.h"
|
||||||
|
#include "TacticaHUD.generated.h"
|
||||||
|
|
||||||
|
class AGridManager;
|
||||||
|
|
||||||
|
// ターン数・陣営・選択中ユニットの残りアクションを表示するHUD。
|
||||||
|
// 状態は持たず、毎フレーム TurnManager / Unit から読んで描くだけ
|
||||||
|
UCLASS()
|
||||||
|
class TACTICA_API ATacticaHUD : public AHUD
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual void DrawHUD() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<AGridManager> GridManager;
|
||||||
|
};
|
||||||
@@ -17,6 +17,9 @@ class TACTICA_API ATacticaPlayerController : public APlayerController
|
|||||||
public:
|
public:
|
||||||
ATacticaPlayerController();
|
ATacticaPlayerController();
|
||||||
|
|
||||||
|
// 選択中のユニット(HUD表示用)。なければnullptr
|
||||||
|
AUnit* GetSelectedUnit() const { return SelectedUnit; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void SetupInputComponent() override;
|
virtual void SetupInputComponent() override;
|
||||||
virtual void PlayerTick(float DeltaTime) override;
|
virtual void PlayerTick(float DeltaTime) override;
|
||||||
@@ -35,7 +38,11 @@ private:
|
|||||||
|
|
||||||
AGridManager* GetGridManager();
|
AGridManager* GetGridManager();
|
||||||
|
|
||||||
|
// Playerのターンで、入力を受け付けてよいか(移動中は不可)
|
||||||
|
bool CanAcceptInput();
|
||||||
|
|
||||||
void OnClick();
|
void OnClick();
|
||||||
|
void OnEndTurnKey();
|
||||||
void SelectUnit(AUnit* Unit, AGridCell* Cell);
|
void SelectUnit(AUnit* Unit, AGridCell* Cell);
|
||||||
void ClearSelection();
|
void ClearSelection();
|
||||||
void OnUnitMoveFinished();
|
void OnUnitMoveFinished();
|
||||||
@@ -56,7 +63,8 @@ private:
|
|||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
TObjectPtr<AGridManager> GridManager;
|
TObjectPtr<AGridManager> GridManager;
|
||||||
|
|
||||||
// 選択中ユニットの移動可能範囲(起点を除く)と探索結果
|
// 選択中ユニットの移動可能範囲(起点を除く)。通常移動とダッシュで分ける
|
||||||
TArray<FIntPoint> RangeCoords;
|
TArray<FIntPoint> RangeCoords;
|
||||||
|
TArray<FIntPoint> DashRangeCoords;
|
||||||
FGridReachResult ReachResult;
|
FGridReachResult ReachResult;
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "TurnTypes.h"
|
||||||
|
|
||||||
|
// ターンの状態。値だけで構成されるので、そのままセーブできる
|
||||||
|
struct FTurnState
|
||||||
|
{
|
||||||
|
// ターン数(Playerのターンに戻るたびに+1。1から)
|
||||||
|
int32 TurnNumber = 1;
|
||||||
|
|
||||||
|
EFaction CurrentFaction = EFaction::Player;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ターンの進行。UObject/Actorに依存しない純粋なクラス。
|
||||||
|
// 状態の変化はDelegateで通知し、見た目や行動ポイントの回復は購読側が行う
|
||||||
|
class TACTICA_API FTurnManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnTurnStarted, int32 /*TurnNumber*/, EFaction /*Faction*/);
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FOnTurnEnded, EFaction /*Faction*/);
|
||||||
|
|
||||||
|
// 通知(購読は Actor 層が行う)
|
||||||
|
FOnTurnStarted OnTurnStarted;
|
||||||
|
FOnTurnEnded OnTurnEnded;
|
||||||
|
|
||||||
|
// 最初のターンを開始する(ターン数は1)
|
||||||
|
void Start(EFaction FirstFaction);
|
||||||
|
|
||||||
|
// 現在のターンを終了して、次の陣営のターンを開始する
|
||||||
|
void EndTurn();
|
||||||
|
|
||||||
|
// セーブデータからの復元。通知は発火しない(二重回復を防ぐ)
|
||||||
|
void Restore(const FTurnState& InState) { State = InState; }
|
||||||
|
|
||||||
|
const FTurnState& GetState() const { return State; }
|
||||||
|
int32 GetTurnNumber() const { return State.TurnNumber; }
|
||||||
|
EFaction GetCurrentFaction() const { return State.CurrentFaction; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FTurnState State;
|
||||||
|
};
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "TurnTypes.generated.h"
|
||||||
|
|
||||||
|
// 陣営
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EFaction : uint8
|
||||||
|
{
|
||||||
|
Player,
|
||||||
|
Enemy
|
||||||
|
};
|
||||||
|
|
||||||
|
// アクションの種類。攻撃などを足すときはここに追加する
|
||||||
|
UENUM(BlueprintType)
|
||||||
|
enum class EActionType : uint8
|
||||||
|
{
|
||||||
|
Move,
|
||||||
|
Dash
|
||||||
|
};
|
||||||
@@ -2,12 +2,15 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "GameFramework/Actor.h"
|
#include "GameFramework/Actor.h"
|
||||||
|
#include "TurnTypes.h"
|
||||||
#include "UnitStats.h"
|
#include "UnitStats.h"
|
||||||
#include "Unit.generated.h"
|
#include "Unit.generated.h"
|
||||||
|
|
||||||
class UStaticMeshComponent;
|
class UStaticMeshComponent;
|
||||||
|
class UMaterialInstanceDynamic;
|
||||||
|
|
||||||
// ユニット(兵士)。見た目と移動の補間だけを担当し、占有などの論理はAGridManager/FGridStateが持つ
|
// ユニット(兵士)。見た目と移動の補間、行動ポイントを持つ。
|
||||||
|
// 占有などの論理はAGridManager/FGridStateが持つ
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class TACTICA_API AUnit : public AActor
|
class TACTICA_API AUnit : public AActor
|
||||||
{
|
{
|
||||||
@@ -29,10 +32,34 @@ public:
|
|||||||
UPROPERTY(EditAnywhere, Category = "Unit")
|
UPROPERTY(EditAnywhere, Category = "Unit")
|
||||||
FUnitStats Stats;
|
FUnitStats Stats;
|
||||||
|
|
||||||
|
// 陣営
|
||||||
|
UPROPERTY(VisibleAnywhere, Category = "Unit")
|
||||||
|
EFaction Faction = EFaction::Player;
|
||||||
|
|
||||||
|
// 残りの行動ポイント
|
||||||
|
UPROPERTY(VisibleAnywhere, Category = "Unit")
|
||||||
|
int32 ActionPoints = 0;
|
||||||
|
|
||||||
|
// 陣営ごとの色
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Unit")
|
||||||
|
FLinearColor PlayerColor = FLinearColor(0.2f, 0.5f, 1.0f);
|
||||||
|
|
||||||
|
UPROPERTY(EditAnywhere, Category = "Unit")
|
||||||
|
FLinearColor EnemyColor = FLinearColor(1.0f, 0.2f, 0.2f);
|
||||||
|
|
||||||
// 移動速度(cm/秒)
|
// 移動速度(cm/秒)
|
||||||
UPROPERTY(EditAnywhere, Category = "Unit")
|
UPROPERTY(EditAnywhere, Category = "Unit")
|
||||||
float MoveSpeed = 400.0f;
|
float MoveSpeed = 400.0f;
|
||||||
|
|
||||||
|
// 陣営を設定して色に反映する
|
||||||
|
void SetFaction(EFaction InFaction);
|
||||||
|
|
||||||
|
// 行動ポイントを消費する(0未満にはならない)
|
||||||
|
void SpendActions(int32 Amount);
|
||||||
|
|
||||||
|
// 行動ポイントを1ターン分に回復する
|
||||||
|
void RestoreActions();
|
||||||
|
|
||||||
// ウェイポイントを順に通って移動する
|
// ウェイポイントを順に通って移動する
|
||||||
void MoveAlongPath(const TArray<FVector>& InWaypoints);
|
void MoveAlongPath(const TArray<FVector>& InWaypoints);
|
||||||
|
|
||||||
@@ -41,10 +68,21 @@ public:
|
|||||||
// 移動完了時に呼ばれる
|
// 移動完了時に呼ばれる
|
||||||
FSimpleMulticastDelegate OnMoveFinished;
|
FSimpleMulticastDelegate OnMoveFinished;
|
||||||
|
|
||||||
|
// 行動ポイントが変化したときに呼ばれる
|
||||||
|
FSimpleMulticastDelegate OnActionPointsChanged;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void BeginPlay() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void RefreshColor();
|
||||||
|
|
||||||
UPROPERTY(VisibleAnywhere, Category = "Unit")
|
UPROPERTY(VisibleAnywhere, Category = "Unit")
|
||||||
TObjectPtr<UStaticMeshComponent> MeshComponent;
|
TObjectPtr<UStaticMeshComponent> MeshComponent;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TObjectPtr<UMaterialInstanceDynamic> DynamicMaterial;
|
||||||
|
|
||||||
TArray<FVector> Waypoints;
|
TArray<FVector> Waypoints;
|
||||||
int32 WaypointIndex = 0;
|
int32 WaypointIndex = 0;
|
||||||
};
|
};
|
||||||
@@ -13,4 +13,12 @@ struct FUnitStats : public FTableRowBase
|
|||||||
// 移動力(マス数)
|
// 移動力(マス数)
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Unit")
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Unit")
|
||||||
int32 MoveRange = 4;
|
int32 MoveRange = 4;
|
||||||
|
|
||||||
|
// 1ターンの行動ポイント
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Unit")
|
||||||
|
int32 ActionsPerTurn = 2;
|
||||||
|
|
||||||
|
// ダッシュ時の移動範囲の倍率(移動力に掛ける)
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Unit")
|
||||||
|
float DashMultiplier = 2.0f;
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user