Asking for Advice

Hello.

Currently i am trying to learn the Godot Engine. Its not going very well. I am hoping to get some advice from more experienced developers on this topic but this is my current situation.

I am trying to create a 2d game in the top down style. I’ve drawn all of my own assets (Pixel Art) and that has gone well, but I’m struggling to put anything to code. For instance, I wanted to start with player movement. I made a script to do so, then tried to add animations, but I made the mistake of playing the animation via code upon WASD input and not using the AnimationPlayer or Tree nodes.

So next I tried to make a way to transition between scenes. The idea was to have a town and some buildings in it that you can enter and exit. The problem is, I’m reloading the Town scene when I leave a building. So I teleport to the entrance of the town rather than outside the door of the building I came out of.

These are just a couple examples but I’m noticing a theme. When i get stuck and try to watch a tutorial, if I use some of the tutorials code it breaks existing things in the game. And when I’m trying to read the documentation, I get lost in the technical nature of it. And I’ve noticed on this forum that a lot of help threads end with a link to the documentation.

I guess my question is how did you all learn Godot? How are there so many amazing devs on Youtube with an encyclopedic knowledge of the engine? Where are you all learning this stuff? Do you just sit down and make a Quizlet and study every node until you know what it does? Or is it better to make minigames that cover different aspects until you have enough knowledge to branch out? When I try to make smaller games, I struggle to stay focused because I feel like that’s time I could spend making my “dream game”.

How did you learn to use this engine, and how can I learn it too?

1 Like

godot is the first place you are likely learning to program. it’s the one of the best places, as you get to see instant results.
this is class node

node is the base class for

node3d Inherits from node meaning anything node can do can be done by node3d.
documentation for you just changed from a list of things memorize to a pyramid, if you learn the top stone can do X you know the bottom stone can do X too.
copy code sure to get results but what’s the concepts in the code.
programming language does not matter they are all the same mostly.
ok so you have a problem when player exit the building your player is at start.
so you look at docs and under Properties you seen nothing called “position” the knowledge you need.

that is because programmers are sneaky and we like to gate keep no one takes our jobs. so we show you docs with assuming you know how to use them,
but now you see
“Inherits: PhysicsBody2D < CollisionObject2D < Node2D < CanvasItem < Node < Object”
you know to work your way up the pyramid no “position” in PhysicsBody2D, CollisionObject2D
till you get to Node2D
where under Properties you see “position” with a detailed explanation that assumes no knowledge of what position means, with examples of how to use it, and frequently encountered problems. all that’s given is that “position” is some sort of Vector2

so if you want to keep that “position” that your characterbody2d has you will need to store it in something that can contain a “Vector2” you will want it change as you go to diffrent houses,
so my current location variable of type Vector2 assign value of player’s position that I know the player has because player is of type characterbody2d that got position from node2d. then the work in the building is done get that location and assign it back to the player’s position
var mycurrentloc : Vector2 = player.position
go inside the house
go outside the house
player.position = mycurrentloc

why I went threw all of this was not to teach you just about this one basic concept but to point out to look for programming concepts, to know how to use the docs and know how to RTFM because the manual assumes too much.
because writing the manual is a a bell around the cats neck a job no one want’s to do.

questions are not just solutions for you they are for everyone even years latter, so it’s always important to actually try to solve the issue not just throw the solution to get resume points, I see like 10 year old questions on stack exchange that if they had just taken like 10 more minutes to explained the inner-workings or concept of a thing, it would fix that no longer works and question is a duplicate of old, death loop. rant over

Don’t be so hard on yourself. It sounds to me like you are on a good path.
Just keep trying things, breaking things, fixing things. It isn’t a race.
And asking questions is a superb strategy.

Yeah, making games is really hard. And a lot of the time it’s also frustrating.
The best thing is to follow tutorials exactly, and make exactly what’s in the tutorial, rather than trying to smoosh loads of different tutorials into your game. That way all the things they’re trying to teach you in the tutotial will work, and you’ll have that toolset in your mind for when you want to solve a problem later. Go through loads of them. When you finish a tutorial, try messing about with it a little bit before moving on. It is going to take a long time to learn how to code but you’ll get there.

And for a good reason. The documentation for Godot is exceptionally good (not just in the ‘for an open-source project’ sense-- it’s better-written and more comprehensive than anything I’ve used in my professional career.) Learning how to use the documentation is worth the time as that will help you track down problems in your code faster and can help you prevent reinventing the wheel.

I disagree. You tried to implement a feature, got at least some of it working as intended, then identified and articulated the reason why your implementation doesn’t do everything you want it to. ‘Real’ programmers do that too.

Developing isn’t like learning a recipe how to bake bread by heart. It’s more like learning how to solve problems rather than solve one particular problem, if that makes sense.

Please don’t think the YouTube tutorial people are the voice of truth when it comes to implementing features. If you have to watch a video, do so keeping in mind that what these people show is how they solved a problem using their intuition and experience. Maybe their solutions are good, bad, or somewhere in between, but theirs is not the only way to go about doing something.

How do you learn how to use this engine? Well, you’re doing it right now.

(Would you like to know how I solved the ‘place the player in the correct part of town’ problem?)

4 Likes

I would very much like that! I tried to fix the issue myself but had no luck. Here is some of my code.

I have a script named “Global” just has this code.

extends Node

var player_pos : Vector2

I have my player movement script:

extends CharacterBody2D


var speed = 50  
@onready var _body_animation = $"Body DFN"

func _unhandled_input(event: InputEvent) -> void:
Global.player_pos = global_position

func get_input():
var input_direction = Input.get_vector("left", "right", "up", "down")
velocity = input_direction * speed

# Code to control sprint speed
if Input.is_action_pressed("Sprint"):
	velocity *= 2.5
	
func _process(delta):

$Label.text = str(global_position)

get_input()

move_and_slide()

I have the code on the Area 2d that acts as my exterior door:

extends Area2D

func _on_door_body_entered(body: CharacterBody2D) -> void:
print("entered")
get_tree().change_scene_to_file("res://Scenes/tent_interior.tscn")

I have the code on the area 2D that acts as my door out of the structure ( A tent in this case)

extends Node2D

func _on_door_body_entered(body: CharacterBody2D) -> void:
print("entered")
get_tree().change_scene_to_file("res://Scenes/nexus.tscn")

And Finally I have the code on my Main Town scene

extends Node2D

@onready var tilemap : TileMapLayer = $Grass

func _ready():
$Player.global_position = Global.player_pos

func _process(delta):
if Input.is_action_just_pressed("interact"):
	var mouse_pos : Vector2 = get_global_mouse_position()
	var tile_map_pos : Vector2i = tilemap.local_to_map(mouse_pos)
	
	tilemap.set_cell(tile_map_pos, 1, Vector2i(2,1))

If I had to guess I think I’ve created the variable for the position of the Player but I’m just not using it? I say this because I made a label to print my position on screen and it works perfectly. And thank you so much for offering to help, i really appreciate it.

I learned by porting a game for Nokia dumbphones I made with some friends many years ago. I decided to do that so I didn’t have to worry about coming with a game idea, assets development
and game design stuff. I also read the docs and open source Godot projects. Sometimes I compare my code and project/node structure with others. For context, I work in software development (but not on game development :frowning_face:), I like coding on my free time and have some experience with Unity3D.

I have to say, I’m by no means an expert, but I’m happy to share whatever I have learned.

I wouldn’t say you made a mistake. You just implemented a different solution for the problem. Could be better? Sure, but if it works for your game and doesn’t affect its performance, then it is good enough. Also, game development is an iterative process, so sometimes you have to come with a quick and dirty solution first so you can try things related to gameplay as soon as possible.

1 Like

You’re not far away from a working solution. In fact, you already have the most important bits. Here’s how I implemented my solution.

I have a SceneManager node that I keep as an autoload:

extends Node

func load_next_level(next_scene : String):
	get_tree().change_scene_to_file(next_scene)

func load_same_level():
	get_tree().reload_current_scene()

My ‘doors’ are only a little different from yours in that I made them into a scene of their own. Node-wise, they’re Area2Ds with a CollisionShape2D attached:

extends Area2D

@export_file("*.tscn") var next_scene : String
@export var new_philomena_position : String = ""
@export var new_philomena_orientation : compass_enum.compass_direction = compass_enum.compass_direction.N  

func _ready():
	self.body_entered.connect(_on_body_entered.bind())

func _on_body_entered(body):
	if body.is_in_group("Philomena"):
		MessageBus.warp_philomena_to_location.emit(new_philomena_position)
		MessageBus.set_philomena_to_orientation.emit(new_philomena_orientation)
		SceneManager.load_next_level.call_deferred(next_scene)

@export_file("*.tscn") var next_scene : String lets me select a scene in the inspector and pass around its path name as a string.

The ‘magic’ of the code lies in the fields new_philomena_position and new_philomena_orientation. With the latter I can specify in which of the eight compass directions (west, north-west, north, et cetera) the player sprite should be oriented upon loading the new scene. The former takes a string and lets me specify where the player should appear in the scene.

MessageBus is another autoload that only contains signal definitions so I can send messages to and from anywhere I please. In this particular case, the messages are received by yet another autoload script (PhilomenaPositionManager) that stores the received new orientation and position.

In all the scenes I want my player to visit, I have a simple Node called WarpPoints. To this node I parent a number of Marker2Ds, which I drag to the places on the map I want the player to spawn at, then I change the Marker2D’s name to something descriptive.

I also add another node in each scene with warp points. It is again a simple Node to which is added this script:

extends Node

@export var warp_points : Node
@export var philomena : Philomena

var new_philomena_position : Vector2 = Vector2.ZERO

func _ready() -> void:
	var philomena_location : String = PhilomenaPositionManager.get_location()
	var philomena_orientation : compass_enum.compass_direction = PhilomenaPositionManager.get_orientation()
	
	if philomena_location != "":
		if warp_points:
			for marker in warp_points.get_children():
				if marker.name == philomena_location:
					new_philomena_position = marker.position
		
		philomena.position = new_philomena_position
		philomena.set_philomena_orientation(philomena_orientation)

PhilomenaPositionManager is a script I mentioned before. When _ready() executes, it gets the name of a position to warp to. warp_points is a reference to the node with all those Marker2Ds from before. This script loops through all of them and finds the one with the name I’m looking for. Once found, it assigns the position of that Marker2D to the player.

It’s not an implementation without its problems. You really don’t want to set a marker near or inside a loading zone, and specifying scene names with strings creates problems if you misspell a name or rename a scene. But it works for me because my ‘game’ is currently three debug rooms the player can enter and leave. I can refactor if I ever need anything more robust.

1 Like

Thank you for this! I really appreciate the detailed response. Ill be honest, I kinda understand about half of that, but the parts I can understand are impressive!

I want to leave it out there for any person learning game development, not just you: abandon the idea of getting it right first time. There’s no one correct way to code a game, and there’s no way you will come up with the perfect solution at your first go.

Embrace throwing away code.

Games are interactive art pieces and they need to evolve and follow the flux of creativity.

This is true both for first time production and for experienced one, the difference is not that you don’t throw away code, the difference is that you already know in which areas you will throw away the most code.

3 Likes

Making games, regardless of the engine, requires you to not just do what other tells you to do, but also understand why you got to do those and what each line of codes and each methods/techniques is meant for.

This universal fact might be the biggest challenge for anyone who try to make something in Godot, coding-wise, because unless you take proper course in programing, what you’ll learn from 1 tutorial will always clash against another tutorial, hence the trick is to find good tutorials that deeply explain what everything does at various levels. To give example, the various “e-school” around that teaches how to program in Godot (for a price) usually use the “Beginner”, “Intermediary” and “Expert” tag to define how deep in explanation the teacher goes. (Beginner basically covers the basics of C# or GD-Script most basics coding. intermediary brush over basic coding fast, but explain advanced tricks & codes formulas. It feels like you’re back in high school advance math with algebra. Expert is where the coding starts to get deep into the actual functions of the engine such as optimization and inter-systems manipulation such as advanced codes & shaders manipulations and such.)

Following & watching channels on something like Youtube can be a good way to learn, but as you’ll be learning from multiple people from various background and each have their own pros & cons, habits and all, it’s also a really hard thing to do as one might explain X is better while another might explain that Y feel better and you’re the only one who can decide by the end and something might break if you use X with Y or vise versa.

I know how it’s hard to force yourself to “stop there and try something different than what’s shown in the tutorial so that you fully understand what was explained”, but that’s how you’ll be able to advance.

If you just “copy what’s explained”, you’ll never be able to do much.

One tip I can give you regarding things like scene handling such as scene transitions is to know that you don’t have to close/load scene from the Tree() level. You can simply load and place scene as children of the Tree() level and simply “disable” scene unused that you know will return shortly later.
It goes a bit like this:
Tree() (top level) is an empty scene with a single Node with a script that has something like “SceneHandling.cs”. In this script, there’s the required code to load the first scene when the game opens, but also to load scene as children of this particular node. Whenever you change scene, you actually only enable a “Loading screen” node under this “Scene Handling” node which hide whatever happens, either “disable” or remove the child scene that is active (if you want to keep it to return to it later or if you want it gone from cache), load the next scene as another children of this node. If necessary (which is mostly is), you can then call some code from the top node of that new scene’s hiearchy to initiate/start/begin/load whatever you want in that particular scene.

For example, set the player in the right place with the right assets and setting the scene to know that the SceneHandling.cs parent has to be called with something like “LoadingIsDone()” function once everything in this new scene is finished).

So, in simple terms: Scene Handling node with a script enable some sort of over-everything loading UI/scene that hides everything, loads a new scene in. If it has to, it either remove or disable any previous existing scene. It call the new scene “handler” to load/begin whatever is required in the new scene. That new scene handler code, once it’s done with loading whatever, call back that Scene Handling Node with a function that tells it “I’m done!” and this allow this handle to remove the loading UI/scene from showing.

And there you go, you got full control of what scene/content gets loaded and unloaded. Your game technically never change scene, but instead change what’s in the scene dynamically. There’s no more any need to try storing tons of primary data outside of the scene since you can now cache anything in the top-level scene handler too. (For example, you can move the player character from Scene A to Scene B by simply changing its parenting while BOTH scene are still present during the loading process.)