Reflection on architecture
First Food class asset loading is hard-coded so in Build version is broken
Food.cpp
void AFood::BeginPlay()
{
Super::BeginPlay();
if (IsGeneratingBlueprint == true)
{
///...
GetActortFromFolder("/Game/Blueprints/FoodTypes", FoodLoadedArray);
}
// ...
}
Also Food class is God object which spawn Food(so Food is actually spawner)
Food.cpp
void AFood::GenerateFood(AFood* WhichFoodType)
{
///...
while (iterator < 150 && !flag)
{
NewCoords = FVector(FMath::RandRange(-2000 + GenerationClass->LastSpawnedSegmentEnd.X, 2000 + GenerationClass->LastSpawnedSegmentEnd.X), FMath::RandRange(-2000 + GenerationClass->LastSpawnedSegmentEnd.Y, 2000 + GenerationClass->LastSpawnedSegmentEnd.Y), 20);
TArray<FHitResult> HitResults;
UKismetSystemLibrary::SphereTraceMulti(World, NewCoords, NewCoords, radius, sphereTraceQuery, false,
ActorsToIgnore, EDrawDebugTrace::ForOneFrame, HitResults, true,
FLinearColor::Green, FLinearColor::Red, drawTime);
if (HitResults.Num() == 0)
{
if (this->id == 0)
{
if (!IsNewCoordsInSnakeSpawn(NewCoords.X, NewCoords.Y, 100))
{
UClass* FoodClass = WhichFoodType->GetClass();
AFood* SpawningFood = World->SpawnActor<AFood>(FoodClass,NewCoords,{0,0,0},SpawnParams);
}
}
else
{
this->SetActorLocation(NewCoords);
//...
There are many types of Food but the implementation is bad(must be inheritance)
Food.cpp
void AFood::Interact(AActor* Interactor, bool bIsHead)
{
if (bIsHead)
{
auto Snake = Cast<ASnakeBase>(Interactor);
if (IsValid(Snake))
{
switch (id)
{
case 1:
Snake->AddSnakeElement(1);
Snake->Hunger(HungerTime);
this->Destroy();
break;
case 2:
Snake->AddSnakeElement(2);
Snake->Hunger(HungerTime);
this->Destroy();
break;
case 3:
if (Snake->SnakeElements.Num() > 1)
{
Snake->RemoveSnakeElement(1);
Snake->Hunger(HungerTime);
this->Destroy();
}
else
{
Snake->Destroy();
}
//e.t.c ...
Infinite world generation just straightly tries random location to find free space for obstacle.Kinda not optimized and has many bottlenecks
Genetation.cpp
void AGeneration::GenerateObtacles(TArray<AObtacle*> ArrayOfObtacles, int count)
{
int iterator = 0;
bool flag = false;
FVector NewCoords;
const float radius = 32.0f;
ETraceTypeQuery sphereTraceQuery = ETraceTypeQuery::TraceTypeQuery1;
const TArray<AActor*> ActorsToIgnore;
const float drawTime = 5.0f;
FActorSpawnParameters SpawnParams;
AObtacle* GeneratingObtacle = nullptr;
UWorld* World = GetWorld();
FRotator NewRotation;
UE_LOG(LogTemp, Warning, TEXT("Initial length of ArrayOfObtacles: %d"), ArrayOfObtacles.Num());
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("World is null"));
return;
}
for (int i = 0; i < count; i++)
{
iterator = 0;
flag = false;
while (iterator < 150 && !flag)
{
NewCoords = FVector(FMath::RandRange(-2000 + LastSpawnedSegmentEnd.X, 2000 + LastSpawnedSegmentEnd.X), FMath::RandRange(-2000 +LastSpawnedSegmentEnd.Y, 2000 + LastSpawnedSegmentEnd.Y), 20);
NewRotation = FRotator(0, FMath::RandRange(0, 360), 0);
TArray<FHitResult> HitResults;
UKismetSystemLibrary::SphereTraceMulti(World, NewCoords, NewCoords, radius, sphereTraceQuery, false,
ActorsToIgnore, EDrawDebugTrace::ForOneFrame, HitResults, true,
FLinearColor::Green, FLinearColor::Red, drawTime);
if (HitResults.Num() == 0)
{
if (ArrayOfObtacles.Num() != 0)
{
GeneratingObtacle = ArrayOfObtacles[FMath::RandRange(0, ArrayOfObtacles.Num() - 1)];
if (!GeneratingObtacle)
{
UE_LOG(LogTemp, Error, TEXT("GeneratingObtacle is null before spawning"));
continue;
}
AObtacle* SpawnedObtacle = GetWorld()->SpawnActor<AObtacle>(GeneratingObtacle->GetClass(), NewCoords,NewRotation, SpawnParams);
if (!SpawnedObtacle)
{
UE_LOG(LogTemp, Error, TEXT("Failed to spawn AObtacle"));
continue;
}
UE_LOG(LogTemp, Warning, TEXT("Spawned Obtacle: %s"), *SpawnedObtacle->GetName());
flag = true;
UE_LOG(LogTemp, Warning, TEXT("Length of ArrayOfObtacles after spawning: %d"), ArrayOfObtacles.Num());
}
else
{
UE_LOG(LogTemp, Error, TEXT("ArrayOfObtacles is empty"));
}
}
else
{
UE_LOG(LogTemp, Error, TEXT("Hit results detected in trace"));
}
iterator++;
}
}
UE_LOG(LogTemp, Warning, TEXT("Final length of ArrayOfObtacles: %d"), ArrayOfObtacles.Num());
}
note
All user widgets in game are made in C++.Not actually a problem but it will be easier to make them in Blueprints