derkork / godot-statecharts

A state charts extension for Godot 4
MIT License
761 stars 39 forks source link

support for _integrate_forces? #6

Closed tupcakes closed 1 year ago

tupcakes commented 1 year ago

From what I've been able to tell this state chart implementation doesn't appear to work with _integrate_forces? If I'm understanding that correctly, is that something that could be added to the State class?

_integrate_forces is used primarily when controlling StaticBody2D objects in kinematic mode.

derkork commented 1 year ago

Hi @tupcakes , could you elaborate on your use case for this?

Right now the approach of using signals, this library uses, does not lend itself very well to _integrate_forces because _integrate_forces is a member method of RigidBody2D/RigidBody3D and as such the state machine has no way of simulating this call because it is called at the right time by the RigidBody2D/RigidBody3D itself.

What could work is some kind of dispatching mechanism that you would call from _integrate_forces, that reads current state machine state and then dispatches to a certain method. But that's a lot less elegant as it's basically a glorified switch/case. It would require you to either refer to states in your code (which I think misses the point of using this library) or repeat your state structure somehow in a secondary node structure which isn't great from a maintenance point of view. Both options are not very appealing.

But in any case, if you could tell me a bit more about your use case, maybe a better way comes to mind, so I'd very much appreciate if you could give me some details. Thanks a lot!

tupcakes commented 1 year ago

Sure. Based on the below documentation, it's my understanding that _integrate_forces is how you affect the physics state of RigidBody2D objects. An example would be space ships in a Newtonian physics based space game.

For example moving a RigidBody2D would use methods like add_focrce() or apply_impulse().

The documentation says that a RigidBody's position or velocity should not be altered directly, but rather use the methods provided and _integrate_forces.

Note: You should not change a RigidBody2D's position or linear_velocity every frame or even very often. If you need to directly affect the body's state, use [_integrate_forces](https://docs.godotengine.org/en/stable/classes/class_rigidbody2d.html#class-rigidbody2d-method-integrate-forces), which allows you to directly access the physics state. https://docs.godotengine.org/en/stable/classes/class_rigidbody2d.html#description

void _integrate_forces ( PhysicsDirectBodyState2D state ) virtual

Allows you to read and safely modify the simulation state for the object. Use this instead of Node._physics_process if you need to directly change the body's position or other physics properties. By default, it works in addition to the usual physics behavior, but custom_integrator allows you to disable the default behavior and write custom force integration for a body. https://docs.godotengine.org/en/stable/classes/class_rigidbody2d.html#class-rigidbody2d-method-integrate-forces

Some example code for a RigidBody2D character controller in a Newtonian physics based space game:

extends Ship

var velocity: Vector2 = Vector2.ZERO
var rotation_dir: float = 0
var thrustSprite

# Load ship stats and scene
@onready var shipStats: ShipStats = preload("res://resources/ships/ship_small_hornet.tres")
var ship_scene = preload("res://scenes/actors/ships/ship_small_hornet.tscn")
var ship_scene_instance = ship_scene.instantiate()

# Called when the node enters the scene tree for the first time.
func _ready():
    add_child(ship_scene_instance)
    Global.player = self
    thrustSprite = $Ship/ThrustSprite

func _integrate_forces(state):
    limit_max_speed(state, shipStats)
    get_input(state)
    move(velocity, rotation_dir, shipStats, thrustSprite)

##### Player Input
func get_input(state):
    velocity = Vector2.ZERO

    rotation_dir = Input.get_action_strength("turn_right") - Input.get_action_strength("turn_left")

    if Input.get_action_strength("move_forward"):
        velocity = forward_velocity(shipStats)

    if Input.get_action_strength("move_reverse"):
        velocity = reverse_velocity(shipStats)

    if Input.get_action_strength("move_stop"):
        state.linear_velocity = stop(state, shipStats)

I only do this as a hobby so it's entirely possible I'm way off base here, but the docs seemed pretty explicit.

derkork commented 1 year ago

Thanks for the explanation. I cannot see however where the state charts come into play here. Does the ship have any states that would affect how it moves physically? Do states limit what input is available?

derkork commented 1 year ago

I'm closing this as I would need more information to derive any meaningful action from it and it's been inactive for two weeks. Please reopen it if you want to continue the discussion.