Доступність ActorComponents глобально за допомогою C++ в Unreal Engine 5.x

pic

Часто нам потрібно взаємодіяти з компонентами акторів, коли ми розробляємо гру. У цій статті я розгляну деякі добрі практики коду, які полегшать розробку та зроблять код більш організованим.

Як приклад, давайте розглянемо наступний сценарій.

У футбольній грі є актор, який є необхідним “М'яч”, і нам потрібно постійно взаємодіяти з цим актором у грі. У більш типовому сценарії для того, щоб отримати актора “М'яч”, який активний у грі, нам потрібно написати код, подібний до цього…

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

Leave a Reply

Your email address will not be published. Required fields are marked *