From d20171032f9acdc3a1eb6d23fd51a8f7435a17b4 Mon Sep 17 00:00:00 2001 From: CatChow0 Date: Fri, 17 Oct 2025 00:23:09 +0200 Subject: [PATCH] =?UTF-8?q?Minor=20-=20Impl=C3=A9mente=20le=20comportement?= =?UTF-8?q?=20de=20la=20centipede=20-=20V01.11.00?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémente la logique de base du mouvement de la centipede, la gestion des collisions et l'ajustement de la direction. Ajoute les classes CentipedeBody et CentipedeController pour gérer les segments et le mouvement. Gère la destruction des segments et la création d'un nouveau segment de tête. --- Source/M4_CPP/private/M4_CentipedeBody.cpp | 30 +++ .../M4_CPP/private/M4_CentipedeController.cpp | 229 ++++++++++++++++++ Source/M4_CPP/private/M4_Gamemode.cpp | 53 +++- Source/M4_CPP/private/M4_Mushroom.cpp | 1 + Source/M4_CPP/public/M4_CentipedeBody.h | 25 ++ Source/M4_CPP/public/M4_CentipedeController.h | 50 ++++ Source/M4_CPP/public/M4_Gamemode.h | 8 + 7 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 Source/M4_CPP/private/M4_CentipedeBody.cpp create mode 100644 Source/M4_CPP/private/M4_CentipedeController.cpp create mode 100644 Source/M4_CPP/public/M4_CentipedeBody.h create mode 100644 Source/M4_CPP/public/M4_CentipedeController.h diff --git a/Source/M4_CPP/private/M4_CentipedeBody.cpp b/Source/M4_CPP/private/M4_CentipedeBody.cpp new file mode 100644 index 0000000..f43d0b1 --- /dev/null +++ b/Source/M4_CPP/private/M4_CentipedeBody.cpp @@ -0,0 +1,30 @@ +#include "M4_CentipedeBody.h" + +AM4_CentipedeBody::AM4_CentipedeBody() +{ + PrimaryActorTick.bCanEverTick = false; + + static ConstructorHelpers::FObjectFinder MeshRef(TEXT("/Game/CTP/04_Mesh/SM_Cube.SM_Cube")); + if (MeshRef.Succeeded()) + { + GetStaticMeshComponent()->SetStaticMesh(MeshRef.Object); + } + + GetStaticMeshComponent()->SetRelativeScale3D(FVector(1.f, 0.4f, 0.4f)); + GetStaticMeshComponent()->SetCollisionEnabled(ECollisionEnabled::QueryOnly); + GetStaticMeshComponent()->SetGenerateOverlapEvents(true); + GetStaticMeshComponent()->SetMobility(EComponentMobility::Movable); +} + +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)); + } +} diff --git a/Source/M4_CPP/private/M4_CentipedeController.cpp b/Source/M4_CPP/private/M4_CentipedeController.cpp new file mode 100644 index 0000000..445d7d8 --- /dev/null +++ b/Source/M4_CPP/private/M4_CentipedeController.cpp @@ -0,0 +1,229 @@ +#include "M4_CentipedeController.h" +#include "M4_Gamemode.h" + +AM4_CentipedeController::AM4_CentipedeController() +{ + PrimaryActorTick.bCanEverTick = true; + Root = CreateDefaultSubobject(TEXT("RootComponent")); + RootComponent = Root; +} + +void AM4_CentipedeController::BeginPlay() +{ + Super::BeginPlay(); + SpawnCentipede(); + + PreviousPositions.SetNum(BodyCount); + for (int i = 0; i < BodyCount; ++i) + { + PreviousPositions[i] = BodySegments[i]->GetActorLocation(); + } + + for (AM4_CentipedeBody* Segment : BodySegments) + { + SegmentDirections.Add(Segment, FVector2D(0.f, 1.f)); + } +} + +void AM4_CentipedeController::SpawnCentipede() +{ + FVector SpawnLocation = GetActorLocation(); + + UE_LOG(LogTemp, 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.")); + 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::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 AM4_CentipedeController::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + if (BodySegments.Num() == 0) return; + + TimeSinceLastMove += DeltaTime; + + if (TimeSinceLastMove >= MoveInterval) + { + TimeSinceLastMove = 0.f; + + TArray TempPositions; + TempPositions.SetNum(BodySegments.Num()); + + for (int32 i = 0; i < BodySegments.Num(); ++i) + { + TempPositions[i] = BodySegments[i]->GetActorLocation(); + } + + for (int32 i = 0; i < BodySegments.Num(); ++i) + { + 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(); + } + + UpdateHeadStatus(); + } +} + +bool AM4_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(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; + } + + // TODO: Ajouter vérification collision avec mushroom + // if (IsMushroomAt(NextPos)) return true; + + return false; +} + +FVector2D AM4_CentipedeController::GetSegmentDirection(AM4_CentipedeBody* Segment) +{ + if (SegmentDirections.Contains(Segment)) + { + return SegmentDirections[Segment]; + } + return FVector2D(0.f, 1.f); +} + +void AM4_CentipedeController::SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction) +{ + if (Segment) + { + SegmentDirections.Add(Segment, Direction); + } +} + +void AM4_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 AM4_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); + } + } + + 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()); +} \ 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 8a98972..27496e7 100644 --- a/Source/M4_CPP/private/M4_Gamemode.cpp +++ b/Source/M4_CPP/private/M4_Gamemode.cpp @@ -1,6 +1,7 @@ // Fill out your copyright notice in the Description page of Project Settings. #include "M4_Gamemode.h" +#include "M4_CTP_Macros.h" #include "M4_Mushroom.h" #include "M4_PlayerController.h" #include "M4_PlayerPawn.h" @@ -45,11 +46,30 @@ void AM4_Gamemode::BeginPlay() for (int32 i = 0; i < MushroomCount; ++i) { FIntPoint Cell; + bool bValidCell; + int32 MaxAttempts = 100; + int32 Attempts = 0; + do { Cell.X = FMath::RandRange(0, GridRows - 1); Cell.Y = FMath::RandRange(0, GridCols - 1); - } while (OccupiedCells.Contains(Cell)); + + // Check if cell and adjacent cells are free + bValidCell = !OccupiedCells.Contains(Cell) && + !OccupiedCells.Contains(FIntPoint(Cell.X - 1, Cell.Y)) && + !OccupiedCells.Contains(FIntPoint(Cell.X + 1, Cell.Y)) && + !OccupiedCells.Contains(FIntPoint(Cell.X, Cell.Y - 1)) && + !OccupiedCells.Contains(FIntPoint(Cell.X, Cell.Y + 1)); + + Attempts++; + } while (!bValidCell && Attempts < MaxAttempts); + + if (!bValidCell) + { + PRINT_SCREEN(TEXT("Could not find valid cell for mushroom"), FColor::Yellow); + continue; + } OccupiedCells.Add(Cell); @@ -58,7 +78,36 @@ void AM4_Gamemode::BeginPlay() SpawnLocation.Y = MushroomSpawnBounds.Min.Y + Cell.Y * CellSize + CellSize / 2.0f; SpawnLocation.X = -400.0f; - GetWorld()->SpawnActor(AM4_Mushroom::StaticClass(), SpawnLocation, FRotator::ZeroRotator); + GetWorld()->SpawnActor( + AM4_Mushroom::StaticClass(), + SpawnLocation, + FRotator::ZeroRotator + ); } } + + // Spawn centipede controller + PRINT_SCREEN(TEXT("Spawning Centipede Controller"), FColor::Green); + + FVector CentipedeSpawnLocation = FVector( + -400.0f, + 0.0f, + MushroomSpawnBounds.Max.X + CellSize + ); + + // 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) + { + CentipedeController->BodyCount = CentipedeBodyCount; + CentipedeController->CellSize = CellSize; + } + } diff --git a/Source/M4_CPP/private/M4_Mushroom.cpp b/Source/M4_CPP/private/M4_Mushroom.cpp index 234932a..a315033 100644 --- a/Source/M4_CPP/private/M4_Mushroom.cpp +++ b/Source/M4_CPP/private/M4_Mushroom.cpp @@ -34,6 +34,7 @@ AM4_Mushroom::AM4_Mushroom() // Custom preset for more advanced collision configuration GetStaticMeshComponent()->SetCollisionProfileName(UCollisionProfile::CustomCollisionProfileName); GetStaticMeshComponent()->SetCollisionResponseToChannel(ECC_Pawn, ECR_Overlap); + } void AM4_Mushroom::BeginPlay() diff --git a/Source/M4_CPP/public/M4_CentipedeBody.h b/Source/M4_CPP/public/M4_CentipedeBody.h new file mode 100644 index 0000000..14855b5 --- /dev/null +++ b/Source/M4_CPP/public/M4_CentipedeBody.h @@ -0,0 +1,25 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Engine/StaticMeshActor.h" +#include "M4_CentipedeBody.generated.h" + +UCLASS() +class M4_CPP_API AM4_CentipedeBody : public AStaticMeshActor +{ + GENERATED_BODY() + +public: + AM4_CentipedeBody(); + + UPROPERTY() + AM4_CentipedeBody* NextBody; + + UPROPERTY() + AM4_CentipedeBody* PreviousBody; + + UPROPERTY(EditAnywhere) + bool bIsHead = false; + + void SetAsHead(bool bHead); +}; \ No newline at end of file diff --git a/Source/M4_CPP/public/M4_CentipedeController.h b/Source/M4_CPP/public/M4_CentipedeController.h new file mode 100644 index 0000000..325b994 --- /dev/null +++ b/Source/M4_CPP/public/M4_CentipedeController.h @@ -0,0 +1,50 @@ +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "M4_CentipedeBody.h" +#include "M4_CentipedeController.generated.h" + +UCLASS() +class M4_CPP_API AM4_CentipedeController : public AActor +{ + GENERATED_BODY() + +public: + AM4_CentipedeController(); + + virtual void BeginPlay() override; + virtual void Tick(float DeltaTime) override; + + UPROPERTY(EditAnywhere) + int32 BodyCount = 10; + + UPROPERTY(EditAnywhere) + float CellSize = 50.f; + + UPROPERTY(EditAnywhere) + float MoveInterval = 0.1f; + + void OnSegmentDestroyed(AM4_CentipedeBody* DestroyedSegment); + +private: + UPROPERTY() + TArray BodySegments; + + UPROPERTY() + USceneComponent* Root; + + float TimeSinceLastMove = 0.f; + + 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 ef577a8..2271f9d 100644 --- a/Source/M4_CPP/public/M4_Gamemode.h +++ b/Source/M4_CPP/public/M4_Gamemode.h @@ -4,6 +4,7 @@ #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" +#include "M4_CentipedeController.h" #include "M4_Gamemode.generated.h" /** @@ -61,6 +62,13 @@ public: private: + + UPROPERTY(EditAnywhere, Category="Centipede") + int32 CentipedeBodyCount = 10; + + UPROPERTY() + TObjectPtr CentipedeController; + int Score = 0; int Lives = 3;