How do you write a static variable in GDScript

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By KramchayDig

I kind of wondered how do you write a static variable if it is possible in GDScript. Hope to find answers!

https://github.com/godotengine/godot/issues/6840

Dlean Jeans | 2019-07-09 12:27

:bust_in_silhouette: Reply From: BraindeadBZH

Not sure what you are calling static variable but GDScript has support for constants through the const keyword.

A static variable is like a static function – it’s available without an object instance, addressed through the class.
So, just like you can call MyClass.function() you should be able to address class variables using MyClass.variable
A constant is something totally different.
This question is the top Google result for “godot static variable” and doesn’t answer the question, so poking it here, because I have the same question …

The answer is that “there are no static variables, because those are effectively global, and would be less thread and memory safe than the current constructs.”
The least-bad work-around I’ve found is to hang some custom node of known name off of the scene root, and put members on that node. Which in turn requires the scene to be available.

Jon Watte | 2019-12-26 12:20

My answer might not be quite clear. I know what is a static variable, but I don’t see why you would use static variable in Godot, I don’t see any good reason that are not covered by a constant.

BraindeadBZH | 2019-12-26 12:33

I just gave you one: Count how many times each particular kind of event happens, or each particular function is called. Not per class instance, but overall.

Jon Watte | 2019-12-28 01:27

Not sure how this example is relevant for game development in GDScript, but that could be easily done with a singleton.

BraindeadBZH | 2019-12-28 09:03

Hey Jon Watte, I’m liking your thinking… I was just wondering today how to implement static variables in godot. I’m coming from a C# background and I’m also missing private variables. In working on my first godot game using just Gdscript. When it’s finished I will redo the whole thing in C# just to see how the experience goes. I the meantime, I will defo use your idea re the static vars.

NautMeg | 2020-02-08 05:38

OK, maybe I need to look into Singletons then…

NautMeg | 2020-02-08 05:38

Come on, this isn’t Stack Overflow. We know it is not always good practice, so just try and answer if you can. Hope you have a good one though.

codythompson | 2020-05-14 12:16

I decided to say this isn’t a good answer, but at least you trying to help us…

thebluetropics | 2023-02-04 13:30

:bust_in_silhouette: Reply From: Ilmari

Check out the section on singletons in the documentation:

“Godot’s scene system, while powerful and flexible, has a drawback: there is no method for storing information (e.g. a player’s score or inventory) that is needed by more than one scene.”

very thanks for info

Dell87 | 2022-03-24 05:54

:bust_in_silhouette: Reply From: uaknight

I agree that it would be convenient to have static variables. These should be common to the class, not the object, just like constants. But contrary to constants, it should be possible to modify them.

I suggest that a var declaration could be prescribed with the static key word:

const MY_CONST # shared with all objects of this class, non-modifiable (existing)
static var my_static_var # shared with all objects of this class, modifiable (suggested)

However, as a workaround, it’s possible to use the metadata for the gdscript of the class. Metadata is a dictionary which every Object in Godot has. Since a GDScript is a Resource and a Resource inherits from Object, also the gdscript (the class itself) has a metadata dictionary as a member.

Here’s an example from my game. I have a number of factions and each faction should start the game with 2 scout units each. I want the names of the scout units to be like “1st Scout”, “2nd Scout”, “3rd Scout”, “4th Scout”, where 1st and 2nd belongs to the first faction, 3rd and 4th belongs to the second faction, and so on. The order number of the scout unit should reflect the total number of units having been created so far of the scout class.

To achieve this, I put a counter variable in the metadata of the Scout gdscript.

I put the scout generation in the Faction script and it looks like this:

const SCOUT_TEMPLATE = preload("res://squad/Scout.tscn")

func setup(game):
	# create scouts
	for i in 2:
		var scout = SCOUT_TEMPLATE.instance()
		var scout_class = scout.get_script()
		var counter = scout_class.get_meta("counter")
		counter += 1
		scout.number = counter	
		scout.name = scout.get_unit_name("Scout")		
		scout_class.set_meta("counter", counter)
		scout.faction = self
		game.squad_node.add_child(scout)
	pass
	
func _ready():
	var scout_class = load("res://squad/Scout.gd")
	if not scout_class.has_meta("counter"):
		scout_class.set_meta("counter", 0)	
	pass

For each scout unit created, the class counter is fetched from the metadata of the class, it is increased by 1, and then saved back into the metadata.

The get_unit_name(“Scout”) function generates the name, like “1st Scout”.

:bust_in_silhouette: Reply From: Levi Lindsey

Here’s another work-around for static variables in GDScript, which hasn’t been mentioned yet.

  1. Define a const Dictionary variable on your class.
  2. Store and access your static variables as properties on that Dictionary.

This works because the const Dictionary instance is shared between instances of the class. Even though the Dictionary is const and can’t be re-assigned, it is still “mutable”, so you can change its properties as much as you want.

Here’s an example:

class_name Main
extends Node


func _ready() -> void:
    var a := MyClass.new()
    var b := MyClass.new()
    
    a.STATIC_VARIABLES.is_my_flag_enabled = true
    
    assert(b.STATIC_VARIABLES.is_my_flag_enabled)


class MyClass extends Reference:

    const STATIC_VARIABLES := {
        is_my_flag_enabled = false,
    }

Prefer not relying too much on this. It’s a hack that might no longer work in the future, and looks confusing. Also it’s not exactly doing what you think: if your script resource gets unloaded (because scripts are resources, there is no rule saying they never unload), it will also unload its constants, therefore you will loose non-constant stuff you’ve put inside.

Zylann | 2022-03-22 13:40

Ah, that’s a good point. It’d be a very confusing bug to track down if this started failing because of script unloading.

I believe this issue with unloading would also prevent the other top-voted pattern for this question form working too, right? Would metadata entries on an Object be lost when the Object is unloaded?

Levi Lindsey | 2022-03-22 15:52

Would metadata entries on an Object be lost when the Object is unloaded?

Yes. Makes sense since the object is destroyed.

The best way to do global vars remains autoloads IMO. But if for some reason you really dont want to add it to project settings, then another way is to hijack an existing singleton (like Engine) using metadata, and this will be legit code, though it remains a bit confusing.

Zylann | 2022-03-24 19:11

:bust_in_silhouette: Reply From: JOELwindows7

Another weird way of static variable workaround: Shader code

  1. create new resource type ShaderMaterial. save it as WhateverNameYouLikeShaderMaterial.tres somewhere in res:// this project.
  2. Make sure your whatever shader material tres is still opened as seen in Inspector tab. in the Shader property, add new Shader code.
  3. Save that new shader code as WhateverShaderCodeNameYouLikeShaderCode.shader. or extension .gdshader or whatever supported.
  4. Open that shader code and you’ll see shader code editor on bottom edit bar. make the shader type canvas_item and put all your variable (called uniform) you want it static there. e.g.:
 // JOELwindows7. GNU GPL v3
//accidentally invented / discovered portable singleton or whatever static
// variable!
// Make sure the ShaderMaterial holding this code is not Local to Scene!

shader_type canvas_item;

uniform bool thaug = false;
uniform bool dolum = false;
  1. Do make sure in the ShaderMaterial tres (WhateverNameYouLikeShaderMaterial.tres), the Local to Scene is unchecked!!! otherwise, the static variable effect won’t work (unless if you don’t want all other objects using this material change look condition every param change).

  2. Now everytime you want to access those static variables, find your node that will use that set of variables & open its script. add a variable that hold your ShaderMaterial. so in YourNodeScript.gd at the top bellow extends:

`export(ShaderMaterial) var theWeirdClassOfStaticVariable = load("WhateverNameYouLikeShaderMaterial.tres")`

basically you load that your shader material somewhere into that variable on your node script.

  1. To read variable, you do theWeirdClassOfStaticVariable.get_shader_param("thaug") which that argument means your variable you’d like to read. e.g., that reads thaug value.

  2. To set variable, you do theWeirdClassOfStaticVariable.set_shader_param("thaug",true) which arguments are, your variable you’d like to set, and what value. e.g., that sets variable thaug value into true.

Every other nodes that uses this material should also got the same last set value. Therefore, e.g. if you set the whiten shader parameter to true, all other nodes using this material will have whiten shader parameter true as well. Which infact makes it feel that this is some sort of static variable effect, albeit is inelegant but it works.

The disadvantage is, is that you need to be knowledgeable about uniform variables of GLSL the Shader Code Language.

Basically, there are types such as:

  • intinteger (that has minus, has sign 1 on last bit marking -)
  • float (has decimal precision, comma or point something)
  • bool true or false
  • uint unsigned integer (that has no minus, no sign 1 on last bit marking -)
  • double double precission float
  • vec2
  • vec3
  • vec4
  • etc.

Here articles & page of book about it:

1 Like