Unreal.hx
Using Haxe with the Unreal Engine 4
http://slides.com/cauewaneck/unreal-hx
By Cauê Waneck ( @cwaneck )
[~]$ whoami
- Chosen engine for many AAA games
- Targets all major platforms - desktop, mobile and console
- Source code available!
- Great out-of-the-box multiplayer support
Let's use it with Haxe then!
- C++ is the only official language
C++ can be difficult to integrate with itself let alone other languages
- Swig documentation about C++
- Supports also visual coding with Blueprints
Unreal.hx !
- Write in Haxe anything that would otherwise need to be compiled in C++
- Extend C++ classes, override functions, call super... You name it
- Reference external C++ code as easily as possible
- Allow it to work with cppia for very fast iteration times
- Now that you mention... Make it compatible with DCE too
- Did I mention support EVERY feature?
The big picture
- Needs latest Haxe 3.3+
- Targets Unreal 4.11
- Notorious directories:
- Externs
- Scripts/Static
Externs
package unreal;
/**
Actor is the base class for an Object that can be placed or spawned in a level.
Actors may contain a collection of ActorComponents, which can be used to control how actors move, how they are rendered, etc.
The other main function of an Actor is the replication of properties and function calls across the network during play.
@see https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/
@see UActorComponent
**/
@:glueCppIncludes("GameFramework/Actor.h")
@:uextern extern class AActor extends unreal.UObject {
/**
Array of ActorComponents that are created by blueprints and serialized per-instance.
**/
public var BlueprintCreatedComponents : unreal.TArray<unreal.UActorComponent>;
#if WITH_EDITORONLY_DATA
/**
Local space pivot offset for the actor
**/
private var PivotOffset : unreal.FVector;
#end // WITH_EDITORONLY_DATA
/**
Returns the point of view of the actor.
Note that this doesn't mean the camera, but the 'eyes' of the actor.
For example, for a Pawn, this would define the eye height location,
and view rotation (which is different from the pawn rotation which has a zeroed pitch component).
A camera first person view will typically use this view point. Most traces (weapon, AI) will be done from this view point.
@param OutLocation - location of view point
@param OutRotation - view rotation of actor.
**/
@:thisConst public function GetActorEyesViewPoint(OutLocation : unreal.PRef<unreal.FVector>, OutRotation : unreal.PRef<unreal.FRotator>) : Void;
/**
Script exposed version of FindComponentByClass
**/
public function GetComponentByClass(ComponentClass : unreal.TSubclassOf<unreal.UActorComponent>) : unreal.UActorComponent;
package unreal;
/**
Actor is the base class for an Object that can be placed or spawned in a level.
Actors may contain a collection of ActorComponents, which can be used to control how actors move, how they are rendered, etc.
The other main function of an Actor is the replication of properties and function calls across the network during play.
@see https://docs.unrealengine.com/latest/INT/Programming/UnrealArchitecture/Actors/
@see UActorComponent
**/
@:glueCppIncludes("GameFramework/Actor.h")
@:uextern class AActor extends unreal.UObject {
/**
Array of ActorComponents that are created by blueprints and serialized per-instance.
**/
public var BlueprintCreatedComponents(get,set):unreal.PPtr<unreal.TArray<unreal.UActorComponent>>;
@:glueCppIncludes("GameFramework/Actor.h", "uhx/Wrapper.h")
@:glueHeaderIncludes("IntPtr.h", "VariantPtr.h")
@:glueHeaderCode("static void GetActorEyesViewPoint(unreal::UIntPtr self, unreal::VariantPtr OutLocation, unreal::VariantPtr OutRotation);")
@:glueCppCode("void uhx::glues::AActor_Glue_obj::GetActorEyesViewPoint(unreal::UIntPtr self, unreal::VariantPtr OutLocation, unreal::VariantPtr OutRotation) {\n\t( (AActor *) self )->GetActorEyesViewPoint(*::uhx::StructHelper< FVector >::getPointer(OutLocation), *::uhx::StructHelper< FRotator >::getPointer(OutRotation));\n}")
@:thisConst
public function GetActorEyesViewPoint(OutLocation : unreal.PRef<unreal.FVector>, OutRotation : unreal.PRef<unreal.FRotator>) : Void {
uhx.glues.AActor_Glue.GetActorEyesViewPoint(unreal.helpers.HaxeHelpers.getUObjectWrapped(this), OutLocation, OutRotation);
}
@:glueCppIncludes("GameFramework/Actor.h", "uhx/Wrapper.h", "Containers/Array.h", "Components/ActorComponent.h", "uhx/glues/TArrayImpl_Glue_UE.h")
@:glueHeaderIncludes("IntPtr.h", "VariantPtr.h")
@:glueHeaderCode("static unreal::VariantPtr get_BlueprintCreatedComponents(unreal::UIntPtr self);")
@:glueCppCode("unreal::VariantPtr uhx::glues::AActor_Glue_obj::get_BlueprintCreatedComponents(unreal::UIntPtr self) {\n\treturn ::uhx::TemplateHelper<TArray<UActorComponent *>>::fromPointer( (&(( (AActor *) self )->BlueprintCreatedComponents)) );\n}")
@:final @:nonVirtual
private function get_BlueprintCreatedComponents() : unreal.PPtr<unreal.TArray<unreal.UActorComponent>> {
return ( @:privateAccess unreal.TArrayImpl.fromPointer( uhx.glues.AActor_Glue.get_BlueprintCreatedComponents(unreal.helpers.HaxeHelpers.getUObjectWrapped(this)) ) : unreal.PPtr<unreal.TArray<unreal.UActorComponent>> );
}
/**
Script exposed version of FindComponentByClass
**/
@:glueCppIncludes("GameFramework/Actor.h", "UObject/ObjectBase.h", "Components/ActorComponent.h")
@:glueHeaderIncludes("IntPtr.h")
@:glueHeaderCode("static unreal::UIntPtr GetComponentByClass(unreal::UIntPtr self, unreal::UIntPtr ComponentClass);")
@:glueCppCode("unreal::UIntPtr uhx::glues::AActor_Glue_obj::GetComponentByClass(unreal::UIntPtr self, unreal::UIntPtr ComponentClass) {\n\treturn ( (unreal::UIntPtr) (( (AActor *) self )->GetComponentByClass(( (TSubclassOf<UActorComponent>) (UClass *) ComponentClass ))) );\n}")
public function GetComponentByClass(ComponentClass : unreal.TSubclassOf<unreal.UActorComponent>) : unreal.UActorComponent {
return ( cast unreal.UObject.wrap(uhx.glues.AActor_Glue.GetComponentByClass(unreal.helpers.HaxeHelpers.getUObjectWrapped(this), unreal.helpers.HaxeHelpers.getUObjectWrapped(ComponentClass))) : unreal.UActorComponent );
}
Externs
// you define templated functions and types!
@:glueCppIncludes('Templates/SharedPointer.h')
@:uextern extern class TSharedPtr<T> {
@:global
public static function MakeShareable<T>(ptr:PPtr<T>):TSharedPtr<T>;
/**
* Constructs an empty shared pointer
*/
@:uname('.ctor') public static function create<T>():TSharedPtr<T>;
/* snip ... */
}
// you can also define C++ enums!
@:glueCppIncludes("IHttpRequest.h")
@:uname("EHttpRequestStatus.Type")
@:uextern extern enum EHttpRequestStatus {
NotStarted;
Processing;
Failed;
Succeeded;
}
// and delegates! Yes you can bind normal Haxe functions to them
@:glueCppIncludes("IHttpRequest.h")
@:uname('FHttpRequestCompleteDelegate')
typedef FHttpRequestCompleteDelegate = Delegate<FHttpRequestCompleteDelegate, TSharedPtr<IHttpRequest>->TThreadSafeSharedPtr<IHttpResponse>->Bool->Void>;
// also C++ method pointers:
/**
* Binds a delegate function to an Action defined in the project settings.
* Returned reference is only guaranteed to be valid until another action is bound.
*/
public function BindAction(actionName:Const<FName>, keyEvent:EInputEvent, object:UObject, func:MethodPointer<UObject,Void->Void>) : PRef<FInputActionBinding>;
// also some operators are supported
@:glueCppIncludes("Array.h")
@:uextern @:noCopy extern class TIndexedContainerIterator<Ar, El, Ind> {
public function op_Increment() : Void;
public function op_Decrement() : Void;
public function op_Dereference() : PRef<El>;
public function op_Not() : Bool;
}
Static/Scripts
- That's where your code will live!
- Static - always compiled as C++ code for performance
- Scripts - sometimes compiled as a cppia script for much faster compilation!
Static/Scripts
package platformer;
import unreal.*;
using unreal.CoreAPI;
// define a new delegate
typedef FRoundFinishedDelegate = DynamicMulticastDelegate<FRoundFinishedDelegate, Void->Void>;
// you can define your own UENUMs through Haxe
@:uenum
enum EGameState {
Intro;
Waiting;
Playing;
Finished;
Restarting;
}
@:uclass
// you can use @:uname to define the target name, without having to use that
@:uname("APlatformerGameMode")
class GameMode extends AGameMode {
/** delegate to broadcast about finished round */
@:uproperty(BlueprintAssignable)
public var OnRoundFinished:FRoundFinishedDelegate;
/* snip .. */
}
@:uclass(Abstract)
@:uname("APlatformerCharacter")
class Character extends ACharacter {
/** player pawn initialization */
override public function PostInitializeComponents():Void {
super.PostInitializeComponents();
SetActorRotation(FRotator.createWithValues(0,0,0));
}
/* snip ... */
}
Unreal Memory Model
UObjects
- Game objects - all derive from the UObject class
- Have either A or U prefix on their name
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "PlatformerClimbMarker.generated.h"
UCLASS(Blueprintable)
class APlatformerClimbMarker : public AActor
{
GENERATED_UCLASS_BODY()
private:
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category=Mesh, meta=(AllowPrivateAccess="true"))
UStaticMeshComponent* Mesh;
public:
/** Returns Mesh subobject **/
FORCEINLINE UStaticMeshComponent* GetMesh() const { return Mesh; }
};
UObjects
- Unreal-driven reflection using a header parser
- Garbage collected - uses reflection to know the references to visit
- Only valid references are member variables with the UPROPERTY metadata
- Can be replicated, accessed by blueprints, RPCs...
- Only accessed as a pointer/reference, never by value
- Have a special TWeakObjectPtr so you can check if the underlying object was garbage collected
Everything Else
- Have the T, F or I prefix
- May be passed by value, reference, pointer or shared pointer:
- FVector, FVector&, const FVector&, FVector *, TSharedPtr<FVector>, const TSharedPtr<const FVector>&
- Returning a reference/pointer is uncommon
- Reference/pointer as an argument are short-lived
- Shared pointers (refcounting) are usually used instead of raw pointers
Using them in Unreal.hx
UObject-derived
- Wrapped by Haxe
- Unreal still controls its lifetime - same UPROPERTY rules apply
- All wrappers are weak pointers - check the lifetime with `isValid()` function call
- You can extend classes, override functions, etc
UObject-derived
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "PlatformerClimbMarker.generated.h"
UCLASS(Blueprintable)
class APlatformerClimbMarker : public AActor
{
GENERATED_UCLASS_BODY()
private:
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category=Mesh, meta=(AllowPrivateAccess="true"))
UStaticMeshComponent* Mesh;
public:
/** Returns Mesh subobject **/
FORCEINLINE UStaticMeshComponent* GetMesh() const { return Mesh; }
};
package platformer;
import unreal.*;
@:uclass(Blueprintable)
@:uname("APlatformerClimbMarker")
class ClimbMarker extends AActor {
@:uproperty(VisibleDefaultsOnly, BlueprintReadOnly, Category=Mesh, meta=[AllowPrivateAccess="true"])
private var Mesh:UStaticMeshComponent;
inline public function GetMesh() {
return Mesh;
}
}
Non-UObjects
- If target type is a pointer, it will not create a wrapper for it, and use the raw pointer instead
- When creating a new type, or getting a struct by value, a very small wrapper is allocated.
- This wrapper allows Haxe garbage collection to take care of the type's ownership
- You may declare new types, but you cannot override functions nor add Haxe-only types
Non-UObjects
C++ | Haxe |
---|---|
FSomeStruct | FSomeStruct |
FSomeStruct& | PRef<FSomeStruct> |
FSomeStruct * | PPtr<FSomeStruct> or POwnedPtr<FSomeStruct> |
const FSomeStruct& | Const<PRef<FSomeStruct>> |
FSomeStruct ** | <not supported... yet> |
Non-UObjects
Uses Haxe abstracts!
package unreal;
/**
Base class for all tick functions.
**/
@:glueCppIncludes("GameFramework/Actor.h")
@:uextern extern class FTickFunction {
/**
The frequency in seconds at which this tick function will be executed. If less than or equal to 0 then it will tick every frame
**/
public var TickInterval : unreal.Float32;
/**
If false, this tick function will never be registered and will never tick. Only settable in defaults.
**/
public var bCanEverTick : Bool;
/* snip... */
}
package unreal;
/**
Base class for all tick functions.
**/
@:glueCppIncludes("GameFramework/Actor.h")
@:uextern
@:ueGluePath("uhx.glues.FTickFunction_Glue")
@:forward(dispose,isDisposed) abstract FTickFunction(unreal.Struct) to unreal.Struct to unreal.VariantPtr {
/**
The frequency in seconds at which this tick function will be executed. If less than or equal to 0 then it will tick every frame
**/
public var TickInterval(get,set):cpp.Float32;
/**
If false, this tick function will never be registered and will never tick. Only settable in defaults.
**/
public var bCanEverTick(get,set):Bool;
/**
Invokes the copy constructor of the referenced C++ class.
This has some limitations - it won't copy the full inheritance chain of the class if it wasn't typed as the exact class
it will also be a compilation error if the wrapped class forbids the C++ copy constructor;
in this case, the extern class definition should contain the `@:noCopy` metadata
**/
@:glueCppIncludes("uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h")
@:glueHeaderCode("static unreal::VariantPtr copy(unreal::VariantPtr self);")
@:glueCppCode("unreal::VariantPtr uhx::glues::FTickFunction_Glue_obj::copy(unreal::VariantPtr self) {\n\treturn ::uhx::StructHelper<FTickFunction>::fromStruct(FTickFunction(*::uhx::StructHelper< FTickFunction >::getPointer(self)));\n}")
public function copy() : unreal.FTickFunction {
return ( @:privateAccess unreal.FTickFunction.fromPointer( uhx.glues.FTickFunction_Glue.copy(this) ) : unreal.FTickFunction );
}
@:glueCppIncludes("<uhx/TypeTraits.h>", "uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h")
@:glueHeaderCode("static bool equals(unreal::VariantPtr self, unreal::VariantPtr other);")
@:glueCppCode("bool uhx::glues::FTickFunction_Glue_obj::equals(unreal::VariantPtr self, unreal::VariantPtr other) {\n\tif (self.raw == other.raw) { return true; }if (self.raw == 0 || other.raw == 0) { return false; }return uhx::TypeTraits::Equals<FTickFunction>::isEq(*::uhx::StructHelper< FTickFunction >::getPointer(self), *::uhx::StructHelper< FTickFunction >::getPointer(other));\n}")
public function equals(other : unreal.PPtr<unreal.FTickFunction>) : Bool {
return uhx.glues.FTickFunction_Glue.equals(this, other);
}
@:glueCppIncludes("uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h", "<hxcpp.h>")
@:glueHeaderCode("static cpp::Float32 get_TickInterval(unreal::VariantPtr self);")
@:glueCppCode("cpp::Float32 uhx::glues::FTickFunction_Glue_obj::get_TickInterval(unreal::VariantPtr self) {\n\treturn ::uhx::StructHelper< FTickFunction >::getPointer(self)->TickInterval;\n}")
@:final @:nonVirtual
@:nonVirtual
private function get_TickInterval() : cpp.Float32 {
return uhx.glues.FTickFunction_Glue.get_TickInterval(this);
}
@:glueCppIncludes("uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h", "<hxcpp.h>")
@:glueHeaderCode("static void set_TickInterval(unreal::VariantPtr self, cpp::Float32 value);")
@:glueCppCode("void uhx::glues::FTickFunction_Glue_obj::set_TickInterval(unreal::VariantPtr self, cpp::Float32 value) {\n\t::uhx::StructHelper< FTickFunction >::getPointer(self)->TickInterval = value;\n}")
@:final @:nonVirtual
@:nonVirtual
@:final @:nonVirtual
@:nonVirtual
private function set_TickInterval(value : cpp.Float32) : cpp.Float32 {
uhx.glues.FTickFunction_Glue.set_TickInterval(this, value);
return value;
}
@:glueCppIncludes("uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h")
@:glueHeaderCode("static bool get_bCanEverTick(unreal::VariantPtr self);")
@:glueCppCode("bool uhx::glues::FTickFunction_Glue_obj::get_bCanEverTick(unreal::VariantPtr self) {\n\treturn ::uhx::StructHelper< FTickFunction >::getPointer(self)->bCanEverTick;\n}")
@:final @:nonVirtual
@:nonVirtual
private function get_bCanEverTick() : Bool {
return uhx.glues.FTickFunction_Glue.get_bCanEverTick(this);
}
@:glueCppIncludes("uhx/Wrapper.h", "GameFramework/Actor.h")
@:glueHeaderIncludes("VariantPtr.h")
@:glueHeaderCode("static void set_bCanEverTick(unreal::VariantPtr self, bool value);")
@:glueCppCode("void uhx::glues::FTickFunction_Glue_obj::set_bCanEverTick(unreal::VariantPtr self, bool value) {\n\t::uhx::StructHelper< FTickFunction >::getPointer(self)->bCanEverTick = value;\n}")
@:final @:nonVirtual
@:nonVirtual
@:final @:nonVirtual
@:nonVirtual
private function set_bCanEverTick(value : Bool) : Bool {
uhx.glues.FTickFunction_Glue.set_bCanEverTick(this, value);
return value;
}
/* snip... */
}
Non-UObjects
No pass-by-value in Haxe!
struct FSceneView {
bool WorldToPixel(const FVector& WorldPoint,
FVector2D& OutPixelLocation);
};
// use like:
FVector2D pixelLocation;
Fvector somePoint = FVector(1,1,1);
FSceneView& view = getSomeView();
view.WorldToPixel(somePoint, pixelLocation);
// on Externs
extern class FSceneView {
function WorldToPixel(WorldPoint:Const<PRef<FVector>>,
OutPixelLocation:PRef<FVector2D>):Bool;
}
// use like:
var pixelLocation = FVector2D.create(); // we need to create this! otherwise it gets sent as null
var somePoint = new FVector(1,1,1);
var view = getSomeView();
view.WorldToPixel(somePoint, pixelLocation);
Cppia
- HXCPP-compatible virtual machine
- Compiles in a couple of seconds
- Allows the creation of live functions, that can be reloaded in the same play-in-editor session!
- Still needs a full C++ recompilation if a UPROPERTY, UFUNCTION, or a new UCLASS is declared/renamed
Wrapping up
- Unreal is a modern, versatile and feature-rich 3D engine
- Haxe is the most feature-rich language binding other than the official C++
- Low risk: you can easily partition your C++ and Haxe code as you'd like
- Porting from C++ to Haxe is very straight-forward!
Wrapping up
- Haxe is a high-level language that can target Unreal
- Client/server interop
- Leverage garbage collection even for non-UObject types!
- Simpler memory model and familiar ECMA-inspired syntax
- You can still access most C++ features from Haxe itself
- Iterate quickly with cppia, and compile the final builds in C++ for better execution speed!
- Hxcpp, Unreal.hx and Haxe are open-source!
- We've reused lots of code!
Resources!
- Unreal C++ documentation
Resources!
- Unreal.hx documentation at github wiki page!
Resources!
- Platformer Game sample project: waneck/HaxePlatformerGame
- Check the commit history!
Showcase
Questions?
Unreal.hx v1
By Cauê Waneck
Unreal.hx v1
- 1,513