Platformer 2.0

This commit is contained in:
2026-07-07 19:03:53 -07:00
commit db83628775
23 changed files with 323 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
extends Resource
class_name State
@export var id: StringName
func enter(_data: Object) -> void:
pass
func exit(_data: Object) -> void:
pass
func process(_data: Object, _delta: float) -> void:
pass
+1
View File
@@ -0,0 +1 @@
uid://b2htg6oquvdho
+71
View File
@@ -0,0 +1,71 @@
extends RefCounted
class_name StateMachine
const MIN_PRIORITY = -2147483648
signal state_changed(f_id: StringName, t_id: StringName)
var states: Dictionary[StringName, State] = {}
var transitions: Array[StateTransition] = []
var current_state: State
var _pending_id: StringName = &""
var _pending_priority = MIN_PRIORITY
func setup(state_list: Array[State], transition_list: Array[StateTransition], initial_id: StringName) -> void:
states.clear()
for s in state_list:
if s != null:
states[s.id] = s
transitions = transition_list
current_state = states.get(initial_id)
if current_state == null and not states.is_empty():
current_state = states.values()[0]
push_warning("State machine initial state '%s' not found, falling back to '%s'" % [initial_id, current_state.id])
func process(data: Object, delta: float) -> void:
if current_state == null:
return
current_state.process(data, delta)
_evaluate_transitions(data)
# Escape hatch for an immediate transition
func request_transition(to_id: StringName, priority: int = 999) -> void:
if priority >= _pending_priority:
_pending_id = to_id
_pending_priority = priority
func _evaluate_transitions(data: Object) -> void:
var best_id: StringName = &""
var best_priority: int = MIN_PRIORITY
# Is there a next state pending that isn't our current state?
if _pending_id != &"" and _pending_id != current_state.id and states.has(_pending_id):
best_id = _pending_id
best_priority = _pending_priority
for t in transitions:
# Skip the current state
if not t.matches_current(current_state.id) or t.to == current_state.id:
continue
if not states.has(t.to):
push_warning("StateMachine: transition targets unknown state '%s'" % t.to)
continue
if t.priority < best_priority:
continue
if t.is_satisfied(data):
best_id = t.to
best_priority = t.priority
_pending_id = &""
_pending_priority = MIN_PRIORITY
if best_id != &"" and best_id != current_state.id:
var from_id = current_state.id
current_state.exit(data)
current_state = states[best_id]
current_state.enter(data)
state_changed.emit(from_id, current_state.id)
+1
View File
@@ -0,0 +1 @@
uid://cp2okhs1ekwad
+15
View File
@@ -0,0 +1,15 @@
extends Resource
class_name StateTransition
const ANY_STATE: StringName = &"*"
@export var from: StringName = ANY_STATE
@export var to: StringName
@export var priority: int = 0
@export var condition: TransitionCondition
func matches_current(current_id: StringName) -> bool:
return from == ANY_STATE or from == current_id
func is_satisfied(data: Object) -> bool:
return condition == null or condition.evaluate(data)
+1
View File
@@ -0,0 +1 @@
uid://e2c8iukkr01l
+18
View File
@@ -0,0 +1,18 @@
extends Resource
class_name TransitionCondition
var _callable: Callable
func _init(callable: Callable = Callable()) -> void:
_callable = callable
func evaluate(data: Object) -> bool:
if _callable.is_valid():
var result = _callable.call(data)
if result is bool:
return result
else:
push_warning("Transition callable did not return boolean! Returning false.")
return false
return true
+1
View File
@@ -0,0 +1 @@
uid://bi3vhketkwgp6