├── Private ├── DataStore.cpp ├── TestProgramApp.cpp └── Windows │ └── TestProgramMainWindows.cpp ├── Public ├── DataStore.h ├── TestProgram.h └── TestProgramApp.h ├── README.md ├── TestProgram.Build.cs └── TestProgram.Target.cs /Private/DataStore.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "DataStore.h" 5 | 6 | 7 | -------------------------------------------------------------------------------- /Private/TestProgramApp.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "TestProgramApp.h" 4 | 5 | #include "DataStore.h" 6 | #include "RequiredProgramMainCPPInclude.h" 7 | #include "TabManager.h" 8 | #include "Framework/Application/SlateApplication.h" 9 | #include "Slate/Public/Slate.h" 10 | 11 | 12 | IMPLEMENT_APPLICATION(TestProgram, "TestProgram"); 13 | 14 | #define LOCTEXT_NAMESPACE "TestProgram" 15 | 16 | int RunTestProgram( const TCHAR* CommandLine ) 17 | { 18 | FTaskTagScope TaskTagScope(ETaskTag::EGameThread); 19 | 20 | // start up the main loop 21 | GEngineLoop.PreInit(CommandLine); 22 | 23 | // Make sure all UObject classes are registered and default properties have been initialized 24 | ProcessNewlyLoadedUObjects(); 25 | 26 | // Tell the module manager it may now process newly-loaded UObjects when new C++ modules are loaded 27 | FModuleManager::Get().StartProcessingNewlyLoadedObjects(); 28 | 29 | // crank up a normal Slate application using the platform's standalone renderer 30 | FSlateApplication::InitializeAsStandaloneApplication(GetStandardStandaloneRenderer()); 31 | 32 | FSlateApplication::InitHighDPI(true); 33 | 34 | // set the application name 35 | FGlobalTabmanager::Get()->SetApplicationTitle(LOCTEXT("AppTitle", "Slate Viewer")); 36 | 37 | //////// 38 | /// This creates the actual slate window down below 39 | /// We've also had success removing all the Sate code and using Dear ImGui's DX11 example for the UI layer 40 | //////// 41 | CreateSlateUI(); 42 | 43 | //////// 44 | /// You can create UObjects to use here, no problem 45 | /// In this case it may be right to use the dreaded singleton pattern 46 | //////// 47 | UDataStore* dataStore = NewObject(); 48 | 49 | // loop while the server does the rest 50 | while (!IsEngineExitRequested()) 51 | { 52 | //////// 53 | /// If you have any exit conditions other than closing the slate window, check them here 54 | /// (This example is contrived) 55 | //////// 56 | // if (dataStore->RunningTime > 100.f) 57 | // { 58 | // RequestEngineExit(TEXT("Hit the time limit")); 59 | // } 60 | 61 | // In case something flagged "should request exit" but didn't flag "requesting exit" 62 | BeginExitIfRequested(); 63 | 64 | // In case any of the above triggered an exit condition, we don't want to run the main loop again 65 | if (IsEngineExitRequested()) 66 | { 67 | continue; 68 | } 69 | 70 | //////// 71 | /// If you have any work to do inside the main loop, here's the place 72 | //////// 73 | // dataStore->RunningTime += FApp::GetDeltaTime(); 74 | 75 | // Let the game thread and slate application tick 76 | FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread); 77 | FStats::AdvanceFrame(false); 78 | FTSTicker::GetCoreTicker().Tick(FApp::GetDeltaTime()); 79 | FSlateApplication::Get().PumpMessages(); 80 | FSlateApplication::Get().Tick(); 81 | FPlatformProcess::Sleep(0.01); 82 | 83 | GFrameCounter++; 84 | } 85 | 86 | FCoreDelegates::OnExit.Broadcast(); 87 | FSlateApplication::Shutdown(); 88 | FModuleManager::Get().UnloadModulesAtShutdown(); 89 | 90 | GEngineLoop.AppPreExit(); 91 | GEngineLoop.AppExit(); 92 | 93 | return 0; 94 | } 95 | 96 | void CreateSlateUI() 97 | { 98 | const TSharedRef testWindow = SNew(SWindow) 99 | .ClientSize(FVector2D(640,480)) 100 | .AutoCenter(EAutoCenter::PrimaryWorkArea) 101 | [ 102 | SNew(STextBlock) 103 | .Text(FText::FromString(TEXT("Hello world!"))) 104 | ]; 105 | FSlateApplication::Get().AddWindow( testWindow ); 106 | } 107 | 108 | #undef LOCTEXT_NAMESPACE 109 | -------------------------------------------------------------------------------- /Private/Windows/TestProgramMainWindows.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "TestProgramApp.h" 5 | #include "Windows/WindowsHWrapper.h" 6 | 7 | //////// 8 | /// This is the entry point for the application on Windows platforms 9 | /// If you want to support other platforms, look at SlateViewer in the engine projects 10 | //////// 11 | 12 | /** 13 | * WinMain, called when the application is started 14 | */ 15 | int WINAPI WinMain( _In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR, _In_ int nCmdShow ) 16 | { 17 | // do the slate viewer thing 18 | RunTestProgram(GetCommandLineW()); 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /Public/DataStore.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/Object.h" 7 | 8 | #include "DataStore.generated.h" 9 | 10 | /** 11 | * 12 | */ 13 | UCLASS() 14 | class UDataStore : public UObject 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UPROPERTY() 20 | float RunningTime; 21 | }; 22 | -------------------------------------------------------------------------------- /Public/TestProgram.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #pragma once 5 | 6 | #include "CoreMinimal.h" 7 | #include "StandaloneRenderer.h" 8 | 9 | -------------------------------------------------------------------------------- /Public/TestProgramApp.h: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "TestProgram.h" 7 | 8 | 9 | /** 10 | * Run the TestProgram . 11 | */ 12 | int RunTestProgram(const TCHAR* Commandline); 13 | 14 | void CreateSlateUI(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a pretty minimal "Hello world!" version of a standalone Unreal program showing a Slate window. 2 | 3 | NO WARRANTY PROVIDED THIS IS FOR EXPERIMENTAL PURPOSES ONLY 4 | 5 | ## Instructions 6 | 7 | Drop contents of this folder into a [Project Folder]/Source/TestProgram directory, go back up to your game's .uproject, right-click and Generate Project Files. 8 | 9 | In your IDE set TestProgram as your build project and hit Run or Debug to build/run it. The generated .exe will be at [Project Folder]/Binaries/Win64/TestProgram.exe 10 | 11 | This only works if you're building the engine from source, even though it does not require any modifications to the engine to work. The Unreal build system doesn't allow you to build program targets if you're using an installed engine. 12 | 13 | Most of this code is based around the SlateViewer program in the Engine/Source/Programs directory. 14 | 15 | You can omit the Slate code entirely and it will work as a headless application. I've had luck using it headless along with Dear ImGui. 16 | 17 | Of note, this is NOT set up to include the core Unreal engine module when building - so while you can use UObjects without issue features that are defined in the Engine module (like FTickableGameObject) are not available. 18 | 19 | This is provided as an example working implementation. I'm sure there are plenty of other restrictions and weird cases I've not run into yet, and will likely not be able to help you figure out. When in doubt: build it in Debug|Win64 profile, hook up the debugger, and step through code until you find what's not working right. 20 | 21 | Good luck! 22 | 23 | 24 | ## Packaging 25 | 26 | If you want to zip this up so that it can be executed like a standalone program - and not just as a tool in your game project - you have to set up the file structure very specifically. This is because things with the Program build target (like our standalone app) assume they can find UnrealEngine files at ../../../Engine relative to the executable. So you'll want to emulate that file structure in your standalone folder, and then copy over some corresponding folders from the engine source. 27 | 28 | ``` 29 | [Root Directory] 30 | ├───Engine 31 | │ ├───Content 32 | │ │ ├───Internationalization 33 | │ │ │ └───icudt64l <--- Copy this folder 34 | │ │ ├───Slate <--- I don't know how much of this folder is needed 35 | │ │ └───SlateDebug <--- Same here 36 | │ └───Shaders 37 | │ └───StandaloneRenderer <--- Copy this folder 38 | └───[Project Name] 39 | └───Binaries 40 | └───Win64 <--- Put your built .exe here (I also copied everything else in the binaries folder that shared a name with it) 41 | ``` 42 | 43 | Of those, you only need to include the Internationalization files over if you're running without Slate. The app will just crash without error if it cannot find those. 44 | -------------------------------------------------------------------------------- /TestProgram.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using EpicGames.Core; 4 | using UnrealBuildBase; 5 | using UnrealBuildTool; 6 | 7 | public class TestProgram : ModuleRules 8 | { 9 | public TestProgram(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | var runtimeLaunchDir = DirectoryReference.Combine(Unreal.EngineDirectory, "Source", "Runtime", "Launch"); 12 | 13 | PublicIncludePaths.Add(DirectoryReference.Combine(runtimeLaunchDir, "Public").FullName); 14 | 15 | PrivateDependencyModuleNames.AddRange( 16 | new string[] { 17 | "AppFramework", 18 | "Core", 19 | "ApplicationCore", 20 | "Projects", 21 | "Slate", 22 | "SlateCore", 23 | "StandaloneRenderer", 24 | } 25 | ); 26 | 27 | PrivateIncludePaths.Add(DirectoryReference.Combine(runtimeLaunchDir, "Private").FullName); // For LaunchEngineLoop.cpp include 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TestProgram.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | [SupportedPlatforms(UnrealPlatformClass.Desktop)] 7 | public class TestProgramTarget : TargetRules 8 | { 9 | public TestProgramTarget(TargetInfo Target) : base(Target) 10 | { 11 | Type = TargetType.Program; // This setting is what makes it work as a program! 12 | LinkType = TargetLinkType.Monolithic; 13 | 14 | LaunchModuleName = "TestProgram"; 15 | ExtraModuleNames.Add("EditorStyle"); 16 | 17 | bBuildDeveloperTools = false; 18 | 19 | // TestProgram doesn't ever compile with the engine linked in 20 | bCompileAgainstEngine = false; 21 | 22 | // We need CoreUObject compiled in as the source code access module requires it 23 | bCompileAgainstCoreUObject = true; 24 | 25 | // TestProgram.exe has no exports, so no need to verify that a .lib and .exp file was emitted by 26 | // the linker. 27 | bHasExports = false; 28 | 29 | // Make sure to get all code in SlateEditorStyle compiled in 30 | bBuildDeveloperTools = true; 31 | } 32 | } 33 | --------------------------------------------------------------------------------