C# scripts
Your first script, builds, lifecycle, paths and checking references after deletion.
Requires the Cube (MeshInstance3D) from project setup.
1. Create and attach a script
- Stop with F8 and select the
Cubeof typeMeshInstance3Din the local hierarchy. - Use the attach-script button. Choose C#, base class
Entity3Dand pathasset://Scripts/Rotator.cs. Create theScriptsfolder if necessary. - If asked to create a C# solution, confirm. It provides the project used to build your code.
- Open
Rotator.csin a code editor, replace its contents with the complete example below and save.
The file and class names must match: Rotator.cs and Rotator. Attach the script to the cube; placing the file in the Assets alone is not enough. MeshInstance3D inherits Entity3D, so this base class is suitable.
If it does not open automatically, find Scripts/Rotator.cs in the project folder and open it in an installed text editor. Any editor that saves plain text with a .cs extension, without appending .txt, is enough for this example. Reckfell still performs the build.
2. The complete script
using Reckfell;
public partial class Rotator : Entity3D
{
[Export] public float DegreesPerSecond { get; set; } = 45.0f;
public override void _Ready()
{
ReckfellApi.Print("Rotator ready");
}
public override void _Process(double delta)
{
float angle = Mathf.DegToRad(DegreesPerSecond) * (float)delta;
RotateY(angle);
}
}3. Build and run
Choose Build Project in the editor's C# tools; its default shortcut is Alt + B. Wait for a successful build. Save the document and press F6.
The cube should rotate around its vertical axis, and Output should contain Rotator ready. If it stays still, check that the latest build succeeded and the script is attached to the visible cube.
Reading the code
| Expression | Meaning |
|---|---|
using Reckfell; | Lets you use engine types without their full namespace each time |
class Rotator : Entity3D | Defines object behaviour based on Entity3D |
partial | Lets the engine's generator add required supporting code |
float | A fractional number; the f in 45.0f specifies this type |
[Export] | Exposes a property in the inspector after building |
_Ready() | Runs when the object and its children are ready |
_Process(double delta) | Runs each rendered frame |
RotateY(angle) | Rotates around Y by an angle in radians |
Braces { } group instructions. A semicolon ; ends a statement. public makes a class member accessible from outside, void means a method returns no value, and override replaces a handler provided by the base class.
Why delta matters
DegreesPerSecond is a speed per second. delta is the elapsed time since the previous frame, in seconds. Multiplying them gives the change for this frame. At a speed of 45, the object rotates about 90 degrees in two seconds despite normal changes in frame rate.
The inspector displays degrees, but RotateY() expects radians. Mathf.DegToRad() converts between them. Without multiplication by delta, the object would turn by a fixed amount every frame and its speed would depend on FPS.
Change speed without editing code
Stop, select the cube and find the script's Degrees Per Second property in the inspector. Set it to 90, save and run: rotation becomes twice as fast. -45 reverses direction and 0 stops it.
Inspector values are saved separately from C# defaults. Once a property has been overridden, editing 45.0f in the script may not replace the value saved on the object.
When the code runs
| Method | Use it for |
|---|---|
_EnterTree() | Every entry into the running hierarchy; connect subscriptions that are active only while in the level |
_Ready() | Once children are ready; obtain references and set the initial state |
_Process(double delta) | Each displayed frame; UI animation and visual changes |
_PhysicsProcess(double delta) | Physics steps; moving physics bodies and working with collisions |
_ExitTree() | Leaving the hierarchy; disconnect subscriptions to objects that remain alive |
The parent enters the tree before its children, while each child's _Ready() runs before the parent's. This makes _Ready() a useful place to connect already initialized child components. A C# constructor does not mean the object is in a level yet: do not obtain GetTree() or gameplay children there.
_Ready() normally runs once per instance lifetime. Removing the same object with RemoveChild() and adding it back with AddChild() repeats _EnterTree() and _ExitTree(), but not _Ready(). If initialization really must repeat, call RequestReady() before the next addition. The request applies to that object, not automatically to its children.
Use _PhysicsProcess() for the character in the movement tutorial. Do not move every operation there just for a fixed frequency: rotating a decorative cube does not require a physics step.
Object references and paths
GetEntity<T>("Child") searches relative to the object running the script. The name and case must match the hierarchy. This is not a file path: load asset://Props/Crate.rprefab with ResourceLoader and instantiate it separately. See runtime objects.
When a missing child is valid, use GetEntityOrNull<T>() and check the result. When the child is required, fix the level's structure: silently skipping the error leaves the component nonfunctional.
After QueueFree(), the native object is deleted at the end of the frame. The C# variable does not automatically become null. Check ReckfellObject.IsInstanceValid(reference) for a retained reference, and also reference.IsQueuedForDeletion() to avoid using an object already scheduled for deletion. This matters particularly after await or a level change.
If the result differs from the example
| Symptom | Check first |
|---|---|
| Code changes do not appear | Save the file and wait for a successful Build Project; an old working assembly does not prove the new one built |
Rotator ready never appears | The script is attached to the cube, the class is named Rotator, includes partial, and builds without errors |
| The speed changed in code, but rotation stayed the same | Check the saved Degrees Per Second value in the Inspector |
_Ready() does not repeat when an object returns | It is the same instance; use _EnterTree() for each entry |
NullReferenceException / child lookup failure | Check the name, case, type and relative path; find the first line of your script in the stack trace |
| Access fails after deletion | Check whether the native object is valid, not just reference != null |
SDK and command-line builds
The distribution includes .NET 10 and Reckfell.NET.Sdk/1.0.0.
The generated .csproj uses this foundation:
<Project Sdk="Reckfell.NET.Sdk/1.0.0">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
</PropertyGroup>
</Project>Run dotnet build from the project folder using the supplied dotnet.exe and configured local Reckfell packages. Rebuild after updating the project's SDK.
Build and script attachment errors: troubleshooting.
Code tools
See the built-in IDE for Roslyn completion, reference search and debugging. You can also create logic in SharpGraph and convert it to separate editable C# files.