Game controller design

This commit is contained in:
nsubiron 2017-03-13 17:32:06 +01:00
parent 5b7eb7a75b
commit 0718b4ad23
12 changed files with 275 additions and 69 deletions

View File

@ -0,0 +1,26 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "CarlaGameController.h"
CarlaGameController::CarlaGameController()
{
bCanEverTick = true;
}
APlayerStart *CarlaGameController::ChoosePlayerStart(
const TArray<APlayerStart *> &AvailableStartSpots)
{
UE_LOG(LogCarla, Error, TEXT("Not implemented"));
return AvailableStartSpots[0u];
}
void CarlaGameController::RegisterPlayer(AController *NewPlayer)
{
UE_LOG(LogCarla, Error, TEXT("Not implemented"));
}
void CarlaGameController::Tick(float DeltaSeconds)
{
UE_LOG(LogCarla, Error, TEXT("Not implemented"));
}

View File

@ -0,0 +1,19 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "CarlaGameControllerBase.h"
/// Implements remote control of game and player.
class CARLA_API CarlaGameController : public CarlaGameControllerBase
{
public:
CarlaGameController();
virtual APlayerStart *ChoosePlayerStart(const TArray<APlayerStart *> &AvailableStartSpots) override;
virtual void RegisterPlayer(AController *NewPlayer) override;
virtual void Tick(float DeltaSeconds) override;
};

View File

@ -0,0 +1,31 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "Array.h"
class APlayerStart;
class AController;
/// Base class for a CARLA game controller.
class CARLA_API CarlaGameControllerBase
{
public:
virtual ~CarlaGameControllerBase() {}
bool CanEverTick() const
{
return bCanEverTick;
}
virtual APlayerStart *ChoosePlayerStart(const TArray<APlayerStart *> &AvailableStartSpots) = 0;
virtual void RegisterPlayer(AController *NewPlayer) = 0;
virtual void Tick(float DeltaSeconds) {}
protected:
bool bCanEverTick = false;
};

View File

@ -3,13 +3,96 @@
#include "Carla.h"
#include "CarlaGameMode.h"
#include "Engine/PlayerStartPIE.h"
#include "EngineUtils.h"
#include "GameFramework/PlayerStart.h"
#include "CarlaGameController.h"
#include "CarlaGameState.h"
#include "CarlaPlayerState.h"
#include "CarlaServerController.h"
#include "CarlaPlayerState.h"
#include "CarlaVehicleController.h"
#include "MockGameController.h"
ACarlaGameMode::ACarlaGameMode()
ACarlaGameMode::ACarlaGameMode() :
Super(),
GameController(nullptr)
{
PlayerControllerClass = ACarlaServerController::StaticClass();
PlayerControllerClass = ACarlaVehicleController::StaticClass();
GameStateClass = ACarlaGameState::StaticClass();
PlayerStateClass = ACarlaPlayerState::StaticClass();
PrimaryActorTick.bCanEverTick = true;
}
void ACarlaGameMode::InitGame(
const FString &MapName,
const FString &Options,
FString &ErrorMessage)
{
Super::InitGame(MapName, Options, ErrorMessage);
if (bUseMockController) {
GameController = MakeUnique<MockGameController>();
UE_LOG(LogCarla, Warning, TEXT("Using mock CARLA controller"));
} else {
GameController = MakeUnique<CarlaGameController>();
}
}
void ACarlaGameMode::RestartPlayer(AController* NewPlayer)
{
TArray<APlayerStart *> UnOccupiedStartPoints;
APlayerStart *PlayFromHere = FindUnOccupiedStartPoints(NewPlayer, UnOccupiedStartPoints);
if (PlayFromHere != nullptr) {
RestartPlayerAtPlayerStart(NewPlayer, PlayFromHere);
GameController->RegisterPlayer(NewPlayer);
return;
} else if (UnOccupiedStartPoints.Num() > 0u) {
APlayerStart *StartSpot = GameController->ChoosePlayerStart(UnOccupiedStartPoints);
if (StartSpot != nullptr) {
RestartPlayerAtPlayerStart(NewPlayer, UnOccupiedStartPoints[0u]);
GameController->RegisterPlayer(NewPlayer);
return;
}
}
UE_LOG(LogCarla, Error, TEXT("No start spot found!"));
}
void ACarlaGameMode::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
GameController->Tick(DeltaSeconds);
}
APlayerStart *ACarlaGameMode::FindUnOccupiedStartPoints(
AController *Player,
TArray<APlayerStart *> &UnOccupiedStartPoints)
{
APlayerStart* FoundPlayerStart = nullptr;
UClass* PawnClass = GetDefaultPawnClassForController(Player);
APawn* PawnToFit = PawnClass ? PawnClass->GetDefaultObject<APawn>() : nullptr;
for (TActorIterator<APlayerStart> It(GetWorld()); It; ++It) {
APlayerStart* PlayerStart = *It;
if (PlayerStart->IsA<APlayerStartPIE>()) {
FoundPlayerStart = PlayerStart;
break;
} else {
FVector ActorLocation = PlayerStart->GetActorLocation();
const FRotator ActorRotation = PlayerStart->GetActorRotation();
if (!GetWorld()->EncroachingBlockingGeometry(PawnToFit, ActorLocation, ActorRotation)) {
UnOccupiedStartPoints.Add(PlayerStart);
}
#ifdef WITH_EDITOR
else if (GetWorld()->FindTeleportSpot(PawnToFit, ActorLocation, ActorRotation)) {
UE_LOG(
LogCarla,
Warning,
TEXT("Player start cannot be used, occupied location: %s"),
*PlayerStart->GetActorLocation().ToString());
}
#endif // WITH_EDITOR
}
}
return FoundPlayerStart;
}

View File

@ -3,8 +3,11 @@
#pragma once
#include "GameFramework/GameModeBase.h"
#include "CarlaGameControllerBase.h"
#include "CarlaGameMode.generated.h"
class APlayerStart;
/**
*
*/
@ -13,7 +16,29 @@ class CARLA_API ACarlaGameMode : public AGameModeBase
{
GENERATED_BODY()
/** Use a mock controller instead of connecting to a client */
UPROPERTY(Category = "CARLA Game Controller", EditAnywhere)
bool bUseMockController = false;
public:
ACarlaGameMode();
virtual void InitGame(const FString &MapName, const FString &Options, FString &ErrorMessage) override;
virtual void RestartPlayer(AController *NewPlayer) override;
virtual void Tick(float DeltaSeconds) override;
private:
/// Iterate all the APlayerStart present in the world and add the ones with
/// unoccupied locations to @a UnOccupiedStartPoints.
///
/// @return APlayerStart if "Play from Here" was used while in PIE mode.
APlayerStart *FindUnOccupiedStartPoints(
AController *Player,
TArray<APlayerStart *> &UnOccupiedStartPoints);
TUniquePtr<CarlaGameControllerBase> GameController;
};

View File

@ -13,7 +13,17 @@ class CARLA_API ACarlaPlayerState : public APlayerState
{
GENERATED_BODY()
public:
/// @todo ?
// virtual void Reset();
// virtual void CopyProperties(APlayerState *PlayerState);
void RegisterCollision(AActor *Actor, FVector NormalImpulse) {}
FVector Location;
FVector Orientation;
float ForwardSpeed;
};

View File

@ -1,37 +0,0 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "CarlaServerController.h"
#include "DrawDebugHelpers.h"
#include "Engine.h"
void ACarlaServerController::Tick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
if (!IsPossessingAVehicle())
return;
auto Speed = GetVehicleForwardSpeed();
auto Location = GetVehicleLocation();
auto Orientation = GetVehicleOrientation();
auto LineEndPoint = Location + std::min(1.0f, Speed) * 1000.0f * Orientation;
auto Color = FColor(255u, 0u, 0u);
DrawDebugPoint(GetWorld(), Location, 10.0f, Color);
DrawDebugLine(GetWorld(), Location, LineEndPoint, Color);
}
void ACarlaServerController::OnCollisionEvent(
AActor* Actor,
AActor* OtherActor,
FVector NormalImpulse,
const FHitResult& Hit)
{
if(GEngine)
GEngine->AddOnScreenDebugMessage(
-1,
15.0f,
FColor::Yellow,
FString::Printf(TEXT("We hit a %s"), *OtherActor->GetName()));
}

View File

@ -1,25 +0,0 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "CarlaVehicleController.h"
#include "CarlaServerController.generated.h"
/**
*
*/
UCLASS()
class CARLA_API ACarlaServerController : public ACarlaVehicleController
{
GENERATED_BODY()
public:
virtual void Tick(float DeltaTime) override;
virtual void OnCollisionEvent(
AActor* Actor,
AActor* OtherActor,
FVector NormalImpulse,
const FHitResult& Hit) override;
};

View File

@ -13,7 +13,9 @@
// -- Constructor and destructor -----------------------------------------------
// =============================================================================
ACarlaVehicleController::ACarlaVehicleController()
ACarlaVehicleController::ACarlaVehicleController() :
Super(),
MovementComponent(nullptr)
{
bAutoManageActiveCameraTarget = false;
@ -60,14 +62,35 @@ void ACarlaVehicleController::SetupInputComponent()
void ACarlaVehicleController::Possess(APawn *aPawn)
{
Super::Possess(aPawn);
if (IsPossessingAVehicle()) {
UE_LOG(LogCarla, Error, TEXT("Controller already possessing a pawn!"));
return;
}
auto *WheeledVehicle = Cast<AWheeledVehicle>(aPawn);
if (WheeledVehicle != nullptr) {
// Attach the camera's spring arm to the pawn.
SpringArm->AttachToComponent(
aPawn->GetRootComponent(),
FAttachmentTransformRules::KeepRelativeTransform);
// Bind hit events.
aPawn->OnActorHit.AddDynamic(this, &ACarlaVehicleController::OnCollisionEvent);
// Get vehicle movement component.
MovementComponent = WheeledVehicle->GetVehicleMovementComponent();
check(MovementComponent != nullptr);
// Get custom player state.
CarlaPlayerState = Cast<ACarlaPlayerState>(PlayerState);
check(CarlaPlayerState != nullptr);
}
}
void ACarlaVehicleController::Tick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
if (IsPossessingAVehicle()) {
CarlaPlayerState->Location = GetVehicleLocation();
CarlaPlayerState->Orientation = GetVehicleOrientation();
CarlaPlayerState->ForwardSpeed = GetVehicleForwardSpeed();
}
}
@ -142,10 +165,13 @@ void ACarlaVehicleController::ToggleManualMode()
// =============================================================================
void ACarlaVehicleController::OnCollisionEvent(
AActor* Actor,
AActor* /*Actor*/,
AActor* OtherActor,
FVector NormalImpulse,
const FHitResult& Hit) {}
const FHitResult& /*Hit*/)
{
CarlaPlayerState->RegisterCollision(OtherActor, NormalImpulse);
}
// =============================================================================
// -- Input bindings -----------------------------------------------------------

View File

@ -38,6 +38,8 @@ public:
virtual void Possess(APawn *aPawn) override;
virtual void Tick(float DeltaTime) override;
virtual void CalcCamera(float DeltaTime, FMinimalViewInfo& OutResult) override;
/// @}
@ -105,7 +107,7 @@ public:
private:
UFUNCTION()
virtual void OnCollisionEvent(
void OnCollisionEvent(
AActor* Actor,
AActor* OtherActor,
FVector NormalImpulse,
@ -150,4 +152,7 @@ private:
UPROPERTY()
UWheeledVehicleMovementComponent *MovementComponent;
// Cast for quick access to the custom player state.
ACarlaPlayerState *CarlaPlayerState;
};

View File

@ -0,0 +1,26 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#include "Carla.h"
#include "MockGameController.h"
MockGameController::MockGameController()
{
bCanEverTick = false;
}
APlayerStart *MockGameController::ChoosePlayerStart(
const TArray<APlayerStart *> &AvailableStartSpots)
{
return AvailableStartSpots[0u];
}
void MockGameController::RegisterPlayer(AController *NewPlayer)
{
ACarlaVehicleController *VehicleController = Cast<ACarlaVehicleController>(NewPlayer);
if (VehicleController != nullptr) {
if (!VehicleController->IsInManualMode())
VehicleController->SetManualMode(true);
} else {
UE_LOG(LogCarla, Warning, TEXT("Player is not a ACarlaVehicleController"));
}
}

View File

@ -0,0 +1,17 @@
// CARLA, Copyright (C) 2017 Computer Vision Center (CVC)
#pragma once
#include "CarlaGameControllerBase.h"
/// Mocks the CARLA game controller class for testing purposes.
class CARLA_API MockGameController : public CarlaGameControllerBase
{
public:
MockGameController();
virtual APlayerStart *ChoosePlayerStart(const TArray<APlayerStart *> &AvailableStartSpots) override;
virtual void RegisterPlayer(AController *NewPlayer) override;
};