Часто нам потрібно взаємодіяти з компонентами акторів, коли ми розробляємо гру. У цій статті я розгляну деякі добрі практики коду, які полегшать розробку та зроблять код більш організованим.
Як приклад, давайте розглянемо наступний сценарій.
У футбольній грі є актор, який є необхідним “М'яч”, і нам потрібно постійно взаємодіяти з цим актором у грі. У більш типовому сценарії для того, щоб отримати актора “М'яч”, який активний у грі, нам потрібно написати код, подібний до цього…
const UWorld* World = GetWorld();
if (!World)
{
UE_LOG(LogTemp, Error, TEXT("World is null. Cannot find BallStaticMeshActor."));
return nullptr;
}
// Returns the first ABallStaticMeshActor in the world
for (TActorIterator It(World); It; ++It)
{
if (ABallStaticMeshActor* BallActor = *It; IsValid(BallActor))
{
UE_LOG(LogTemp, Error, TEXT("World is BallStaticMeshActor."));
}
}
Це повторюється кілька разів у грі, тому давайте створимо ActorComponent і інстанціюємо цей компонент у класі ABallStaticMeshActor.
Спочатку створимо інтерфейс для нашого компонента.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "CurrentBallComponentInterface.generated.h"
// Цей клас не потребує змін.
UINTERFACE()
class UCurrentBallComponentInterface : public UInterface
{
GENERATED_BODY()
};
/**
*
*/
class NEWPROJECT_API ICurrentBallComponentInterface
{
GENERATED_BODY()
// Додайте функції інтерфейсу до цього класу. Це клас, який буде успадкований для реалізації цього інтерфейсу.
public:
virtual AActor* CurrentBall() = 0;
};
Створення класу UCurrentBallComponent і реалізація інтерфейсу ICurrentBallComponentInterface
// CurrentBallComponent.h
#pragma once
#include "CoreMinimal.h"
#include "NewProject/Interfaces/StaticMeshActorComponents/CurrentBallComponentInterface.h"
#include "CurrentBallComponent.generated.h"
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class NEWPROJECT_API UCurrentBallComponent : public UActorComponent, public ICurrentBallComponentInterface
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UCurrentBallComponent();
virtual AActor* CurrentBall() override;
};
// CurrentBallComponent.cpp
#include "Components/StaticMeshActor/CurrentBallComponent.h"
UCurrentBallComponent::UCurrentBallComponent()
{
PrimaryComponentTick.bCanEverTick = true;
}
AActor* UCurrentBallComponent::CurrentBall()
{
return GetOwner();
}
Тепер давайте створимо Service, щоб зручно отримувати доступ до цього компонента з будь-якої частини нашої гри (Глобально).
// UCurrentBallService.h
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "NewProject/Interfaces/StaticMeshActorComponents/CurrentBallComponentInterface.h"
#include "CurrentBallService.generated.h"
/**
*
*/
UCLASS()
class NEWPROJECT_API UCurrentBallService : public UObject
{
GENERATED_BODY()
// Статична змінна для глобальної інстанції сервісу
static ICurrentBallComponentInterface* BallServiceInstance;
public:
static void RegisterService(ICurrentBallComponentInterface* Service);
static bool IsServiceRegistered();
static AActor* CurrentBall();
};
// UCurrentBallService.cpp
#include "Services/CurrentBall/CurrentBallService.h"
ICurrentBallComponentInterface* UCurrentBallService::BallServiceInstance = nullptr;
void UCurrentBallService::RegisterService(ICurrentBallComponentInterface* Service)
{
if (!Service)
{
UE_LOG(LogTemp, Log, TEXT("Невдало зареєстровано сервіс CurrentBallService."));
return;
}
BallServiceInstance = Service;
}
bool UCurrentBallService::IsServiceRegistered()
{
return BallServiceInstance != nullptr;
}
AActor* UCurrentBallService::CurrentBall()
{
return BallServiceInstance->CurrentBall();
}
Тепер у класі нашого актора “М'яч у футболі” — ABallStaticMeshActor, ми будемо “Реєструвати Сервіс”, щоб ми могли мати доступ до активного М'яча у грі.
// ABallStaticMeshActor.h
#pragma once
#include "CoreMinimal.h"
#include "Components/StaticMeshActor/CurrentBallComponent.h"
#include "BallStaticMeshActor.generated.h"
UCLASS()
class NEWPROJECT_API ABallStaticMeshActor : public AStaticMeshActor
{
GENERATED_BODY()
public:
ABallStaticMeshActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
void SetupComponents(); // Ініціалізуємо компоненти
void SetupServices() const; // Реєструємо сервіси
}
// ABallStaticMeshActor.cpp
#include "Application/BallStaticMeshActor/BallStaticMeshActor.h"
#include "Services/CurrentBall/CurrentBallService.h"
ABallStaticMeshActor::ABallStaticMeshActor()
{
PrimaryActorTick.bCanEverTick = true;
SetupComponents();
}
void ABallStaticMeshActor::BeginPlay()
{
Super::BeginPlay();
SetupServices();
}
void ABallStaticMeshActor::SetupServices() const
{
// Реєструємо сервіс
if (ICurrentBallComponentInterface* BallComponent = this->FindComponentByClass())
{
UCurrentBallService::RegisterService(BallComponent);
}
}
void ABallStaticMeshActor::SetupComponents()
{
CurrentBallComponent = CreateDefaultSubobject(TEXT("CurrentBall"));
CurrentBallComponent->RegisterComponent();
}
Тепер переходимо до цікавої частини: ми можемо отримати активний м'яч у грі — ABallStaticMeshActor через сервіс, який ми створили.
// Використовуйте в будь-якій частині гри, коли потрібно маніпулювати класом ABallStaticMeshActor
AActor* ActiveActor = UCurrentBallService::CurrentBall();
Сподіваюся, що це було корисно. До наступного разу 😊.
Перекладено з: Disponibilizando ActorComponents globalmente com c++ na Unreal Engine 5.x