Doing some electrical work
The goal for this week was to come up with concepts for basic gameplay mechanics and start implementing them. Here are the three major mechanics I have come up with:
- Keeping a generator running
- The generator will need to be refueled at regular intervals
- Can be refueled at anytime
- If the generator runs out of fuel, all power will be lost. The player will then need to rely on a simple flashlight until refueled
- Repair radio tower
- There is a radio tower within the area that the player needs to maintain
- Something mysterious is damaging the tower. The player needs to manually repair it
- The radio tower plays non-licensed classical music
- Extra: Informs player of equipment locations/drops
- Configure equipment at base
- The radio tower relies on computers inside the main house to function
- The equipment breaks at random intervals and needs to be reset
That is the rough outline for what is to come. I decided to spend this week programming some of the generator code.
Lets Create Some Delegates!
In order to turn off all the lights at the same time, I decided to use Unreal Engine Delegates to bind all the lights to a single function in the main GameMode class. For the time being, I will be only creating a class for point lights. I may create a spotlight variant in the future for more flexibility.
To be honest, I still do not fully understand how delegates work, but I was able to find some tutorial videos. They were able to give me a rough idea as to how delegates work. The official Unreal Engine Documentation defines delegates as such,
Delegates can call member functions on C++ objects in a generic, type-safe way. A delegate can be bound dynamically to a member function of an arbitrary object, calling the function on the object at a future time, even if the caller does not know the object`s type.
https://docs.unrealengine.com
To get things working, I have created two classes. The first class was a new GameModeBase to create our two delegates in. When we create our PoweredLight class, they will all add their PowerOn and PowerOff functions to their respective delegate in MyGameModeBase.
// Events used for lights to subscribe to
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPowerActivate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPowerDeactivate);
UCLASS()
class GAME_API AMyGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable, Category = "EventDispatchers")
FPowerActivate PowerActivate;
UPROPERTY(BlueprintAssignable, Category = "EventDispatchers")
FPowerDeactivate PowerDeactivate;
UFUNCTION(BlueprintCallable, Category = "Power Function")
void TurnPowerOn();
UFUNCTION(BlueprintCallable, Category = "Power Function")
void TurnPowerOff();
void TestBroadcast();
bool tempBool = true;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
#include "MyGameModeBase.h"
void AMyGameModeBase::TestBroadcast() {
if (tempBool) {
PowerDeactivate.Broadcast();
tempBool = false;
}
else {
PowerActivate.Broadcast();
tempBool = true;
}
}
void AMyGameModeBase::TurnPowerOn() {
PowerActivate.Broadcast();
}
void AMyGameModeBase::TurnPowerOff() {
PowerDeactivate.Broadcast();
}
void AMyGameModeBase::BeginPlay() {
Super::BeginPlay();
UWorld* World = GetWorld();
// TODO: Temporary Code for binding lights to Player's E key
if (World) {
World->GetFirstPlayerController()->InputComponent->BindAction("Interact", IE_Released, this, &AMyGameModeBase::TestBroadcast);
}
}
For the sake of testing, I have set the Broadcast function for both delegates to the player’s E key. The “power” will toggle on and off when pressed.
Now that we have some delegates, lets create the point lights that will add their on/off functions to the MyGameModeBase Delegates
UCLASS()
class GAME_API APoweredLight : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
APoweredLight();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
UPointLightComponent* PointLight;
UFUNCTION()
void PowerOn();
UFUNCTION()
void PowerOff();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
#include "PoweredLight.h"
// Sets default values
APoweredLight::APoweredLight()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
PointLight = CreateDefaultSubobject<UPointLightComponent>(TEXT("PointLight"));
if (PointLight) {
PointLight->SetMobility(EComponentMobility::Stationary);
RootComponent = PointLight;
}
}
// Called when the game starts or when spawned
void APoweredLight::BeginPlay()
{
Super::BeginPlay();
AMyGameModeBase* currentGameMode = Cast<AMyGameModeBase>(GetWorld()->GetAuthGameMode());
if (currentGameMode) {
currentGameMode->PowerActivate.AddDynamic(this, &APoweredLight::PowerOn);
currentGameMode->PowerDeactivate.AddDynamic(this, &APoweredLight::PowerOff);
}
}
// Called every frame
void APoweredLight::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void APoweredLight::PowerOn() {
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, "Power On");
RootComponent->SetVisibility(true);
}
void APoweredLight::PowerOff() {
GEngine->AddOnScreenDebugMessage(-1, 10.f, FColor::Red, "Power Off");
RootComponent->SetVisibility(false);
}
Using the AddDynamic() function, we pass the address of the functions we want to be called when the power is turned on or off. For the time being, the lights simply set their visibility to true or false. I would like to add a flickering effect in the future.
Demo time!
As you can see, it works! With this setup, I am able to easily create new lights wherever I want and they will automatically be assigned to the proper delegates. I am also able to change individual attributes of these lights in instances where I need a certain color or intensity. Next week I will be implementing these lights with a generator that the player will be able to interact with.
Thanks for reading! See you next week.