Minor - Implémente le comportement de la centipede - V01.11.00
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.
This commit is contained in:
30
Source/M4_CPP/private/M4_CentipedeBody.cpp
Normal file
30
Source/M4_CPP/private/M4_CentipedeBody.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "M4_CentipedeBody.h"
|
||||
|
||||
AM4_CentipedeBody::AM4_CentipedeBody()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = false;
|
||||
|
||||
static ConstructorHelpers::FObjectFinder<UStaticMesh> 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));
|
||||
}
|
||||
}
|
||||
229
Source/M4_CPP/private/M4_CentipedeController.cpp
Normal file
229
Source/M4_CPP/private/M4_CentipedeController.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include "M4_CentipedeController.h"
|
||||
#include "M4_Gamemode.h"
|
||||
|
||||
AM4_CentipedeController::AM4_CentipedeController()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
Root = CreateDefaultSubobject<USceneComponent>(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>(
|
||||
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<FVector> 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<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;
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
@@ -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>(AM4_Mushroom::StaticClass(), SpawnLocation, FRotator::ZeroRotator);
|
||||
GetWorld()->SpawnActor<AM4_Mushroom>(
|
||||
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>(
|
||||
AM4_CentipedeController::StaticClass(),
|
||||
CentipedeSpawnLocation,
|
||||
FRotator::ZeroRotator
|
||||
);
|
||||
|
||||
if (CentipedeController)
|
||||
{
|
||||
CentipedeController->BodyCount = CentipedeBodyCount;
|
||||
CentipedeController->CellSize = CellSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
25
Source/M4_CPP/public/M4_CentipedeBody.h
Normal file
25
Source/M4_CPP/public/M4_CentipedeBody.h
Normal file
@@ -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);
|
||||
};
|
||||
50
Source/M4_CPP/public/M4_CentipedeController.h
Normal file
50
Source/M4_CPP/public/M4_CentipedeController.h
Normal file
@@ -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<AM4_CentipedeBody*> BodySegments;
|
||||
|
||||
UPROPERTY()
|
||||
USceneComponent* Root;
|
||||
|
||||
float TimeSinceLastMove = 0.f;
|
||||
|
||||
TArray<FVector> PreviousPositions;
|
||||
|
||||
TMap<AM4_CentipedeBody*, FVector2D> SegmentDirections;
|
||||
|
||||
void SpawnCentipede();
|
||||
void UpdateHeadStatus();
|
||||
|
||||
bool CheckCollision(AM4_CentipedeBody* Segment, FVector2D Direction);
|
||||
|
||||
FVector2D GetSegmentDirection(AM4_CentipedeBody* Segment);
|
||||
void SetSegmentDirection(AM4_CentipedeBody* Segment, FVector2D Direction);
|
||||
};
|
||||
@@ -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<AM4_CentipedeController> CentipedeController;
|
||||
|
||||
int Score = 0;
|
||||
int Lives = 3;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user