Unreal Engine

C++

Cheat-Sheet

Deleting a C++ Class

  1. Close Visual Studio and UE4
  2. Delete the .cpp and .h files
  3. Delete the Binaries folder
  4. Right click the .uproject file and click Generate Visual Studio project Files

GameMode

  • Derive from GameModeBase
    • GameMode class has extra stuff for matches
  • Make GameMode blueprint, then in the Class Settings Details panel, make it derived from the C++ class
  • GameModeBase
    • GameMode
  • GameState
    • stores replicable game info
  • PlayerState
    • stores replicable player info

Gameplay

There are 4 main types that are inherited from: - UObject - AActor - UActorComponent - UStruct

Important Miscellaneous Stuff

// To get the player's pawn:
APawn* playerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();

// To get the player's location:
playerPawn->GetActorLocation();

// To iterate over all pawns:
for (FConstPawnIterator It = GetWorld()->GetPawnIterator(); It; ++It) {
    APawn* thePawn = It->Get();
    if (!thePawn || thePawn->IsPlayerControlled()) {
        continue;
    }
    USSomeComponent* someComponent = Cast<USSomeComponent>( thePawn->GetComponentByClass( USSomeComponent::StaticClass() ) );
}

// Iterating over PlayerControllers is the same
FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator();

// Getting the GameMode:
auto GM = GetWorld()->GetAuthGameMode();

// Getting the GameState from within the GameMode:
auto gameState = GetGameState<ASGameState>();

Calculating bullet spread

In SWeapon.h:

protected:
    UPROPERTY(EditDefaultsOnly, Category = "Weapon", meta = (ClampMin=0.0f))
    float Spread;
FVector ShotDirection = EyeRotation.Vector();

float HalfRad = FMath::DegreesToRadians(Spread/2);
ShotDirection = FMath::VRandCone(ShotDirection, HalfRad, HalfRad)

Timers

TimerExample.h

protected:
    FTimerHandle TimerHandle_Example;

TimerExample.cpp

// Setting a timer:
GetWorldTimerManager().SetTimer(TimerHandle_Example, this, &ASGameMode::ExampleFunc, 60, false); // Calls ExampleFunc every 60 seconds

// Checking if a timer is active:
GetWorldTimerManager().IsTimerActive(TimerHandle_Example);

// Clearing a timer:
GetWorldTimerManager().ClearTimer(TimerHandle_Example);

Events (Delegates)

This will just be a quick example to demonstrate how the system works. There are many types of delegates and events, so check the documentation for the specifics of each.

Documentation

All a delegate does is serve as a global pointer to a function that will be called. Anything that has a reference to the delegate can then call the function the delegate is pointing to.

Using them is a 3 step process:

  1. Declaring the event
  2. Binding the event (Telling the event which function to call)
  3. Executing/Broadcasting

Declaring the Event

Define the Events’s Type

Outside (before) class scope, within the header:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FItemAddedEvent, const FActorRecord&, AddedActorRecord);

Define the Event Itself

In the same header file, in the class:

public:
    UPROPERTY(BlueprintAssignable)
    FItemAddedEvent ItemAddedEvent;

Calling the event

ItemAddedEvent.Broadcast(record);

Blueprints

Timelines

  • Use one timeline with multiple lerps to change multiple variables of differing scales from the same timeline

Blueprint-Material Communication

  • Set a parameter in the material
  • In the blueprint constructor, add a Create Dynamic Material Instance node and promote its result to a variable. Use this in order to edit only this blueprint’s instance of the material.

C++ and Blueprint Interoperation

Properties

Declare properties in the header file with the UPROPERTY() macro.

Default values

Set defaults in the constructor. These will be overridden by values set in editor (if exposed via UPROPERTY). The following two examples are equivalent:

AMyActor::AMyActor() {
    TotalDamage = 200;
    DamageTimeInSeconds = 1.f;
}

AMyActor::AMyActor() :
    TotalDamage(200),
    DamageTimeInSeconds(1.f) {
}

To set a value based off of the defaults (e.g. damage per second), override PostInitProperties() and PostEditChangeProperty():

void AMyActor::PostInitProperties()
{
    Super::PostInitProperties();

    CalculateValues();
}

#if WITH_EDITOR
void AMyActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
    CalculateValues();

    Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif

void AMyActor::CalculateValues()
{
    DamagePerSecond = TotalDamage / DamageTimeInSeconds;
}

PostEditChangeProperty() ensures that the value for DPS is updated when the others affecting it are changed in the editor. Otherwise, it’ll only run during gameplay, displaying an inaccurate value.

UFUNCTION(...)

Blueprint Callable Functions

UFUNCTION(BlueprintCallable, Category="Damage")
void SomeFunction();

Category is required when using BlueprintCallable.

Blueprint Implementable Events

UFUNCTION(BlueprintImplementableEvent, Category="Damage")
void CalledFromCpp();

The preprocessor automatically generates the C++ implementation (thunk) of CalledFromCpp() that handles calling the actual Blueprint event. If you don’t actually have a Blueprint implementation, the call runs silently like an empty function.

Blueprint Overridable Events

In the header:

UFUNCTION(BlueprintNativeEvent, Category="Damage")
void CalledFromCpp();

BlueprintNativeEvent generates a function declaration MyFunction_Implementation, which you need to override:

In the header:

void CalledFromCpp_Implementation();

In the source file:

void AMyActor::CalledFromCpp_Implementation()
{
    // Do something cool here
}

Now, this C++ will run iff there is not an implementation in Blueprint.

Interfaces

Making an Interface Class

#pragma once

#include "UObject/Interface.h"
#include "Pickup.generated.h"

// This class does not need to be modified.
UINTERFACE(Blueprintable)
class UPickup : public UInterface
{
    GENERATED_BODY()
};

class YOURGAME_API IPickup
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
    void Pickup(AActor* Instigator);
    virtual void Pickup_Implementation(AActor* Instigator); // Default implementation
};

Source

#include "Pickup.h"

void IPickup::Pickup_Implementation(AActor* Instigator) { DoSomething(); }

Using the Interface

Extremely important note: If you want the default implementation in C++ to run for your Blueprint class, that Blueprint class must inherit a native C++ class which implements the interface itself. It will not run the default code otherwise.

https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/Reference/Interfaces#implementingyourinterfaceinc++

Sound

Cues

  • In a sound cue, select from random sound waves using the Random node
  • Use Modulate to vary the pitch

Physics

The Physics Type in PhAT overrides the Simulate Physics property of the owning component. In other words, having Simulate Physics enabled/disabled on a component only changes what the physics asset does if Physics Type in PhAT is set to Default for that bone!

Physical Animations

  1. Create/open the Physics Asset for the mesh
  2. Create a new physics profile under Window->Profiles
  3. Change to the profile in the icon bar above the viewport
  4. Select the joints (excluding root because this is not a joint)
  5. In the Details panel, under Physical Animation, click “Add to profile”
  6. In the blueprint for the item using physical animations, enable collision
  7. Use the Set Skeletal Mesh Component node for the Physical Animation component

Replication

Blueprint

C++

Example

In the constructor of the object to be replicated:

SetReplicates(true);
SetReplicateMovement(true);

You must also use a server function to handle the spawning of the object on the server. In the header:

UFUNCTION(Server, Reliable, WithValidation)
void ServerFire();

In the source file:

void Poop::ServerFire_Implementation() {
    
}

bool Poop::ServerFire_Validate() { // required since we used WithValidation in the header
    return true; // do actual checking here if you want real validation
}

Useful things

IsLocallyControlled();