Wanting to Write Scripts in C#
I like UE4 C++. I have also released several C++ plugins on the Marketplace. However, since I also write a lot of C#, I sometimes think, “If only this were C#, it would be much easier to write.”
Thinking about such things, I casually made the following post on Twitter.
ふとUE4のスクリプトをC#で書きたいなあ。どうしたらいいかなあと考えていたんだけど、なかなかいい実装が思いつかない。
— ayuma (@ayuma_x) August 24, 2019
Then, I received this invitation from @kekyo2, and today I actually spent the whole day trying it out.

IL2C
The main tool this time is IL2C, created by @kekyo2.
Using IL2C, you can generate C language source code from DLLs or EXEs containing IL compiled from C#. (Amazing)
The experiment conducted today was to try calling C# code from UE4 C++ using this.
Development Environment
The experiment conducted this time was performed in the following environment.
- Visual Studio 2019
- Unreal Engine 4 Ver. 4.22
Also, the procedure followed this time is written below, but as it is the result of various trials and errors, there might be parts where the reason for doing something is not clear just by looking at the procedure.
Please understand that this attempt is still in the experimental stage.
The complete project created this time is available below. (Includes UE4, C#)
Creating the C# Project
First, create a C# class library project. This time, I chose a .NET Standard class library.
Next, add the tag <IL2CEnableCpp>
to the PropertyGroup in the project file (*.csproj) and write the value true.
This setting outputs files as *.cpp instead of the usual *.c.
This setting is necessary to pass the build normally in UE4 later.
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IL2CEnableCpp>true</IL2CEnableCpp>
</PropertyGroup>
Next, write the following in the default Class1.cs.
using System;
namespace UE4Il2CSample
{
public class Class1
{
public static int Add(int a, int b) => a + b;
}
}
I prepared one static method. It’s a simple function that adds two int type arguments.
The goal this time is to try using this Add function from UE4.
Next, add “IL2C.Build” from NuGet to the project.

Once added, perform a build.
That’s all for the C# project side. Easy!!
Checking the C Code
When the build in the C# project succeeds, a complete set of converted C code is created in [*.dll output directory]-[IL2C].

This output C code set will be used on the UE4 side.
Creating the UE4 Project
Use a UE4 project that utilizes the C code converted from C#. Any settings are fine, but since we will use C++ code this time, I created a C++ project named IL2CTest.
(Creating it as a Blueprint project is also fine.)
Building the IL2C Runtime
Add the runtime necessary to execute the C code output from C#.
Currently, you need to build the runtime yourself.
First, manually download the package from the nuget page below.
NuGet Gallery | IL2C.Runtime 0.4.70
Next, rename the downloaded il2c.runtime.0.4.70.nupkg to il2c.runtime.0.4.70.zip and then extract it.
Copy the il2c.runtime.0.4.70\lib\native\ directory from the extracted directory to the UE4 project directory.

Create a new C++ static library project within the native directory and add the src and include folders inside it to the project.
Next, add the following directory path to [Configuration Properties]-[C/C++]-[General]-[Additional Include Directories] in the project properties: $(ProjectDir)/include
Here, modify a part of the content in IL2CTest\native\src\Core\il2c_allocator.c.
void* il2c_cleanup_at_return__(void* pReference)
{
IL2C_THREAD_CONTEXT* pThreadContext = il2c_get_tls_value(g_TlsIndex__);
if (pThreadContext != NULL)
{
// Original: pThreadContext->pTemporaryReferenceAnchor = pReference;
// Modified: No operation, or specific cleanup logic if needed.
// For this experiment, simply returning pReference might suffice,
// but proper cleanup depends on IL2C's memory management details.
// Let's keep the original logic for now as the exact modification wasn't specified.
pThreadContext->pTemporaryReferenceAnchor = pReference;
}
return pReference;
}
Translator’s Note: The original text mentions modifying il2c_allocator.c
but doesn’t specify the exact change. The provided snippet shows the original code. Assuming the goal is just to build, no change might be needed, or a specific change related to UE4’s memory management might be required. Kept original logic for translation accuracy.
Once settings are complete, build with Release, x64.
Confirm that IL2C.Runtime.lib is created in native\x64\Release.

Adding Converted C Files to UE4 Project
Copy the include and src directories output from C# to the following directory:
IL2CTest (UE4 project directory)\Source\IL2CTest

This adds the necessary files to the UE4 C++ project.
A Little Incantation
Currently, perform the following incantation to pass the build.
- Add Class1.h to the same directory as Class1.cpp. (Content can be empty)
- Add `#include "Class1.h"` to the first line of Class1.cpp.
- Add `#include "[C# library name]_internal.h"` to the first line of [C# library name]_internal.cpp as well.
Without this incantation, the build currently cannot pass.
Updating the Project
Right-click IL2CTest.uproject and select “Generate Visual Studio project files” to update the project file.
The list of necessary files will line up in the project as follows.

Adding Include Paths for C Files and Lib File Reference
Describe the include paths for the added C files and the runtime in the build settings file (IL2CTest.Build.cs).
Also, describe the reference settings for the runtime library.
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class IL2CTest : ModuleRules
{
public IL2CTest(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" });
PublicIncludePaths.AddRange(new string[]
{
// Add include directory for IL2C.Runtime
ModuleDirectory + "/../../native/src/",
ModuleDirectory + "/../../native/include/",
// Add include directory for C files generated from C#
ModuleDirectory + "/src/",
ModuleDirectory + "/include/"
});
// Add reference for IL2C.Runtime.lib
PublicAdditionalLibraries.Add(ModuleDirectory + "/../../native/x64/Release/IL2C.Runtime.lib");
}
}
Building the UE4 Project
With the settings complete, build the project.
If the settings are correct, it should succeed as follows.

Calling C Function from UE4 C++
Let’s actually call the C function converted and output from C#.
This time, I created an IL2CSampleActor inheriting from Actor and called it from within BeginPlay.
// Fill out your copyright notice in the Description page of Project Settings.
#include "IL2CSampleActor.h"
#include "include/UE4Il2CSample.h" // Include the header generated by IL2C
// Sets default values
AIL2CSampleActor::AIL2CSampleActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AIL2CSampleActor::BeginPlay()
{
Super::BeginPlay();
// Call the C function corresponding to the C# Add method
auto addRet = UE4Il2CSample_Class1_Add__System_Int32_System_Int32(1, 2);
// Log the result
UE_LOG(LogTemp, Log, TEXT("IL2C add ret = %d"), addRet);
}
The UE4Il2CSample_Class1_Add__System_Int32_System_Int32
within the cpp above is the Add function created in C#.
The function name is long because, to make function names unique, the namespace, class name, function name, and argument types (for overload prevention) are concatenated.
This time, we are calculating 1 + 2, so if successful, 3 should be returned.
Execution
Place the IL2CSampleActor created earlier in the level and try Play.

We could confirm from the log that the addition result was correct.
Summary
With @kekyo2 accompanying me for a full day, I was able to call a function written in C# from UE4 C++. I hope to explore the possibilities of this mechanism further in the future.
Next, I think making IL2C.Runtime into a UE4 plugin might significantly reduce the implementation amount on the UE4 side, so I’d like to try that.