Replies: 6 comments 7 replies
-
I'm going to experiment to see if I can test out something in this direction to see if anything glaring comes up to make it not possible/reasonable. |
Beta Was this translation helpful? Give feedback.
-
Which scale is this game expected to reach? Beat 'em ups should be very lightweight, so I think performance shouldn't be a concern. If it comes for free, sure 😄 |
Beta Was this translation helpful? Give feedback.
-
He. Interesting; I didn't think that the states could be extended 😅 I think I understand the architecture described. |
Beta Was this translation helpful? Give feedback.
-
I'm not sure the sparse set thing will matter for us based on the amount of components/entities I expect we will end up with, unsure about that as your starting point for basing the design off/if it's premature optimization I'm not yet 100% sold yet and haven't quite pieced together implications for scripting/extensibility. Using components as state is really nice because you can just directly query for the components, and have a system/systems responsible for behavior. I think it would end up with systems that are very readable/maintainable. I really like the input priority handling stuff, this would be something similar to what that the parsing system in an input buffer would usually require to decide priority of actions based on sets of inputs over a certain window #148, so it's something that I was already imagining to be in future design. What I was going to implement for the event buffer sounds very similar to what you are suggesting for the player input event component, only extended over a multi frame window so as to account for actions triggered by sequential inputs. (and maybe would not drain?) I'm going to keep chewing it over. I look forward to looking at code if you want to put up a draft PR. |
Beta Was this translation helpful? Give feedback.
-
I'm working on a real PR, but here's what the player state looks like so far. I got flopping ( animation-only ) and idling working in the actual game with the new design, but I've commented out most of the game just to get it compiling, since I've removed the https://gist.github.com/zicklag/b9ada0123580d0bfb8c54b71d2837394 I would have avoided stages and just used I could have made 3 new stages, instead, but it seems like we may as well allow the collector and transition systems to run at the same time as any other PreUpdate systems, and we might as well allow the state handler systems to run at the same time as other Update systems. We can use
I did make a change ( we may want to make more ) that just replaces the animation states with the animation |
Beta Was this translation helpful? Give feedback.
-
In #203 I now have pretty much everything other than items and attack collision detection working, which I can hopefully finish tomorrow. I think the design is turning out very clean and easy to read. I also removed the need to clamp the player movements in multiple places. Now all movement is controlled by modifying the Before the Here's the list of files that are just about where I want them:
Also, I'm sorry if I changed some stuff I didn't need to change for the sake of the player state effort! It just helps my brain to fix the stuff that is bugging me about design while I work on it, but I know it makes it so hard to review when everything is changing at the same time. If we want to merge this at all we can do whichever of these you feel is easiest:
I know I could have tried to change one thing at a time, but for me it it'd be easier just to break it into pieces later because so much is changing that it's heard to make standalone commits. Again, sorry this is so difficult to review! |
Beta Was this translation helpful? Give feedback.
-
A reply to #195 (comment).
OK, I had another idea which I'm going to brain-dump here. Forgive the length and ambition and anything that I might have missed that might make this all pointless. 😜
SparseSet
Components Instead of EventsWe can use components with
SparseSet
storage to trigger actions on the player in other systems, instead of using events.SparseSet
storage is important because we an add and remove components withSparseSet
storage more efficiently without causing archetype table moves.For instance, when we want the player to flop, would add a
ShouldFlop
component to the player entity that we want to flop. Then the system that makes the player flop would iterate through all players with theShouldFlop
component. That way we let Bevy handle efficiently iterating through all the entities that we need to worry about flopping or moving, etc.I feel like this could be a good idea regardless of the rest of this comment which is far more ambitious and possibly overreaching for what we should do right now.
Extensible Player Actions/Effects
This got me thinking about the bigger scheme of things and how we would manage scriptable actions and even things like player effects later.
What if we structured it so that each player state would be governed by a system responsible for controlling the player while they are in that state. The state of the player would be represented by a component on the player.
For instance, let's consider the following state systems and their components:
idle
, Component:Idling
flop
, Component:Flopping
move
, Component:Moving { velocity: Vec2 }
fall
, Component:Falling { from_super_move: bool }
( handles when the player is getting hit )If the player is flopping, they will have a
Flopping
component and theflop
state will operate on that player and be responsible for modifications to the player such as animation frames, movement, etc.Additionally, whatever state the player is in is responsible for triggering transitions to other states. For instance, if the player is flopping, they won't stop flopping until the
flop
system willingly removes theFlopping
component and adds another state component.Inputs and Attempted State Transitions
Finally, we have to manage input both from the player gamepad/keyboard and from other player influencing factors such as attacks that are hitting them, etc. For instance we could have the following systems that will want to try and control the player:
player_input
: This system grabs input from theActionState
that is generated from our controller bindings and will want to cause the player to move or start flopping, etc. when buttons are pressed.enemy_attacks
: This system will want to trigger the playerFalling
when the player gets hit by an attack.Each player will have a
PlayerStateEvents
component added to it that will simply be a vector containing all the events that are sent by systems that are attempting to control the player. We use this instead of normal Bevy events because each event should only be handled once, and only by the system for the players currently active state.For instance, if we are currently
Flopping
, theflop
system will run on the player anddrain(..)
all of events out of the player'sPlayerStateEvents
component and handle them as it deems fit. Each event will be aPlayerStateEvent
defined like so:This is where things get interesting. Because we could end up with any number of player states eventually, and ones that have been added by scripts, for that matter, we can't have an enum that lists all of the possible states we might need to transition the player to. Therefore we must have our
PlayerStateEvent
contain the information needed to transition to the new state, even though the current state may not know what actual component type to add at compile time.That is where the power of
ReflectComponent
andBox<dyn Reflect>
come into play.We will implement a
DynamicCommands
system parameter that acts just likeCommands
, but that has an additional method for inserting components not known at compile time from it'sReflectComponent
andBox<dyn Reflect>
.Flopping Example
Let's consider a hypothetical flop system. It could be implemented like this:
With this design, we can add new attacks and fighter moves in Rust and in scripts without having to know them all upfront, and each state can intelligently decide when to transition out of the state.
Summary
At this point you can probably imagine how you might implement the other states:
idle
would be very low priority and would transition to whatever the highest priority event it gets in a given frame without asking questions.move
would transition back to idle every frame, so that you would stop moving as soon as the player released the buttonfall
would work similar toflop
There could be things I'm missing, and I wouldn't really know if this would work well without trying it, but there's the idea!
Beta Was this translation helpful? Give feedback.
All reactions