-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Fighter State Design and Gameplay Refactor #203
Conversation
a7fc047
to
9edfc85
Compare
@odecay and @64kramsystem I'd like to get your review on this when you get the chance. Since so much has changed, you might find it easier to browse the repo after the changes instead of doing a side-by side diff. GitHub1s can make this easier to do in the browser: https://github1s.com/zicklag/punchy/blob/fighter-state-refactor/src. The most important file is Again, I know this is a lot of changes and feel free to tell me I've got to break this down into more reviewable chunks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really liking all the changes especially the machine state for the fighter, looks very ergonomic and much more structured.
There are two complete PRs of mine, that have been open for some time 🤔 |
Yeah, sorry about that. While I was busy with this one it conflicts with almost everything and my local sandbox was a mess while it was in progress so switching to testing other PRs was difficult. I'll look at those now. 👍 |
- Comments out tons of the codebase - Just makes basic idling, walking, and flopping work - Incredibly work-in-progress
- Add a CustomCommands<Marker> type that can be used to create any number of custom command queues that get flushed when the user decides.
Found out I could put it at the end of the existing PreUpdate stage.
Accidentally disabled when we removed default features of Bevy.
This needs futher investigation as to why action_state.just_pressed() isn't working.
- Moved code that should probably reworked into another module into the orphans.rs file.
- Re-enabled the debug feature now that there are updated packages for Bevy 0.8. - Updated most dependencies to their crates.io releases.
s/Velocity/LinearVelocity s/Torque/AngularVelocity Torque is the wrong word, becuse toque is a force, not a velocity.
There's another bug; the enemies are now jumping when they attack. |
So! I'm approving this so it can go in, however, I'm actually approving because it's very large, and I can't give a meaningful review, at least, without taking a very considerable time to understand it. It's difficult to review because there are many commits, and on the other hand, when looking it in one pass, there are several large blocks that have been moved around. I'll put my considerations about the design in a separate comment. |
On-demand flushing is very interesting. I think something like this should be published 😄 at least to make devs aware of what needs to and can be done. A doubt I have is that, since (I presume that) this design deals only with fighter states, in the future at least, the other states will need commands flushing handling. However, I guess that it's assumed that non-fighter-state-related systems will be a very small minority, therefore, easy to handle, so I think that works. On the other hand, this is a quite custom approach to commands flushing, which I think it has some downsides:
That's all. Based on my understanding of the last commits, the Y-rise issue stands, but anyway, I think that, at this point, it can be worked on it seperately, so that this PR goes in. Generally speaking, I'm... underwhelmed, by the large amount of work required to manage storage sinchronization, even for a relatively simple game (less that 5k lines, counted by cloc), when using a parallel+asynchronous ECS (ie. Bevy). This is not related to anything in our game - it's my general impression of game engines design. |
Yeah, I'm realizing it would have been good for me to give a summary walkthrough to help reviewing. I'll do that in another comment below. You don't have to go back and look at anything, but @odecay might find it helpful. Also, if you notice anything I changed later that you don't like, feel free to open a PR to fix it, or an issue to discuss. Sorry this turned out so massive!
That's a simple fix that we can do after merge. I wanted to avoid adding more gameplay code that could easily come after, so I just shared the attack system for the enemies with the players.
I know I was thinking that would probably make a good crate. I think the idea is somewhat similar to the new command flushing design in the Bevy Stageless RFC. Since
That's why
The implementation is super simple, and the only change to the systems themselves is that they call And if the Bevy Stageless proposal gets accepted, we can just migrate to that, as it provides the same functionality. So if we needed to update it would probably take me less than half-an-hour to do it.
Yep, I'm pretty sure that's a "physics" issue, because we need to decide how to handle our "up" axis, not a scheduling issue, so it shouldn't go into this PR. |
Summary Of Changes@odecay this is mostly to help summarize the changes I made to aid in reviewing. Sorry I didn't do this earlier before you reviewed @64kramsystem! Fighter StateThe fighter and enemy state management changed completely. It uses the flow of: Collect Transition Intents ( from user input, enemy AI, or things like collisions ) → Evaluate Apply State Transitions → Run State Handlers. The enemy AI logic is placed separately in Movement ModelI removed almost all places where we manually set an item/player translation and replaced it with a modification to a The By having all our movement controlled by modifying velocities instead of translation, we are able to centralize the constraints, which are also placed in I also removed the Attack and DamageI made the damage system more generic, adding a The attack system doesn't change much, but is now generic over anything with Damage events are emitted when an attack lands, and this event is used by the knockback collector system to put the player into a knockback state when they get attacked. Projectiles & LifetimeI moved the DespawnI removed the OrganizationI moved almost all remaining systems that were in I removed the I think its easier to browse the codebase and find the systems that effect different parts of the game now. Tweak To Item HandlingInstead of matching on the item name to determine behavior, I add a Eventually I think we will want a dynamic way to handle item kinds, similar to the player state, so that they can be scripted and we don't have to have a central list of what kinds of items there are in an Enum. But it's a good temporary solution. Enemies Attack Like the PlayerRight now enemies and the player share the same attack, though they have different animations. This is trivial to fix, but I didn't want to work on any more things non organizational/architectural before this is merged so that can be done afterwards. Removed
|
7295748
to
39c3e45
Compare
Apparently they're not needed! IyesGames/iyes_loopless#30 (comment)
I just discovered that the whole custom command queues thing I made isn't even necessary! Bevy automatically flushes the command queues at the end of every That's rather convenient! Pushed an update that switches back to normal |
yep this was the intent of splitting stuff up into stages 👍 I think I'm starting to recover a bit from being sick so I should have a chance to review this and catch up with the ongoing discussion. |
I intend to review code but before that
I like this general design intent 👍
I like the damage changes, will be a useful addition for when we get to breakable items.
This is what I had originally implemented the lifetime for, (was called AttackLifetime before) but moved it to be projectile specific because that seemed to be the only place it made sense design wise after introducing the frame based melee attack changes.
The
I am fine with simplifying stages, but also with the understanding that if we find the need for more granular stages in future we will add as needed. I do agree that its annoying to figure out where a stage is added though, especially if stages are added outside of I'll return with more feedback as I go over code. |
Oh, yeah, I forgot to come back to this after discovering that command flushing happened between stages and not at the end of the frame. The original design makes a lot more sense after realizing that. If you want I can add it back, or we could wait until we have another problem, because the re-organization of the stages might have fix any problems from earlier.
Absolutely. I just didn't want to keep extra stages around if they weren't helping yet.
👍 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Massive undertaking, nice job. Overall looking good, left comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great review!
I left responses where applicable and I'll push an update once I get the chance.
Migrated to a much simpler implementation using `Commands::add` instead.
Addresses review comments.
OK, pushed a few updates resolving all but the last two unresolved conversations. |
677cb4b
to
ec820c3
Compare
bors r+ |
@zicklag I really like the description on the wiki for the state machine. Are you ever considering creating a crate with it? |
The thought hadn't actually crossed my mind. 🤔 Interestingly, the non-game-specific code only adds up to 62 lines of code! Still, it does seem like it could be useful as a crate, so that might be worth doing. |
OK I think this is finished!
This refactors almost everything related to the gameplay 😅. It does an enormous amount of re-organizing, and clean up in addition to implementing the new fighter state design I started to describe in #196. I think a lot of it turned out easier to understand and simpler to modify.
I'm hoping this helps make it easier to maintain and easier to continue to add features as we continue to develop.
I think the only "regression" is that enemies jump around just like the player does when you attack, instead of just standing and punching, which should probably be fixed.
I want to wait to add different enemy attacks until this gets review, and we can probably do that in a separate PR.
Honestly, I kind of got carried away and I'll leave it up to you guys whether or not we should merge this an whether or not we do it all at the same time, if we do. If it's better, I can break this into smaller pieces and merge it one piece at a time ( as much as possible ). Removing the
State
enum alone pretty much necessitates a change to everything, so it can only be broken down so far, but I did re-organize a lot of things, too, so I could break the re-organizations into separate PRs if necessary.Summary Of Changes
@odecay this is mostly to help summarize the changes I made to aid in reviewing.
Fighter State
The fighter and enemy state management changed completely.
fighter_state.rs
contains the entire fighter state machine. It's absolutely possible to split this out to separate files later if it gets too big, but I think it's still pretty easy to read through right now.It uses the flow of: Collect Transition Intents ( from user input, enemy AI, or things like collisions ) → Evaluate Apply State Transitions → Run State Handlers.
The enemy AI logic is placed separately in
enemy_ai.rs
.Movement Model
I removed almost all places where we manually set an item/player translation and replaced it with a modification to a
LinearVelocity
component.The
movement.rs
file has all of the systems that modify entity transforms based on linear and angular velocities and forces.By having all our movement controlled by modifying velocities instead of translation, we are able to centralize the constraints, which are also placed in
movement.rs
. We are able to arbitrarily define new constraints on any kind of entity, be it player, fighter, item, etc. that operate on the Velocity of the entity before the the transform is applied. So we no longer have the issue of clamping fighter positions all over the place!I also removed the
move_in_arch
system in favor of using forces and velocities to create the arch. I think this is cleaner, but that's subjective, so we could add move in arch again after this PR if you wanted. Though I think we might want to wait to do any more work in that direction until we figure out how we'll handle our collisions/coordinates/y-movement: #17.Attack and Damage
I made the damage system more generic, adding a
Health
component, aDamageable(bool)
component andDamageEvent
indamage.rs
. I changed the player stats to record amax_health
, so that we always know how much health they can go up to, and their actual health is stored in theHealth
component. This will allow us to add health to all kinds of stuff, such as destructible scenery, that can be damaged by attacks, without having to specialize the attack system for what it is attacking.The attack system doesn't change much, but is now generic over anything with
Health
andDamageable
and attacks now have avelocity
that can be used to figure out which direction and how hard an attack should push what was attacked, if applicable. This is used for knockback on the player, but would be ignored if a wall was attacked for instance.Damage events are emitted when an attack lands, and this event is used by the knockback collector system to put the player into a knockback state when they get attacked.
Projectiles & Lifetime
I moved the
ProjectileLifetime
component to a genericLifetime
component, so we can use it on anything that we want to despawn on a timer.Despawn
I removed the
DespawnMarker
component and thedespawn_entities
system, because it's effectually exactly the same as just callingcommands.entity(entity).despawn_recursive()
which isn't really less ergonomic ( IMO ) thancommands.entity(entity).insert(DespawnMarker)
and doesn't require an extra import of theDespawnMarker
component.Organization
I moved almost all remaining systems that were in
main.rs
to modules that made sense for them, adding plugins as necessary to each module that had systems in it.I removed the
y_sort
module to move it's system and component in with the camera, and lots of other small organizations.I think its easier to browse the codebase and find the systems that effect different parts of the game now.
Tweak To Item Handling
Instead of matching on the item name to determine behavior, I add a
kind
field to the item YAML definition that allows you to specify what the item does. For health items you can specify how much health it refills, for throwable items you can specify how much damage it does.Eventually I think we will want a dynamic way to handle item kinds, similar to the player state, so that they can be scripted and we don't have to have a central list of what kinds of items there are in an Enum.
But it's a good temporary solution.
Enemies Attack Like the Player
Right now enemies and the player share the same attack, though they have different animations.
This is trivial to fix, but I didn't want to work on any more things non organizational/architectural before this is merged so that can be done afterwards.
That's it! 🙃
Other than the massive list of changes above, the rest of the game stayed the same. Enemy AI logic is the same, the player clamping logic was adapted to a constraint so trip points and camera movement all works the same. The UI didn't change. Etc.