0

I am new to the Godot engine so please forgive me if this is a very simple question.

So I am creating a game (2d platformer) that pretty much has the same level layout as every other level. However, each level has a trick to it. Say: For level 1:

  • flick the lever (by pressing f) and a portal opens up.
  • enter the portal and go to next level (level 2)

For level 2:

  • flick the lever (by pressing f) 5 times and portal opens up.
  • enter the portal and go to next level (level 3)

For level 3:

  • flick the lever (by clicking with mouse cursor unlike level 1 and 2) and portal opens up.
  • enter the portal

etc etc.

So this is the layout for the game I'm developing. Problem is when I duplicate the level, the scripts (for lever, portal etc) don't seem to be "individual" to each level node. Is there a way to make it such that each level node and its sub-nodes (like the lever for level 2 having a separate script to level 1's lever)?

Thanks, and sorry again if this is a stupid question.

kal
  • 11
  • 1
  • 2

2 Answers2

0

There isn't a trick.

This issue has come up in the discussion on the Godot-Proposals repository. Feel to offer your ideas on how to improve Godot there.

Godot is rather flexible on how you organize your project. This also means that keeping the organization you pick is up to you.

First of all, I must mention that scripts do not need to be their own files. On the "Attach Node Script" dialog (which you can open from "Attach Script" on the context menu on Scene) you can select "build-in" script. And this will store the script in the scene file. As a result, if you copy the scene file, you are also copying the script. Although, having the script in a separate file is often preferible for version control, and for using an external editor.


Beyond that, there are a couple tools that will help you:

  • On the context menu from the FileSystem, you can select "View Owners…". This will show you where resources (including scripts) are used explicitly (references using load or anything dynamic does not count).
  • On the Project menu, under tools, select "Orphan Resource Explorer…". This will show you any resources (including scripts) that are not used anywhere (again, it ignores dynamic loading, so it might say something you are actually using isn't).

With those tools you can - for example - make a folder for each level (and a folder for resources to be shared across levels, perhaps organized on folders by type), and check if the script on the folder of a level are referenced from the scenes on that same folder and not the folders of the other levels.

Some people prefer to put all the scripts on a single folder regardless of where they are used, and all the scenes on the same folder, and so on. That is somewhat harder to keep track of, but with the tools mentioned above you can check. Perhaps you could have a naming scheme so you can tell for which level each resource is (or if the resource is intended to be reused in multiple levels)?


Consider also:

  • Signals. For example, you might have identical levers except for what they do. Instead of coding in the script what they do, add a custom signal and connect it form the editor. Again, those signal connections are stored in the scene file. I would, for example, have the counter for how many times the lever is switched in the code connected to the lever instead of having it on the lever itself. By the way, see signal bus and resource based communication.

  • Exported variables. If you have similar scripts, identify the differences (e.g. to which level a portal goes), and consider turning them and turn them into exported variables (export var) which can be edited from the Inspector. Those variables are stored in the scene file, so they will remain unique even when you duplicate the scene. This also means that if you find an error on the code, you don't have to fix it on two different scripts. However, be aware that if you generalize things that might seem related but actually aren't, you will run into trouble later, so the general advice is to not do it preemptivly.

These also mean that those scripts would no longer be unique of the scene, and you can move them to a common folder for all such scripts and not worry anymore if they are used in - and only in - the correct scene.


By the way, I would like to point you to:

  • Scene composition: in Godot scenes can have instances of other scenes inside. So you can make scenes for the individual components (e.g. an scene for the portal) and put them together in the level scene. Then these scripts we are talking about do not really belong to a level, but to a component. And that component could be used in multiple scenes without issue (again, with the help of signals and exported variables).

  • Scene inheritance. What you are doings sounds like you could have a base scene with the common elements, and then create inherited scenes for each level from it (you find the option in the context menu of an existing scene in the FileSystem). The inherited scenes would have everything the base one has, but can add new nodes or change the properties of existing nodes.

Theraot
  • 31,890
  • 5
  • 57
  • 86
0

There are diffrent approaches you could try to accomplish this.

The first that comes to mind for me is that you implement something like a current state for the diffrent items. So for example add a export variable to track the level you are in (or have it as an global variable somewhere). Then you check this variable in the _ready function and enter the correct state for the according level.

Your lever script would then only have the common functions like opening the portal and starting the flick animation, but the triggers, which call these functions are handled in the diffrent state scripts.

So your lever would look something like this:

extends Node

export(int) var level = 1

var state_paths = [ "path_to_level_1_state", "path_to_level_2_state" ]
var current_state = null

func _ready() -> void:
    current_state = load(state_paths[level-1].new()
    current_state.enter(self)


# delegate events to the state objects.
func _unhandled_input(event: InputEvent) -> void:
    current_state.handle_input(event)


func _process(delta: float) -> void:
    current_state.update(delta)


func _physics_process(delta: float) -> void:
    current_state.physics_update(delta)

func open_gate() -> void:
   $AnimationPlayer.play("flick")
   #notify gate to open code..

There are definitly better and much safer ways to store the diffrent state scripts Like creating subnodes in your lever scene which conatin the scripts like done here for the state maschine but I guess this will do for the example.

(path_to_level_1_state is the full path to a script like "res:/scripts/states/lever/level1.gd")

This script could look like this (you can write a base class for all your states which hold the reference to the Item and implement base functions, but I'm not doing this here atm, because I write all this from my phone):

extends Node

var lever = null

func enter(parent) -> void:
   lever = parent


func handle_input(event) -> void:
   if event is InputEventKey:
        if event.pressed and event.scancode == KEY_F:
            lever.open_gate()

func update(delta):
   pass #nothing to do here bit maybe in another level?

func physics_update(delta):
   pass
Bugfish
  • 725
  • 5
  • 14