From 987856ca094d93faad3f646afcd1ab58254fed1b Mon Sep 17 00:00:00 2001 From: CatChow0 Date: Fri, 17 Oct 2025 16:32:27 +0200 Subject: [PATCH] =?UTF-8?q?Major=20-=20Impl=C3=A9mente=20le=20contr=C3=B4l?= =?UTF-8?q?eur=20de=20mille-pattes=20-=20V2.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- Config/DefaultEngine.ini | 3 + Content/CTP/05_Material/MI_Body.uasset | Bin 0 -> 8972 bytes Content/CTP/05_Material/MI_Head.uasset | Bin 0 -> 8923 bytes Source/M4_CPP/private/M4_CentipedeBody.cpp | 46 +++- .../M4_CPP/private/M4_CentipedeController.cpp | 242 +++++++++++------- Source/M4_CPP/private/M4_Gamemode.cpp | 15 +- Source/M4_CPP/private/M4_Mushroom.cpp | 5 +- Source/M4_CPP/public/M4_CentipedeBody.h | 6 + Source/M4_CPP/public/M4_CentipedeController.h | 42 +-- Source/M4_CPP/public/M4_Gamemode.h | 2 +- 10 files changed, 225 insertions(+), 136 deletions(-) create mode 100644 Content/CTP/05_Material/MI_Body.uasset create mode 100644 Content/CTP/05_Material/MI_Head.uasset diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 1b1c8ac..5db7bd6 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -94,3 +94,6 @@ ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= + +[CoreRedirects] ++ClassRedirects=(OldName="/Script/M4_CPP.M4_CentipedeController",NewName="/Script/M4_CPP.M4_CentipedeController") \ No newline at end of file diff --git a/Content/CTP/05_Material/MI_Body.uasset b/Content/CTP/05_Material/MI_Body.uasset new file mode 100644 index 0000000000000000000000000000000000000000..092eb17535fb7582932967d7d2479a293fd684e6 GIT binary patch literal 8972 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.Object); } + // Charger les matériaux par défaut (ajustez les chemins selon vos assets) + static ConstructorHelpers::FObjectFinder HeadMatRef(TEXT("/Game/CTP/05_Material/MI_Head.MI_Head")); + if (HeadMatRef.Succeeded()) + { + HeadMaterial = HeadMatRef.Object; + } + + static ConstructorHelpers::FObjectFinder 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); + } + } +} \ No newline at end of file diff --git a/Source/M4_CPP/private/M4_CentipedeController.cpp b/Source/M4_CPP/private/M4_CentipedeController.cpp index 445d7d8..2c965cf 100644 --- a/Source/M4_CPP/private/M4_CentipedeController.cpp +++ b/Source/M4_CPP/private/M4_CentipedeController.cpp @@ -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(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::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(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 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); } \ No newline at end of file diff --git a/Source/M4_CPP/private/M4_Gamemode.cpp b/Source/M4_CPP/private/M4_Gamemode.cpp index 27496e7..304dfeb 100644 --- a/Source/M4_CPP/private/M4_Gamemode.cpp +++ b/Source/M4_CPP/private/M4_Gamemode.cpp @@ -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::StaticClass(), - CentipedeSpawnLocation, - FRotator::ZeroRotator - ); - - if (CentipedeController) + UM4_CentipedeController* Controller = GetWorld()->GetSubsystem(); + if (Controller) { - CentipedeController->BodyCount = CentipedeBodyCount; - CentipedeController->CellSize = CellSize; + Controller->BodyCount = CentipedeBodyCount; + Controller->CellSize = CellSize; + + Controller->StartCentipede(CentipedeSpawnLocation); } } diff --git a/Source/M4_CPP/private/M4_Mushroom.cpp b/Source/M4_CPP/private/M4_Mushroom.cpp index a315033..456a14f 100644 --- a/Source/M4_CPP/private/M4_Mushroom.cpp +++ b/Source/M4_CPP/private/M4_Mushroom.cpp @@ -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")); } diff --git a/Source/M4_CPP/public/M4_CentipedeBody.h b/Source/M4_CPP/public/M4_CentipedeBody.h index 14855b5..81a2416 100644 --- a/Source/M4_CPP/public/M4_CentipedeBody.h +++ b/Source/M4_CPP/public/M4_CentipedeBody.h @@ -22,4 +22,10 @@ public: bool bIsHead = false; void SetAsHead(bool bHead); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Materials") + UMaterialInterface* HeadMaterial; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Materials") + UMaterialInterface* BodyMaterial; }; \ No newline at end of file diff --git a/Source/M4_CPP/public/M4_CentipedeController.h b/Source/M4_CPP/public/M4_CentipedeController.h index 325b994..53a98ca 100644 --- a/Source/M4_CPP/public/M4_CentipedeController.h +++ b/Source/M4_CPP/public/M4_CentipedeController.h @@ -1,50 +1,60 @@ #pragma once #include "CoreMinimal.h" -#include "GameFramework/Actor.h" #include "M4_CentipedeBody.h" +#include "Subsystems/WorldSubsystem.h" #include "M4_CentipedeController.generated.h" UCLASS() -class M4_CPP_API AM4_CentipedeController : public AActor +class M4_CPP_API UM4_CentipedeController : public UTickableWorldSubsystem { GENERATED_BODY() public: - AM4_CentipedeController(); + UM4_CentipedeController(); + + virtual void Initialize(FSubsystemCollectionBase& Collection) override; + virtual void Deinitialize() override; + void StartCentipede(FVector SpawnLocation); - virtual void BeginPlay() override; virtual void Tick(float DeltaTime) override; + + virtual TStatId GetStatId() const override; - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Centipede") int32 BodyCount = 10; - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Centipede") float CellSize = 50.f; - UPROPERTY(EditAnywhere) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Centipede") float MoveInterval = 0.1f; - + + UFUNCTION(BlueprintCallable, Category = "Centipede") void OnSegmentDestroyed(AM4_CentipedeBody* DestroyedSegment); private: UPROPERTY() TArray BodySegments; - - UPROPERTY() - USceneComponent* Root; + TArray> SegmentHistory; // Historique pour chaque segment + int32 MaxHistorySize = 200; + + UPROPERTY(EditAnywhere, Category = "Centipede") + int32 SegmentSpacing = 15; // Distance en nombre de frames entre segments float TimeSinceLastMove = 0.f; - + float CentipedeSpeed = 400.f; + bool bFirstTick = true; + TArray PreviousPositions; - + TMap SegmentDirections; void SpawnCentipede(); void UpdateHeadStatus(); - + bool CheckCollision(AM4_CentipedeBody* Segment, FVector2D Direction); - + FVector2D GetSegmentDirection(AM4_CentipedeBody* Segment); void SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction); -}; \ No newline at end of file +}; diff --git a/Source/M4_CPP/public/M4_Gamemode.h b/Source/M4_CPP/public/M4_Gamemode.h index 2271f9d..0f940fc 100644 --- a/Source/M4_CPP/public/M4_Gamemode.h +++ b/Source/M4_CPP/public/M4_Gamemode.h @@ -67,7 +67,7 @@ private: int32 CentipedeBodyCount = 10; UPROPERTY() - TObjectPtr CentipedeController; + TObjectPtr CentipedeController; int Score = 0; int Lives = 3;