Compare commits

...

4 Commits

Author SHA1 Message Date
NisemonoQ
f57f6b8832 Minor - Ajout de la logique de Projectile au PlayerPawn
Le playerPawn possède une fonction qui pourra appeler le script de projectile.
Prochaine étape, complétion du système de Projectile.
2025-10-17 15:27:19 +02:00
NisemonoQ
8733dfd87f Merge branch 'main' into Bullet 2025-10-17 14:02:35 +02:00
NisemonoQ
df25b61b8f Merge branch 'Bullet' 2025-10-17 14:01:37 +02:00
d20171032f 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.
2025-10-17 00:23:09 +02:00
12 changed files with 420 additions and 4 deletions

Binary file not shown.

View 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));
}
}

View 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());
}

View File

@@ -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;
}
}

View File

@@ -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()

View File

@@ -32,6 +32,12 @@ AM4_PlayerPawn::AM4_PlayerPawn()
{
MoveAction = MoveActionRef.Object;
}
static ConstructorHelpers::FObjectFinder<UInputAction> ShootActionRef(TEXT("/Game/CTP/03_Input/IA_Shoot.IA_Shoot"));
if (ShootActionRef.Succeeded())
{
ShootAction = ShootActionRef.Object;
}
}
void AM4_PlayerPawn::BeginPlay()
@@ -64,10 +70,23 @@ void AM4_PlayerPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputCompo
{
Input->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AM4_PlayerPawn::Move);
}
if (ShootAction)
{
Input->BindAction(ShootAction, ETriggerEvent::Triggered, this, &AM4_PlayerPawn::Shoot);
}
}
}
void AM4_PlayerPawn::Move(const FInputActionInstance& Instance)
{
LastMoveValue = Instance.GetValue().Get<FVector2D>().GetSafeNormal();
}
}
void AM4_PlayerPawn::Shoot(const FInputActionInstance& Inst)
{
if (Inst.GetValue().Get<bool>() == true)
{
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("Input Space Pressed"));
}
};

View File

@@ -11,7 +11,7 @@ AM4_Projectile::AM4_Projectile()
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
MeshComp-> SetRelativeScale3D(FVector(1.f, .1f,.1f));
MeshComp->BodyInstance.SetCollisionProfileName(TEXT("Projectile"));
MeshComp->OnComponentHit.AddDynamic(this, &AM4_Projectile::OnHit);
MeshComp->OnComponentHit.AddDynamic(this, &AM4_Projectile::OnHit);
}
void AM4_Projectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector Impulse, const FHitResult& Hit)

View 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);
};

View 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);
};

View File

@@ -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;

View File

@@ -18,6 +18,7 @@ public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
void Move(const FInputActionInstance& Instance);
void Shoot(const FInputActionInstance& Inst);
protected:
@@ -27,6 +28,10 @@ protected:
UPROPERTY()
UInputAction* MoveAction;
UPROPERTY()
UInputAction* ShootAction;
//bool HasShot = false;
float MoveSpeed = 500.f;
FVector2D MeshScale = FVector2D(0.6f, 0.5f);
FVector2D LastMoveValue = FVector2D::ZeroVector;