Nuitrack integration guide for Unreal Engine 4.10
1.1 Setting up Unreal Engine for VicoVR
Change the template of Android app . Typically path to template folder is: c:\Program Files (x86)\Epic Games\4.10\Engine\Build\Android\Java\Actions needed:
- Copy nuitrackhelper.jar file from SDK to \libs\
- Add Nuitrack initialization into SplashActivity ({TemplateFolder}\src\com\epicgames\ue4\SplashActivity.java)
- Add import at the beginning of the file: import com.tdv.nuitrack.sdk.Nuitrack;
- Move onCreate content to the new method: private void StartGame();
- Create class field for logger: private static Logger Log = new Logger("UE4");
- Create Nuitrack loading method:
private void LoadNuitrack() { Nuitrack.init(this, new Nuitrack.NuitrackCallback() { public void onInitSuccess(Context context) { Log.debug( "Nuitrack init SUCCESS in onCreate()"); StartGame(); } public void onInitFailure(int errorId) { Log.debug( "Nuitrack init FAILED in onCreate()"); finish(); } }); } - Call LoadNuitrack(); method in onCreate method.
Warning: You need to do UE4 setup for VicoVR only once until you have update for UE4 or VicoVR SDK - then you will need to repeat the adjustment procedure.
Optional: If you want to prevent automatic screen lock in your applications, add AndroidThunkJava_KeepScreenOn(true); line to the end of onCreate method in GameActivity ({TemplateFolder}\src\com\epicgames\ue4\GameActivity.java)
1.2 Setting up your project
- Copy Nuitrack folder from SDK to project Source folder.
- Generate Visual Studio project files (using Windows Explorer context menu option on .uproject-file)

- Add Extra Permissions in Project Settings -> Platforms -> Android :
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.INTERNET - Add Nuitrack libraries and headers to project dependences file Build.cs (Games/{ProjectName}/Source/{ProjectName}/{ProjectName}.Build.cs):
PrivateDependencyModuleNames.AddRange(new string[] { "Nuitrack" }); PrivateIncludePaths.AddRange(new string[] { Path.GetDirectoryName(RulesCompiler.GetModuleFilename( this.GetType().Name)) + "Nuitrack/Nuitrack/include" });(Path class requires using System.IO;)
1.3 Nuitrack usage
- For Nuitrack initialization, skeleton tracker creation and event subscription you need to use the following code (you can put it to BeginPlay() method of GameMode class):
Nuitrack::init(); SkeletonTracker::Ptr skeletonTracker = SkeletonTracker::create(); skeletonTracker->connectOnUpdate(std::bind(&ANuiSampleGameMode::OnSkeletonUpdate, this, std::placeholders::_1)); Nuitrack::run();
- For Nuitrack terminating call release() method (you can put it to BeginDestroy() method of GameMode class):
Nuitrack::release();
- For raising events with new data from modules call update() method (you can put it to Tick(float dt) method of GameMode class):
Nuitrack::update();
- Skeleton tracking data will be available inside OnSkeletonUpdate method (was subscribed to OnUpdate event from SkeletonTracker):
void ANuiSampleGameMode::OnSkeletonUpdate(SkeletonData::Ptr userSkeletons) { auto skeletons = userSkeletons->getSkeletons(); ... }
2. Nuitrack sample project
- Create new project.

- Delete everything except PlayerStart from the World.

- Set player locations and rotation as on screenshot.

- Save current World.

- In Project Settings -> Maps & Modes:
- Set your World as Game Default Map and Editor Startup Map.
- Set Default GameMode.
- Set your World as Game Default Map and Editor Startup Map.
- In Project Settings -> Android press Configure Now and fill Android Package Name field.
- Setup Android Unreal Engine for VicoVR (as it was described in point 1.1)
- Set up your project (as it was described in point 1.2)
- Open Visual Studio for writing C++ code:
NuiSample.Build.cs File:
using UnrealBuildTool;
using System.IO;
public class NuiSample : ModuleRules
{
public NuiSample(TargetInfo Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PrivateDependencyModuleNames.AddRange(new string[] { "Nuitrack" });
PrivateIncludePaths.AddRange(new string[] { Path.GetDirectoryName(
RulesCompiler.GetModuleFilename(this.GetType().Name)) +
"Nuitrack/Nuitrack/include" });
}
}
NuiSample.h File:
#pragma once #include "Engine.h" #include#include using namespace std; #include "nuitrack/Nuitrack.h" #include "nuitrack/modules/SkeletonTracker.h" #include "nuitrack/types/Skeleton.h" #include "nuitrack/types/SkeletonData.h" using namespace tdv::nuitrack;
NuiSample.cpp File:
#include "NuiSample.h" IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, NuiSample, "NuiSample" );
NuiSampleGameMode.h File:
#pragma once
#include "GameFramework/GameMode.h"
#include "NuiSampleGameMode.generated.h"
UCLASS()
class NUISAMPLE_API ANuiSampleGameMode : public AGameMode
{
GENERATED_BODY()
UWorld* World;
ANuiSampleGameMode();
void Tick(float dt) override;
void BeginPlay() override;
void BeginDestroy() override;
SkeletonTracker::Ptr skeletonTracker;
void OnSkeletonUpdate(SkeletonData::Ptr userSkeletons);
void DrawSkeleton(int skeleton_index, vector joints);
void DrawBone(Joint j1, Joint j2);
static FVector RealToPosition(Vector3 real);
};
NuiSampleGameMode.cpp File:
#include "NuiSample.h"
#include "NuiSampleGameMode.h"
ANuiSampleGameMode::ANuiSampleGameMode()
{
this->PrimaryActorTick.bCanEverTick = true;
}
void ANuiSampleGameMode::Tick(float dt)
{
Nuitrack::update();
}
void ANuiSampleGameMode::BeginPlay()
{
Super::BeginPlay();
UE_LOG(LogTemp, Warning, TEXT("ANuiSampleGameMode::BeginPlay()"));
World = GetWorld();
if (World)
{
UE_LOG(LogTemp, Warning, TEXT("Nuitrack::init() CALLING..."));
Nuitrack::init();
UE_LOG(LogTemp, Warning, TEXT("SkeletonTracker::create() CALLING..."));
skeletonTracker = SkeletonTracker::create();
UE_LOG(LogTemp, Warning, TEXT(
"skeletonTracker->connectOnUpdate() CALLING..."));
skeletonTracker->connectOnUpdate(
std::bind(&ANuiSampleGameMode::OnSkeletonUpdate,
this, std::placeholders::_1));
UE_LOG(LogTemp, Warning, TEXT("Nuitrack::run() CALLING..."));
Nuitrack::run();
}
else
{
UE_LOG(LogTemp, Error, TEXT("NULL WORLD"));
}
}
void ANuiSampleGameMode::BeginDestroy()
{
Super::BeginDestroy();
Nuitrack::release();
}
void ANuiSampleGameMode::OnSkeletonUpdate(SkeletonData::Ptr userSkeletons)
{
auto skeletons = userSkeletons->getSkeletons();
FlushPersistentDebugLines(World);
if (!skeletons.empty())
{
for (auto skeleton : skeletons)
{
DrawSkeleton(skeleton.id, skeleton.joints);
}
}
}
void ANuiSampleGameMode::DrawSkeleton(int skeleton_index, vector joints)
{
if (joints.empty())
return;
DrawBone(joints[JOINT_HEAD], joints[JOINT_NECK]);
DrawBone(joints[JOINT_NECK], joints[JOINT_TORSO]);
DrawBone(joints[JOINT_RIGHT_SHOULDER], joints[JOINT_LEFT_SHOULDER]);
DrawBone(joints[JOINT_WAIST], joints[JOINT_LEFT_HIP]);
DrawBone(joints[JOINT_WAIST], joints[JOINT_RIGHT_HIP]);
DrawBone(joints[JOINT_TORSO], joints[JOINT_WAIST]);
DrawBone(joints[JOINT_LEFT_SHOULDER], joints[JOINT_LEFT_ELBOW]);
DrawBone(joints[JOINT_LEFT_ELBOW], joints[JOINT_LEFT_WRIST]);
DrawBone(joints[JOINT_RIGHT_SHOULDER], joints[JOINT_RIGHT_ELBOW]);
DrawBone(joints[JOINT_RIGHT_ELBOW], joints[JOINT_RIGHT_WRIST]);
DrawBone(joints[JOINT_RIGHT_HIP], joints[JOINT_RIGHT_KNEE]);
DrawBone(joints[JOINT_LEFT_HIP], joints[JOINT_LEFT_KNEE]);
DrawBone(joints[JOINT_RIGHT_KNEE], joints[JOINT_RIGHT_ANKLE]);
DrawBone(joints[JOINT_LEFT_KNEE], joints[JOINT_LEFT_ANKLE]);
}
void ANuiSampleGameMode::DrawBone(Joint j1, Joint j2)
{
DrawDebugLine(World, RealToPosition(j1.real), RealToPosition(j2.real),
FColor::MakeRedToGreenColorFromScalar((j1.confidence + j2.confidence)*0.5),
true, -1, 0, 4);
}
FVector ANuiSampleGameMode::RealToPosition(Vector3 real)
{
return FVector(-real.x, real.z, real.y)*0.1f;
}
