Finite state machine/code for running and aiming (mutually exclusive, but complex logic)?

Warning: I wrote the following post when I was tired, this may add some context to the situation or just confuse you. If you want a clearer understanding of the original question, scroll into the comments where I left two pictures and a video.

I have 6 variables, 3 are to be set by a function, and the other 3 are set by inputs
inputs:
self.stance.crou
self.stance.aimi
self.stance.runn
(these variables are set when the player does inputs, e.g if they press C it will be set to true, if not it will be false, Shift will set runn to true, if not false, Right mouse button clicked runn will be set to true, if not it will be false)
variables:
stance.aiming
stance.running
stace.crouching
these should be set to the function, which overrides the desired to either true or false based on the input
I tried to come up with the logic in notepad:

they are NOT running
-they can aim
-AND they can crouch
they are NOT crouching
-they can run
-if they are not running, they can aim
they are NOT aiming
-they can run
-they can crouch

they are ALREADY running
they want to crouch
-stop runnning
-start crouching / sliding
-can aim
they want to aim
-stop running
-start aiming
-can crouch
neither aim or crouch
-keep running
-not aiming
-not crouching

they are ALREADY crouching
they want to run
-stop crouching
-start running
-stop aiming
they want to aim
*doesn’t really matter
neither run or aim
-keep crouching
-not running

they are ALREADY aiming
they want to run
-stop aiming
-start running
-stop crouching
they want to crouch
*doesn’t really matter
neither run or crouch
-keep aiming
-not running

I’m trying to make kinda like how phantom forces work, so that you can’t crouch and run at the same time, you can’t aim and run at the same time, if you aim while running it will start aiming but if you still hold shift while taking off mouse2 it will return to running, or pressing shift again while aiming will set it to running, and if you try to run while aiming and hold aiming while still holding shift but take off shift it will go back to aiming. if you already running and try to crouch it will crouch but if you are still holding shift and stop crouching it will return to running or if you press shift again it will stop crouching and start running, and if you are crouching but press shift and still holding crouch but stop pressing shift it will stop running and go back to crouching, you get the point, basically:

RUNNING? YES.
WANT TO CROUCH? YES.
STOP RUNNING.
START CROUCHING.
STILL WANT TO CROUCH? NO.
STILL HOLDING SHIFT TO RUN? YES.
STOP CROUCHING.
START RUNNING.
RUNNING? YES.
WANT TO CROUCH? YES.
STOP RUNNING.
START CROUCHING.
STILL WANT TO CROUCH? NO.
STILL HOLDING SHIFT TO RUN? NO.
STOP CROUCHING.
STOP RUNNING.

I honestly have tried as hard as I can to describe this issue and I really hope someone can understand what I mean… :smile:
thanks :smiley:

3 Likes

I’m confused. I never saw a question asking what explicitly you want to use this information for and I don’t know whether you’re asking for code or guidance. If I can pinpoint a question, it seems like:

“How do I make X function if Y isn’t?”

Not sure though. Don’t know if you want things cancelled out by other priority actions or whether you want them disabled until the other actions stopped. There’s more information that has been redundantly offered 3 times and I can’t seem to understand what specifically you need.

Try gathering your thoughts and explaining again in a way that’s easy to understand (find new words if you can).

And my usual thread cop remark: code or guidance? If you’re asking for code, you came to the wrong neighbourhood. If you’re asking for guidance, welcome abroad.

While I wait for that to be processed, can I interest you in some services to facilitate input? UserInputService and ContextActionService. You’ll need them to start.

1 Like

sorry I wrote this when I was tired, and not requesting code just would like to know how to go about it logically

It sounds like you are describing a finite-state machine.
There are many ways to implement one. Here’s an article explaining one in the context of a game’s character controller: State · Design Patterns Revisited · Game Programming Patterns

3 Likes

I think it’s exactly that!
I drew up a diagram, like how shown online:
diagram
I then also made a test trace table for the results we would EXPECT to occur when certain inputs happen:


I got these results by testing what I wanted on phantom forces (they presumably already have a function for this so it is implementable)
Here’s the video:

Of course this only includes running and aiming but they are mutually exclusive events (can’t happen at the same time), so it’s little bit easier that adding a third variable to it aswell (crouching).

Okay, I’ve done this, but I don’t understand how I get the last point: The code;

I have 3 input variables, 2 of which will be used:

inputs.aiming
inputs.running

inputs.aiming is set to TRUE when the right mouse button is being held down and FALSE when the its not being held down, this is done through the mouse object.
inputs.running is set to TRUE when the left shift button is being held down and FALSE when it’s not being held down, this is done through context action service.

Multiple output (stance) values, but only 2 will be used:

stance.aiming
stance.running

I need to figure out how to set these stance values, like how described in the diagrams and video above. They both can’t be true at the same time (mutually exclusive) but they can be both false at the same time, or one can be true and one is false.

Please help me figure this out, thank you :smile:

I actually didn’t write this one when I was tired, so it should make alot more sense than the original post.

1 Like

My understanding is that you have a table of inputs that gets set just based on the raw current input, and you want to calculate the true action states based on the current state and inputs.

What about a table where the keys are the names of inputs, and the values are tables. The inner tables would store names of inputs as keys as well, and whether or not there is an objection present as a boolean:

local state_objections = {
	aiming = {
		running = true,
	},
}

For example, if we have input of aiming, we iterate through state_objections.aiming, and check to see if any of the keys are current input, or if they are a current action state. If a conflict is found, that input can safely be ignored since an objection to processing the input was found. If there is no conflict found, we can safely update the action states based on the input.

2 Likes

I’m not sure if this is what you meant, keep in mind I wrote this by head without testing, but…

local stance = {
	aiming = false,
	running = false,
	oldAiming = false,
	oldRunning = false
}

local input = {
	aiming = false,
	running = false
}

local function updateStance()
	local inputAiming = input.aiming
	local inputRunning = input.running
	local oldOutputAiming = stance.oldAiming
	local oldOutputRunning = stance.oldRunning
	local newOutputAiming = false
	local newOutputRunning = false
	
	if not inputAiming and not inputRunning then
		newOutputAiming = false
		newOutputRunning = false
	else
		if inputAiming and inputRunning then
			if oldOutputAiming then
				newOutputAiming = false
				newOutputRunning = true
			elseif oldOutputRunning then
				newOutputAiming = true
				newOutputRunning = false
			end
		elseif inputAiming then
			newOutputAiming = true
			
			if oldOutputRunning then
				newOutputRunning = false
			end
		elseif inputRunning then
			newOutputRunning = true
			
			if oldOutputAiming then
				newOutputAiming = false
			end
		end
	end
	
	stance.oldAiming = stance.aiming
	stance.oldRunning = stance.running
	stance.aiming = newOutputAiming
	stance.running = newOutputRunning
end

You’d call this everytime either of the input states changed.

1 Like

only just managed to test it and it works :smile: thank you so much

1 Like