DISCLAIMER: This is a 'work-in-progress' and many things are still missing.
I just prefer to have something than nothing!
Example of a script in autoload - It will be instantiated when the game starts and will be on root.
using Godot;Example of a script accessing to the script in root, this case to use or change the data on GameData.
using Godot;
public partial class PlayerOne: CharacterBody2D
{
GameData gameData; //variable of the script type
public override void _Ready()
{
gameData = (GameData)GetNode("/root/GameData"); //set the reference to the script in autoload
gameData.p1Scrore = 0; //Variable in the script at root
gameData.p1Life = 100; //Variable in the script at root
}
public override void _Process(double delta)
{
}
}
Direct access to the main node of a scene can significantly streamline various operations in your game project, such as removing sections, changing levels, or instantiating bullets. To facilitate this, it's essential to have a global and clear way to reference the main node.
Here is how it works:
Create a script called MainNode.cs.
Add the provided code and attach the script to the scene's main node.
This script creates a singleton variable that references the script itself. When you call MainNode.currentScene from anywhere in your code, you are directly referencing the main node of the scene, with no expensive 'Get' procedures to find it.
Now, from any other script in your game...
Do you need to remove this level scene? Use MainNode.currentScene.QueueFree();
Do you need to instantiate an object in the scene? Use MainNode.currentScene.AddChild(objectInstance);
Do you need to find something from the scene's main node? Use MainNode.currentScene.GetNode<>()... etc. - See the INSTANTIATION section to the full example.
With this setup, you gain a convenient reference point just below the root node, serving as the parent for the entire scene. This simplifies scene management and object manipulation, enhancing your game development workflow.
Instantiating objects, such as projectiles or characters, is a common requirement in game development.
This process allows you to dynamically create instances of prefabricated objects (PackedScenes) at runtime.
Here's a practical example focusing on instantiating a laser projectile when a player fires a cannon.
First, you need a reference to the game element scene you wish to instantiate. This is done using the [Export] attribute in Godot, which allows you to assign the game element scene in the Godot editor.
The main functionality occurs within the _Process method, which is called every frame.
Here's how it works:
Check if the fire button (defined as "fireCannon") is pressed by the player.
When the button is pressed, create an instance of the laser element scene.
Set the instantiated laser's global position to match the current object's global position, ensuring the laser appears at the correct location in the scene.
Finally, add the laser instance to the current scene, making it visible and interactive in the game world.
Importante note:
that in this example I'm instantiating the object as child of the "main node", but to do this you need to use the script in the "Get The Main Node of the scene" section as described there.
This example demonstrates a straightforward way to respond to player input by instantiating objects. By assigning the instantiated object's position and adding it to the scene, you ensure that the game dynamically reacts to player actions, adding depth and interactivity to the gameplay.
Remember to define the laser PackedScene in the Godot editor by dragging your desired scene into the exported variable slot of your script component.
Check the oficial Godot's Documentation for more details.
Signals are a powerful way to decouple game logic, allowing different parts of your game to communicate efficiently.
The Connect method is used to connect a signal to a method, ensuring that a specific action is taken when the signal is emitted.
In the given example demonstrate this concept using a button.
Here's how it works:
The Connect method is used within _Ready to ensure the connection is made as soon as the script enters the scene tree.
The Connect method takes two main arguments: the signal's name and a reference to a callable method.
"button_down" is the name of the signal that is being connected. (This signal is emitted when the button is pressed.)
new Callable(this, "RunOnButtonDown") specifies the target method to be called when the signal is emitted.
this, in the first argument of the callable method, refers to the location of the method, which in the example, is the same script (this script).
The RunOnButtonDown method is defined to perform a specific action.
When the signal button_down is emitted (i.e., when the button is pressed), the RunOnButtonDown method is called.
Inside the RunOnButtonDown method, GD.Print("The button was pressed") is executed, printing a message to the output window.
Important note:
The Connect method is not referencing the source node of the signal because it is supposed to be put in the button itself, once it inherits from the Button class.
It is common to have a script connecting a signal from a child node. In that case, it is necessary to reference the source of the signal.
Let's say the script is in the parent. To connect the same way, the code changes to:
Check the oficial Godot's Documentation for more details.
The switch statement is a control structure that allows for more efficient multi-way branching. Instead of using a series of if-else statements, you can use a switch case to simplify the code when dealing with multiple conditions that depend on a single variable. In the following example, the variable named racePosition is checked against several cases.
Here's how it works:
The switch statement checks the value of racePosition, which is 3 in this instance.
It then searches for a case that matches this value, finding case 3: in this scenario.
Upon finding a match, the code associated with that case is executed, resulting in "Third place" being written in the Output window.
The break statement immediately follows the code execution, ensuring that the program exits the switch block right after executing the matched case, thereby avoiding the execution of any subsequent case checks.
If no matching case is found, the default clause takes over. If racePosition doesn't match any of the explicit cases (1, 2, or 3), "Lose" will be written.
In programming, choosing the right type for your variables is crucial for efficient memory usage and clear code.
Here's an overview of variable types and declarations using a car-themed example in C#:
Boolean (bool): Represents a true or false value. For instance, bool engineOn = false; indicates whether the car's engine is on or off.
Integer (int): Used for whole numbers. In int carNumber = 96;, it's used to represent the car's number.
Floating Point (float): Suitable for numbers with a fractional part. float carSpeed = 5.3f; denotes the car's speed, allowing for decimal precision.
Double: Similar to float but with double the precision, perfect for more precise calculations. For example, double totalDistance = 382.756485; could represent the total distance traveled by the car.
Note: A float number needs to have the character 'f' at the end of the number when represented with decimal places, or else, it will be seen as a double. This distinction is important for ensuring the correct data type is used and avoiding potential issues with precision or arithmetic operations.
String: A sequence of characters. string carBrand = "Ferrari"; stores the brand of the car as text.
Enumeration (enum): Defines a set of named constants. enum Lights { Off, On, Blinking }; creates a category for light states, and Lights lightState = Lights.Off; sets the initial state.
Array: A collection of items of the same type. float[] gearSpeed = { 40, 80, 120.5f, 160, 210, 250 }; holds the speed associated with each gear.
Note: The [] symbols after the type define the variable as an array. An array could be multi dimensional by adding more '[]'.
To access the values inside you provide the index inside the '[]': gearSpeed[2] = 120.5f;
Don't forget the index starts at 0!
List: A collection of elements, more dynamic than an array. List<Area2D> trackCheckPoint = new List<Area2D>(); is used to store checkpoints on the track, allowing for dynamic addition or removal of elements.
Note: Lists belong to the namespace:
using System.Collections.Generic;This needs to be declared in the beginning on the script. Also, lists need to be initialized before use by making the variable equal to a new List of type and be only available
Each variable type serves a specific purpose, and choosing the right one helps make your code more intuitive and efficient.
In C#, variables are private by default, meaning they can only be accessed within the class they are declared in.
However, you can make them public to change their accessibility or use the [Export] attribute to expose them in the Godot Inspector.
Set a variable public - read and write on it from outside the script.
By default, carSpeed is private. This means you can only access carSpeed within the class where it's declared.
To make carSpeed accessible from other classes (for example, if you want to read the car speed from another script), you declare it as public:
public float carSpeed = 5.3f;
Now, any other class can access and modify carSpeed.
Show a variable in the Godot's inspector panel
If you want to make lightState adjustable from the Godot Inspector (useful for designers or for tweaking values without diving into the code), you use the [Export] attribute:
enum Lights { Off, On, Blinking };[Export] public Lights lightState = Lights.Off;This not only makes lightState public but also exposes it in the Godot Inspector. Now, you can easily adjust the light state directly from the Godot Editor.
Use public variables when you need to access or modify a variable from other classes. This is useful for interaction between different components of your program.
Use the [Export] attribute when you want to expose a variable to the Godot Inspector. This is particularly useful for non-programmers on your team (like designers or level editors) who can tweak values without touching the code. It's also handy for quickly adjusting values and seeing the results in real-time during development.