Major - Implémente le contrôleur de mille-pattes - V2.0.0
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.
This commit is contained in:
@@ -10,21 +10,43 @@ AM4_CentipedeBody::AM4_CentipedeBody()
|
||||
GetStaticMeshComponent()->SetStaticMesh(MeshRef.Object);
|
||||
}
|
||||
|
||||
// Charger les matériaux par défaut (ajustez les chemins selon vos assets)
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> HeadMatRef(TEXT("/Game/CTP/05_Material/MI_Head.MI_Head"));
|
||||
if (HeadMatRef.Succeeded())
|
||||
{
|
||||
HeadMaterial = HeadMatRef.Object;
|
||||
}
|
||||
|
||||
static ConstructorHelpers::FObjectFinder<UMaterialInterface> BodyMatRef(TEXT("/Game/CTP/05_Material/MI_Body.MI_Body"));
|
||||
if (BodyMatRef.Succeeded())
|
||||
{
|
||||
BodyMaterial = BodyMatRef.Object;
|
||||
}
|
||||
|
||||
GetStaticMeshComponent()->SetRelativeScale3D(FVector(1.f, 0.4f, 0.4f));
|
||||
GetStaticMeshComponent()->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
||||
GetStaticMeshComponent()->SetGenerateOverlapEvents(true);
|
||||
GetStaticMeshComponent()->SetMobility(EComponentMobility::Movable);
|
||||
GetStaticMeshComponent()->SetCollisionObjectType(ECollisionChannel::ECC_WorldDynamic);
|
||||
GetStaticMeshComponent()->SetGenerateOverlapEvents(true);
|
||||
GetStaticMeshComponent()->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
||||
GetStaticMeshComponent()->SetCollisionProfileName(TEXT("OverlapAll"));
|
||||
}
|
||||
|
||||
void AM4_CentipedeBody::SetAsHead(bool bHead)
|
||||
{
|
||||
bIsHead = bHead;
|
||||
if (bIsHead)
|
||||
{
|
||||
GetStaticMeshComponent()->SetDefaultCustomPrimitiveDataVector4(0, FVector4(1.0, 0.0, 0.0, 1.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
GetStaticMeshComponent()->SetDefaultCustomPrimitiveDataVector4(0, FVector4(0.0, 1.0, 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
bIsHead = bHead;
|
||||
|
||||
if (bIsHead)
|
||||
{
|
||||
if (HeadMaterial)
|
||||
{
|
||||
GetStaticMeshComponent()->SetMaterial(0, HeadMaterial);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (BodyMaterial)
|
||||
{
|
||||
GetStaticMeshComponent()->SetMaterial(0, BodyMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,110 @@
|
||||
#include "M4_CentipedeController.h"
|
||||
#include "M4_LOG.h"
|
||||
#include "M4_Gamemode.h"
|
||||
|
||||
AM4_CentipedeController::AM4_CentipedeController()
|
||||
UM4_CentipedeController::UM4_CentipedeController()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
Root = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
|
||||
RootComponent = Root;
|
||||
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::BeginPlay()
|
||||
void UM4_CentipedeController::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
Super::BeginPlay();
|
||||
SpawnCentipede();
|
||||
Super::Initialize(Collection);
|
||||
|
||||
PreviousPositions.SetNum(BodyCount);
|
||||
for (int i = 0; i < BodyCount; ++i)
|
||||
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)
|
||||
{
|
||||
PreviousPositions[i] = BodySegments[i]->GetActorLocation();
|
||||
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());
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::SpawnCentipede()
|
||||
TStatId UM4_CentipedeController::GetStatId() const
|
||||
{
|
||||
FVector SpawnLocation = GetActorLocation();
|
||||
RETURN_QUICK_DECLARE_CYCLE_STAT(UM4_CentipedeController, STATGROUP_Tickables);
|
||||
}
|
||||
|
||||
void UM4_CentipedeController::SpawnCentipede()
|
||||
{
|
||||
AM4_Gamemode* GM = Cast<AM4_Gamemode>(GetWorld()->GetAuthGameMode());
|
||||
if (!GM) return;
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("Controller spawn location: X=%.2f, Y=%.2f, Z=%.2f"),
|
||||
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(LogTemp, Error, TEXT("Controller position is zero! Check spawn parameters."));
|
||||
UE_LOG(M4_CPP, Error, TEXT("Controller position is zero! Check spawn parameters."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,80 +133,73 @@ void AM4_CentipedeController::SpawnCentipede()
|
||||
}
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::Tick(float DeltaTime)
|
||||
void UM4_CentipedeController::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (BodySegments.Num() == 0) return;
|
||||
|
||||
TimeSinceLastMove += DeltaTime;
|
||||
|
||||
if (TimeSinceLastMove >= MoveInterval)
|
||||
// Déplacer la tête
|
||||
AM4_CentipedeBody* Head = BodySegments[0];
|
||||
if (Head && Head->bIsHead)
|
||||
{
|
||||
TimeSinceLastMove = 0.f;
|
||||
|
||||
TArray<FVector> TempPositions;
|
||||
TempPositions.SetNum(BodySegments.Num());
|
||||
|
||||
for (int32 i = 0; i < BodySegments.Num(); ++i)
|
||||
FVector CurrentPos = Head->GetActorLocation();
|
||||
FVector2D SegmentDirection = GetSegmentDirection(Head);
|
||||
|
||||
bool bShouldDescend = CheckCollision(Head, SegmentDirection);
|
||||
|
||||
if (bShouldDescend)
|
||||
{
|
||||
TempPositions[i] = BodySegments[i]->GetActorLocation();
|
||||
FVector NewPos = CurrentPos - FVector(0.f, 0.f, CellSize);
|
||||
Head->SetActorLocation(NewPos);
|
||||
|
||||
SegmentDirection.Y *= -1.f;
|
||||
SetSegmentDirection(Head, SegmentDirection);
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < BodySegments.Num(); ++i)
|
||||
else
|
||||
{
|
||||
AM4_CentipedeBody* Segment = BodySegments[i];
|
||||
|
||||
if (!Segment) continue;
|
||||
|
||||
if (Segment->bIsHead)
|
||||
{
|
||||
FVector CurrentPos = Segment->GetActorLocation();
|
||||
FVector2D SegmentDirection = GetSegmentDirection(Segment);
|
||||
|
||||
bool bShouldDescend = CheckCollision(Segment, SegmentDirection);
|
||||
|
||||
if (bShouldDescend)
|
||||
{
|
||||
FVector NewPos = CurrentPos - FVector(0.f, 0.f, CellSize);
|
||||
Segment->SetActorLocation(NewPos);
|
||||
|
||||
SegmentDirection.Y *= -1.f;
|
||||
SetSegmentDirection(Segment, SegmentDirection);
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("Head at index %d descending"), i);
|
||||
}
|
||||
else
|
||||
{
|
||||
FVector NewPos = CurrentPos + FVector(0.f, SegmentDirection.Y * CellSize, 0.f);
|
||||
Segment->SetActorLocation(NewPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
Segment->SetActorLocation(PreviousPositions[i - 1]);
|
||||
|
||||
if (BodySegments[i - 1])
|
||||
{
|
||||
FVector2D PrevDirection = GetSegmentDirection(BodySegments[i - 1]);
|
||||
SetSegmentDirection(Segment, PrevDirection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < BodySegments.Num(); ++i)
|
||||
{
|
||||
PreviousPositions[i] = BodySegments[i]->GetActorLocation();
|
||||
FVector NewPos = CurrentPos + FVector(0.f, SegmentDirection.Y * CentipedeSpeed * DeltaTime, 0.f);
|
||||
Head->SetActorLocation(NewPos);
|
||||
}
|
||||
|
||||
UpdateHeadStatus();
|
||||
// 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 AM4_CentipedeController::CheckCollision(AM4_CentipedeBody* Segment, FVector2D Direction)
|
||||
bool UM4_CentipedeController::CheckCollision(AM4_CentipedeBody* Segment, FVector2D Direction)
|
||||
{
|
||||
if (!Segment) return false;
|
||||
|
||||
@@ -154,13 +217,10 @@ bool AM4_CentipedeController::CheckCollision(AM4_CentipedeBody* Segment, FVector
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Ajouter vérification collision avec mushroom
|
||||
// if (IsMushroomAt(NextPos)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FVector2D AM4_CentipedeController::GetSegmentDirection(AM4_CentipedeBody* Segment)
|
||||
FVector2D UM4_CentipedeController::GetSegmentDirection(AM4_CentipedeBody* Segment)
|
||||
{
|
||||
if (SegmentDirections.Contains(Segment))
|
||||
{
|
||||
@@ -169,7 +229,7 @@ FVector2D AM4_CentipedeController::GetSegmentDirection(AM4_CentipedeBody* Segmen
|
||||
return FVector2D(0.f, 1.f);
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction)
|
||||
void UM4_CentipedeController::SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction)
|
||||
{
|
||||
if (Segment)
|
||||
{
|
||||
@@ -177,7 +237,7 @@ void AM4_CentipedeController::SetSegmentDirection(AM4_CentipedeBody* Segment, FV
|
||||
}
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::UpdateHeadStatus()
|
||||
void UM4_CentipedeController::UpdateHeadStatus()
|
||||
{
|
||||
for (int32 i = 0; i < BodySegments.Num(); ++i)
|
||||
{
|
||||
@@ -193,37 +253,29 @@ void AM4_CentipedeController::UpdateHeadStatus()
|
||||
}
|
||||
}
|
||||
|
||||
void AM4_CentipedeController::OnSegmentDestroyed(AM4_CentipedeBody* DestroyedSegment)
|
||||
void UM4_CentipedeController::OnSegmentDestroyed(AM4_CentipedeBody* DestroyedSegment)
|
||||
{
|
||||
if (!DestroyedSegment) return;
|
||||
|
||||
int32 SegmentIndex = BodySegments.Find(DestroyedSegment);
|
||||
|
||||
if (SegmentIndex == INDEX_NONE) return;
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("Segment %d destroyed"), SegmentIndex);
|
||||
|
||||
if (SegmentIndex + 1 < BodySegments.Num())
|
||||
{
|
||||
AM4_CentipedeBody* NewHead = BodySegments[SegmentIndex + 1];
|
||||
if (NewHead)
|
||||
{
|
||||
NewHead->SetAsHead(true);
|
||||
NewHead->PreviousBody = nullptr;
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("New head created at index %d"), SegmentIndex + 1);
|
||||
NewHead->PreviousBody = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (SegmentIndex > 0 && BodySegments[SegmentIndex - 1])
|
||||
{
|
||||
BodySegments[SegmentIndex - 1]->NextBody = nullptr;
|
||||
}
|
||||
|
||||
BodySegments.RemoveAt(SegmentIndex);
|
||||
PreviousPositions.RemoveAt(SegmentIndex);
|
||||
SegmentDirections.Remove(DestroyedSegment);
|
||||
|
||||
// TODO: Spawner un mushroom à la position du segment détruit
|
||||
// SpawnMushroomAt(DestroyedSegment->GetActorLocation());
|
||||
BodySegments.RemoveAt(SegmentIndex);
|
||||
SegmentHistory.RemoveAt(SegmentIndex);
|
||||
SegmentDirections.Remove(DestroyedSegment);
|
||||
}
|
||||
@@ -98,16 +98,13 @@ void AM4_Gamemode::BeginPlay()
|
||||
// PRINT SCREEN Max.X value
|
||||
PRINT_SCREEN(*FString::Printf(TEXT("Mushroom Spawn Bounds Max.X: %.2f"), MushroomSpawnBounds.Max.X), FColor::Green);
|
||||
|
||||
CentipedeController = GetWorld()->SpawnActor<AM4_CentipedeController>(
|
||||
AM4_CentipedeController::StaticClass(),
|
||||
CentipedeSpawnLocation,
|
||||
FRotator::ZeroRotator
|
||||
);
|
||||
|
||||
if (CentipedeController)
|
||||
UM4_CentipedeController* Controller = GetWorld()->GetSubsystem<UM4_CentipedeController>();
|
||||
if (Controller)
|
||||
{
|
||||
CentipedeController->BodyCount = CentipedeBodyCount;
|
||||
CentipedeController->CellSize = CellSize;
|
||||
Controller->BodyCount = CentipedeBodyCount;
|
||||
Controller->CellSize = CellSize;
|
||||
|
||||
Controller->StartCentipede(CentipedeSpawnLocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,9 +31,8 @@ AM4_Mushroom::AM4_Mushroom()
|
||||
const FVector2D MushroomScale = FVector2D(0.3f, 0.25f);
|
||||
GetStaticMeshComponent()->SetRelativeScale3D(FVector(1.f, 0.45f, 0.20f));
|
||||
|
||||
// Custom preset for more advanced collision configuration
|
||||
GetStaticMeshComponent()->SetCollisionProfileName(UCollisionProfile::CustomCollisionProfileName);
|
||||
GetStaticMeshComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap);
|
||||
// Set collision profile to overlap all
|
||||
GetStaticMeshComponent()->SetCollisionProfileName(TEXT("OverlapAll"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user