This commit introduces a centipede controller subsystem, managing centipede spawning, movement, and segment behavior. It adds centipede body materials, movement logic, collision detection, and head assignment. The controller handles segment following, direction changes at boundaries, and dynamic head updates upon segment destruction.
281 lines
7.5 KiB
C++
281 lines
7.5 KiB
C++
#include "M4_CentipedeController.h"
|
|
#include "M4_LOG.h"
|
|
#include "M4_Gamemode.h"
|
|
|
|
UM4_CentipedeController::UM4_CentipedeController()
|
|
{
|
|
|
|
}
|
|
|
|
void UM4_CentipedeController::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
|
|
UE_LOG(M4_CPP, Warning, TEXT("Centipede Controller Initialized"));
|
|
|
|
}
|
|
|
|
void UM4_CentipedeController::Deinitialize()
|
|
{
|
|
UE_LOG(M4_CPP, Warning, TEXT("Centipede Controller Deinitialized"));
|
|
|
|
BodySegments.Empty();
|
|
PreviousPositions.Empty();
|
|
SegmentDirections.Empty();
|
|
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
void UM4_CentipedeController::StartCentipede(FVector SpawnLocation)
|
|
{
|
|
if (BodySegments.Num() > 0)
|
|
{
|
|
UE_LOG(M4_CPP, Warning, TEXT("Centipede already spawned"));
|
|
return;
|
|
}
|
|
|
|
for (int32 i = 0; i < BodyCount; ++i)
|
|
{
|
|
FVector SegmentLocation = SpawnLocation - FVector(0.f, 0.f, i * CellSize);
|
|
|
|
AM4_CentipedeBody* Body = GetWorld()->SpawnActor<AM4_CentipedeBody>(
|
|
AM4_CentipedeBody::StaticClass(),
|
|
SegmentLocation,
|
|
FRotator::ZeroRotator
|
|
);
|
|
|
|
if (Body)
|
|
{
|
|
BodySegments.Add(Body);
|
|
|
|
if (i > 0)
|
|
{
|
|
Body->PreviousBody = BodySegments[i - 1];
|
|
BodySegments[i - 1]->NextBody = Body;
|
|
}
|
|
|
|
Body->SetAsHead(i == 0);
|
|
}
|
|
}
|
|
|
|
// Initialiser l'historique
|
|
SegmentHistory.SetNum(BodyCount);
|
|
for (int32 i = 0; i < BodyCount; ++i)
|
|
{
|
|
if (BodySegments.IsValidIndex(i) && BodySegments[i])
|
|
{
|
|
FVector InitialPos = BodySegments[i]->GetActorLocation();
|
|
|
|
// Remplir avec position initiale
|
|
int32 InitialHistorySize = SegmentSpacing * (i + 1) + MaxHistorySize;
|
|
for (int32 j = 0; j < InitialHistorySize; ++j)
|
|
{
|
|
SegmentHistory[i].Add(InitialPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (AM4_CentipedeBody* Segment : BodySegments)
|
|
{
|
|
SegmentDirections.Add(Segment, FVector2D(0.f, 1.f));
|
|
}
|
|
|
|
UE_LOG(M4_CPP, Warning, TEXT("Centipede spawned with %d segments"), BodySegments.Num());
|
|
}
|
|
|
|
TStatId UM4_CentipedeController::GetStatId() const
|
|
{
|
|
RETURN_QUICK_DECLARE_CYCLE_STAT(UM4_CentipedeController, STATGROUP_Tickables);
|
|
}
|
|
|
|
void UM4_CentipedeController::SpawnCentipede()
|
|
{
|
|
AM4_Gamemode* GM = Cast<AM4_Gamemode>(GetWorld()->GetAuthGameMode());
|
|
if (!GM) return;
|
|
|
|
FVector SpawnLocation = FVector(
|
|
GM->MushroomSpawnBounds.Max.X,
|
|
0.f,
|
|
0.f
|
|
);
|
|
|
|
UE_LOG(M4_CPP, Warning, TEXT("Controller spawn location: X=%.2f, Y=%.2f, Z=%.2f"),
|
|
SpawnLocation.X, SpawnLocation.Y, SpawnLocation.Z);
|
|
|
|
if (SpawnLocation.IsZero())
|
|
{
|
|
UE_LOG(M4_CPP, Error, TEXT("Controller position is zero! Check spawn parameters."));
|
|
return;
|
|
}
|
|
|
|
for (int32 i = 0; i < BodyCount; ++i)
|
|
{
|
|
FVector SegmentLocation = SpawnLocation - FVector(0.f, i * CellSize, 0.f);
|
|
|
|
AM4_CentipedeBody* Body = GetWorld()->SpawnActor<AM4_CentipedeBody>(
|
|
AM4_CentipedeBody::StaticClass(),
|
|
SegmentLocation,
|
|
FRotator::ZeroRotator
|
|
);
|
|
|
|
if (Body)
|
|
{
|
|
BodySegments.Add(Body);
|
|
|
|
if (i > 0)
|
|
{
|
|
Body->PreviousBody = BodySegments[i - 1];
|
|
BodySegments[i - 1]->NextBody = Body;
|
|
}
|
|
|
|
Body->SetAsHead(i == 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UM4_CentipedeController::Tick(float DeltaTime)
|
|
{
|
|
Super::Tick(DeltaTime);
|
|
|
|
if (BodySegments.Num() == 0) return;
|
|
|
|
// Déplacer la tête
|
|
AM4_CentipedeBody* Head = BodySegments[0];
|
|
if (Head && Head->bIsHead)
|
|
{
|
|
FVector CurrentPos = Head->GetActorLocation();
|
|
FVector2D SegmentDirection = GetSegmentDirection(Head);
|
|
|
|
bool bShouldDescend = CheckCollision(Head, SegmentDirection);
|
|
|
|
if (bShouldDescend)
|
|
{
|
|
FVector NewPos = CurrentPos - FVector(0.f, 0.f, CellSize);
|
|
Head->SetActorLocation(NewPos);
|
|
|
|
SegmentDirection.Y *= -1.f;
|
|
SetSegmentDirection(Head, SegmentDirection);
|
|
}
|
|
else
|
|
{
|
|
FVector NewPos = CurrentPos + FVector(0.f, SegmentDirection.Y * CentipedeSpeed * DeltaTime, 0.f);
|
|
Head->SetActorLocation(NewPos);
|
|
}
|
|
|
|
// Enregistrer position de la tête
|
|
SegmentHistory[0].Add(Head->GetActorLocation());
|
|
if (SegmentHistory[0].Num() > MaxHistorySize)
|
|
{
|
|
SegmentHistory[0].RemoveAt(0);
|
|
}
|
|
}
|
|
|
|
// Faire suivre les autres segments
|
|
for (int32 i = 1; i < BodySegments.Num(); ++i)
|
|
{
|
|
AM4_CentipedeBody* Segment = BodySegments[i];
|
|
if (!Segment) continue;
|
|
|
|
// Récupérer position dans l'historique du segment précédent
|
|
int32 PrevIndex = i - 1;
|
|
int32 HistoryLookback = SegmentSpacing;
|
|
|
|
if (SegmentHistory[PrevIndex].Num() > HistoryLookback)
|
|
{
|
|
int32 HistoryIndex = SegmentHistory[PrevIndex].Num() - HistoryLookback - 1;
|
|
FVector TargetPos = SegmentHistory[PrevIndex][HistoryIndex];
|
|
|
|
Segment->SetActorLocation(TargetPos);
|
|
|
|
// Enregistrer position actuelle
|
|
SegmentHistory[i].Add(Segment->GetActorLocation());
|
|
if (SegmentHistory[i].Num() > MaxHistorySize)
|
|
{
|
|
SegmentHistory[i].RemoveAt(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateHeadStatus();
|
|
}
|
|
|
|
bool UM4_CentipedeController::CheckCollision(AM4_CentipedeBody* Segment, FVector2D Direction)
|
|
{
|
|
if (!Segment) return false;
|
|
|
|
FVector CurrentPos = Segment->GetActorLocation();
|
|
FVector NextPos = CurrentPos + FVector(0.f, Direction.Y * CellSize, 0.f);
|
|
|
|
AM4_Gamemode* GM = Cast<AM4_Gamemode>(GetWorld()->GetAuthGameMode());
|
|
if (!GM) return false;
|
|
|
|
const float LeftBound = GM->MushroomSpawnBounds.Min.Y;
|
|
const float RightBound = GM->MushroomSpawnBounds.Max.Y;
|
|
|
|
if (NextPos.Y <= LeftBound || NextPos.Y >= RightBound)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FVector2D UM4_CentipedeController::GetSegmentDirection(AM4_CentipedeBody* Segment)
|
|
{
|
|
if (SegmentDirections.Contains(Segment))
|
|
{
|
|
return SegmentDirections[Segment];
|
|
}
|
|
return FVector2D(0.f, 1.f);
|
|
}
|
|
|
|
void UM4_CentipedeController::SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction)
|
|
{
|
|
if (Segment)
|
|
{
|
|
SegmentDirections.Add(Segment, Direction);
|
|
}
|
|
}
|
|
|
|
void UM4_CentipedeController::UpdateHeadStatus()
|
|
{
|
|
for (int32 i = 0; i < BodySegments.Num(); ++i)
|
|
{
|
|
AM4_CentipedeBody* Body = BodySegments[i];
|
|
if (!Body) continue;
|
|
|
|
bool bShouldBeHead = (i == 0) || (Body->PreviousBody == nullptr);
|
|
|
|
if (Body->bIsHead != bShouldBeHead)
|
|
{
|
|
Body->SetAsHead(bShouldBeHead);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UM4_CentipedeController::OnSegmentDestroyed(AM4_CentipedeBody* DestroyedSegment)
|
|
{
|
|
if (!DestroyedSegment) return;
|
|
|
|
int32 SegmentIndex = BodySegments.Find(DestroyedSegment);
|
|
if (SegmentIndex == INDEX_NONE) return;
|
|
|
|
if (SegmentIndex + 1 < BodySegments.Num())
|
|
{
|
|
AM4_CentipedeBody* NewHead = BodySegments[SegmentIndex + 1];
|
|
if (NewHead)
|
|
{
|
|
NewHead->SetAsHead(true);
|
|
NewHead->PreviousBody = nullptr;
|
|
}
|
|
}
|
|
|
|
if (SegmentIndex > 0 && BodySegments[SegmentIndex - 1])
|
|
{
|
|
BodySegments[SegmentIndex - 1]->NextBody = nullptr;
|
|
}
|
|
|
|
BodySegments.RemoveAt(SegmentIndex);
|
|
SegmentHistory.RemoveAt(SegmentIndex);
|
|
SegmentDirections.Remove(DestroyedSegment);
|
|
} |