Code Monkey home page Code Monkey logo

snowstate's Introduction

SnowState 3.1.4

@sohomsahaun


SnowState is a robust finite state machine for GameMaker Studio 2.3+. It's easy to set up and keeps the code neat and organized. No more managing a thousand different scripts for an object, it's all in one place!

You can find downloadable .yyz (project file with demo) and .yymps (SnowState only) in the Releases tab.

 

   

Documentation

To know more about SnowState, please visit the Wiki!

Note

This version of SnowState is for GameMaker Studio 2022.9.1 or higher. If you are using earlier version(s) of GameMaker Studio, please check out earlier version(s) of SnowState.

snowstate's People

Contributors

meseta avatar sohomsahaun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

snowstate's Issues

Leave event

When a state change is triggered from an ancestor state, leave event of the ancestor state is executed.
Expected: Leave event of the current state should be executed.

Update the demo

It's better to have multiple demos with different levels of complexity. Possibly different genres.

Allow an option to not trigger enter/leave events using the change() method

It would be useful for cases where I'm changing to a state regardless of the current state, but I might not want to trigger enter/leave events if I'm changing to my current event (going from state "A" to state "A").
I can do this currently by checking the current state before switching but it could be nice to have an optional argument in the change method, or a config option when setting up the state machine.

allow using return to change state

Consider allowing

if (HDIR != 0) return "run";

instead of

if (HDIR != 0) {
  state.change("run");
  return;
}

Using return to indicate the new state is much more convenient, since:

  • You don't need to type out both curly braces
  • You don't need to type out state.change

state.change can still be kept to have custom enter/leave events, or to run the whole event before changing.

It could be renamed to state.change_with

state_exists

Would be nice to have such a feature

Relying on dynamically created states- need to check if they exist before transitioning to it.

Internal variables for states

It would be useful to have states' own internal state variables, e.g., the variable that is available only within this state and accessible and can be mutated by all state events.

currently, this will be an error

fsm = new SnowState("walk")

fsm.add("walk", {
	_some_internal_var: 123,

        enter: function() {
            // error Variable _some_internal_var not set before reading it.
           show_debug_message(_some_internal_var)
        }

       step: function() {
           // error Variable _some_internal_var not set before reading it.
           show_debug_message(_some_internal_var)
       }

History bug

Changing the state before invoking the enter event of the init state includes the init state in history.

Assignment of an empty value - When running using YYC

The framework works perfectly fine when running with VM, however, as soon as I clear the project and compile it using YYC, I get the following compile time exception:

Script: SnowState at line 327 : Assignment of an empty value (function does not return anything?)

Once again, everything works perfectly fine with VM, and all of my functions in the add_transition functions return something, no matter what.

UPDATE: Same thing happens in the included DEMO project, if run using YYC.

GMS2 version: 2022.3.0.625
Runtime version: 2022.3.0.497
SnowState version: v3.1.0

Changing state during multiplayer causes a crash

I have this state machine.

// Script assets have changed for v2.3.0 see
// https://help.yoyogames.com/hc/en-us/articles/360005277377 for more information
function TankGears() {
	fsm = new SnowState("neutral")
	
	fsm.add("neutral", {
		step: function(input) {
			if (input.FORWARD_pressed) {
				fsm.change("first")
			}
			if (input.BACK_pressed) {
				fsm.change("reverse")
			}
			velocity = 0
		}
	})

	fsm.add("first", {
		step: function(input) {
			if (input.FORWARD) {
				velocity += acceleration
			}
			if (input.BACK) {
				velocity -= deceleration
				if velocity <= 0 {
					fsm.change("neutral")
				}
			}
			velocity = clamp(velocity, 0, top_speed)
			if (velocity >= 0 && !can_go_forward) {
				velocity = 0;
			}
		}
	})

	fsm.add("reverse", {
		step: function(input) {
			if (input.FORWARD) {
				velocity += acceleration / 2 
				if velocity >= 0 {
					fsm.change("neutral")
				}
			}
			if (input.BACK) {
				velocity -= deceleration
			}
			velocity = clamp(velocity, reverse_top_speed, 0)
			if (velocity <= 0 and !can_reverse) {
				velocity = 0;
			}
		}
	})
	
	return fsm
}

During a multiplayer game using the GX games implementation I get a crash when the state is changed, e.g. from neutral to first.

Diff at byte position 4800 Instance 9 (obj_player) diff in varMap.gears.varMap.__owner.varMap.fsm.varMap.step.varMap.exec.varMap.__stateStartTime: 1708945.000000 1709230.000000

The step methods are called in the obj_player step event

   var input = rollback_get_input();
   gears.step(input)

Is the FSM intended to work with multiplayer?

Thank you.

Third Level Inherited States Execute Their Inherited Parent's Events Twice

Standard state inheritance seems to be working just fine; however, if I extend that inheritance and inherit from a state that is already inheriting from another parent state (third level), then the parent's event code executes twice. This only happens if the child state does not explicitly/manually implement said event, and allows it to be implemented implicitly. If that event is explicitly/manually implemented, then the parent's code does not run twice and everything works as expected.

Screen_Shot_2023-01-18_at_9 08 29_AM

Object: oPlayerT Event: User Event 15 at line 53 : "move_and_collide" is read-only function in latest GameMaker monthly version

I'm getting this message when opening in latest GameMaker monthly version (2023.11.1.129 runtime 2023.11.1.160)
and trying to run it.

Object: oPlayerT Event: User Event 15 at line 53 : "move_and_collide" is read-only function
Object: oPlayerD Event: User Event 15 at line 47 : "move_and_collide" is read-only function
Object: oPlayerT Event: User Event 15 at line 53 : malformed assignment statement
Object: oPlayerD Event: User Event 15 at line 47 : malformed assignment statement

GM Version
image

Temporary fix (at least I was able to run it) search and replace all
serach:move_and_collide
replace:_move_and_collide

image

Wondering if its a case where GM actually implemented the function move_and_collide in a later version

Passing arguments to triggers

Enable passing arguments to triggers, to be used as part of trigger validation, and optionally also to be used as part of some method that is run as part of that transition.

fsm.add_transition("transition", "from_state", "to_state", function(_data) {}, function(_data) {}, function(_data) {});
fsm.trigger("transition", data);

Beta Variable not set before reading error

On the beta to use new rollback. From what I can tell SnowState should work with it but getting an error


############################################################################################
ERROR in
action number 1
of Step Event0
for object obj_player:

Variable struct.event(100060, -2147483648) not set before reading it.
at gml_Script_anon_anon_SnowState_gml_GlobalScript_SnowState_4416_SnowState_gml_GlobalScript_SnowState_4532_anon_SnowState_gml_GlobalScript_SnowState_4416_SnowState_gml_GlobalScript_SnowState (line 159) - exec(event, undefined, _args);
############################################################################################
gml_Script_anon_anon_SnowState_gml_GlobalScript_SnowState_4416_SnowState_gml_GlobalScript_SnowState_4532_anon_SnowState_gml_GlobalScript_SnowState_4416_SnowState_gml_GlobalScript_SnowState (line 159)
gml_Object_obj_player_Step_0 (line 9) - fsm.step();

When running the game after downloading get move_and_collide is a read-only function

Looks great piece of software but getting this on runtime:

Object: oPlayerT Event: User Event 15 at line 53 : "move_and_collide" is read-only function
Object: oPlayerD Event: User Event 15 at line 47 : "move_and_collide" is read-only function
Object: oPlayerT Event: User Event 15 at line 53 : malformed assignment statement
Object: oPlayerD Event: User Event 15 at line 47 : malformed assignment statement

On Mac v 2023.1

thanks

HTML5 support is broken

SnowState crashes on HTML5 with the latest version of GameMaker.

It has something to do with GM's built-in method() function not receiving a proper function, as it causes the exception:
"method : argument needs to be a function".

I tested a clean project with SnowState (code below) and the SnowState demo (I had to fix move_and_collide naming collision for it to build), neither of which worked.

Here's the code I used for the clean project:

/// Create Event
state  = new SnowState("test");
state
	.add("test", {
		enter:  function() {
			show_debug_message("Entering");
		},
		
		step: function() {
			x = mouse_x;
			y = mouse_y;
		},
		
		leave: function() {
			show_debug_message("Leaving");	
		}
	})
	
/// Step Event
state.step();

I tested Chromium-based browsers and Firefox.

Changing another object's state in a leave event still cause the object to run the now-previous state's step event

In the obj_unit

fsm.add("combat", {
	step: function() {
		// If the player clicks on this unit then do an attack against this unit (if applicable)
		if (mouse_check_button_pressed(mb_left) && position_meeting(mouse_x, mouse_y, self)) {
			// Make sure that this is not a unit of the player who's turn it currently is
			// Also make sure this is a valid attack
			if (squad.owner_index != obj_game.unit_turns[obj_game.current_unit_turn_index].squad.owner_index && properties.attack.is_valid_target(obj_game.unit_turns[obj_game.current_unit_turn_index], self)) {
				// Cast the attack onto this unit as the destination unit
				obj_game.unit_turns[obj_game.current_unit_turn_index].properties.attack.cast(obj_game.unit_turns[obj_game.current_unit_turn_index], self);
				obj_game.increment_unit_turn();
			}
		}
		
		// Blend Mode (DEBUG)
                clipboard_set_text(fsm.get_current_state()); // Shows idle
		if (squad.owner_index != obj_game.unit_turns[obj_game.current_unit_turn_index].squad.owner_index) {
			image_blend = properties.attack.is_valid_target(obj_game.unit_turns[obj_game.current_unit_turn_index], self) ? c_lime : c_red;
		}
	}
});

In the obj_game

fsm.add("combat", {
	leave: function(_squads) {
		// Remove available movement from attacking squad
		attacking_squad.available_movement -= attacking_squad.battle_movement_cost;
		if (attacking_squad.available_movement < 0) attacking_squad.available_movement = 0;
		
		// Set all the squads to idle
		with (obj_squad) {
			fsm.change("idle");
		}
		
		// Set all the units to idle
		with (obj_unit) {
			fsm.change("idle");
		}
		
		// Delete the losing squad and it's units
		var _loser_squad_units = convert_units_array_to_1d(_squads.loser.units)
		for (var i = 0; i < array_length(_loser_squad_units); i++) instance_destroy(_loser_squad_units[i]);
		instance_destroy(_squads.loser);
		
		// Give XP to the winner squad
		
		// Reset the squads
		attacking_squad = noone;
		defending_squad = noone;
	}
});

However, I get an error stating that the squad variable provided in the obj_unit is undefined. I've tried removing all the instance destroy functions for the units but that doesn't seem to alleviate the issue. For now I just added a simple if (instance_exists) clause, but it's strange that the step event of the previous event is being run instead of the new one.

add_child doesn't "override"

Workaround is to just use add(), which still keeps the parent-child relationship.

base = new SnowState("base");

base.add("base", {
	enter: function() {
		show_debug_message("base");	
	}
});

base.add_child("base", "child1", {
	enter: function() {
		base.inherit()
		show_debug_message("child1");	
	}
});

base.add_child("base", "child1", {
	enter: function() {
		base.inherit()
		show_debug_message("child1 override!");	
	}
});

base.change("child1");

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.