Redot 26.3 stable release

The stable release for Redot 26.3 is here! This is Redot's biggest release, with new features, enhancements, and bugfixes. A lot of work into Redot 26.3, and it adds quite a bit, so let's get right into it.
You can download Redot 26.3 over on our releases page.
New Features
WorldScape3D
3D Redot devs can now edit terrain directly in the engine without the need for addons or extensions with WorldScape 3D!
WorldScape 3D is a built-in terrain editor for 3D games, based on the Terrain 3D extension by Cory Petkovsek and ported into Redot by OldDev78.
To use it, add a WorldScape3D node to your scene, set the data directory to a folder that holds your terrain data, and then select the WorldScape3D node to use the sculpt and paint tools.
For more in-depth guides and tutorials, you can check out the Terrain3D documentation here.
Mode7Sprite2D and Manual Scroll for Parallax2D
The Mode7Sprite2D Node recreates SNES Mode7 effects in Redot, which gives 2D games the illusion of having 3D effects.
Originally, the SNES used Mode 7 graphics to control affine transformations per scanline that enabled iconic perspective projection effects that allowed games like F-Zero to have a convincing pseudo-3D effect without the use of polygons.
In Redot, the Mode7Sprite2D node replaces the standard Sprite2D material with a custom canvas_item shader that reads per-scanline transforms from a 3 pixel-wide texture that uses transform matrix, offset/pivot, and modulate.
The Manual Scroll setting for Parallax2D allows devs to dynamically change the scroll effect while the game is running.
You can see both of these features being used in the video above to recreate the nostalgic feeling of navigating an airship through the overworld of a 2D game. The Mode7Sprite2D node is used for the ground, which gives a bit of depth to the overworld, and the sky uses a Parallax2D node with manual scrolling, where it moves left or right, or stops, depending on the player's input. All of this is done entirely in 2D.
Structs
Structs are a data type allow devs to group related variables of different data types under a single name. While this does sound like what dictionaries already do, structs differ by allowing you to use typed variables, which improves performance, and they don’t rely on strings like dictionaries do to access the data inside.
struct_name Stats:
var name: String
var level: int = 1
var max_hp: int
var hp: int
var attack: int
var defense: int
This is an example of how to create a global struct by declaring it with struct_name in its own .gd file. From here, any script can use the Stats struct.
class_name Goblin extends Node2D
var stats: Stats = Stats.new("Goblin", 2, 20, 20, 4, 1)
This creates a copy of Stats for the Goblin class, and assigns new values for each variable.
You can read more about structs here.
Traits
Traits represent a set of methods that can be used to extend the functionality of a class. Basically, this is a way for you to use code from one or more scripts, without having that class inherit from those scripts. It’s a very powerful feature focused on composition and reusable code.
# interactable.gd
trait_name Interactable
extends Node2D
@export var prompt_text: String = "Talk"
func interact(by: Node) -> void:
print(by.name, " interacts with ", name)# damagable.gd
trait_name Damageable
extends Node2D
signal died
@export var max_hp: int = 10
var hp: int = 10
func take_damage(amount: int) -> void:
hp = max(hp - amount, 0)
if hp == 0:
died.emit()
# npc.gd
class_name NPC extends Node2D
uses Interactable, Damagable
func _ready() -> void:
hp = max_hp
died.connect(_on_died)
func interact(by: Node) -> void:
super(by)
if hp == 0:
print("(I think he's dead)")
elif hp <= 9:
print("Bloody hell!")
else:
print("Oi bruv!")
func on_died() -> void:
print("A bit rude innit?") In this example, we have interactable and damagable traits which are used by a NPC where the dialogue changes depending on how much health they have.
You can click here to see a more practical example.
Nullable types
This is a feature that allows a typed variable to hold a null value, which represents the absence of a value. This becomes very useful when you need to declare a variable in code and dynamically assign or remove values to it as the game is running.
To allow a variable to hold a null value, simply put ? after declaring a type.
class_name AttackData extends Resource
@export var attack_name: String
@export var power: int
@export var accuracy: float?
func hits() -> bool:
var chance: float? = accuracy
return chance == null or randf() < chanceIn this example there's an AttackData resource with a function that determines whether or not the attack hits depending on the accuracy. If the accuracy is null, the attack never misses, otherwise randf() picks a random number between 0.0 and 1.0 and the attack can miss if the number falls outside of the accuracy range.
For more information on nullable types, click here.
Full Changelog
GDScript Enhancements
- Simplify
ScriptServer::get_global_class_listby @YYF233333 & Cherry picked by @Arctis-Fireblight - Core: Fix ClassDB class list sorting regression- #115923 by @GusatuDamianAlexandru & Cherry picked by @Arctis-Fireblight
- Introduced Traits to GDScript by @SeremTitus & @Arctis-Fireblight
- Add nullable static types to GDScript by @mcdubhghlas
- Introduced structs to GDScript by @mcdubhghlas & @decryptedchaos
- Improve GDScript keyword errors and dependency error navigation by @Arctis-Fireblight
UI Changes
- "create new node" description panel is now resizable by @joey-wheeler99
- add margin to file thumbnails in file explorer window by @joey-wheeler99
- Move script name to top by KoBeWi & ported by @joey-wheeler99
- Make ProjectList visually consistent with the ProjectManager by @DaveTheEggman
Editor Enhancements
- Cherry pick: Path3d snap to collider by @GustJc Cherry picked by @Tekkitslime
- Add custom folder coloring by @JoltedJon
- Cherry pick [Add game speed controls to the embedded game window] by DexterFstone & cherrypicked by @JoltedJon
- Animation selection in sprite or sprite frames editor are now undo-able by @GeneralProtectionFault
2D Enhancements
Parallax2DExpose manual scroll for manual control of infinite scrolling by @GeneralProtectionFault- Add Mode7Sprite2D Node - Replicate Super Nintendo Mode 7 graphics by @GeneralProtectionFault
3D Enhancements
- Introduce 3D Terrain Editor - World Scape 3D by @OldDev78 - Port based on Terrain 3D by @TokisanGames
Performance Enhancements
- Improved clustered rendering efficiency by reducing unnecessary buffer updates by @mcdubhghlas
- Added Math::sin_cos() and strategically replaced trigonometric calls to increase performance by @mcdubhghlas
- Enhanced performance of Vector3::slerp() by @mcdubhghlas
- Increased struct performance by 19% and reduced memory footprint by 33% by @mcdubhghlas
Networking Enhancements
General Enhancements
- Enable C++20 standard by @OldDev78
- Add option to save default project path on project creation by @GeneralProtectionFault
- Refactored, cleaned up, and optimized theming system internals by @GeneralProtectionFault
- Misc enhancements to MCP server implementation by @MichaelFisher1997
- Add function OS::get_real_path() by @samuelvenable
- Engine now respects end user’s power settings by default by @addmix
- Move server files into their subfolders by @aaronfranke cherrypicked by @JoltedJon
Rendering Enhancements
Shaders
Physics
- Add ray and segment intersection methods to MeshInstance3D by @JamesK89
- Optimize Physics2D constraint scheduling + convex polygon normal transforms by @mcdubhghlas
Movie Maker Mode
Bugs Squashed
- Fixed incorrect app_id in Wayland config by @JoltedJon
- Fixed edge case where invalid casts were allowed in GDScript, such as int → string by @mcdubhghlas
- Fixed subviewport renderer flicker by @GeneralProtectionFault
- Fixed import error in EmbeddedGodotGame.kt during Android editor build by @Daniyal09
- Replacement text in the find-and-replace tool now correctly interprets C-style escape sequences before applying replacements by @JoltedJon
- Render targets now initialize newly created color textures to transparent black, preventing undefined visual results on first use by @GeneralProtectionFault
- Fix sky shader
SCREEN_UVbuiltin in Compatibility renderer by @DaveTheEggman - Fix window wrapper disabling prematurely on close request by @DaveTheEggman
- Fix AO Light Affect in Compatibility renderer by @DaveTheEggman
- Fixed missing icon for monotonic cubic interpolation by @VictorSohier
- Fix
SpotLight3D/OmniLight3Dlight_projectordoesn't render whenshadow_enabledisfalseby @DaveTheEggman - Revert fposmod implementation code to match Godot more closely by @OldDev78
- Revert broken is_normalized checks by @OldDev78
- Fixed GH-1354 Modern Godot Animations not importing Properly by @JoltedJon & @mcdubhghlas
- Fix AtlasTexture not appearing until restart GH-1307 by @JoltedJon
- Fixed Typed Arrays cannot erase pairs with null/invalid keys by @JoltedJon
- Fix incorrectly mapped move & drag icons on Linux by @GeneralProtectionFault
- Fix trait type constraint validation in GDScript by @Arctis-Fireblight
- Fix MCP host and game process shutdown by @dominicbytes
- Rename the about dialog's tooltip text by @DaveTheEggman
- Fix decreasing
ReflectionProbe's intensity property below1.0not reducing the strength of its ambient light condition by @DaveTheEggman - Fix OpenXR failing to initialize rendering device by @JoltedJon
- Fix MCP scene_action connect persistence and callback stubs by @MichaelFisher1997
- Fix Cannot access inner struct of a trait or assign struct as a type alias by @Arctis-Fireblight
- Fix nullable struct fields defaulting to zero instead of null by @mcdubhghlas
- Fix spacing in REDOT_AUTHORS.md & clean up core_builders.py- #1385 by @GeneralProtectionFault
- Fix
EditorSpinSlider::get_minimum_size()&EditorSpinSlider::_draw_spin_slider()by @DaveTheEggman - Fix WorldScape3D - Undoing addition of node breaks viewport interaction by @OldDev78
- Fix support for
trait supercalls in GDScript by @Arctis-Fireblight - Fixed error output in FP16 Unit Tests by @mcdubhghlas
- Fix FP16 networking unit test by @mcdubhghlas
- CanvasLayer dialogue UI renders black in Compatibility when a spatial shader uses hint_depth_texture by @Kaleb-Reid & cherry picked by @JoltedJon
- Make GDScript structs local-scoped by default and introduce
struct_namekeyword by @mcdubhghlas - Fix Handling of
@export_groupin GDScript traits by @Arctis-Fireblight - Fix Compiler bug: unresolved assign for trait-typed conversion assigns. by @mcdubhghlas
- Fixed several Mode 7 issues affecting saved state, materials, and target-following behavior. by @GeneralProtectionFault
- Fixed crash in headless mode by preventing objects from getting gueued for deletion twice by @OldDev78
- Fix
memnew_placementwithchar *arguments by @Ivorforce and Cherry Picked by @morgothaufheroin88 - Enforce WorldScape3D tools ranges by @OldDev78
- Fixed WorldScape3DGradientOperationBuilder by @OldDev78
- Reduced WorldScape3D log spam by @OldDev78
- Fix WorldScape3D support for region size 4096x4096 by @OldDev78
- Fided Structs not showing in the inspector when exported by @mcdubhghlas
- Fix polygon trying to solve center with fewer than 3 vertices by @GeneralProtectionFault
- Fix count bug in _get_polygon_count by @GeneralProtectionFault
- Fixed structs not showing in the inspector when exported by @mcdubhghlas
- Fixed missing syntax highlights for structs by @mcdubhghlas
- Fixed code completion support for local structs in GDScript by @Arctis-Fireblight
- Fixed Inspector does not update when traits add/remove @export fields by @Arctis-Fireblight
- Fixed trait-typed exported arrays losing node references by @Arctis-Fireblight
- Fixed script and trait types in GDScript struct fields by @Arctis-Fireblight
- Fixed exported GDScript struct array editing in the Inspector by @Arctis-Fireblight
- Fix false nullable warnings after null checks by @Arctis-Fireblight
- Fixed "Terrain Assets" dock opens by default when creating a new project instead of "Output" by @OldDev78
- Fix trait symbol lookup by @Arctis-Fireblight
- Fixed data loss on parse error by @mcdubhghlas
- Fixed inability to close paused games on Linux under Wayland by @Arctis-Fireblight
- Fix editor crash when completing nullable assignments by @Arctis-Fireblight
- Fix GDScript highlighting for static variables, traits, and structs by @Arctis-Fireblight
- Fix nullable narrowing for GDScript struct fields by @Arctis-Fireblight
- Improved WorldScape3D dock behavior when interacting with the editor window, including focus changes and minimized states by @OldDev78
- Fix set_prop not assigning resources and ignoring failed sets by @tyrannyme
- MCP: Fix trigger_action blocking the game instead of holding the action by @tyrannyme
- MCP: Fix inspect_live ignoring node_path by @tyrannyme
- MCP: Send initialized notification in verify_workflow.py by @tyrannyme
Documentation
- Fix WebXR documentation errors by @dominicbytes
- Fix dead "Compiling from source" URL in README by @BLOKBUSTR
- Update documentation feedback link to correct repository URL by @Arctis-Fireblight
Chores & Buildsystem
- Update default CI permissions to read only by @dominicbytes
- Preserve
dotnet formatreturn code in static checks by @dominicbytes - Update workflows to use the latest actions by @DaveTheEggman