Godot-proposals: Add a fighting game input buffer.

Created on 25 Sep 2019  路  84Comments  路  Source: godotengine/godot-proposals

Describe the project you are working on:
A 2D remake of a NES fighting game.

Describe how this feature / enhancement will help your project:
It would make my life easier, implementing an input buffer for my game.

Show a mock up screenshots/video or a flow diagram explaining how your proposal will work:
A 2D fighting game input buffer.

Describe implementation detail for your proposal (in code), if possible:
Basically a circular list that reads the inputs and output a signal if a certain movement is input.

If this enhancement will not be used often, can it be worked around with a few lines of script?:
Sure, but making a good input buffer is not a trivial solution, my code while it works, is buggy and I think having a basic Input buffer supported by the engine would be good for action games, maybe devs who want a cheat code, fighting games, metroidvanias, action rpgs, etc.

Is there a reason why this should be core and not an add-on in the asset library?:
Is not such a trivial solution, specialy for guys who may be more on the art side than programming side, like me.

archived input

Most helpful comment

Since a majority of games won't need this, this can likely be provided as an asset on the Asset Library :slightly_smiling_face:

All 84 comments

Keeping in mind that I've never tried to implement a input buffer before myself, and therefore wouldn't know if it entails anything else: Perhaps the engine could keep track of the last time each action in the input map was activated/deactivated, using something like OS.get_ticks_msec? Then you could call something like Input.is_action_recently_pressed("some_action_name", time_in_seconds) (returns true if the action has been pressed within that time) or Input.get_action_last_press("some_action_name") (returns a float representing the amount of time since the action was last pressed). I wonder if something like this could be an acceptable solution?

Since a majority of games won't need this, this can likely be provided as an asset on the Asset Library :slightly_smiling_face:

@Calinou How can you dismiss this proposal right away with the assessment that the "majority of games" (whatever this means), won't need this feature before anyone making games had the time to express their need here?

Input buffers would be useful for a variety of already existing genres (OP named a few: cheat code, fighting games, metroidvanias, action rpgs.) but would also allow to easily test and come up with new game mechanics and concepts, would be fantastic as one line solution for rhythm games and quicktime events as well for debugging.

@golddotasksquestions because "proposal for it to be core" means that feature would have to be ALWAYS present in the engine, and because of this, maintained FOREVER by core devs, while only a handful of projects will actually use it. I say "handful" and "will" because it looks like that feature wasn't requested for years. It would have been if it was an actual common need rather than "a cool thing to have". Also that feature is easy to figure out and implement as an addon to help people less proficient at coding, so... that's why an addon makes sense.
It doesn't mean it shouldnt be available in Godot, it means it should be available as an option.

Since a majority of games won't need this, this can likely be provided as an asset on the Asset Library slightly_smiling_face

I already mentioned a few cases where this may become handy, like a konami cheat code, fighting games, DMC clones, rythm games, smash clones, metroidvanias, action rpgs.
Even FPS could use an input command for things like dash or cover.
It's a very handy feature to add.
Even GTA had some kind of cheat code.

@golddotasksquestions because "proposal for it to be core" means that feature would have to be ALWAYS present in the engine, and because of this, maintained FOREVER by core devs, while only a handful of projects will actually use it. I say "handful" and "will" because it looks like that feature wasn't requested for years. It would have been if it was an actual common need rather than "a cool thing to have". Also that feature is easy to figure out and implement as an addon to help people less proficient at coding, so... that's why an addon makes sense.
It doesn't mean it shouldnt be available in Godot, it means it should be available as an option.

I don't think is that horrible complex feature to add, is mostly a circular buffer that receives input from the input class and can output an user made custom signal.
Maybe as an aditional function of a class could be implemented, I kind of did it once for a game of mine in godot as a script, but I'm not the best coder, so it was kind of clumsy.

This can be done with timers and a dictionary/array to store the commands I believe?
If done as an Addon, would be neat. With a nice API, etc.

I say "handful" and "will" because it looks like that feature wasn't requested for years.

What's the purpose of having a "proposal" repo if only proposals that have been made before the existence of this proposal repo are even considered for a discussion and everything else is immediately shut down as with the argument "noone has asked for this so far", "bloat", or casted to the asset libary (a jungle you only ever find something unless someone directly points you there)

If your goal is to stop people from making proposals to improve the engine, not the asset library, why have a proposal repo in the first place?

@golddotasksquestions proposals can be made with arguments which many other people can relate or have related in the past (and eventually will when seeing your post, as we answered it). It's part of what makes them solid (but not mandatory indeed). Also this is just one part of my explanation, you have to read it as a whole. I'm just trying to explain why @Calinou 's suggestion makes sense, not stopping people from proposing features.

It would have been if it was an actual common need rather than "a cool thing to have".

If you want to find out if there is common need to have it in core, you cannot dismiss it just a few hours after it has been posted. We all know Godot Github is frequented more often by contributors then is is by general users.

@golddotasksquestions

What's the purpose of having a "proposal" repo if only proposals that have been made before the existence of this proposal repo are even considered for a discussion

I think this is a bit of an over-exaggeration. They are just saying that there have been no previous Issues requesting the feature, so suggesting that something which can be implemented as a separate asset should instead go into the main repository is overkill.

everything else is immediately shut down as with the argument "noone has asked for this so far", "bloat"

And with good reason. Those are exactly the kinds of things that shouldn't go into the main engine repository.

I think the difference of opinion here is in what should be considered "bloat". While you are seeing, "this would be useful for a variety of games," the devs are seeing something that can easily be implemented with a script-based solution. The whole idea of having the proposals repo is to better filter OUT things that match that scenario (Edit: unless it is provably desired heavily by the community - more on that later).

casted to the asset libary (a jungle you only ever find something unless someone directly points you there)

This implies that we need to 1) improve the Asset Library's discoverability and/or 2) add support for other online asset vendors (#12). The solution is not to start porting things that we can easily host and develop separately all into the main engine repository.

If you want to find out if there is common need to have it in core, you cannot dismiss it just a few hours after it has been posted. We all know Godot Github is frequented more often by contributors then is is by general users.

The fact that engine change proposals are more heavily seen by contributors more so than general Godot users is a natural side effect of hosting the GIPs on GitHub. That won't be changing anytime soon (probably), so if people want more public feedback on their idea, that's what sharing the proposal on social media is for. I would invite OP to do that.

What is more practical would be implementing this as a separate plugin first (since that can be done easily enough) and then, if enough people are interested in and using that plugin, then it's a safe bet that people would appreciate having it merged into the engine. It's the same kind of thing that I'm going to have to do for #13.

Make a proof of concept. Prove the people want it. Get a preliminary implementation up and running. When it's clear that it would be useful as an integrated thing, then submit a PR. Converting script code to engine code isn't all that hard, so no biggie.

Prove the people want it.

I won't rally for a race of "who clicks that thumbs button first before the proposal is dismissed". If people want this feature in core, eventually more people will thumb it up. If not, then it simply won't happen. The only thing sure is that you won't get a meaningful result within a few hours of submitting a proposal because you don't have regular users eyeballing the proposal list every day. Immediately dismissing it counters the fundamental idea of a repo called "proposals". If we also had an official "ideas" repo, then this discussion could be moved there, but we don't.

When it's clear that it would be useful as an integrated thing, then submit a PR. Converting script code to engine code isn't all that hard, so no biggie.

You know very well that I'm not capable of doing that (yet). OP is in the same boat.

I won't rally for a race of "who clicks that thumbs button first before the proposal is dismissed". If people want this feature in core, eventually more people will thumb it up. If not, then it simply won't happen. The only thing sure is that you won't get a meaningful result within a few hours of submitting a proposal because you don't have regular users eyeballing the proposal list every day.

And having a many proposals in exactly this style is something they want to avoid here (which you also acknowledge with the "ideas" suggestion). Too many "maybe someday people will want this" Issues sitting around clutters the space reprehensibly, so yeah, they shut them down if they aren't clearly defined, imminently useful things with a demonstrable use case.

If we also had an official "ideas" repo, then this discussion could be moved there, but we don't.

I agree that we need a place for things like this. My suggestion in #47 for things that involve clearly useful assets/addons/plugins/modules/templates/projects would cover this case. Rather than re-iterating the same old arguments here as we do there though, we should simply refer to this case in that Issue and move along; simply close this as a "should be a separate addon" is good for now. It can be re-opened and migrated later on. Maybe @Xrayez can convert it into a tracker that starts cataloging all of the Proposal Issues to which it could reasonably apply.

Regardless of whether an open Issue exists somewhere for it, the status in practical terms for "when should this be merged into the engine" doesn't change. If someone builds it separately and it becomes popular, i.e. people star / download the addon a lot, then that will be evidence we can point to when suggesting that it be integrated into the engine in a formal Proposal. Until then, there is no proof of its public viability that warrants full-time maintenance by the core team, ergo, the Proposal should be closed.

You know very well that I'm not capable of doing that (yet). OP is in the same boat.

I was just saying that, once a formal implementation is done (which anyone who can write script code can do), migrating the code is quite easy. Any developer would be able to assist with that. I wasn't implying that you guys would have to necessarily.

Anyway, the godot-extended-libraries/godot-ideas repo is still hanging around. @ca3games is free to transfer the Issue there if they want. There's nothing stopping us from using it and promoting the Issues on social media. Once Juan/Remi make a decision on #47, then we can see if those issues get moved to a different place in the godotengine organization or if they stay where they are. Either way, this is not the place to argue the case.

This is not a case of "if the idea is popular enough it will be merged". The reason this proposal will not be accepted is because there is no benefit to having this in core rather than providing it as a downloadable script.

The last question in the template is:

Is there a reason why this should be core and not an add-on in the asset library?:

Saying that it is too complicated for artists is not a reason to include it in core. That is just asking the core developers to code your game for you. Simple, reusable scripts like this belong on the asset library and/or in the community based godot-extended-libraries (as willnationsdev, kindly pointed to).

Community interest is only a valid metric for proposals that meet the base criteria set out in the template.

This kinda illustrates my previous thoughts, there's a difference between a use case and a general-purpose proposal. One person is unlikely to provide many use cases (different game genres for instance), so he has to document it at least. Seeing this proposal being opened, I created another similar one #104 which I felt like is really related to this (otherwise I wouldn't create it in the first place tbh).

My point is that if enough use cases pile up for a particular problem, it can prove to be useful to be part of the core, not necessarily what the use case describe but to satisfy all those use cases to simplify implementing them via script/plugin/module etc.

@Xrayez

Well, #104 does things "correctly" where you have a valid use case, i.e. the separate system you describe when explaining the context, but the actual suggested change is scripting API updates and a new method in the Input class, i.e. things that can only be done with source code changes.

This proposal, on the other hand, has all of its necessary changes bundled into the "separate system". No part of it actually requires changes to the engine. Without any necessary engine changes, it has no purpose in being a "Proposal to Improve Godot"/"Godot Improvement Proposal." It fits better in godot-ideas.

@willnationsdev Reading that is really confusing me as to what is allowed in this repo. If a proposal has to require changes to existing systems, then what's the point of ever suggesting new features or systems now? Don't new features or systems, by their very nature, bundle most/all of their changes into the separate system?

Or am I somehow misunderstanding this?

@LikeLakers2

Let's say I want to write some kind of script to do something. This is my "task".

If I can fully implement the task's features with script code, but I want the engine to come with my task pre-implemented and available, then I should NOT create a Proposal for adding that task's solution to the engine. It should go in a different repository that can be maintained independently and downloaded as a separate asset in the Asset Library.

If it is impossible to write the code purely with scripts, i.e. if you need to make engine/editor changes to write that script code the way you want to, then you can submit a proposal to make the necessary engine/editor changes while stating your task as the "use case"/problem you are attempting to solve.

So, for example, this Issue proposes that we introduce an input buffer into the engine. However, the code for this can be fully written in script code. It does not require any engine or editor changes to function. Having it in the engine would simply be a matter of convenience. New features as a matter of convenience, however, when they can be easily done with scripts instead of engine changes, are things the core devs want isolated to separate repositories and so should not clutter the proposal repository.

104's OP is attempting to do things with scripts, with a similar purpose, but they specifically state that it cannot operate efficiently because the engine's API does not support the operation they want to perform. Their proposal is to add the necessary operations to the API. It IS NOT a proposal to add their scripted functionality to the engine. They have a problem that cannot be resolved through scripts. They are suggesting that we fix the things to resolve that obstacle (which empowers the script code to do more than it could before, i.e. enables users to handle their own problems more effectively).

This results in less of a maintenance burden on the core team and improves the usability and power of Godot's scripting API for solving future problems.

Edit:

But then what's the point of ever suggesting new features now, if it has to require changes to other systems?

It's not that it requires changes to other systems. It's that you must have a problem that you could not already do yourself and then with the changes, you are then able to solve your problem. If you can already solve the problem on your own, then it belongs in an addon/plugin/module, not the engine repository.

Another good example is #103. I have a change to the engine/editor that I would like to make (an Instantiation Palette toolbar). However, I know that it may or may not be desired as an integrated part of the engine. It can be entirely written via script code already, so what I should do is write a plugin that adds the features to the editor.

However, as I'm thinking about how I might implement it, I realize that it has all these different performance limitations that will inhibit its responsiveness as the project's size grows. To avoid this, I came up with a list of engine/editor changes that would be required to improve its performance.

I didn't submit a proposal to add the toolbar to the editor (because even if Godot could really use it, it belongs in a separate plugin). I submitted a proposal to change things impossible for scripts that would enable my plugin to behave efficiently.

I've heard mixed things about whether "a highly successful, generic plugin that assists with game development on a large scale" can be considered for editor integration though. @clayjohn earlier said no. I've heard Remi say yes in the past. So on that point, I'm not 100% sure what the word is today.

I'm still confused, possibly even more so than before I read your comments.

If I can fully implement the task's features with script code, but I want the engine to come with my task pre-implemented and available, then I should NOT create a Proposal for adding that task's solution to the engine. It should go in a different repository that can be maintained independently and downloaded as a separate asset in the Asset Library.

If it is impossible to write the code purely with scripts, i.e. if you need to make engine/editor changes to write that script code the way you want to, then you can submit a proposal to make the necessary engine/editor changes while stating your task as the "use case"/problem you are attempting to solve.

[...]

It's not that it requires changes to other systems. It's that you must have a problem that you could not already do yourself and then with the changes, you are then able to solve your problem. If you can already solve the problem on your own, then it belongs in an addon/plugin/module, not the engine repository.

I don't understand the sense in this line of logic. Many (if not all) of the nodes (or perhaps even the SceneTree concept itself) already within the engine could be ripped out and put into GDScript with next to no penalty (besides a performance hit). Given the line of logic I'm seeing, that means they don't belong in the engine, as they could be made into assets, therefore they should.

For example, I want to draw text to the screen. You could tell me to use the Label control. Or I can use CanvasItem.draw_text. Let's make a theoretical from this, and let's say the Label control doesn't already exist. I make a suggestion for such a Label node on this repo. From what I'm reading here, I would expect "You can draw text already using CanvasItem.draw_text" or "This would be better suited as an asset on the asset library" as the response I'd get, as everything about the Label control can already be replicated in GDScript code without changes to the engine. Yet, because the Label node already exists in the core and is much easier to use than CanvasItem.draw_text, it is widely used and is how we recommend people draw text to the screen. I don't understand how this line of logic holds up.

The same could be said about the Sprite node, being replicable with CanvasItem.draw_texture. Or the MultiplayerAPI, using the PacketPeer class. We can implement a JSON parser ourselves in GDScript, with little more than a few String functions to back it up. Even the InputMap concept could be replicated using scripts. The only reason we still need them in the engine is because they're already widely used nodes... but they're widely used because they're in the engine... this feels like a big, convoluted Catch-22 to me. (not sure if there's any better term to describe this)

I've heard mixed things about whether "a highly successful, generic plugin that assists with game development on a large scale" can be considered for editor integration though. @clayjohn earlier said no. I've heard Remi say yes in the past. So on that point, I'm not 100% sure what the word is today.

This is one of the other issues I have with the whole "make it an asset" argument that I keep seeing on this repo: If I make an asset for something suggested here/elsewhere, I don't know if it'll be allowed in the engine later down the line (regardless of the asset being useful to people), specifically because it's already a publicly-available asset (despite that having been the original suggestion because it wasn't thought to be useful to the engine).

So I'd be very interested in hearing a concrete answer on this.

@LikeLakers2 it does sound like a Catch-22 (that's the right term). I think it is supposed to be somehow about balancing usability against the technical debt of core devs maintaining it. My guess is something along the lines of, "we like the current state of the core engine. Keep it where it is unless absolutely necessary." There are even plans to strip out things like InterpolatedCamera and VehicleBody, afaik, so that they will exist as assets (probably GDNative).

I too would love to hear more about how far down that particular rabbit hole goes.

@ca3games If you don't mind and since you have the code already, you can submit it to https://github.com/godot-extended-libraries/godot-next I think. Would be a very nice addition. Or, it could just be released as an addon perhaps.

No, yeah, that sounds like a good addition. If @ca3games submits a PR, I'll review it to see if I'd like any changes made before merging.

@LikeLakers2 The reason there's Label and Sprite node in the engine is that they provide much more functionality than simply using draw_string or draw_texture. The label.cpp code has over 700 lines of code and it's not something you can replicate easily. Also lots of Control nodes are used internally by the editor, so they need to be in core anyways. Also虏, I used Sprite in literally every of my (2D) projects, whereas what's proposed here I needed, like, twice and it took me just few lines to implement it.

So most of things in the core are integral parts of the engine or something used so extremely often that it makes it obvious that they should be there. There are some nodes that could qualify as add-on material, and they might be eventually moved there if the plan for official plugin library is realized (https://github.com/godotengine/godot/issues/19486).

@ca3games If you don't mind and since you have the code already, you can submit it to https://github.com/godot-extended-libraries/godot-next I think. Would be a very nice addition. Or, it could just be released as an addon perhaps.

No, yeah, that sounds like a good addition. If @ca3games submits a PR, I'll review it to see if I'd like any changes made before merging.

I have made a basic demo with my own implementation, but needs maybe a refactoring or better design.
https://github.com/ca3games/Godot-fighter-demo
Here's my implementation of said issue.

If anyone is willing to take a look at this issue, here's an example of an input buffer on the XNA old educational demoes.
https://github.com/SimonDarksideJ/XNAGameStudio/wiki/Input-Sequence

So far my code has similar ideas, but is lacking a timer to clean the input after there's not input, and they also seem to use another trick, which is to merge inputs if they're too close.

@KoBeWi

The reason there's Label and Sprite node in the engine is that they provide much more functionality than simply using draw_string or draw_texture. The label.cpp code has over 700 lines of code and it's not something you can replicate easily.

I think you misunderstood the point. I understand it's not something you can replicate easily, and I understand those two nodes do much more than CanvasItem.draw_string and CanvasItem.draw_texture (Label does a good bit of math with the position, for example). The problem is that you can replicate them as assets for the Asset Library, even if it takes a lot of work. From the line of logic I was seeing from Will, the fact that they could be made as assets means that they should be made as assets, rather than be included in the engine.

We can both agree that removing the Sprite node, or Label node, or any of the Control nodes would be a bad idea, considering their widespread usage. But the problem I was seeing is that this line of logic never takes that into account, that it's a bad idea. It never considers whether it's a good idea or a bad idea for something to be made as an asset, compared to being in the engine. And if the logic can't hold up against the nodes already in the engine, then there's no reason we should be using it to determine which proposals get approved, since basically anything can be made with Godot's asset system... and since basically anything can be made as an asset, next to nothing will ever get approved under this line of logic.

(edit: removed the second part of this comment; I felt it might come off as rude, which wasn't my intention, and I couldn't really find a better way to word it)

I think what makes more sense in what @willnationsdev says, is not really that they "can" be replicated at all (this argument alone doesnt hold), but more that they require a lot of work for a frequent use. Some things really can't be replicated (like access to an OpenGL feature) but you can't say that of all features. But the way to measure these things is relative: less proficient coders find it hard, other coders find it easy. It's an ever-going battle, finding the right balance can't content everyone :p

is not really that they "can" be replicated at all (this argument alone doesnt hold), but more that they require a lot of work for a frequent use.

This argument is also very hard to follow since the core code is filled to the brim with methods that are just shorthand for a few lines of code or less: Take myarray.pop_back(), myarray.push_back(), myarray.front(), myvector.angle(), myvector.length() just to name a few.
Correct me if I'm wrong, myvector.length() is the same as sqrt(pow(abs(myvector.y),2) + pow(abs(myvector.x),2)), is it not?
Even worse, myarray.front() is to my understanding even exactly the same as myarray[0], no?

Don't get me wrong, I love choice and I love readable code. I like to have and use those examples.

But in the light of the above, how can a request to add an actually valuable method to the Input class (like: Array set_input_buffer ( int buffer_length, bool include_time_between_inputs=false, bool include_mousemotion=false ) , Array get_input_buffer ( ))
be considered immediate bloat to the core without even giving folks adequate time to express their need. Was myvector.length() not included to the core for exactly the same reasons OP has requested an input buffer? To make life easier, to get projects done faster and more readable and Godot more accessible for people who are less proficient in the technicalities of coding.

This sucks.

cc-ing @reduz and @akien-mga so that they are aware that more #10 -like discussion is happening in this Issue too. Slightly different arguments being put forth though, by @LikeLakers2.

One thing that should be brought up is the importance and widespread usage of input buffers in video games. Input buffers exist in a majority of all video games that exist; they reduce the frustration of inputting repetitive actions and allow for combination actions that don't necessitate frame perfect inputs. Given time I could produce a list of every video game that isn't a fighting game that has an input buffer, and the list would be so massive that it would eclipse the total text of the entire rest of this thread. Not having an input buffer of some kind nearly guarantees a game that isn't enjoyable to control, unless that game is immensely simplistic.

I bring this up to this topic because at the current moment, people are arguing that this wouldn't be used enough to necessitate its existence. I don't think that's a fair statement, as most people who wouldn't implement an input buffer into their game are making that decision because of a lack of understanding of how important having one is.

One thing that should be brought up is the importance and widespread usage of input buffers in video games. Input buffers exist in a majority of all video games that exist; they reduce the frustration of inputting repetitive actions and allow for combination actions that don't necessitate frame perfect inputs. Given time I could produce a list of every video game that isn't a fighting game that has an input buffer, and the list would be so massive that it would eclipse the total text of the entire rest of this thread. Not having an input buffer of some kind nearly guarantees a game that isn't enjoyable to control, unless that game is immensely simplistic.

I bring this up to this topic because at the current moment, people are arguing that this wouldn't be used enough to necessitate its existence. I don't think that's a fair statement, as most people who wouldn't implement an input buffer into their game are making that decision because of a lack of understanding of how important having one is.

It's already possible to do in Godot.. I don't know what you mean by "to necessitate its existence"

I think it's whether this is added to the core, or used as an addon. I think the lead devs will lean towards the latter

@AmericanTrailMix Can you give a few concrete exmaples?

To tack onto Calinou's request, I would be interested to know what other game engines provide in the way of an input buffer, or if they just provide the tools for a user to make one of their own the way we do.

Does any other game engine have an input buffer built-in?

Given time I could produce a list of every video game that isn't a fighting game that has an input buffer, and the list would be so massive that it would eclipse the total text of the entire rest of this thread. Not having an input buffer of some kind nearly guarantees a game that isn't enjoyable to control, unless that game is immensely simplistic.

You don't need to list them all, but naming a few would help your point.


I think this is fine as an addon. There are many ways to do input buffering and settling in one for the engine doesn't seem helpful. There might be people who needs the feature but can't use the built-in because it will be tied to a specific way of use. We could add something that it's needed to improve implementing the buffer in scripting (akin to what #104 is asking), but not simply adding the whole feature.

Thinking it of an addon should not be seen as a bad thing. Sure, we may need to improve the asset lib, but that doesn't mean we should put everything in the engine before doing so.

Very well said @vnen 馃憤

Thinking it of an addon should not be seen as a bad thing. Sure, we may need to improve the asset lib, but that doesn't mean we should put everything in the engine before doing so.

@vnen I think you misunderstood why there's this big debate over addons here. We're not complaining about addons being a bad thing, and we're not complaining about someone saying "I think this would be better as an addon". We're complaining about how the "this should be an addon" response is (in our view) being abused. It seems like it's being used to stifle discussion rather than encourage it, seeing as how it's being thrown out into the comments before there's any chance of discussion. We don't like it, and we're complaining in hopes that we can get that changed.

There might be people who needs the feature but can't use the built-in because it will be tied to a specific way of use.

I'd be inclined to accept this point, if it wasn't for the fact that we really haven't even discussed how we might implement it yet (barring a couple suggestions, but those were meant as examples). If you feel that way about the implementations that have been suggested, then I understand, but please say that it's in reference to suggested implementations then. But we can't just say "it'll be tied to a specific way of use" until we know how we want to implement it. Currently? We have no idea, we haven't even gotten past the stage of determining whether we want it, so how can we tell if an implementation will be focused towards one genre or the other, or one game or the other, or what-not?

There might be people who needs the feature but can't use the built-in because it will be tied to a specific way of use.

I'd be inclined to accept this point,

I'm less inclined to accept this point. Following this logic, any built in feature ties the user to a specific way of use.
To my understanding, one purpose of these GIP discussion is in fact to draw, as good as we can, a complete picture of all it's possible usecases, and if possible, implement the feature in a way that allows it's application in those usecases.
If we did miss an application in our discussions, users can still submit further GIP suggestions to improve it after it has been implemented

I ask you all to understand that everyone has opinions. If someone come to a proposal and say "this can be an addon", it's just that person's opinion. It's not "dismissing every proposal", it's just a particular feedback. If the feedback that follows from other comments justifies the proposal and makes it accepted, that one particular comment will be just a data point and won't stop the feature being implemented. Nobody is taking a final decision on the first comment.

I feel that, in fact, you are taking "this should be an addon" as a bad thing, while it's just one opinion. It's not stopping the discussion, and it's not meant to. If you think that the opinion is not valid, all you need to do is provide a counter-argument. If we don't allow opinions against the proposal, will any proposal be ever rejected? We also can't just accept a proposal because of one valid use case (that's the point of this repo, so other people can chime in and increase the use cases.

The same applies to "this won't be used in many games". It's an opinion, a guess. If you disagree you can express your opinion too.


For instance, the OP's implementation description is:

Basically a circular list that reads the inputs and output a signal if a certain movement is input.

Honestly I don't think it's just that. You need to configure at least the size of the buffer and the allowed time between inputs. How do you read that buffer to compare with a game-specific combo? Will the engine provide a compare() function that takes a list of input events and compare with the buffer? Will you have to read the buffer and match accordingly? Will it register only InputMap actions or any InputEvent (which means that moving the mouse may break a combo)? How does moving an analog joystick register the input (in every poll or only when changing a major cardinal direction)?

You might think any of this questions have obvious answers, but it really depends how you intend to use it, and different games have different needs.

But we can't just say "it'll be tied to a specific way of use" until we know how we want to implement it. Currently? We have no idea, we haven't even gotten past the stage of determining whether we want it, so how can we tell if an implementation will be focused towards one genre or the other, or one game or the other, or what-not?

The way it's implemented is also a criteria to decide whether we want it or not. If the feature meets the standard for merging but the proposed implementation is convoluted with a complex API, we may refrain to merge it until a better implementation is proposed.

The same way, if an implementation proposed is fleshed out to be simple and very useful, we might consider accepting even if it's not strong on other points.

I posted my thoughts on the complications of a general implementation in the last comment.

How do you read that buffer to compare with a game-specific combo? Will the engine provide a compare() function that takes a list of input events and compare with the buffer? Will you have to read the buffer and match accordingly? Will it register only InputMap actions or any InputEvent (which means that moving the mouse may break a combo)? How does moving an analog joystick register the input (in every poll or only when changing a major cardinal direction)?

My ideal Input buffer would have a physics_process and process mode (records every physics tick vs records every drawn frame).
It would record the duration(or frame in case of _process mode) for each input as well as the duration of a lack of input. So if I keep pressing a button, key or keep holding the joyaxis vector in one vector direction, a millisecond counter goes up, same as if I press nothing at all.
Since multiple inputs can happen at the same time, each input would have to have it's own "thread". (Think instruments in a DAW)

The user can add buttons to record and set a buffer_length like so:
Input_Buffer.inputs(ARROW_UP, ARROW_DOWN, KEY_ENTER, KEY_9, "right", ...) #any string like "right" is the GlobalScope looked up from the Input map.
Input_Buffer.set_buffer_length(1500) #in milliseconds
Input_Buffer.start() #able to start and stop recording inputs any time.
input_buffer01

To compare, the user queries if certain inputs happened for a period of time at specific timestaps in the input_buffer:
if Input_Buffer.has(specialmove_dic):
do my thing
input_buffer02

For joystick movement and mouse movement, you define a timestamp, vector2 and a circular radius in which the comparing vector is still valid.
Input map actions would auto translate to GlobalScope according to my definitions in the Input map: So I write cheatcode = {20: [["up", 50]], 100: [["down", 50]], 170: [["down", 50], ["B", 50]}

Also my ideal Input_Buffer could be printed to output so I don't need to write all my cheatcode and special move timings by hand, but rather just do them while the Input Buffer is "recording" them, print them out, and copy/paste them into my code for comparison. <- resulting in lightning fast balancing with guaranteed tight game controls and extremely user friendly UX while developing.

In addition, you would also want to be able to somehow specify a sensitivity level for vector direction subdivisions on joypad axes OR explicitly register certain axis ranges as belonging to the same "input." Something of that sort.

In addition, you would also want to be able to somehow specify a sensitivity level for vector direction subdivisions on joypad axes OR explicitly register certain axis ranges as belonging to the same "input." Something of that sort.

This is exactly what this would archive, no?

For joystick movement and mouse movement, you define a timestamp, vector2 and a circular radius in which the comparing vector is still valid.

@vnen

I ask you all to understand that everyone has opinions. If someone come to a proposal and say "this can be an addon", it's just that person's opinion.

[trimming everything else in-between; see [here](https://github.com/godotengine/godot-proposals/issues/100#issuecomment-537265039) for full comment]

The same applies to "this won't be used in many games". It's an opinion, a guess. If you disagree you can express your opinion too.

I find this hard to follow.

Most opinions are worded to ensure they come off as opinions. Sometimes, this is simple, using "I think" or "I feel" to express that it's just an opinion. Other times, it's a little more complicated, but not impossible. The examples you gave do not try to explain in any way that they are opinions (not even an "I think"), so I find it hard to understand that these could so easily come off as opinions.

But even then, even if I try to assume that they are opinions even in the face of that, I find that hard to do. What makes it hard is, if they were really opinions, I would expect more thought put into the message than a simple "I don't think this will be used in many games", more elaboration than a simple summary. Right now, it feels less like an opinion and more of a statement of decision.

But even if I took it as opinion now, I'm not sure that would matter. Right now, the current wording seems a lot like a statement of decision, and I'm concerned that people reading through the issue for the first time will see that sort of thing, and think that a decision has already been made (even though you've said otherwise in your comment). They might decide not to bother reading the rest of the discussion (which would let them find out that it was just an opinion), which in turn means that they won't know they can still insert their own opinion or desire for this feature.

But then, even if they did recognize it as an opinion, I don't really think it would help matters. I don't know if you or the other team members realize this, but comments coming from those perceived as higher-ups are going to be taken differently compared to comments coming from people who aren't higher-ups (like me), even under the exact same wording.

For example, if I were to say "I don't think this fits in the engine", chances are my comment would come off as one opinion in a million (figuratively speaking). On my end, it's clearly an opinion. However, if you or another team member were to say the same thing, people are going to take that in a different manner, sometimes in a much different manner. Coming from you or another team member, it holds a lot more value, a lot more weight on the discussion, as the general understanding of someone being on a team is that they know a heck of a lot more about what fits in that team's project, compared to someone who isn't.

Thus, those sorts of comments could end up becoming barriers to overcome (if they can even be overcome at all). Now we've got to "appease the gods", if you will -- we've got to show them why their concern is not a concern at all. (see footnote) That's easier said than done... especially with people who simply want to show their support, they may not feel capable of doing so. I fear this could easily drive away comments, discussion, etc.... rather than trying to get it to flourish like the Godot team seems to want.

I'm sorry, but I really find "they're just opinions" hard to believe; and even if I were to believe it, I find it hard to understand why it's being used as a justification given the various implications that those statements hold.


(footnote: This is hard to do when the statement in question is true in some form. For example, "This could be an addon" is technically true because nearly anything could be an addon, with how extensive the addon system is... so how do we show that this shouldn't be an addon? This is also hard when the statement in question is something like "this won't be used in many games" -- how do we show that it will be used in many games? And so on.)

(Also, I'm hoping my initial suggestion could perhaps get some feedback, since we seem to be talking about suggestions now.)

Sooo, another purpose of this repository was that you share the proposals, so they can get support from community. And if they get a lot of support, they are more likely to be in engine, even if they were "better for asset library". Meantime this proposal has 3 upvotes, while the post saying it's better as external asset has 10 upvotes.

Of course there's the concern that not everyone has GitHub account, but you could e.g. create a topic on Reddit or whatever platform and then link it here, so if the linked topic gets support, it wouldn't be less important than GitHub upvotes. At least that's how I'd see it.

Now, to not derail this topic even more, I'm really confused by @AmericanTrailMix 's comment. It sounds almost like input buffers are the most important thing ever and every game needs this. Unless I'm misunderstanding something, I needed this functionality literally twice - one was fighting game (surprising) and the other was a rogue-like with combos. Out of over 15 bigger and smaller projects. You don't need this in platformers, RTS games, FPS games, jRPG games, racing games and probably other genres. And I doubt these games are "immensely simplistic". But it might be possible I don't understand what we are talking about here. I'd really like to see that huge list of games using input buffers, along with their use cases ;)

btw, @LikeLakers2 's idea is a dozen lines of code. Having this in core wouldn't hurt even if it's not "used widely". And it makes implementing combos easier.

@LikeLakers2 Fully agree with your concerns.

As for your initial suggestion, I would find it quite problematic to work with. When designing button combinations for game mechanics, you want precise control about the timing and the duration of button presses as well as how tight the margins or error are supposed to be. I'm not sure how you imagine balancing and iteration would practically work with your approach. (maybe you want to elaborate more)

This is quite complicated, because action games and fighting games require very precise control and a lot of balancing. Those button combinations have to be iterated over a lot throughout production. For that reason iteration would also have to be as straight forward as possible without compromising the power and versatility of it's application.

How would I for instance check if "left" pressed for 2 seconds, while neither "A" or "B" has been pressed, "downleft", "down", "right" each for 100 milliseconds and then "A" for 200ms? with maximum of 50 ms in between all presses?
What if I want to change the timing of those presses or the pauses in between, or try of another combination? Changes like that happen throughout production a quadrillion times. My proposed solution above would allow all that to happen easily for the user.

@golddotasksquestions My proposal is not intending to implement a full input buffer as you seem to want. Rather, it is meant to build a foundation for other concepts (such as a timer or an array) to be layered onto it to achieve exactly what you want, just as I believe many things within Godot are built to do.

My proposal alone would solve simple problems on its own -- for example, if the player presses the jump button again, but we haven't fully touched the ground yet. We might use Input.is_action_recently_pressed("jump", 0.2) (or something similar) when we hit the ground to detect such a situation, and initiate our jump once more.

However, it cannot (and should not) solve problems like the one you propose, at least not on its own. In those cases, you'll want to layer other things on top of it to achieve something like what you want.

For example, to implement a solution to your problem, I would think to use my proposal, an array, and a timer. To explain what I mean:

  • My proposal would provide functions to see when the last press/release of an action was. This would allow you to do things like... get how long a button is pressed for (last release time minus last press time), or see if a button was pressed before another button was released (action 1 press time < action 2 release time), among other things.
  • The array in this case would keep track of the last few actions that were pressed, and could be used with the match keyword's array matching (you'll have to scroll down to "array patterns") to see if the latest sequence of buttons matches a sequence that corresponds to an attack you have implemented.
  • The timer would keep track of the time since the last button press, and if it passes a threshold (i.e. 50 ms), then it triggers a signal to clear the array.

I hope this makes my proposal clearer.


Also, if it's desired by a few people here, I might make a more in-depth version of my proposal, with all the implementation details and functions that I would think to expect from it. So far, I've only given the general idea, and not an actual full implementation.

In this proposal: https://github.com/godotengine/godot-proposals/issues/100#issuecomment-538105674

Is this system flexible to implement a system where each input is saved to a animation player animation keyframe for a movement recording demo?

@LikeLakers2
I don't see how this would be much of any help. You can already do all that with practically the same amount of effort by using existing API of Input class and Timers. Implementing a proper input buffer and not just a gimmick would still be a lot of work. A gimmick array input buffer is written in 2 seconds with existing Godot features. What this proposal asked for, as far as I understood, is an actually useful input buffer that could be used right away.
The benefit of having an actual input buffer included in Godot would be that users can focus on making games, not reinventing the wheel. The solution I proposed would still be flexible enough to cover any simple or complex usecase (at least any I could think while thinking about this for a long time) while also just work right out of the box.

@fire the first snippet in #104 is actually part of an experimental InputPlayer I've written, each event is bound to frame, comprising a key. The way it's illustrated in https://github.com/godotengine/godot-proposals/issues/100#issuecomment-537308119 is very reminiscent to animation player with tracks indeed, this is why I linked my proposal as well.

@fire Assuming you're talking to me:

If you're asking if my input buffer proposal alone could do such a thing... no, not unless you only plan on recording each action once.

However, I'm not sure why an input buffer would be needed to do such a thing. If you plan to make a input recorder for something like a demo, wouldn't you implement such a thing by listening for (and recording) press and release events using _input(event)?


@golddotasksquestions

I don't see how this would be much of any help. You can already do all that with practically the same amount of effort by using existing API of Input class and Timers.

I'm not sure I understand. Indeed, mine could be implemented using the existing Input API, but I already explained that the vast majority of the engine's nodes (and even some concepts) could also be re-implemented into GDScript with little to no loss in functionality. It doesn't make it a good idea to actually do that, mind you, but the point remains that it is still possible. Thus, I don't understand why we would arbitrarily apply this sort of logic to my proposal and not the rest of the engine.

On that note, I wish to point out that this same line of logic could also be said about your own proposal. Indeed, if your proposal were made in GDScript, would it not use the Input system to detect inputs? Thus, if my proposal is invalid because it could be implemented using existing APIs, then I refuse to believe that your proposal is a valid proposal either, as yours could be implemented using existing APIs too.

Implementing a proper input buffer and not just a gimmick would still be a lot of work. A gimmick array input buffer is written in 2 seconds with existing Godot features.

If I'm to understand that my solution to your proposed problem is a gimmick, I'm not sure what to tell you. You asked me for a solution as to how my proposal could do that, and I gave you one. Maybe it doesn't cover every use case you had in mind? Or maybe I missed something and confused you? What is it that makes this inferior to your solution?

You say it's because it was written in two seconds with existing Godot features. If that's the case, then I have two contentions with that:

  1. Much of Godot builds upon itself, using existing concepts, nodes, etc. to build new ones, then using those to build new ones on top of that. Wouldn't this qualify as "using existing Godot features", in much the same way that my proposal builds upon the existing Input API to provide new functionality?

  2. Saying something was built in "2 seconds" is, bluntly, pretty insulting to the work put into it. It completely ignores the thought and effort put into thinking up that idea. I'll admit that it didn't take me too long to think up combining my proposal, an array and a timer (maybe half an hour at best, with the rest dedicated to figuring out how to word the solution I gave)... but it's still pretty insulting to feel that you've taken my idea and thrown it out the window like it's nothing, rather than try to work with it and see if we can't fix the idea.

What this proposal asked for, as far as I understood, is an actually useful input buffer that could be used right away.
The benefit of having an actual input buffer included in Godot would be that users can focus on making games, not reinventing the wheel. The solution I proposed would still be flexible enough to cover any simple or complex usecase (at least any I could think while thinking about this for a long time) while also just work right out of the box.

Believe me, I'd love just as much as you to give people a full complete package full of everything they could ever possibly want. But it's just not possible, not without careful planning.

My proposal was meant to cover the general area of input buffers -- so that if something more complex is needed, combining my proposal with other systems would usually be enough for all but the most most complex of use-cases. I don't find it worth my time to consider whether my proposal will work with those extreme use-cases -- rather, I would like to cross those bridges when we get there, if there is no other better way about it.

I apologize if that means my proposal isn't useful to you. Maybe my idea could be improved upon? You could tell me what's missing, or maybe you could explain what you'd change, or you could give me problems to solve that you think aren't covered by my current proposal. I appreciate that sort of feedback, and that's why I asked for it.

@LikeLakers2 This is a misunderstanding, I completely agree with you. I'm really sorry that you feel insulted, this is really the opposite of my intentions.
My point was not that it could be done with gdscript and existing API (I'm on the same page with you on that matter), but that I am under the impression it would take nearly the same effort to use your proposed solution or use existing API to get to an Input buffer that would be more than a gimmick (of real practical use for various gamedesign).

I can already write a simple input buffer to an array any time using Input class and Timer, but it would be of little use. What I would like to discuss here is a solution that would be of immediate use. Those are way more complicated as I tried to illustrate above. At the same time the API should be very intuitive and their use flexible for any possible application.
I believe the UX I proposed above would archive that. But I don't know anything about implementation, so I stay out of that discussion. I only know what I would need from a UX perspective to have a practical value from this feature as a game designer and Godot user. I did not feel like your proposal would give me that because actually implementing a useable Input Buffer would be nearly as much work as using todays tools.

I'm really sorry for not being more clear earlier.

No worries. I don't get insulted easily, as I've had a lot of them thrown my way in the past, and I recognized that you probably didn't mean for it to come off as an insult (hence why I made a point of mentioning it -- nobody ever really intends to insult unless they're trolls). Honestly, I hope I didn't come off insultingly, and I don't want to discourage your solution either (even if I do think it's a bit complex compared to mine).

That said, I had a realization as I read your comment (don't worry, I've seen your edits). There's a good chance that both proposals could be used, but in different ways. I still feel mine would be suitable for simpler situations, where recording a series of inputs like yours would might be overkill. On the other hand, yours could be used as a input recorder, keeping track of when inputs are pushed and released, which could then be utilized by more complex use-cases like the ones you wanted to describe, when mine would not work well.

My only concern with that is whether we want to have the developer have to explicitly start/stop this recording, or whether we want it to automatically drop events from... say, more than 60 seconds ago, when it receives new ones. We would want it to not keep track of every input ever (except when the developer explicitly wants that), as otherwise it would make memory usage grow and grow.

We would want it to not keep track of every input ever (except when the developer explicitly wants that), as otherwise it would make memory usage grow and grow.

Yes, that's why I proposed:
The user can add buttons to record and set a buffer_length like so:
Input_Buffer.inputs(ARROW_UP, ARROW_DOWN, KEY_ENTER, KEY_9, "right", ...) #any string like "right" is the GlobalScope looked up from the Input map.
Input_Buffer.set_buffer_length(1500) #in milliseconds
Input_Buffer.start() #able to start and stop recording inputs any time.

Ah, I forgot that you proposed those. My bad!

Looking back over your comment, I'm actually starting to think your idea could work a lot better than mine with some work. The only exception is the has() function you offered, which seems a little complex in how you offer a parameter to it.

If I could offer some alternative APIs to replace has() with, which might be simpler to understand and use:

  • is_action_held_at_point(action, time_in_seconds_ago) -- takes a float time_in_seconds_ago which is how long ago from the current point that we want to check, and returns true if action was being pressed at that point in time in the buffer

  • get_action_press_times(action) -- Returns an array of dictionaries of the times in the buffer that action was being held down, in a format like this: {"press_timestamp": 1.2, "press_length": 0.25} (press_timestamp in this case could be the same format as time_in_seconds_ago above)

  • get_actions_held_at_point(time_in_seconds_ago) -- Returns strings and integers representing all the actions and buttons held at a specific point in the buffer

  • get_action_last_press_time(action) and get_action_last_release_time(action) -- returns the latest time that an action action was pressed/released, in seconds ago

As for the usage of this for cheat codes, or perhaps just getting the order of button presses, I do have a suggestion. However, I'm not so sure if it will be as useful as needed:

  • get_action_presses(start_time = 0, end_time = buffer_length, include_releases = false) -- Returns all action presses between the start and end times, in the order of which they were pressed from oldest press to newest press. These could then be processed in a loop, or run through a match keyword, as needed.

These functions seem to cover most use cases I can think of, with perhaps only the most extreme not being handled here. I could imagine these APIs being used in tandem with each other to cover most use cases. What do you think?

For an input buffer I expect only that it stores X last inputs, filtered by specific actions, and clears the buffer after Y milliseconds has passed since the last input. Comparison is just seeing if the end of buffer match a combo array that I have (which can be an array of strings). Checking that is trivial, but it could have a convenience function. It could allow any event, not only actions, but those are harder to set.

Maybe we can also add a minimum time for each action to register (that could be part of InputMap, not necessarily the input buffer). This way you can do combos with "hold X for 3 seconds" or something. You don't really need to store for how long it was pressed, since the action would only register if it was pressed for long enough.

This should be enough for a fighting game (and for cheat codes).

For other cases that require a more complex solution, then that's what this repo is for: to find use cases. Trying to design for something that might be is a recipe for a big refactoring later on, when we actually need it.

Don't take me wrong: the solutions proposed here are interesting. I just to want to see some examples to justify the complexity.

@vnen

You don't really need to store for how long it was pressed, since the action would only register if it was pressed for long enough.

Yes you do need to store for how long an Input was pressed because you need to be able to check if a even a single button was pressed like this: tab, tab, hold short, tab, hold long.
As a game designer I want to be able to configure exactly how long a tab should be, how long a long press should be, a medium press, or short press should be.
The engine should not pre define those, as this is totally project specific and the amount of various button presses and button press length may vary even within a project.

@golddotasksquestions I think you may have misunderstood him. If an action is always going to require a minimum push length, and we don't care to know that the button is being held until it's been held for long enough, he is suggesting we be able to configure that sort of hold-time-before-registering in the InputMap so that we don't have to implement it in code. This way, the action event is not emitted at all until it's been held for long enough.

On the other hand, if we need to know how long an action was held, this would be a different matter. You could compare the press and release event times, and that would give you the time held, for example.

@LikeLakers2

On the other hand, if we need to know how long an action was held, this would be a different matter.

yes, if you want to do button combinations with varying longer and shorter presses, you always want to know if a button was pressed for a period of time AND if it was pressed starting at a certain point in time.
If you only know if a button was pressed for a period of time, it won't help you at all. If you only know if it was pressed at a certain point in time, it won't help you either. You need both informations. Please have another look at the graphic I made. I tried to illustrate this as good as I can.
Sometimes button presses start before you care about them, sometimes they start after. Sometimes they are too short, sometimes too long.

You could compare the press and release event times, and that would give you the time held, for example.

This again would needlessly complicate the game design process and would be unnecessary with my proposal: All you would need to write is
if Input_Buffer.has( { 234: [[KEY_SPACE, 150]] } ):
do stuff

  • 234 is the timestamp in miliseconds (starting from 0 at the beginning of the buffer_length.)
  • KEY_SPACE is the GlobalScope for the input we are checking (can be an input action string too like "space")
  • 150 is the press duration

A game designer also needs to be able to say "the condition should be true/false if X button is not pressed for X milliseconds starting at timestamp X", to make sure a button has not been pressed for a certain time before it should be pressed.
This is also easily possible with my proposal:
if Input_Buffer.has( { 234: [[KEY_SPACE, 1500, NO_INPUT]], 260: [[KEY_SPACE, 100]] } ):
do stuff

@LikeLakers2
I really like your API suggestions. I think they would compliment .has, not replace them.

get_action_press_times(action)
I would not call it "action" though, because it might not always be an action, but maybe just an input.
And I would prefer it would return the dictionary in the same format I would use to compare with the input buffer: { timestamp: [[ input, duration]] }
The benefit would be that I could immediately use the return result to build another more complex compare dictionary.

get_action_presses(start_time = 0, end_time = buffer_length, include_releases = false)
is_action_held_at_point(action, time_in_seconds_ago) and
get_actions_held_at_point(time_in_seconds_ago)
the functionality of those are all covered by the imho much more elegant
if Input_Buffer.has( { 234: [[KEY_SPACE, 150]] } ):

get_action_last_press_time(action) and get_action_last_release_time(action)
those sound interesting and have not been covered by my proposal. I can't think of a usecase right now, but my feelings say it could come in handy.

@golddotasksquestions Thanks for the feedback. I really appreciate it.

However, I want to make a reply to your suggestion to use the { timestamp: [[input, duration]] } syntax. While I understand that you find this syntax elegant (and perhaps understandable to you), I find it hard to understand (even after you've already explained it to me), and I worry that this may extend to other users trying to understand the syntax.

To explain why I think this, I want to explain to you how the engine normally uses arrays and dictionaries.

An array will normally be used to denote several values that are not necessarily related to each other, but are usually of the same type of information. For example, take a shopping list. A shopping list, shown as an array, could be ["Bread", "Eggs", "Milk"]. The different items are all grocery items, but are not necessarily similar in other ways.

A dictionary, on the other hand, is normally used to denote several key->value pairs that are related to each other, but that are usually not of the same type of information. Going back to our shopping list example, each item on our shopping list could have additional information attached to it. So for something like bread, we might have {"item": "Bread", "aisle": 12, "shelf": "top"} to represent that we want an item Bread, and that it is located on the top shelf of aisle 12. This new information is linked to the item "Bread", as it is related to the bread by means of being its store location, but this new info does not contain the same type of information as the other pieces of info attached to "Bread".

Your syntax, while certainly describing the data in a meaningful way, unfortunately does not follow these rules. As it does not follow the same rules that the engine normally uses, I worry that this inconsistency will confuse many users who try to use this API, even if we do try to explain it to them in the docs. And even if it doesn't confuse people, I would still take issue with it not being consistent with the rest of the engine.

(P.S. I'm going to pre-emptively apologize, as I get the feeling that this explanation may not make you feel too happy, but I'm not sure why I'm getting that feeling. So, sorry. But I hope you understand why I made this explanation.)


I also have a reply to another bit of your comment:

get_action_press_times(action)
I would not call it "action" though, because it might not always be an action, but maybe just an input.

Indeed, it will not always be an action. That said, the only real reason I'm using "action" here is because I don't really know of a better term to use. So meh. :/

@LikeLakers2 No need to apologize, I really appreciate your input.
I'm not really much experienced with the use of dictionaries, they may not be the most ideal data structure. The reason I chose a dictionary as wrapper in my examples was because they would result in a more human readable structure compared to an arrays in array in array. However I think I also read arrays are more performant compared to dictionaries, so maybe that alone would be reason enough to use an array as wrapper.
Using only arrays instead of dictionaries would make little difference to my proposal though:
(this example data is the same as in the visualization above)

dictionaries and arrays:

if Input_Buffer.has( {  124: [[KEY_3, 50], [KEY_9, 710, NO_INPUT]],
            483: [[ARROW_DOWN, 50], [ARROW_RIGHT, 50], [KEY_ENTER, 50]],
            775: [[ARROW_UP, 50]],
            861: [[KEY_9, 100]],
            1180: [[ARROW_UP, 100]] } ):
    do stuff

would just be

if Input_Buffer.has([   [124, [[KEY_3, 50], [KEY_9, 710, NO_INPUT]]],
            [483, [[ARROW_DOWN, 50], [ARROW_RIGHT, 50], [KEY_ENTER, 50]]],
            [775, [[ARROW_UP, 50]]],
            [861, [[KEY_9, 100]]],
            [1180, [[ARROW_UP, 100]]] ]):
    do stuff

In words:

if Input_Buffer.has([[timestamp, [[input or action, duration, optional flag NO_INPUT]]]]):
    do stuff

All of the above would all be a timestamp-priority format. An input-priority format would work as well, same data as before:

In words:

if Input_Buffer.has({input or action: [[timestamp, duration, optional flag NO_INPUT]]}):
    do stuff

Input-priority format, dictionaries and arrays:

if Input_Buffer.has({   KEY_3: [[124, 50]],
            ARROW_DOWN: [[483, 50]],
            ARROW_RIGHT: [[483, 50]],
            ARROW_UP: [[775, 50], [1180, 100]],
            KEY_9: [[124, 710, NO_INPUT], [861, 100]],
            KEY_ENTER: [[483, 50]] }):
    do stuff

Input-priority format, only arrays:

if Input_Buffer.has([   [KEY_3, [[124, 50]]],
            [ARROW_DOWN, [[483, 50]]],
            [ARROW_RIGHT, [[483, 50]]],
            [ARROW_UP, [[775, 50], [1180, 100]]],
            [KEY_9, [[124, 710, NO_INPUT], [861, 100]]],
            [KEY_ENTER, [[483, 50]]] ]):
    do stuff

@golddotasksquestions All three pieces of data (the timestamp, the input, and the duration) are as equally necessary as each other to describe a recorded input, and they are all related to each other in a way. Therefore, I think it would be best to format the data as dictionaries, like so:

[
    {"timestamp": 124, "input": KEY_3, "duration": 50},
    {"timestamp": 483, "input": ARROW_DOWN, "duration": 50},
    {"timestamp": 483, "input": ARROW_RIGHT, "duration": 50},
    {"timestamp": 775, "input": ARROW_UP, "duration": 50},
    {"timestamp": 1180, "input": ARROW_UP, "duration": 100},
    {"timestamp": 124, "input": KEY_9, "duration": 710, "no_input": true},
    {"timestamp": 483, "input": KEY_ENTER, "duration": 50}
]

Indeed, this means repeating data where you could possibly make it a bit more compact, but it is also far more readable (it describes itself). It also fits with the style of how the engine already does things... and from experience, I would guess that this sort of format is a lot easier to code, as now we're not spending extra code compacting things where we honestly didn't need to.

The only other option we have is making a class out of that style of dictionary, with properties timestamp, input, duration, and no_input. However, I'm not sure if that would be acceptable in the Godot code, as they typically refrain from such small data classes... or so the docs have shown me. Regardless, using a class would have the same effect as a dictionary in this instance.

Also...

However I think I also read arrays are more performant compared to dictionaries

Perhaps, but if it is more performant, it's so imperceptible at such small sizes that I don't honestly think it would matter to anybody. Someone may be able to correct me on this, but I don't believe dictionaries really get slow unless you're doing operations on dictionaries with thousands upon thousands of key->value pairs.

@LikeLakers2 As long as the aforementioned UX would be there, I personally don't care so much about the which specific data structure and format to use. I also would be more than fine with what you are proposing here in your last comment (as I am sure other people know more about what is ideal than me).

Now, to not derail this topic even more, I'm really confused by @AmericanTrailMix 's comment. It sounds almost like input buffers are the most important thing ever and every game needs this. Unless I'm misunderstanding something, I needed this functionality literally twice - one was fighting game (surprising) and the other was a rogue-like with combos. Out of over 15 bigger and smaller projects. You don't need this in platformers, RTS games, FPS games, jRPG games, racing games and probably other genres. And I doubt these games are "immensely simplistic". But it might be possible I don't understand what we are talking about here. I'd really like to see that huge list of games using input buffers, along with their use cases ;)

Platformers, racing games, and "other genres" absolutely have input buffers. Input buffers aren't about inputting quarter circles, or having button combos, or cheat codes, or anything like that. Input buffers are about game feel, and making sure that the controls feel responsive and solid.
One example of a dramatically noticeable input buffer is the games in the Soulsborne series. In fact, the input buffer is so long in games like Dark Souls 3 that often times it can kill you by making you roll 2 seconds after you pressed the button.
It's hard to come up with specific examples for games and be able to prove it, however. Obviously, I can't dig into the code of most games, and most input buffers are so small it's hard to know if they're even there. But an input buffer for, say, a platformer like Celeste, is mandatory as otherwise jumping as soon as you hit the ground would be a frame perfect input every time.
https://www.caseportman.com/single-post/2018/08/16/Input-Buffering-2D-Platforming
This page talks about it pretty well, in my opinion.

When I say "input buffers are important", I mean it. It's absolutely necessary for any game that has actions that take time, or have endlag, or what have you, to have an input buffer. Be it as short as 3 frames or long as 60, they're there so that you're not struggling to get inputs out because you didn't realize you were still on the final frame of an uninterruptible animation. Not to mean any offense, but if you're not including a jump buffer for a platformer you've made, it's probably not going to feel very good to play, especially if it proves to be a challenging and intense one like the aforementioned Celeste.

@AmericanTrailMix The use case you linked can be achieved with a Timer node, or even by making the jump action allow continuous jumping (i.e. "pogo sticking", which you find in many popular games today).

@Calinou Apologies, but I'm not sure either of those ideas would really be good ideas. Please let me know if I'm missing something.

The use case you linked can be achieved with a Timer node

It could be achieved that way, but I'm not sure what benefit it would gain from doing so.

First off, now we're tasked with managing lots of timer nodes. This means having to instantiate each one, and make sure we can keep track of which one is for which action... starting and stopping them, and so on. In some cases, it can be a mess to figure out; and in any case, I don't think this sort of timer management should be something that we make the developer implement when we could implement it in a simple manner for them.

Second, a lot of the Timer node's features are probably not going to be useful to the average need of an input buffer. While these features can be disabled, and probably wouldn't incur any noticable performance penalty even when we're using hundreds of timers with those features disabled, I don't see the need to allow for those optional features when we probably won't ever use them.

by making the jump action allow continuous jumping (i.e. "pogo sticking", which you find in many popular games today).

This solution actually wouldn't replace a input buffer. Indeed, to allow continuous jumping, the player would have to be pressing the button as they hit the ground. The thing is, the problem the article describes is when the player does press the button right before landing, but has already released it once the character hits the ground -- which can often cause the "I PRESSED JUMP!" reaction that is shown at the top of the article.

So to be able to detect this situation, we would need to implement some sort of buffer that checks if the player has pressed the button within a certain timeframe before the current point -- this is exactly what the article suggests as a solution.

@LikeLakers2 "Pogo sticking" is the opposite of what you described; it means you can jump as many times as you'd like by holding down the jump key just once :wink:

It's a matter of taste, but it tends to be more accessible as it doesn't require players to press keys repeatedly.

@Calinou Oh, I understood that "pogo sticking" is that. But you said:

The use case you linked can be achieved [...] by making the jump action allow continuous jumping

My understanding of this comment is that you are saying "pogo sticking" would be a suitable alternative to implementing some sort of input buffer. This is simply not true.

To explain: From my understanding, the problem proposed in that article is that it can be frustrating when a player presses a button down for a split second, but they were just a frame or two too early with releasing the button -- and thus the game refuses to recognize the input as it was not being pressed at the correct time (when the player is on the ground), causing the all-too-well-known "I pressed jump!" response. The solution is to have some sort of mechanism for detecting a situation like that, and being able to respond to it appropriately. An input buffer would be this sort of system, but a "pogo jumping" system would not.

Whereas input buffers are meant to help where a button was released just a little too early, "pogo sticking" is the idea that holding down the button would cause the action to happen again and again, until the button is released. But as the problem in the article is about when the button is released too early, "pogo sticking" would not be a viable option, as the player would not be pressing/holding the button when they hit the ground.

It's a matter of taste, but it tends to be more accessible as it doesn't require players to press keys repeatedly.

Don't get me wrong though, both can totally be used together (and when implemented in a thoughtful manner, it could potentially make the experience even better for players of all button-pressing preferences). But my comment was not about which is better, but about which would even be a viable solution in the context of the article's proposed problem.

This is random, but what does Unity do for "game input buffers"? Do they have a system like @golddotasksquestions s/s above?

This is random, but what does Unity do for "game input buffers"? Do they have a system like @golddotasksquestions s/s above?

@girng it's not random, it's a valid question. In fact, I asked before if other engines has something like it: https://github.com/godotengine/godot-proposals/issues/100#issuecomment-537036213 but I didn't see any answer in the discussion.

Oh wow @vnen, lol. I totally forgot about that.
I did some searching online. I found this qa post, which apparently this doesn't exist in Unity either. I also stumbled upon this, this, this, and this. Seems like it can be done in C#. I guess the same could be said for Godot (done with gdscript or gdnative)

from the Unity Q&A:

I'd disagree, I don't think built-in support is needed for this sort of thing, we have to leave something for developers to do still

How about if developers could focus on making games?

Here is a guy showcasing his Unity Input buffer implementation: https://www.youtube.com/watch?v=3ZDHhr50fIY. Note the necessity of setting inputs to "used" as well as the cancellation of inputs. The Dark Souls "roll" is a good example for what happens if you have no input cancellation.
Here is a MIT licensed Input buffer for unreal4 https://github.com/Isatin/UE4InputBuffer
I hear xna has something built in, not sure how useful that is.

@golddotasksquestions I had some thinks about how to implement your proposal. I think I'm back to supporting my own proposal over yours, at least for the purpose of input buffers. Here's why:

I realized that your proposal came off much more as an input recorder, one with a limit past which any stored inputs are deleted, than as a input buffer. While that's not a bad thing, I don't know if something of that caliber is needed for every usage of an input buffer. I don't imagine we'd need more than the last press/release times for most usages, and if we do need more than that, then your input recorder would be a decent solution (and would double as a replay-maker) -- but I don't see it replacing something simple-to-use like my solution except where my solution already doesn't work.

This isn't to say your input recorder idea couldn't be implemented and used as an input buffer -- but I think it would be better implemented as its own thing rather than as the singular solution to input buffers.

I hope this doesn't seem too rude to you. And I hope you understand my reasoning. Please let me know if I'm missing something.


Speaking of which, I would like to throw a full description of my original proposal into the pile.

My original proposal, described in detail

First off, within the Action struct, we would include two new variables: last_press_time and last_release_time. These two would get updated with the rest of the action's state, perhaps with the value of OS.get_ticks_usec, with last_press_time only updating when the button was just pressed, and last_release_time only updating when the button was just released.

For the user to find out if they were pressed within a certain timeframe ago, we would include a new parameter to is_action_just_pressed and is_action_just_released. This parameter would be something like a float that represents how long ago we want to allow the last press/release to be, for it to still count as "just pressed" or "just released". It would probably default to null or some negative number, so that the old functionality of the functions is still available.

We could also introduce get_action_last_press_time() and get_action_last_release_time() which would get the two new variables, for any advanced usages.

Also, I posted the rambling below earlier today, but quickly deleted it because I thought afterwards that I had misinterpreted the situation. However, I want to repost it, because I feel again that it is relevant.

Please don't feel like any of you have to respond to this, though. This is simply my thoughts laid out, and I admit it's not perfect, and honestly I feel weird bringing it back up, but I feel like I should repost it as it expresses how I feel right now.

Rambling under this fold

Just going to ramble a little here. This is at nobody in particular, but it does regard the issue at hand.

I have to wonder why we're so concerned with the amount of current games use it, or if other engines implement it, and so on. I mean, yes I get that we don't want to implement a feature that we don't see being used very often. But with how simple this could be, it makes me wonder why we can't just implement it anyways. If it ends up unused, we can revert the addition. If it ends up being used, great.

But not helping matters with this is how input buffers, when implemented in certain manners, are designed to be effectively invisible, imperceptible to the average human (and maybe even to those looking for if a input buffer is there). If we don't see it, how do we know it's there? On the flip side, how do we know it's not there either? We'd be better off asking lots of developers of existing games whether they use input buffers or not, and if they consider them important.

This isn't to say that being concerned with its usage count is somehow a bad thing, or that it would be fruitless. But to show how often it's already used would either require a lot of emails (which come with its own implications) or a lot of hard work and maybe even reverse-engineering of games... which would be hard, to a point where I would argue we should just implement it and watch the numbers, before making a decision on whether to keep the proposal.


But now I'm faced with another issue. If I were to propose such a thing, the obvious response would be to make it an addon, and watch those numbers.

But I'm worried that we won't really get any accurate numbers from such a solution. Specifically, I don't think the numbers we'd get would support anything except how many people used the addon. That's a number we want, but it doesn't show us how many people chose to code their own solution, or how many looked on Google instead of the asset library, or how many just gave up immediately and chose to use a different mechanic.

And without those other numbers, I feel concerned that people will say "well it won't be used that much anyways, no point in putting it in the engine". That sort of conclusion could be true, but without the other numbers to support it, it's no better than a premature conclusion.


And so the questions remain: Will it be worth it to add this to the engine? Or will it not be worth it? How do we know either way? Especially important: How do we know without several other factors getting in the way?

It seems like this issue will go nowhere, unless we can prove one way or the other -- which is next to impossible to do without adding it to the engine.

...At least, that's how I see it.

Haha, not rude at all, but I am also afraid I can't quite follow your reasoning, tbh. Your proposal is so minimal, I would not even dare call this an input buffer. I feel like if it would only cover the last input, it would also be useless for 90% of all input buffer usecases.

My proposal above though is easily scalable. It's a one liner use to compare. From a user perspective, it's at least equally simple to use as yours, while it offers much more flexibility and an incredible wide range of usecases. If you haven't already, check out this guys approach: https://youtu.be/3ZDHhr50fIY?t=100. He also describes how he uses it to check for last input for jumps like @AmericanTrailMix described earlier (including prioritization, cancellation, used). It's pretty much identical to mine, he just has it visualized differently.

Many features in Godot exist, but they feel halfheartedly implemented and are in consequence lot less useful than their title suggests. I would really hate if "Input Buffer" would make the next addition in this sad group of enthusiastic disappointments.

As for your ramblings, I would not worry too much. Input Buffers are really important in gamedesign. They will come to Godot sooner or later. Think how much easier it would be to get your statemachine to sort out states correctly if you had a proper input buffer. The usecase list is just too long: Any action game, quicktime events games, Rhythm games, brawler and fighting games, reaction based games, typing games, cheat-codes ... anything that has to work with simultaneous inputs ...

It's worth repeating. The threshold to implementing a new feature is not how many different games could use it. It's whether or not something is impossible to do without this feature. Then you need to show that that something is very important for a lot of games. As it stands there is no benefit to having a built in input buffer over having one in an asset in the asset library.

Additionally, as your discussion has shown, there is a wide range of opinion on what a proper input buffer should look like, forcing one form on all users makes little sense. Especially since the only benefit of having it built-in is ease of access.

On top of all that, we would be the only engine that ships a built-in input buffer, so the expectation that we cover all valid use cases for all types of games will be very high. Accordingly, it will take a lot of development effort to craft an input buffer that satisfies everyone's needs. It is much better to let everyone create their own that is tailored to their own game's needs.

The threshold to implementing a new feature is not how many different games could use it. It's whether or not something is impossible to do without this feature. Then you need to show that that something is very important for a lot of games. As it stands there is no benefit to having a built in input buffer over having one in an asset in the asset library.

This is BS and you know it.
This list is filled with added features that just require a few lines of code to work around.
Can you point me to where this feature has been requested by the community before it was made?

"Impossible" ... you know as much as I do that engines (as all tools of creation) are not about making something possible or not.
It's not impossible to create a 3D game with similar graphics to Godot in machine code. Godot's tools just make it a hole lot more faster and more convenient. Tools make things more feasible to do.
This engine is filled to the brim with features that are feasible even for me to do with a few lines of code, and even more that I will probably never use in any of my projects.

This input buffer feature on the other hand _has been_ requested by the community. It is _not_ something anyone can just write in a few lines of code. If it would exist it would make a lot more games _a lot more feasible_ to create in Godot, even for people who do not have years of programming experience already. It would lower the barrier of entry, yet provide everything to make complex game features.

If you brush this off while at the same time don't apply the same rigor you show here on all other implementations of the aforementioned list, it makes your arguments seem immensely arbitrary. Especially in light of the time and effort we spend here to illustrate the use, need and purpose of an input buffer feature.

Honestly, this discussion is becoming increasingly pointless. It's less about the input buffer and more about the proposal system itself.

Also, you can't compare features introduced before the proposal system was in place. Those "few lines of code" features might have not made the grade if this system were up at that point. In fact, it's one point that the proposals are trying to avoid (i.e. bloating the engine with small rarely used functions). The line is arbitrary, and the point here is that we are trying to define it objectively. But obviously not everyone agrees on the criteria (and many might not even know what they are).


About the input buffer itself, I'd very much like to see an implementation, in particular one provided in the assetlib. Then we can see the uses that people make of it to understand it's flaws when applied to a general public.

Because this is about use cases first. But in the thread barely any concrete example is presented. The OP is only about fighting game combos (and the title reflects that). And that can be done with a small helper class entirely in scripting, which is the kind of thing that we want to be provided by the assetlib.

So being provided as an addon is not a bad thing. Developers can still just care about making their game instead of infrastructure, because they can easily download the addon using the engine interface itself and then start using. We don't want people to implement their own solutions everytime, but improve a common, community-provided, useful asset that does not need to be in the base engine itself. Asking all of that to be in-engine is like asking for the npm registry be included in the node.js binary. We don't want that. We want a healthy ecosystem of addons that can extend the engine and used by the projects when needed.

We won't improve the ecosystem by jamming every feature into the engine just because the assetlib usability is not great. We improve it by understanding the flaws of the assetlib and fixing them so content (like an input buffer class) can easily be reached by anyone who needs it.

Because this is about use cases first. But in the thread barely any concrete example is presented.

Really?!? I'm taking myself out from this discussion now, because that's just makes my blood boil. What a waste of time.

Uh, use cases have been presented in this thread over and over again?

Because this is about use cases first. But in the thread barely any concrete example is presented.

Really?!? I'm taking myself out from this discussion now, because that's just makes my blood boil. What a waste of time.

Definitely a waste of time. 90% of what I said on this thread was utterly ignored, with people nitpicking on a particular point and ignoring the rest. Barely taking time to understand what I tried to say and simply rejecting without understanding.

BTW, (even though this will be ignored anyway, or have a word nitpicked, don't know why I bother) saying "fighting game" isn't a use case. There are myriads of ways to make fighting games. Some don't need an input buffer. If you need to do a combo system, describe how your combo system works. The platformer jump article was a really good example of specific use case. The proposal template answers should come from that, then we can see if it's needed often, can't be worked around easily, etc. (and make sure the proposed design cover all cases).

Sooo, I kind of understood the importance of input buffer in platformers now and realized I sort of use one too. If it was built in the engine, I'd probably use it for my project. But that would save me literally few lines of code, so not sure if writing a complex system for this purpose is worth it.

Really?!? I'm taking myself out from this discussion now, because that's just makes my blood boil. What a waste of time.

I've been in your shoes and understand how you feel. The sole fact vnen is replying is a good thing. I've been back and forth with some core developers, but honestly, in the end do they have their heart in the right place regarding the engine. I've came to this conclusion after many years. This is not abnormal, happens in the crystal-lang repo and other communities :). I would say just continue to stay positive, sometimes the vision doesn't align with core developers, that's normal, not a negative. I view open source suggestions like dating / job interviewing, sometimes it just doesn't work out, and that's okay.

@vnen

Definitely a waste of time. 90% of what I said on this thread was utterly ignored, with people nitpicking on a particular point and ignoring the rest. Barely taking time to understand what I tried to say and simply rejecting without understanding.

Hey look, something we can agree upon! I felt like 90% of what I said was ignored too. I could go in-depth if you want me to, I'm not afraid to be blunt about it if you ask me to.

But I'd like to point out something. Just because I don't respond doesn't mean I'm somehow ignoring it. Most of my responses to you (and to everyone else) were trying to hit on the key points -- the idea being that the rest of the argument falls apart without them. (How does a building stay standing when it has no solid foundation to stand on?) I also do that to avoid giving you twelve pages worth of the same arguments over every little comment made.

If that is still somehow ignoring the rest of your comments, then I don't know what to say. Do you want me to make a comment on everything you say, no matter how minor it is to your point as a whole, if I'm going to reply to you at all? Because there's clearly a divide between how I read comments and how you read comments. Clearly, I'm not understanding what's key to your point like you are.

Tell me, what do I do to fix this?


That said, I'm going to remove myself from this discussion too. Far too many times I feel like my points have been utterly ignored, and I've had to repeat them over and over. But it seems that was not enough like I originally thought, and my comments have been continually ignored anyways.

This issue has worn on my mind for far too long, where sometimes I spend entire hours typing what I thought was a decent reply, only to be ignored.

Goodbye.

Haven't read but this seems way too heated, so locking. I might reopen after assessing it, or a constructive discussion can be restarted in another thread without the flaming.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

davthedev picture davthedev  路  3Comments

Xrayez picture Xrayez  路  3Comments

WilliamTambellini picture WilliamTambellini  路  3Comments

wijat picture wijat  路  3Comments

SleepProgger picture SleepProgger  路  3Comments