realtime development
Unity - GPU Instancing
Using Graphics.DrawMeshInstanced with a custom ScriptableObject derived class to instance large mesh counts.
Separates creation of PointClouds from their use by allowing configurable instancing using seeding.
500,000,000 Triangles at 15fps at decent quality.
Material overrides to come next.
Unreal C++ API TIPS
EDITOR/Project
Use the Blueprint Template instead of the C++
IDE
4.25 VSCODE bug intellisense wont work until defines in properties.json is updated
Header Files, Includes
If autocomplete is not working on an object in visual studio, #include it, the hint of what to include is in the Unreal docs.
To decrease compile times use forward declaration to declare classes, simply declare the class using in the header file after any includes; You will also need to #include the class into the body.
class MyClassName
Unreal basic types
Unreal uses it’s own basic types. These have many inbuilt helper methods.
STRINGS
Use the Text Macro to enclose any string;
FString MyFooString = FString(TEXT(“MyFoo“));
REFERENCES
The reference operator is the ampersand &. References function differently in C++ than in dynamically typed languages. They must be initialised, and they cannot be reassigned.
POINTERS
Pointers are a reference, that points to a memory address with a type.
Pointer syntax is tricky, the * operator is the key to creating a pointer.
You also need to use the address operator, &, to point to an address. The arrow operator, ->, is the way to reach into the pointer to extract it’s member variables.
FString MyString = "Foo"; FString *PtrToThing = &Foo; PtrToThing->Len();
Pointers are variables, so they can be reassigned if they still point to the correct type.
FString MyOtherString = "Baa"; PtrToThing = &MyOtherString
Always check your pointers before using them, always initialise them to nullptr
if(PtrSomeRef && PtrSomeRef.DoSomething())
COMPONENTS
Creating components can be done in code with the following static method;
CapsuleComp = CreateDefaultSubobject<UCapsuleComponent>(TEXT("Capsule Collider")); RootComponent = CapsuleComp; BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Base Mesh")); BaseMesh->SetupAttachment(RootComponent);
BLUEprint Properties
Properties can be exposed using the following decorator;
UPROPERTY() UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=”HEALTH”)
BLUEprint FUNCTIONS & Events
Blueprint functions can be called with events with this macro;
Blueprint functions can also be pure, they will not have an execution pin, as they will not modify the class.
UFUNCTION(BlueprintImplementableEvent) void CreateMeInBP(); UFUNCTION(BlueprintPure) bool IsDead() const;
Player Input
Player input is bound in the project settings, then bound in the SetupPlayerInputComponent() function like this;
PlayerInputComponent->BindAxis(TEXT("LookRight"), this, &AShooterCharacter::LookRight); PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &AShooterCharacter::Jump);
Delegates
Delegates function exactly the same in Unreal/C++ as they do in Unity/C#.
The AddDynamic method takes a reference to the binding component and a method that matches the signature of the delegate.
GetOwner()->OnTakeAnyDamage.AddDynamic(this, &UHealthComponent::TakeDamage);
DEBUG
Debug objects can be drawn by #include "DrawDebugHelpers.h"
DrawDebugCamera(GetWorld(), GetActorLocation(), GetActorRotation(), 90.0f, 2, FColor::Red, true);
Blackboard
Implementing a new Blackboard Task node will create a linker error. this can be resolved by adding GameplayTasks to your projects Build.cs file.
The node will need a name variable
Each node type has a few important virtual methods to override, the primary of those being;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "GameplayTasks" }); UBTTask_ClearBlackboardValue::UBTTask_ClearBlackboardValue() { NodeName = TEXT("Clear Blackboard Value"); } EBTNodeResult::Type UBTTask_ClearBlackboardValue::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) { Super::ExecuteTask(OwnerComp, NodeMemory); OwnerComp.GetBlackboardComponent()->ClearValue(GetSelectedBlackboardKey()); return EBTNodeResult::Succeeded; }