CrossyRoad minigame

This commit is contained in:
Oleg Petruny 2024-09-23 18:21:26 +02:00
parent 7c1610534c
commit 33ea9807d6
16 changed files with 342 additions and 9 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Input/Minigame/IMC_Minigame_CrossyRoad.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Levels/Test/L_Test.umap (Stored with Git LFS)

Binary file not shown.

BIN
Content/MaterialsLibrary/Colors/M_Color_Black.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/MaterialsLibrary/Colors/M_Color_White.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/MaterialsLibrary/Colors/M_Color_Yellow.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,11 +3,35 @@
#include "CrossyRoadManager.h"
#include "Camera/CameraComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/SplineComponent.h"
#include "EnhancedInputComponent.h"
#include "InputMappingContext.h"
#include "CrossyRoadObstacle.h"
#include "CustomGameInstanceBase.h"
#include "MainGameModeBase.h"
#include "PlayerBase.h"
#include "Widgets/WidgetsManager.h"
ACrossyRoadManager::ACrossyRoadManager()
{}
: AMinigame()
{
static ConstructorHelpers::FObjectFinder<UInputMappingContext> asset{ TEXT("/Script/EnhancedInput.InputMappingContext'/Game/Input/Minigame/IMC_Minigame_CrossyRoad.IMC_Minigame_CrossyRoad'") };
input = asset.Object;
auto root = CreateDefaultSubobject<USceneComponent>(TEXT("Scene"));
playerPos = CreateDefaultSubobject<USceneComponent>(TEXT("PlayerPos"));
playerPos->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform);
camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
camera->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform);
mannequin = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Mannequin"));
mannequin->AttachToComponent(root, FAttachmentTransformRules::KeepRelativeTransform);
}
void ACrossyRoadManager::Start(APlayerBase* playerPawn, FMinigameEndCallback delegate)
{
@ -17,11 +41,211 @@ void ACrossyRoadManager::Start(APlayerBase* playerPawn, FMinigameEndCallback del
AMinigame::Start(playerPawn, delegate);
player->LockPlayer(FPlayerLock::All());
player->SwitchToView(this);
for(auto& mapping : input.LoadSynchronous()->GetMappings())
{
if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Up"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Up).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Down"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Down).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Left"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Left).GetHandle());
else if(mapping.Action->ActionDescription.EqualTo(FText::AsCultureInvariant(TEXT("Right"))))
inputHandlers.Add(player->inputComponent->BindAction(mapping.Action, ETriggerEvent::Triggered, this, &ACrossyRoadManager::Right).GetHandle());
}
playerPos->SetWorldLocation(lines[0]->GetLocationAtTime(lines[0]->Duration * playerLineTime, ESplineCoordinateSpace::World));
mannequin->SetWorldLocation(playerPos->GetComponentLocation());
for(auto line : lines)
if(FMath::RandRange(0, 1))
RotateLine(line);
PrimaryActorTick.SetTickFunctionEnable(true);
}
void ACrossyRoadManager::End()
{
PrimaryActorTick.SetTickFunctionEnable(false);
for(int32 handler : inputHandlers)
player->inputComponent->RemoveActionBindingForHandle(handler);
player->UnlockPlayer(FPlayerLock::All());
player->ReturnPlayerView();
AMinigame::End();
}
void ACrossyRoadManager::Hit()
{
if(ended)
return;
ended = true;
if(score > 1000)
{
Finish();
return;
}
result = EMinigameResult::Loss;
End();
}
void ACrossyRoadManager::Finish()
{
result = EMinigameResult::Win;
End();
}
void ACrossyRoadManager::PostInitializeComponents()
{
AMinigame::PostInitializeComponents();
GetComponents<USplineComponent>(lines);
for(int32 i = 0; i < lines.Num(); ++i)
{
int32 id = FCString::Atoi(*lines[i]->GetName().Mid(6));
if(i != id - 1)
{
auto l = lines[i];
lines[i] = lines[id - 1];
lines[id - 1] = l;
}
linesRotated.Add(lines[i], false);
}
}
void ACrossyRoadManager::Tick(float deltaTime)
{
AMinigame::Tick(deltaTime);
time += deltaTime;
if(time >= spawnDelay / speed)
{
time = 0; //FMath::RandRange(0.0f, spawnDelay / 4 * speed); // (time -= spawnDelay) is not good on lags
CreateObstacle();
}
for(int32 i = 0; i < obstacles.Num(); ++i)
{
if(obstacles[i].time > obstacles[i].line->Duration)
{
obstacles[i].actor->Destroy();
obstacles.RemoveAt(i);
//CreateObstacle();
}
else
{
obstacles[i].time += deltaTime * speed * obstacles[i].speed;
obstacles[i].actor->SetActorLocation(obstacles[i].line->GetWorldLocationAtTime(obstacles[i].time));
}
}
}
void ACrossyRoadManager::CreateObstacle(int32 index)
{
auto obstacleClass = obstacleClasses.CreateIterator();
for(int32 i = 0; i < FMath::RandRange(0, obstacleClasses.Num() - 1); ++i) // std::advance not works :(
++obstacleClass;
auto spline = lines[FMath::RandRange(0, lines.Num() - 1)];
FActorSpawnParameters spawnParams{};
spawnParams.Owner = this;
auto obstacle = GetWorld()->SpawnActor<ACrossyRoadObstacle>(*obstacleClass, spline->GetComponentLocation(), GetActorRotation(), spawnParams);
obstacle->manager = this;
if(index < 0)
{
obstacles.Add({ .actor = obstacle, .line = spline, .time = 0, .speed = FMath::RandRange(0.2f, 1.0f) * speed });
}
else
{
obstacles[index].actor->Destroy();
obstacles[index].actor = obstacle;
obstacles[index].line = spline;
obstacles[index].time = 0;
obstacles[index].speed = FMath::RandRange(1.0f, 2.0f) * speed;
}
}
void ACrossyRoadManager::RotateLine(USplineComponent* line)
{
auto pos = line->GetComponentLocation();
auto rot = line->GetComponentRotation();
auto dist = line->GetWorldLocationAtSplinePoint(1) - line->GetWorldLocationAtSplinePoint(0);
if(linesRotated[line])
{
line->SetWorldLocationAndRotation(pos - dist, FRotator{ rot.Pitch, rot.Yaw - 180, rot.Roll });
linesRotated[line] = false;
}
else
{
line->SetWorldLocationAndRotation(pos + dist, FRotator{ rot.Pitch, rot.Yaw + 180, rot.Roll });
linesRotated[line] = true;
}
}
void ACrossyRoadManager::Up()
{
if(ended)
return;
auto line = lines[0];
auto diffY = lines[0]->GetComponentLocation().Y - lines[1]->GetComponentLocation().Y;
for(int32 i = 1; i < lines.Num(); ++i)
{
lines[i]->AddRelativeLocation(FVector{ 0, diffY, 0 });
lines[i - 1] = lines[i];
}
line->AddRelativeLocation(FVector{ 0, (lines.Num() - 1) * diffY * -1, 0 });
lines[lines.Num() - 1] = line;
//if(FMath::RandRange(0, 1))
// RotateLine(line);
speed += 0.15;
score += 100 * speed;
}
void ACrossyRoadManager::Down()
{
//
}
void ACrossyRoadManager::Left()
{
if(ended || playerLineTime >= 1.0f)
return;
playerLineTime += 0.1f;
float lineTime = playerLineTime;
if(linesRotated[lines[0]])
lineTime = 1 - lineTime;
playerPos->SetWorldLocation(lines[0]->GetLocationAtTime(lines[0]->Duration * lineTime, ESplineCoordinateSpace::World));
mannequin->SetWorldLocation(playerPos->GetComponentLocation());
}
void ACrossyRoadManager::Right()
{
if(ended || playerLineTime <= 0.0f)
return;
playerLineTime -= 0.1f;
float lineTime = playerLineTime;
if(linesRotated[lines[0]])
lineTime = 1 - lineTime;
playerPos->SetWorldLocation(lines[0]->GetLocationAtTime(lines[0]->Duration * lineTime, ESplineCoordinateSpace::World));
mannequin->SetWorldLocation(playerPos->GetComponentLocation());
}

View File

@ -16,4 +16,51 @@ public:
virtual void Start(class APlayerBase* playerPawn, FMinigameEndCallback delegate) override;
virtual void End() override;
void Hit();
void Finish();
protected:
struct ObstacleData
{
class ACrossyRoadObstacle* actor;
class USplineComponent* line;
float time;
float speed;
};
virtual void PostInitializeComponents() override;
virtual void Tick(float deltaTime) override;
void CreateObstacle(int32 index = -1);
void RotateLine(class USplineComponent* line);
void Up();
void Down();
void Left();
void Right();
UPROPERTY(EditDefaultsOnly)
TSet<TSubclassOf<class ACrossyRoadObstacle>> obstacleClasses;
UPROPERTY(EditAnywhere)
class UCameraComponent* camera;
UPROPERTY(EditAnywhere)
class USkeletalMeshComponent* mannequin;
UPROPERTY(EditDefaultsOnly)
float speed = 2;
UPROPERTY(EditDefaultsOnly)
float spawnDelay = 3;
float time = spawnDelay;
TArray<class USplineComponent*> lines;
TMap<class USplineComponent*, bool> linesRotated;
TArray<ObstacleData> obstacles;
TArray<int32> inputHandlers;
class USceneComponent* playerPos;
float playerLineTime = 0.5f;
};

View File

@ -0,0 +1,17 @@
// Oleg Petruny proprietary.
#include "CrossyRoadObstacle.h"
#include "CrossyRoadManager.h"
void ACrossyRoadObstacle::Hit()
{
if(!manager)
{
Destroy();
return;
}
manager->Hit();
}

View File

@ -0,0 +1,21 @@
// Oleg Petruny proprietary.
#pragma once
#include "GameFramework/Actor.h"
#include "CrossyRoadObstacle.generated.h"
UCLASS(Blueprintable, BlueprintType, MinimalAPI, Abstract)
class ACrossyRoadObstacle : public AActor
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
void Hit();
class ACrossyRoadManager* manager = nullptr;
};