Skip to content

A simple Godot system that helps with performance and calculations

License

Notifications You must be signed in to change notification settings

deadlykam/CodeOptPro_Godot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeOptPro

Introduction

This is a simple Godot system that helps with performance.

Table of Contents:

Prerequisites

Godot Game Engine

Godot version v4.1.3.stable.mono.official [f06b6836a] and above should work. Previous Godot version of v4.1.1.stable.mono.official [bd6af8e0e] should work but any version before that have not been tested and may give errors.


Stable Build

Stable-v1.13.0 is the latest stable build of the project. The compressed file for this project can also be found there. If development is going to be done on this project then it is adviced to branch off of any Stable branches because they will NOT be changed or updated except for README.md. Any other branches are subjected to change including the main branch.


Installation

  1. First download the latest CodeOptPro-v1.13.0.zip from the latest Stable build.
  2. Once downloaded extract/unzip the file.
  3. Enter the folder and copy the folder named kamran_wali.
  4. Now paste the folder in the addons folder of your Godot project. If your Godot project does not have the addons folder then just create it in the root folder, res://, and paste the copied folder there.
  5. (Optional) To open the interface for CodeOptPro simply go to the menu Project -> Projct Settings. Click the Plugins tab and enable the Variable Creator or Instantiate Object. This should open and dock the Variable Creator or Instantiate Object.
  6. (Optional) If you want access to the CodeOptPro's script templates then go to the folder addons -> kamran_wali -> code_opt_pro and copy then copy the folder named script_templates. Paste the copied folder in the root folder, res://. Now the script_templates should be available while inherting from Resource.

Features

Performant Data Share/Use:

I have also added a feature that allows to share/use data in a performant way by extending the Resource script. For now there are two categories of data share and each have their own different data types.

1. Fixed Vars

In this category different type of data types are shared, example bool, float, int, string etc. You only need to create one fixed var and share it with multiple objects, example - If five objects needs an int value of 1 then create a fixed var of type int that has the value 1 and share that. In that way only one int value of 1 is created instead of five which saves some memory. Like the name suggests the values are fixed and can/should NOT be updated. Otherwise it defeats the purpose of its function. Only call the get_value() function to get the value and do NOT change the property _value through script.

  • COP_FixedBoolVar - This FixedVar type shares bool data types. When creating the COP_FixedBoolVar set the value either true or false by clicking the tick box. To get the value simply call the method bool COP_FixedBoolVar.get_value(). To use COP_FixedBoolVar simply change the type of a var to COP_FixedBoolVar.
  • COP_FixedFloatVar - This FixedVar type shares float data types. When creating the COP_FixedFloatVar set the value to any float type value. To get the value simply call the method float COP_FixedFloatVar.get_value(). To use COP_FixedFloatVar simply change the type of a var to COP_FixedFloatVar.
  • COP_FixedIntVar - This FixedVar type shares int data types. When creating the COP_FixedIntVar set the value to any int type value. To get the value simply call the method int COP_FixedIntVar.get_value(). To use COP_FixedIntVar simply change the type of a var to COP_FixedIntVar.
  • COP_FixedStringVar - This FixedVar type shares string data types. When creating the COP_FixedStringVar set the value to any string type value. To get the value simply call the method string COP_FixedStringVar.get_value(). To use COP_FixedStringVar simply change the type of a var to COP_FixedStringVar.
  • COP_FixedVector2Var - This FixedVar type shares Vector2 data types. When creating the COP_FixedVector2Var set the value to any Vector2 type value. To get the value simply call the method Vector2 COP_FixedVector2Var.get_value(). To use COP_FixedVector2Var simply change the type of a var to COP_FixedVector2Var.
  • COP_FixedVector3Var - This FixedVar type shares Vector3 data types. When creating the COP_FixedVector3Var set the value to any Vector3 type value. To get the value simply call the method Vector3 COP_FixedVector3Var.get_value(). To use COP_FixedVector3Var simply change the type of a var to COP_FixedVector3Var.
  • COP_FixedVar Template - For creating a new COP_FixedVar type you can simply use the script templates that are already present in the addon. Go to the folder addons -> kamran_wali -> code_opt_pro and then copy the folder script_templates. Paste the copied folder to the root folder res://. Now you can use the script template to create a new COP_FixedVar. Just create a new script and make sure to Inherit from Resource. Then in the Template section select Resource: Fixed Var Template. Give the script any name you want and finally create it. Now in the script make sure to give it a class name if you want to which has been commented out. For the value change it to any type you want. Finally for the get_value() method make sure to give it a return type as well which may help with performance a bit.
2. Managers

This category shares different type of managers instead of data types. Managers are scripts that have a bit of complex logic to it and these manager resources helps to share the references to those managers in a decoupled way. In this case unfortunately Variable Creator will only create managers that are created in CodeOptPro but if you want to create your own custom manager then you can use the manager_helper_template which is under the Resources while creating a script. The manager that is going to be referenced MUST be the only one that calls the method set_manager(manager) void and provides self as reference. Other scripts using this manager helper resource reference MUST only call the method get_manager() and then use the manager's methods from there. You can check out the script cop_update_manager_global_helper to see how the manager resource script is created.

  • COP_UpdateManagerGlobalHelper - This manager reference type stores and uses the reference for global update managers and global update objects.
3. Vars

Just like FixedVars this category shares different type of data types as well, example bool, float, int, string etc. The only difference is that you can NOT set any values here like FixedVars and the values may change through custom scripts. Vars basically share values that are constantly changing. For example - You have 5 objects that wants to know the player's position. Then just create a COP_Vector3Var and make the player script constantly update the newly created Vector3Var. Then add the newly created Vector3Var to the other 5 objects. Now all of those 5 objects have access to the player's position without the need of player script reference. Also use the functions get_value() and set_value(type) for getting and setting the value. Do NOT get or set the property _value directly through script as this may result in error later on. Below are all the types.

  • COP_BoolVar - This Var type shares bool data types. To set the value simply call void COP_BoolVar.set_value(bool value). To get the value just call bool COP_BoolVar.get_value(). To use COP_BoolVar simply change the type of a var to COP_BoolVar.
  • COP_Camera2DVar - This Var type shares Camera2D data types. To set the value simply call void COP_Camera2DVar.set_value(Camera2D value). To get the value just call Camera2D COP_Camera2DVar.get_value(). To use COP_Camera2DVar simply change the type of a var to COP_Camera2DVar.
  • COP_Camera3DVar - This Var type shares Camera3D data types. To set the value simply call void COP_Camera3DVar.set_value(Camera3D value). To get the value just call Camera3D COP_Camera3DVar.get_value(). To use COP_Camera3DVar simply change the type of a var to COP_Camera3DVar.
  • COP_FloatVar - This Var type shares float data types. To set the value simply call void COP_FloatVar.set_value(float value). To get the value just call float COP_FloatVar.get_value(). To use COP_FloatVar simply change the type of a var to COP_FloatVar.
  • COP_IntVar - This Var type shares int data types. To set the value simply call void COP_IntVar.set_value(int value). To get the value just call int COP_IntVar.get_value(). To use COP_IntVar simply change the type of a var to COP_IntVar.
  • COP_NodeVar - This Var type shares Node data types. To set the value simply call void COP_NodeVar.set_value(Node value). To get the value just call Node COP_NodeVar.get_value(). To use COP_NodeVar simply change the type of a var to COP_NodeVar.
  • COP_Node2DVar - This Var type shares Node2D data types. To set the value simply call void COP_Node2DVar.set_value(Node2D value). To get the value just call Node2D COP_Node2DVar.get_value(). To use COP_Node2DVar simply change the type of a var to COP_Node2DVar.
  • COP_Node3DVar - This Var type shares Node3D data types. To set the value simply call void COP_Node3DVar.set_value(Node3D value). To get the value just call Node3D COP_Node3DVar.get_value(). To use COP_Node3DVar simply change the type of a var to COP_Node3DVar.
  • COP_StringVar - This Var type shares string data types. To set the value simply call void COP_StringVar.set_value(string value). To get the value just call string COP_StringVar.get_value(). To use COP_StringVar simply change the type of a var to COP_StringVar.
  • COP_Vector2Var - This Var type shares Vector2 data types. To set the value simply call void COP_Vector2Var.set_value(Vector2 value). To get the value just call Vector2 COP_Vector2Var.get_value(). To use COP_Vector2Var simply change the type of a var to COP_Vector2Var.
  • COP_Vector3Var - This Var type shares Vector3 data types. To set the value simply call void COP_Vector3Var.set_value(Vector3 value). To get the value just call Vector3 COP_Vector3Var.get_value(). To use COP_Vector3Var simply change the type of a var to COP_Vector3Var.
  • COP_Var Template - Fir creating a new COP_Var type you can simply use the script templates that are already present in the addon. Go to the folder addons -> kamran_wali -> code_opt_pro and then copy the folder script_templates. Paste the copied folder to the root folder res://. Now you can use the script template to create a new COP_Var. Just create a new script and make sure to Inherit from Resource. Then in the Template section select Resource: Var Template. Give the script any name you want and finally create it. Now in the script make sure to give it a class name if you want to which has been commented out. For the value change it to any type you want. For the get_value() method make sure to give it a return type as well which may help with performance a bit. Finally for the set_value(value) give the parameter a type as well for helping with performance.
Variable Creator

For now the only way to create a new variable is to use the Variable Creator plugin. You can open the Variable Creator window by going to the menu Project -> Project Settings then select the Plugins tab and finally enable the Variable Creator. This will open the Variable Creator by docking it at the bottom right side. You can dock it how ever you wish. Below I will explain the highlighted parts of the Variable Creator.

Variable-Creator1.png
Variable Creator
  • a. Name - This is where you give the name of the variable you want to create. If name given already exists in the path then the newly created variable will replace the old one and it does not matter what type it was.
  • b. Path - This is the path or folder location where the new variable will be created. You can update this path as well. Foll the instructions in c. to see how to update the path.
  • c. Update Path - If you want to update the path where the new variable will be created then right click the folder where the variable should be created and select Copy Path. Paste the copied path in the path field, b.. Finally press the Update Path button and the path will be updated. This will only update the path for 1 variable type, in this case FixedBoolVar types. This way the Variable Creator will allow you to have different paths for different variable types. The default path is res://addons/kamran_wali/code_opt_pro/variables/.
  • d. Category - This is where you get to select from which category the variable will be created. For now there are 2 categories which are FixedVars and Vars.
  • e. Variable Type - This is where you get to select which type of variable to create. Each category have different type of variable types.
  • f. Create Variable - This button will create the new variable type. Remember to give a name to the variable otherwise this button will NOT be visible. Also the name of the button Create Variable will change with the variable type selected so that you will know what type you are creating.

Vector Performant Calculation:

I have also added performant Vector calculations that may save some performance issue in the long runespecially when it comes to Vector distance calculation. The class is called Vec and it contains static functions. I will give just brief explanation of the functions.

  1. float Vec.distance_vec3(Vector3, Vector3) - This method calculates the distance between two Vector3s and the returned value is a squared value. This means that if you want to check if the distance of the two vector point is greater/less than 5 units then you must make 5 squared which is simply 5x5 = 25. Meaning you are comparing against 25. This will save lot of performance issue later down the line when too many objects needs distance check.
  2. float Vec.distance_fixed_vec3_var(COP_FixedVector3Var, COP_FixedVector3Var) - This method is similar to 1. so please read that discription for explanation. Only difference is that it takes two COP_FixedVector3Vars.
  3. flaot Vec.distance_vec3_var(COP_Vector3Var, COP_Vector3Var) - This method is similar to 1. so please read that discription for explanation. Only difference is that it takes two COP_Vector3Vars.
  4. float Vec.distance_vec2(Vector2, Vector2) - This method is similar to 1. so please read that discription for explanation. Only difference is that it takes two Vector2s.
  5. float Vec.distance_fixed_vec3_var(COP_FixedVector2Var, COP_FixedVector2Var) - This method is similar to 1. so please read that discription for explanation. Only difference is that it takes two COP_FixedVector2Vars.
  6. float Vec.distance_vec2_var(COP_Vector2Var, COP_Vector2Var) - This method is similar to 1. so please read that discription for explanation. Only difference is that it takes two COP_Vector2Vars.
  7. Vector3 Vec.subtract_vec3(Vector3, Vector3) - This method subtracts two Vector3 without needing any extra var variable and returns a Vector3 value.
  8. Vector3 Vec.subtract_fixed_vec3_var(COP_FixedVector3Var, COP_FixedVector3Var) - This method is similar to 7. so please read that discription for explanation. The only difference is that it takes two COP_FixedVector3Vars.
  9. Vector3 Vec.subtract_vec3_var(COP_Vector3Var, COP_Vector3Var) - This method is similar to 7. so please read that discription for explanation. The only difference is that it takes two COP_Vector3Vars.
  10. Vector2 Vec.subtract_vec2(Vector2, Vector3) - This method subtracts two Vector2 without needing any extra var variable and returns a Vector2 value.
  11. Vector2 Vec.subtract_fixed_vec2_var(COP_FixedVector2Var, COP_FixedVector2Var) - This method is similar to 10. so please read that discription for explanation. The only difference is that it takes two COP_FixedVector2Vars.
  12. Vector2 Vec.subtract_vec2_var(COP_Vector2Var, COP_Vector2Var) - This method is similar to 10. so please read that discription for explanation. The only difference is that it takes two COP_Vector2Vars.
  13. Vector3 Vec.add_vec3(Vector3, Vector3) - This method adds two Vector3s without needing any extra var variable and returns a Vector3 value.
  14. Vector3 Vec.add_fixed_vec3_var(COP_FixedVector3Var, COP_FixedVector3Var) - This method is similar to 13. so please read that discription for explanation. The only difference is that it takes two COP_FixedVector3Vars.
  15. Vector3 Vec.add_vec3_var(COP_Vector3Var, COP_Vector3Var) - This method is similar to 13. so please read that discription for explanation. The only difference is that it takes two COP_Vector3Vars.
  16. Vector2 Vec.add_vec2(Vector2, Vector2) - This method adds two Vector2s without needing any extra var variable and returns a Vector2 value.
  17. Vector2 Vec.add_fixed_vec2_var(COP_FixedVector2Var, COP_FixedVector2Var) - This method is similar to 16. so please read that discription for explanation. The only difference is that it takes two COP_FixedVector2Var.
  18. Vector2 Vec.add_vec2_var(COP_Vector2Var, COP_Vector2Var) - This method is similar to 16. so please read that discription for explanation. The only difference is that it takes two COP_Vector2Var.
  19. Vector3 Vec.divide_vec3(Vector3, float) -> This method divides the Vector3 value with the float value without needing any extra var variable and returns a Vector3.
  20. Vector3 Vec.divide_fixed_vec3_var(COP_FixedVector3Var, float) - This method is similar to 19. so please read that discription for explanation. The only difference is that it takes COP_FixedVector3Var.
  21. Vector3 Vec.divide_vec3_var(COP_Vector3Var, float) - This method is similar to 19. so please read that discription for explanation. The only difference is that it takes COP_Vector3Var.
  22. Vector2 Vec.divide_vec2(Vector2, float) - This method divides the Vector2 value with the float value without needing any extra var variable and returns a Vector2.
  23. Vector2 Vec.divide_fixed_vec2_var(COP_FixedVector2Var, float) - This method is similar to 22. so please read that discription for explanation. The only difference is that it takes COP_FixedVector2Var.
  24. Vector2 Vec.divide_vec2_var(COP_Vector2Var, float) - This method is similar to 22. so please read that discription for explanation. The only difference is that it takes COP_Vector2Var.
  25. Vector3 Vec.multiply_vec3(Vector3, float) - This method multiplys the Vector3 value with the float value without needing any extra var variable and returns a Vector3.
  26. Vector3 Vec.multiply_fixed_vec3_var(COP_FixedVector3Var, float) - This method is similar to 25. so please read that discription for explanation. The only difference is that it takes COP_FixedVector3Var.
  27. Vector3 Vec.multiply_vec3_var(COP_Vector3Var, float) - This method is similar to 25. so please read that discription for explanation. The only difference is that it takes COP_Vector3Var.
  28. Vector2 Vec.multiply_vec2(Vector2, float) - This method multiplys the Vector2 value with the float value without needing any extra var variable and returns a Vector2.
  29. Vector2 Vec.multiply_fixed_vec2_var(COP_FixedVector2Var, float) - This method is similar to 28. so please read that discription for explanation. The only difference is that it takes COP_FixedVector2Var.
  30. Vector2 Vec.multiply_vec2_var(COP_Vector2Var, float) - This method is similar to 28. so please read that discription for explanation. The only difference is that it takes COP_Vector2Var.
  31. Vector3 Vec.set_vec3(Vector3, float, float, float) - This method sets the target Vector3 axis values with the provided float values. It then returns the Vector3 without needing any extra var variables.
  32. Vector2 Vec.set_vec2(Vector2, float, float, float) - This method sets the target Vector2 axis values with the provided float values. It then returns the Vector2 without needing any extra var variables.

Even though Godot has a Countdown Timer there are certain functionality that are missing which would help a lot. So I added a Timer Countdown feature. The script does as the name suggests which is it count downs to 0. This timer also calculates the normal value for the count down which may help later to sync up some other logic or features of yours.

To use the Timer Countdown feature you must first add a Node and then add the script called timer_countdown.gd or timer_countdown_time.gd. Both are same but the only difference is that for timer_countdown.gd you need to provide the time through script where as for timer_countdown_time.gd you can provide the time as COP_FixedFloatVar in the inspector. For the example below we will be using timer_countdown_time.gd. You can either drag and drop the script from addons -> kamran_wali -> code_opt_pro -> scripts -> timers -> timer_countdown_time.gd or click the drop down button under the Script tab and select Quick Load and then just type timer_countdown_time to get it. Once you have added the Timer Countdown then you must provide a COP_FixedFloatVar resource to the field Time Seconds which must have a value greater than 0.0. The value provided here are in seconds. Now in your main script create an export var with type COP_BaseTimer.

SomeScript.gd
extends Node
@export var timer: COP_BaseTimer

Finally you must call the timer_countdown_time.update_timer(float) function to start the timer countdown. This function can be called in either _process(delta) or _physics_process(delta). Also the delta value must be provided. If you multiply the delta value with more than 1 then the countdown will happen faster than 1 second and if you multiply the delta value with less than 1 then the countdown will happen slower than 1 second. Example: For _process(delta)

SomeScript.gd
extends Node
@export var timer: COP_BaseTimer

func _ready() -> void:
  timer.reset_timer() # Resetting the timer at start

func _process(delta) -> void:
  if !timer.is_timer_done: # Checking if timer is NOT done only then will update the timer
    timer.update_timer(delta) # If only delta is passed then the countdown will be every 1 second. Any value else will be multiple of it.

For _physics_process(delta)

SomeScript.gd
extends Node
@export var timer: COP_BaseTimer

func _ready() -> void:
  timer.reset_timer() # Resetting the timer at start

func _physics_process(delta) -> void:
  if !timer.is_timer_done: # Checking if timer is NOT done only then will update the timer
    timer.update_timer(delta) # If only delta is passed then the countdown will be every 1 second. Any value else will be multiple of it.

I will briefly explain what each of the method does in base_timer.gd which is the blue-print for all timer scritps:

  1. void set_time(float) - This method sets a new time second for the timer and overrides the provided time in the export.
  2. bool is_timer_done() - This method checks if the timer is done counting down.
  3. float normalized() - This method gets the normalized value of the timer which is within the range of 0.0 to 1.0.
  4. float get_current_time_seconds() - This method gets the current countdown time of the timer.
  5. void reset_timer() - This method resets the timer for countdown.
  6. void stop_timer() - This method stops the timer countdown.
  7. float get_time_seconds() - This method gets the max time or the set time for the timer.
  8. void update_timer(float) - This method updates the timer countdown.

Instantiate Object:

I have added a new feature that allows to quickly add instantiation of packed scenes objects from the filesystem to the scene editor. No need to drag and drop or add an insantiated object 1 by 1 from the default Godot system. In future will try to make this system even faster. You can open the Instantiate Object window by going to the menu Project -> Project Settings then select the Plugins tab and finally enable the Instantiate Object. This will open the Instantiate Object by docking it at the bottom right side. You can dock it how ever you wish. Below I will explain the highlighted parts of the Instantiate Object.

Instantiate-Object1.png
Instantiate Object
  • a. Scene - This is the scene or packed scene you need to select from the FileSystem. This is also the object that will be instantiated into the Scene Editor.
  • b. Scene Status - This is the status of the selected scene object. It will tell you if you have selected a scene or packed scene object from the filesystem or not. When selecting a correct scene or packed scene from the filesystem then the status' font will turn green and show the name of the object indicating that a correct object has been selected.
  • c. Lock Scene - If enabled this will make sure that the current selected scene does not get overriden when any other filesystem objects are selected. This helps to navigate through the filesystem without worrying about getting the selected filesystem object overriden.
  • d. Parent - This is the node object where the selected scene or packed scene will be instantiated into as a child. This node object must be selected in the Scene Editor.
  • e. Parent Status - This is the status of the selected parent node object. It will tell you if you have selected a parent node from the Scene Editor or not. When selecting a correct node object from the Scene Editor then the status' font will turn green and show the name of the object indicating that a correct object has been selected.
  • f. Lock Parent - If enabled this will make sure that the current selected node does not get overriden when any other Scene Editor objects are selected. This helps to navigate through the Scene Editor without worrying about getting the selected parent object overriden.
  • g. Number of Instantiation - This will instantiate n number of objects. n being greater than 1. This feature is optional and does NOT need to be populated to work.
  • h. Instantiate Object - This button will instantiate an object. If a value is given in Number of Instantiation then it will instantiate that amount of objects. This button will ONLY appear if a correct Scene and Parent objects are selected.

Bars:

Added bar feature which acts like any normal bar. This can, for example, be used for character health. The get_normal() method in the bar is a powerful function that allows you to sync the bar with any other features. Below I will briefly explain what each method does.

  1. void set_max(int) - This method sets the maximum limit for the bar. The max limit can NOT be less than 1. If a value of less than 1 is given the bar will make the max limit to 1. If this method needs to be called then calling it once when the scene is ready is recommended. This will make sure to avoid any wrong results. Also make sure that this method is called before set_current(int) method. Otherwise setup might give wrong results.
  2. void set_current(int) - This method sets the current value of the bar. The current value can NOT be less than 0 or more than max value. If a value of less than 0 is given then the current value will be set to 0. If a value of more than max is given then the current value will be set to max value. If this method needs to be called then calling it once when the scene is ready is recommended. This will make sure to avoid any wrong results. Also make sure that this method is called after set_max(int) method. Otherwise setup might give wrong results.
  3. float get_normal() - This method gets the normal value for the bar which is in the range of 0 to 1. You can use this value to sync other features with the bar.
  4. void add(int) - This method adds value to the bar's current value. The current value of the bar will never go above maximum value.
  5. int get_value_max() - This method gets the maximum value of the bar.
  6. int get_value_current() - This method gets the current value of the bar.
  7. bool is_full() - This method checks if the bar is full that is current value equals to max value.
  8. bool is_depleted() - This method checks if the bar is empty that is current value equals to 0.
  9. void restore() - This method makes the bar full again that is current value equals to max value.
  10. void subtract(int) - This method removes value from the bar's current value. The current value of the bar will never go below 0.

I have added different type of bars with different functionalities. I will explain how to use them briefly below.

  1. normal_bar - This bar acts like a normal bar which means that adding a value will just add the value and subtracting a value will just subtract it. To use this bar make sure to set the maximum value at the scene start otherwise the default value will be used as maximum which is 1. Optionally you can set the current value as well if you want to at the scene start otherwise its value will be 0.
SomeScript.gd

extends Node
@export var bar: COP_BaseBar

func _ready() -> void:
  bar.set_max(10)
  bar.set_current(10) # Optional: If you also want to set the current value at the start but it is NOT necessary if NOT needed

## This method hurts the character with the given value.
func hurt(value: int) -> void:
  bar.subtract(value) # Removing health by subtracting bar's current value

## This method heals the character with the given value.
func heal(value: int) -> void:
  bar.add(value) # Adding health by adding to bar's current value

## This method checks if the character is dead or NOT.
func is_dead() -> bool:
  return is_depleted()

## This method returns the health's normal value so that the UI bar can be synced.
func get_health_normal() -> float:
  return bar.get_normal() # The health's normal value which is the bar's normal value
  1. normal_bar_values - This bar is same as the normal_bar in functionalities. The only difference is that you must provide the max and current value in the script itself. It has two properties which are _max: COP_FixedFloatVar and _is_set_cur_value: COP_FixedBoolVar. The _max property takes in a COP_FixedFloatVar value which is the maximum limit for the bar and the value MUST be greater than 1. The _is_set_cur_value property takes in a COP_FixedBoolVar value which is the flag that decides if to set the current value of the bar as max value or NOT. True means the current value at the start will be same as the max value. False means current value will be 0 at the start. You don't need to call the set_max(int) and set_current(int) methods at the start but you do have the option to do so if you want to. You can use the code example in 1. normal_bar and just remove func _ready() method from it.

Performant Update:

In CodeOptPro you can use another powerful feature that allows you to use custom update to update your script, that is the _process(delta) and __physics_process(delta)_. This custom update allows you to share one _process(delta) or _physics_process(delta) method with many scripts. This in turn saves lot of performance issues as one script is handling the call for processes. There are two types of custom update classes in CodeOptPro they are COP_UpdateManager and COP_UpdateManagerGlobalHelper. Each of these custom update classes have 2 more types which are process_manager_local and physics_process_manager_local for the COP_UpdateManager. And process_manager_global and physics_process_manager_global for the COP_UpdateManagerGlobalHelper. The main logic between all of them are same but the only difference is that the local ones needs to be referenced in coupled way while the global ones are referenced in a decoupled way. Below I will explain how to use the custom update manager.

  1. Adding The Update Manager - The first task you need to do is to add at least one update manager in the scene. This could be added any where but it is best to add all update managers under one Node called UpdateManagers so that it remains organized and easy to debug. So create a new Node called UpdateManager1 and add any of the update manager scripts called physics_process_manager_global, process_manager_global, physics_process_manager_local or process_manager_local. Now let me explain the properties of the update manager.
  • a. Helper (Only available in the global types) - This the cop_update_manager_global_helper resource that helps to keep the code decoupled. The update objects will use this reference to interact with the update manager. This property is only available for the global types. There is already a cop_update_manager_global_helper resource created called default_update_manager. You can use this as well for your project if you want to.
  • b. Objects - This is an array which will contain all the update objects related to this update manager. These objects will share one _physics_process or _process method from the update manager. You can manually add the update objects here but that is NOT recommended. Instead we shall use the Auto Setup plugin to add update objects to update managers automatically. Will talk about how to create an update object and using Auto Setup plugin later below.
  • c. Num Update - This value handles how many objects should be updated per frame. For example if this value is set to 5 then 5 objects will be update in one frame cycle. It there are too many update objects that needs to be updated then increasing thsi value should make the update process much better but that depends on your scripts and their logic.
  • d. Is Set Num Update - If set true then will automatically set the Num Update value to the number of objects added to the update manager. If null or false then the Num Update value will be used and auto setup for Num Update won't work. For example if there are 5 objects added and the Num Update value is and Is Set Num Update value is true then the actual num update value will be 5. Note: This value can remain null as null is taken as a false flag here.
  1. Creating And Adding Update Object - The second step is to create an update object. It is very easy to create an update object. If you haven't copied the folder script_templates to the main folder res:// then do that now as we will needed the update object template to create our scripts. Once that is done then create a new script. In the Inherits: field select Node. Then in the Template: field either select Cop Update Object Global Tepmlate, if you have added the global update manager, or select Cop Update Object Local Template, if you have added the local update manager. Name the script anything you want and then press Create button. I have commented the script with much details but I will explain what each of these properties and functions does.
    • a. update_manager (COP_UpdateManagerGlobalHelper or COP_UpdateManager) - This property keeps the reference of the update manager. Depending on what type you used it could be either COP_UpdateManagerGlobalHeler, for global update objects, or COP_UpdateManager, for local update objects. This reference is mainly used by the Auto Setup plugin to add the update object automatically to the update managers but can also be used for using any data from the update manager.
    • b. update(float) void - This is the method that will update the update object every frame. So any update logic that you were going to put in either _physics_process or _process should be put in here.
    • c. set_active(bool) void - This method enables/disables the update object. So if any flags that are going to be used for activation check must be able to be updated by this function.
    • d. is_active() bool - This method checks if the update object is active or NOT. If it is NOT active then the update manager will NOT call it's update(float) method. Again use a separate flag that will be used to check for activation.
    • e. on_enable() void - This method is called whenever the linked Update Manager is enabled by calling the method UpdateManager.set_enabled(true).
    • d. on_disable() void - This method is called whenever the linked Update Manager is disabled by calling the method UpdateManager.set_enabled(false).

The last two methods, which are NOT shown here, can be ignored and should NEVER be called by any script or overridden. These are used by the Auto Setup plugin for automation. I have commented extensively here to avoid any errors. Also you can change the extension of the script to anything else you want but as long as the object is a child of Node then it will be fine. Below is an example script of a global update object called update_object1.gd.

@tool
extends Node

## The global update manager that will update this object.
@export var update_manager: COP_UpdateManagerGlobalHelper:
	set(p_update_manager):
		if update_manager != p_update_manager:
			update_manager = p_update_manager
			update_configuration_warnings()

@export var _counter:= 0
@export var _is_active:= true

func _get_configuration_warnings():
	var warnings = []

	if !update_manager:
		warnings.append("Update Manager: Please assign a COP_UpdateManagerGlobalHelper 
			otherwise object will NOT be updated and auto setup will give error.")
	
	return warnings

## This method updates the update object.
func update(delta: float) -> void:
	_counter += 1
	print("Counter: ", _counter)

## This method activates/deactivates the update object.
func set_active(is_enable: bool) -> void:
	_is_active = is_enable

## This method checks if the update object is active or NOT.
func is_active() -> bool:
	return _is_active

## This method is ONLY called by the Update Manager when the update
## manager is enabled through script, which is UM.set_enable(true). 
## Note: This method can be removed if NEVER needed.
func on_enable() -> void:
	_counter = 0 # Resetting counter on object enable
	print("Counter has been resetted")

## This method is ONLY called by the Update Manager when the update
## manager is disabled through script, which is UM.set_enabled(false).
## Note: This method can be removed if NEVER needed.
func on_disable() -> void:
	print("Counter stopped at: ", _counter) # Showing the last value of the counter

#region The logic in this section MUST NOT BE CHANGED OR OVERRIDDEN!
## This method adds this object to the update manager._action_options
## THIS METHOD SHOULD NOT BE CALLED OR OVERRIDDEN. IT IS ONLY USED
## FOR AUTOMATION!
func _add_self_to_manager():
	if update_manager:
		update_manager._add_object(self)
	else:
		push_error("Error: ", name, " does not have update manager assigned!")

## This method always sends true as the script is an update object.
## This method is needed for duck typing check and SHOULD NOT BE
## OVERRIDDEN OR CHANGED!
func _is_update_object():
	return true
#endregion

When this script runs it will just show the value of the counter going up. Create a new Node in the example scene and attach this script to it. 3. Auto Setup - The final step is to use the Auto Setup plugin to setup the update managers and update objects. Enable the Auto Setup plugin from the Plugins tabs in the Project Settings.

Variable-Creator1.png
Auto Setup
  • a. Run Project - This will run the auto setup and then play the project which is the main scene.
  • b. Run Current Scene - This will run the auto setup and then play the edited scene which is the currently active scene.
  • c. Manual Setup - This will only run auto setup.
  • d. Log - Here the logs will be shown for the auto setup process.
  • e. Auto Save (Current Scene) - If enabled then the current scene will be automatically saved once the auto setup process is done. It is recommended to keep it enabled so that if you forget to save the scene then this will do the saving for you.

Before using the Auto Setup plugin we must first setup the update managers and the update objects. If you have used local update manager then there is nothing needed to setup the update manager but if you have used global update manager then you must set the Helper. You can either select or drag and drop the default_update_manager, which is found in this path res://addons/kamran_wali/code_opt_pro/variables/, or you can create a new cop_update_manager_global through the Variable Creator. In this example we will use the default one if global update manager is used. Now we need to setup the update object. Select the update object. If local update object is used then drag and drop the update manager into the Update Manager field in the update object. If global update object is used then either select the defaul_update_manager or the one you created. Finally make sure the Is Active flag is true.

Alright. We done setting up the update manager and update objects. Now in the Auto Setup plugin press the Run Current Scene button. If everything is alright then this should start the auto setup process and then run the scene. Once the scene runs you will see the Counter value going up in the Output. You will also notice in the Auto Setup plugin that the Log has been updated showing all the process of the auto setup.

Update Manager Runtime Functions/Methods:

I have also added some runtime methods for the update manager. This will help tremendously during runtime of the game. Below are the methods:

  • void add_object(Node): This method adds a new object to the update manager. Also please do NOT confuse this method with the private method _add_object(Node) as this private method does NOT handle some of the validation checks and is ONLY used by the automation script. If you don't want any duplicate objects added then make sure to check using the method bool COP_UpdateManager.has_object(Node) before calling the add_object(Node) method. Below is a small example of avoiding duplicate object addition when adding object to update manager.
extends Node

@export var update_manager: COP_UpdateManager

func some_method(object: Node) -> void:
	if !update_manager.has_object(object): # Checking if the object does NOT exist in the Update Manager
		update_manager.add_object(object)
  • void remove_object(Node): This method removes the given object. Note: When calling this method the update method will stop working till all remove actions are done. If number of remove objects are small then the update pause won't be noticable. It is recommended NOT to call this method every frame
  • void remove_object_index(int): This method removes an object using an int index value. Note: When calling this method the update method will stop working till all remove actions are done. If number of remove objects are small then the update pause won't be noticable. It is recommended NOT to call this method every frame
  • int get_size(): This method gets the number of objects added to the update manager.
  • Node get_object_index(int): This method gets the indexth object. If the index value given is higher than the size of the update object array then a null object will be returned.
  • void set_enabled(bool): This method enables/disables the update manager. True means to enable and false means to disable. If disabled then another script MUST be used to enable it.
  • bool is_enabled(): This method checks if the update manager is enabled or NOT.
Auto Setup Objects:

This feature allows objects to be setup automatically during the auto setup process in the editor mode. This helps with certain tasks like automatically populating an array with objects. If you haven't copied the folder script_templates to the main folder res:// then do that now as that contains the script template for auto setup object called COP_auto_setup_object_template.gd under Node. To declare an object as auto setup object is easy. You can create a new script using the script template COP_auto_setup_object_template.gd or add the methods func auto_setup() -> void: and _func is_auto_setup_object() -> bool: and @tool to an existing script. The auto setup object can extend from any other script as long as that script is a child of Node object. These two methods will make the script an auto setup object. Let me explain what each me method does.

  • void auto_setup(): This method is called during the auto setup process and this is where all the logic for auto setup should be placed.
  • bool _is_auto_setup_object(): This method is used by the auto setup process to check if the object is an auto setup object. This method MUST NOT be changed or overridden. Below I am sharing a very simple code on how to use the auto setup object. This code just populates the Arrya[Node] with children from a Node.
@tool
extends Node

@export var _object_holder: Node
@export var _some_objects: Array[Node]

var _index: int

## This method handles all the setup that needs to be done during 
## automation setup process.
func auto_setup() -> void:
	_some_objects.clear() # Making sure every auto setup process the array is cleared so no duplication occurs
	_index = 0
	while _index < _object_holder.get_child_count(): # Loop for populating the array from the given Node
		_some_objects.append(_object_holder.get_child(_index))
		_index += 1

#region The logic in this section MUST NOT BE CHANGED OR OVERRIDDEN!
## This method always sends true as the script is an auto setup object.
## This method is needed for duck typing check and SHOULD NOT BE
## OVERRIDDEN OR CHANGED!
func _is_auto_setup_object() -> bool:
    return true
#endregion

If you want to you can also make any update objects to be auto setup objects as well just by adding the methods void func auto_setup() and _bool is_auto_setup_object() to the update object script. That way the object will be both update object and auto setup object.

Debug:

I have added a debug feature that will help with printing debugs. For now the debug will work with nodes, scripts, resources and objects. To use the debug tool simply call COP_Debug from the script. Let me explain the debug methods.

  1. void print_script(Object, String) - This method will print the name of the script and the log message provided. If the object provided does NOT have a script or is NOT a script then the method will print an error. To use this method just call it by COP_Debug.print_script(some_script, "some_log").
  2. void print_node(Node, String) - This method will print the name of the node and the log message provided. To use this method just call it by COP_Debug.print_node(some_node, "some_log").
  3. void print_resource(Resource, String) - This method will print the name of the resource and the log message provided. If the object provided is NOT a resource then the method will print an error. To use this method just call it by COP_Debug.print_resource(some_resource, "some_log").
  4. void print_object(Object, String) - This method will print the name of the object and the ID. To use this method just call it by COP_Dubug.print_object(some_object, "some_log").
  5. String get_script_log(Object, String) - This method will return a String with the name of the script and the log message. Use this method for custom printing if needed.
  6. String get_node_log(Node, String) - This method will return a String with the name of the node and the log message. Use this method for custom printing if needed.
  7. String get_resource_log(Resource, String) - This method will return a String with the name of the resource and the log message. Use this method for custom printing if needed.
  8. String get_object_log(Object, String) - This method will return a String with the name and ID of the object and the log message. Use this method for custom printing if neeeded.

I am sharing some examples for using the COP_Debug tools.

Example for printing debugs.

extends Node

@export var _some_flag: COP_FixedBoolVar

func _ready() -> void:
	COP_Debug.print_script(self, "This is an attached script.")
	COP_Debug.print_node(self, "This is the node.")
	COP_Debug.print_resource(_some_flag, "This is a resource object.")
OUTPUT:
=========
***script_path.some_script.gd****
This is an attached script.
=========
some_node_name -> This is the node.
some_resource_name -> This is a resource object.

Example for printing custom debugs.

extends Node

func _ready() -> void:
	COP_Debug.print_script(self, COP_Debug.get_node_log(self, "This is the node."))
OUTPUT:
=========
***script_path.some_script.gd***
some_node_name -> This is the node
=========

Pooling System:

Added Pooling System feature to CodeOptPro. For now any Node or child of Node can be used as a pooling object. This feature will help a lot in performance for objects that will share some other common objects. For example if two gun objects have similar bullet type then they can both use a pool of those bullets. That way it will give an illusion that there are a lot of bullets but in reality they are sharing the same bullets. I have also given the option to create your own custom pooling system. To use the pooling system you will first need to create a new PoolManagerHelper from the Managers tab in the Variable Creator or you can use the already created pool manager resource called default_pool. For any examples below we will be using the default pool manager, default_pool. First lets look into the pool manager helper.

  1. void add_request(Node) - This is the method for requesting a pool object and MUST be called by the pool object receiver scripts. From the pool manager this is the ONLY method that needs to be called to get a pool object.

To make an object into a pool object receiver you MUST add certain methods to it or just simply use the script template called COP_pool_receiver_object_template.gd which is under Node. Let me explain the properties and methods for the pool object receiver.

Properties:
  1. pool_manager: COP_PoolHelper - This is the pool manager that will be used to request for pool objects. Below is an example on how to requset for a pool object.
func some_func() -> void:
	pool_manager.add_request(self) # Requesting a pool object
Methods:
  1. void _receive_pool_object(Node) - This method receives the pool object from the pool manager. It is advised to change the parameter type to the ones you are expecting to receive. That way it may help with performance a little bit more. Below is an example of when a pool object has been received.
extends Node
var _received_object: Node3D

func _receive_pool_object(object: Node3D) -> void:
	_received_object = object # Storing the received object
	print("Object Received: ", _received_object.name) # Printing the name of the received object
  1. bool _is_pool_receiver() - This method just checks if the script is a pool receiver through duck typing. This method will not effect your code but may later be needed for automation check.

Lets use the pool_local for this example. Firstly create a new Node and name it Update_Manager_Local and add the script process_manager_local.gd. Secondly create a new Node in a scene and name it Pool. Then attach the script called pool_local.gd. Now let me explain all the properties for the pool_local.gd script.

  1. Update Manager - The reference to the update manager. For local pool manager it should be local update manager and for global pool manager it should be global update manager which is the update manager helper resource.
  2. Helper - This is the manager helper resource for the pool manager. This manager will be set by the pool manager ONLY and will be called by the pool receiver objects. By default there is already a pool manager created called default_pool. You can use that for your game or use a new one by creating it from the Variable Creator under the Manager tab called PoolManagerHelper.
  3. Is Enable At Start - This flag will decide if the pool manager will be enabled when the game starts. If true then the pooling manager will work from the start. If false then the pooling manager will NOT work from the start. You can also enable/disable the pooling manager through script by calling it's method called set_active(bool). You will get access to this method either through the COP_PoolHelper reference or by COP_Pool reference. It is recommended to use the COP_PoolHelper reference.
@export var pool_manager: COP_PoolHelper

func some_func() -> void:
	pool_manager.get_manager().set_active(true) # Enabling the pooling system
  1. P Objects - This array contains all the pool objects, _NOTE: The prefix p means the variable is protected. You do NOT need to populate the array as it will be done automatically when running the auto setup process. The pool manager will add all the children from Pool Object Holder during the auto setup process.
  2. Pool Object Holder - This is the Node that will contain all pool objects from which the pool objects will be added to the pool manager automatically during the auto setup process. If NO holder is provided to this field then by default the script's Node will be considered as the object holder.

Alright now lets fill up the fields for the pool_local.gd. For the Helper field select the default_pool, NOTE: default_pool resource can be found in res://addons/kamran_wali/code_opt_pro/variables/default_pool.tres. For the Is Enable At Start select the true variable resource, NOTE: true resource can be found in res://addons/kamran_wali/code_opt_pro/variables/true.tres. Keep the other two fields as is because they are going to be auto populated by the auto setup process.

Now add 10 Node3D objects as children for the Pool Node. Name all of them as Obj1, Obj2, Obj3... Obj10 etc. Now run the auto setup process manually, for how to run auto setup process manually please check out the link Auto Setup Objects. You will notice that the P Objects array will be populated with Node3Ds children from the Pool Object Holder. Also you will notice that the Pool Object Holder will be set as the Pool Node because by default if a Pool Object Holder is NOT provided then the Node that the script is attached to will be stored as the Pool Object Holder.

Ok, now we need to create the pool object receiver script. It is very simple. The best way to create a new pool object receiver script is to create a new script and then using COP_pool_receiver_object_template template, under the Node, to create a new pool object receiver. If you haven't already then you should copy the script_templates folder to the root folder to get the script templates from the CodeOptPro. Another way to make a script into a pool receiver object is to add an export variable called var pool_manager: COP_PoolHelper and two more methods to the script called _void receive_pool_object(object) and _bool is_pool_receiver() and then finally making the script a @tool script. Below is an example of a pool receiver object script.

@tool
extends Node

## The pool manager for requesting pool objects.
@export var pool_manager: COP_PoolHelper

var _pool_object: Node3D

func _process(delta) -> void:
	if Input.is_action_just_pressed("ui_left"):
		pool_manager.get_manager().

## This method receives a pool object
func _receive_pool_object(object) -> void:
	_pool_object = object
	print("Pool Object: ", _pool_object.name)

## This method always sends true as the script is a pool receiver.
## This method is needed for duck typing check and SHOULD NOT BE
## OVERRIDDEN OR CHANGED!
func _is_pool_receiver() -> bool:
	return true

Now go back to the editor and create a new Node and name it Pool Receiver Object and attach the pool receiver object script to it. Select the Pool Receiver Object and give it the default_pool resource in the Pool Manager field. Finally just run the game.

After running the game keep pressing the left arrow key button. You will notice that the Output window is printing all the pool objects' name 1 by 1 and then it cycles back to the first pool object. This is how the pooling system works in CodeOptPro.

You can also create your own custom pooling managers. All you have to do is to create a new script using the either COP_pool_manager_local_template.gd or COP_pool_manager_global_template.gd. I have given notes in the template as well to further help to understand how to create a new custom pooling manager. Let me explain the methods in both the templates.

  1. void update(float) - This is the frame update method of the pooling system. It is recommended to call the parent's update method here as well so that the request processes can happen. Also any type of frame dependent logic should be put here.
  2. void _p_setup_object_pool() - This method is responsible for populating the pool object array during the auto setup process. It is recommended to keep it as is but if you want to make some changes here then you can.
  3. bool _p_is_pool_object(Node) - During auto setup process this method checks if the added object is a pool object. The default way is the fastest way to check if the object is a pool object. It does so by sending the value true all the time. But if you want to check for a pool object, lets say if the object has a certain method by calling the method has_method(String), then you should do it here.
  4. bool _p_is_pool_object_available(Node) - This method checks if a pool object is available to send to a request object for example if a flag of a certain object is false only then send it.

Updates

Here I will share all the updates done to the current versions. Below are the updates.

  1. Added runtime functions for the update manager. Now the user can add and remove objects during runtime.
  2. Added a feature in update manager that makes the Num Update value to the number of objects added to the update manager.
  3. Fixed a bug where _time_delta value wasn't calculated properly.
  4. Added auto setup object feature. This feature allows setup to happen during the auto setup process in the editor mode.
  5. Added print debug feature. This feature will help the user to debug a script much better.
  6. Added pooling system feature. This feature will help with performance by reusing certain objects.
  7. Added a feature in update manager which enables and disables it.
  8. Added on enable and on disable method calls on update objects by the update managers.

Bug Fixes:

  1. Fixed a bug in auto setup process where the number of auto setup object calls are increasing exponentially after each process call. This was due to the array of the auto setup objects NOT being cleared after each process call. This bug has been fixed.
  2. Fixed a bug in auto setup process where an object could ONLY be any one type that is only an update manager or update object or auto setup object. This was due to if else checks that was only adding the object to one type of array and NOT all the types if the object were of multiple types. This bug has been fixed and now an object can have multiply types.

Versioning

The project uses Semantic Versioning. Available versions can be seen in tags on this repository.


Authors


License

This project is licensed under the MIT License - see the LICENSE.md file for details.