[Question] Statemachine logic organization
Asked Answered
C

5

0

Hi, I'm making a Beholder Enemy that uses a Statemachine.
Basicly The Beholder will have a secondary Sprite (an eye) that tracks the Players movement. The Beholder will have states like Chase and Attack. The eye will track the player in both of these states.

I'm relatively new to programming but i have heard you want to avoid copy-pasting code when possible. So because i want the tracking functionality in multiple states should i make the function that tracks the players movement in the Beholders main script?
Also when in the Attack state i want the eye that's tracking the player to shoot a laser, so i guess the functionality would differ a bit in each state? Not sure how to set everything up in a good way.

If i go the route of placing the tracking logic in the main script, do i want to call the tracking function that's in the main script separately in each state?

Or would i make an if-statement in the main script saying like
if statemachine.state == attack or statemachine.state == chase Then track player.
Maybe something like this would work?

Help is very much appreciated Thanks!

Crossstitch answered 9/3 at 12:43 Comment(0)
O
0

This is hard to answer, because there are different ways to implement statemachines. It also depends on how many different states you want to have. If you have only few states you can put all states in one script.

I guess this is also personal preference. I usually like to have seperate scripts for each state as soon as I have 3 or more states.
This way the scripts contains only the things required for the state and I find things faster.

Burge once posted a simple statemachine that can be used in one script using dictionaries, but unfortunately I can't find the post.
Maybe he sees this and can share the link to his post 🙂
Found it. See next post.

You can of course do something like:

func _process(delta:float):
     if statemachine.state == attack:
        attack()
    elif statemachine.state == chase:
       chase()

func chase():
     #follow player logic

func attack():
     # attack logic

But as soon as you have more states it gets complicated using if and else etc.
Instead store the current state in a variable and on process/physics_process run the current state logic:

var current_state

func _process(delta:float):
     current_state.update()  

func switch_state(new_state: StringName):
   #exit current state
   # assign new state as current state
   # enter new state

You would have to make sure each state has the same name for their process methods. Most people like to call it "update or tick".

Ogg answered 9/3 at 16:54 Comment(0)
O
0

I found the discussion I had with xyz:
https://mcmap.net/q/1499/godot-statemachine-using-states-derived-from-refcounted

and his post:
https://mcmap.net/q/245/godot-modular-state-machine

Maybe you can find something there that can help you find an answer.

Ogg answered 9/3 at 16:56 Comment(0)
C
0

I am currently using a node based Statemachine, so basicly separate scripts and i like it. I do like the separation of scripts as it feels more organized and easier to read.
But implementing logic in a good way is something i need to learn and that's why i made this post.

But what i'm currently thinking is that i implement the tracking of the player logic in the Beholders main script and then I'm still not sure if i should call the functions from the individual states yet or if i do the if statement in the main script like i wrote earlier like this: if statemachine.state == attack or statemachine.state == chase Then track.

But yeah i will probably have more than one attack so the question then would be should i have sub-states like "LaserAttackState", "MeleeAttackState" etc. Or would they all just be in a single "AttackState" script? That's a good question. No idea what would be best.

Crossstitch answered 9/3 at 17:29 Comment(0)
S
0

Ogg I always forget to bookmark potentially useful stuff I post 🤔
Crossfertilization Node based state handling is a hack addressing 3.x's lack of first class functions. This was remedied in 4.x with introduction of callables. No need whatsoever to use nodes. Beside all the pitfalls and overkills I mentioned in the post Sarmentose linked, imagine you have an enemy with 10 states. And you need to instantiate many of them. It will multiply the amount of nodes in your scene tenfold, just to host a couple of functions.

Sectary answered 9/3 at 17:44 Comment(0)
L
0

Considering you want to learn, I suggest you to have a look at my OOP videos:

Just using State Machines doesn't guarantee good design pattern. You may very well misuse the system.

This video and the next 3 ones in the list are about my implementation of the State Machine (just in case you want to skip and only look at State Machine)

Based on the ideas discussed in the playlist, I've created an addon for Godot 4.2, and here's the first tutorial where I showcase the State Machine of the addon (addon download link in the description of the video):

The second tutorial (which will be published on Monday...) teaches how to use the hierarchical aspect of the State Machine. The addon will help you achieve the separation of scripts pretty easily. But keep it in mind that the addon is currently at a "preview" stage. So just use it for learning purposes and wait for the official release of the addon in Godot Assetlib.

Lakes answered 9/3 at 17:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.