Inherited Scenes

This is not a tutorial, but it's my own experience with stumbling upon the perfect solution for a problem I was ready to over-think.

by Χάρων

Share

So I was working on a reusable First Person Character setup for my games because my strategy up until now regarding FPS Controllers were to scavenge my own projects and tech demos for a the one I liked the most, bringing it to my new project and fix/update whatever was necessary, while throwing out everything that I didn't need. This strategy worked in the sense that in just 1 hours of development I was able to adapt a random FPS controller to a new project, by which time I already got bored by it. It beats creating a controller from scratch every time, I think I'd go insane if I did that, but it's definitely not a good a good approach to handling code re-use.

I already have a project for generic and re-usable bits and pieces that I use across projects, but I just never got around to designing a decent FPS controller so far! Glad I did, because I did re-learn a lesson that I already forgot somehow, and that is the concept of inherited scenes in Godot. You see, I wanted to have me First Person controller pre-set-up with a left and right hand places, where 3D models can be attached and appear correctly for the camera. On top of that I also wanted to already have these be rendered on top of the main camera, so whatever I pick as the Left and Right hand or weapon wouldn't clip into the level geometry. So I created my left and right hand markers and started brainstorming how to make things attached to them from outside the scene, so that it's easy to set-up, but also gives me the ability to handle game-specific animations for the hands as well as a way to manage them. I finally decided that the best way to do this would be using an exported Node3D that I could then set from the Editors property panel and it would just center everything itself. That meant exporting a bunch of things, including additional transform options for the hands in case the models weren't rotated or scaled properly, handling copying the location and rotation of the camera/hands to the exported node, so that it follows the player and also had to write update logic in case properties that affect the hands change. After all this I could drop a player scene anywhere in my level, drop two weapon scenes in the level somewhere with them (or child them) and then just link them to the Left and Right properties to get them pop up in the camera!

Decent system, but it raised several red flags for me. For one, The FPS controller is now managing nodes that don't belong to it. I thought it's a good idea at the time, but it just felt 'dirty'. Secondly handling player actions and animations felt weird. I would have to manage them from another node, probably a PlayerScene parent of some kind, but that would also mean the player logic would just be this weird, ephemeral script floating on top of the actual controller scripts, kinda puppeteering half of the things from above. That felt even weirder to me... This realization quickly led me to the idea of trying to extend the FPS Controller and try to handle the hands from there... I right clicked my scene, and there it was... "New Inherited Scene"... Wheels started turning... I clicked it and immediately after seeing how the SceneTree looked things fell into place... I can attach the weapons to the hand markers here, I can set up the transforms here, and finally I can just extend the character script here directly, without anything feeling off or weird!

This was an interesting experience for me and I thought I'd share it here just in case it helps someone else.

What are Inherited Scenes.

Inherited scenes are kinda like extending a script, except for scenes. You get to create a new scene from a different scene, with the entire SceneTree available to you and configurable, but you can't delete any of the underlying nodes. You can, however, attach new ones to them.

The way to create a new Inherited scene is pretty easy, simply right click on your parent scene and select "New Inherited Scene" in the menu

How to create a new inherited scene

What you will be welcomed to is a SceneTree that contains all of your parent nodes marked with an orange/yellow color.

This is the trick. You get to create and extend your existing Scene, while still being able to rely on the underlying structure. The FPS Script can still be sure there will be a 'PlayerCamera/RightHand' node and other paths in the tree. The collision shape is still set up automatically, the ray casting still works... Everything works AND I can now attach additional models to the Left and Right hand! They are placed exactly where they should be so that all the transformations work automatically, this is also exactly the place where to drop my AnimationPlayer or AnimationTree node and it's also the ideal setup for extending the FirstPersonCharacter.gd script as a Player.gd script.

Updated Implementation

First I just need to change my FirstPersonCharacter script to have a class_name defined, and then in my Inherited scene I can simply right click the root node and select "Extend Script" which will automatically make a new GDScript that extends my FirstPersonCharacter. Here I can set up any logic or input handling that's specific to a game project. Only detail is you have to not forget to call the parents methods if you are overriding them.

For examply my FirstPersonCharacter.gd has a bunch of FPS related logic in the _process function. This means that my Player.gd should now look like this:

extends FirstPersonCharacter

func _process(delta: float) -> void:
	
	# game specific logic here ...

	super._process(delta) # call the parent

That is all. I can now simply copy my FirstPersonCharacter.tscn and FirstPersonCharacter.gd file to any new project, right click it to create an inherited scene and then drag and drop my inherited scene to any level map. This process will take like 5 minutes and instantly give me a fully functional and clean FPS setup!

For me, this has been a reminded of Inherited Scenes and how they can help to create re-usable and clean components.