Godot Version
v4.2.1.stable.official [b09f793f5]
Question
Hello, I was recently inspired to try making a game for the first time by Pirate Software’s content / recommendation of Godot and I’m having a lot of fun, but I think I could use some help. I have some general questions I want to ask, but I also have a specific question that I think can help me move forward. I’ll ask the specific question first. How can I structure combat code to process unit turns one at a time (e.g. player character 1, enemy 1, pc2, pc3, e2, e3, e4, pc4, etc.)? I have attempted a few things and this is my most recent attempt:
combat_engine.gd
func _ready():
# "party" is an array of Definitions retrieved from a PartyVariables Singleton which contains information about party members (stats, personal details).
party = PartyVariables["characters_in_party"]
# I am starting with this simple "confirmed" event to try to trigger changing which character is being controlled.
signal confirmed
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_accept"):
confirmed.emit()
# This function is called when combat is triggered (battle happens on the same map as exploration and I want to trigger with a signal). Right now I am triggering combat with a button.
func combat():
#start processing combat
var round : int = 0
while(true):
# "party" is sorted by the speed stat stored in the Singleton.
party.sort_custom(sort_characters_by_speed)
# For each character in the active party...
for character in party:
# ...check if there is an active character...
if(active_character):
# ...disable them if there is...
active_character["is_active_character"] = false
# ...set the new character as the active character.
# I am switching which character is being controlled by user input with this bool.
character["is_active_character"] = true
active_character = character
print(character)
# Wait until the player hits 'ui_accept' to move to the next character in the turn order.
# Doesn't seem to work; only waits once per loop?
await confirmed
print("Character Turn Ended")
# Increment round count each time every character has acted
round += 1
#if combat over return (eventually will check victory condition is met)
return
Some further context: I want to make a strategy RPG with 2 combat layers, (1) a map (same as exploration map) where units move around and select general actions (attack, wait, magic, use item) and (2) a combat interface that appears when attack is selected. Right now, I am working on (1).
I think I must be using a bad design pattern (I suspect I am using await incorrectly at least). This is what I’m printing to the console:
{ “name”: “Aristotle”, “is_in_party”: true, “is_active_character”: true, “stats”: { “health”: 10, “strength”: 5, “speed”: 2, “move”: 5 } }
Character Turn Ended
{ “name”: “Thomas Aquinas”, “is_in_party”: true, “is_active_character”: true, “stats”: { “health”: 10, “strength”: 5, “speed”: 1, “move”: 4 } }
{ “name”: “Aristotle”, “is_in_party”: true, “is_active_character”: true, “stats”: { “health”: 10, “strength”: 5, “speed”: 2, “move”: 5 } }
— Debugging process stopped —
When I start combat the character Aristotle is given control (because he is fastest). When I hit ‘enter’, I print ‘Character Turn Ended’ as expected, but then it bypasses the second active party member (of 2 total) and goes back to the top of the turn order.
I think if I figure out a good general approach to processing turns in sequence, I can start making some progress on combat, but for anyone who is still reading, I could also use general feedback on best design practices. ^^
My project is structured like this so far:
Assets
–Art (contains all my sprites, tilesets)
Scenes
–Characters
----Enemies (scenes representing enemies)
----Players (scenes representing the party members)
–Levels (scenes representing maps)
–UIs
Scripts
–Characters (scripts attached to characters)
–UIs (scripts attached to UIs)
General questions:
(A) I have a generic “character_map_sprite.tscn” that contains a Sprite2D, CollisionShape2D, and RayCast2D that I am dropping into my specific player scenes and enemy scenes. I then drop the character scene into the level scene. Is this a good pattern?
(B) Additionally, this is how my first level is laid out and I suspect it is not well designed. Any suggestions for better organizing the scene?
- The buttons are just for testing (e.g. setting active character, starting combat).
- “combat” has a script called “combat_engine.gd” which I was planning to put all of the combat logic into.
- The player scenes (aristotle, thomas) were children of combat because I was directly referencing them in the script before, but now that I moved all their attributes to the Singleton I can move them back to being children of level_1.
- “character_map_sprite” has a script called “character_map_sprite.gd” that contains movement & collision logic (_undhandled_input function where if the character the script is attached to is the active character (stored in Singleton), then it will check for collision / move that sprite).
(C) My PartyVariables Singleton contains a bunch of Dictionaries which are grouped into arrays by _ready(). Then I use the arrays in other scripts (e.g. the party variable in the combat function). Is this good? Should I define a custom class for a “Character” rather than using a generic dictionary / can I create a complex dictionary with nested dictionaries in a class?
(D) I was planning to have another Singleton for storing enemy stats, but is this necessary since they won’t be persistent? Should I just define the enemy stats in the enemy scene (“goblin.tscn”, “zombie.tscn”, etc.)? I tried to create a generic class (“character.tscn”), which would have all the relevant variables for any character (enemy or player), but I was having trouble figuring out how to do this and couldn’t find a tutorial that went over it. Is there a good tutorial for this concept? (i.e. multiple monster scenes that extend a generic character class that contains all the stats needed for a monster, how to get and set those stats, etc.)
(E) It seems tedious & risky (typos) to access party Dictionaries like character[“stats”][“speed”] every time I want to reference player information. Is there a better way to do this? Ideally I would like to type “character.” and have the editor auto-fill the potential properties of the character (“stats”, “is_active_character”, etc.).
I know that is a lot of dense questions! If you have any resources you think would help me (video or text tutorials, specific pages of the Godot documentation), I would be very happy to learn from them.