Unreal.hx

Using Haxe with the Unreal Engine 4

https://bit.ly/unrealhx

By Cauê Waneck ( @cwaneck )

[~]$ whoami

Haxe in World Zombination

  • Technology-agnostic game logic
  • Free to choose the technology that best suits us
  • Run same game logic on both client and server
  • Used macros to transform synchronous code in asynchronous
  • Static typing + concise language
  • Native access
  • Used by many AAA games
  • Targets all major platforms - desktop, mobile and console
  • VR support
  • Source code available
  • Great out-of-the-box multiplayer support
  • C++ is the only official language
C++ can be difficult to integrate with itself let alone other languages
  • Supports also visual coding with Blueprints

Unreal.hx !

  • Uses Haxe's C++ backend
  • Write everything in Haxe
  • Use external C++ code as easily as possible
  • Compatible with the cppia VM
  • DCE compatible
  • Support EVERY C++ feature

Why Unreal.hx?

  • Well, it uses Haxe
  • Garbage collection
  • Porting from C++ to Haxe is easy
  • Partition between C++ and Haxe
  • Hxcpp for performance, cppia+@:live for fast iteration
  • Most feature-rich language binding for Unreal
  • Free, Open Source Software
  • Target other platforms - e.g. server

Streamline

Streamline

Streamline

Closed beta this year!

The big picture

  • Needs latest Haxe 3.3+
  • Targets Unreal 4.11

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

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 - which can be used by C++!
@: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 {
  var someHaxeArray:Array<String>;
  var someUnrealArray:TArray<Int8>;
  @:uexpose var someExposedUnrealArray:TArray<FString>;

  /** delegate to broadcast about finished round */
  @:uproperty(BlueprintAssignable)
  public var OnRoundFinished:FRoundFinishedDelegate;

  public function someHaxeFunction(json:{ name:String, age:Int }):Void { /* ... */ }

  @:uexpose public function someUnrealFunction(char:Character, basicType:Bool):Void { /* ... */ }
  /* 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 Types

UObjects

  • Always have special reflection metadata
  • Unreal garbage collection
  • Virtual functions, always pointers

"Structs"

  • May have reflection metadata (USTRUCT)
  • Manual management
  • May be accessed by value or by reference/pointer

in c++

// 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; }
};
class FPlatformerMessageData
{
	/** text to display */
	FString Message;
	
	/** how long this FMessageData will be displayed in seconds */
	float DisplayDuration;

	/** TimeSeconds when this FMessageData was first shown */
	float DisplayStartTime;
	
	/** x axis position on screen <0, 1> (0 means left side of the screen) ; text will be centered */
	float PosX;
	
	/** y axis position on screen <0, 1> (0 means top of the screen) ; text will be centered */
	float PosY;
	
	/** text scale */
	float TextScale;
	
	/** if red border should be drawn instead of blue */
	bool bRedBorder;
};

UObjects

  • Still depend on @:uproperty metas :(
  • All Haxe types are weak pointers - no crash :)
  • Normal hxcpp wrapper: can be cast, interfaces...
  • Unreal.hx knows that it's accessed via pointer

"Structs"

  • If haxe created the type, it can be gc'd :)                                                                                   
  • External pointers don't need wrappers!
  • Need to specify how the C++ type is accessed

in Haxe

"Struct" types

C++ Haxe
FSomeStruct FSomeStruct
FSomeStruct& PRef<FSomeStruct>
FSomeStruct * PPtr<FSomeStruct> or POwnedPtr<FSomeStruct>
const FSomeStruct& Const<PRef<FSomeStruct>>
FSomeStruct ** <not supported... yet>

Future plans

  • Use Unreal hooks to build the project
  • Fix Unreal header includes
  • Optimize C++ build speed
  • Cppia generate UClasses, UFunctions and UProperties directly
  • General Pointer type
  • Automatically generate all C++ types externs
  • General incremental support

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

Resources!

  • Unreal C++ documentation
  • Unreal.hx documentation at github wiki page!

Resources!

  • Platformer Game sample project: waneck/HaxePlatformerGame
  • Check the commit history!

Questions?

Made with Slides.com