godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.15k stars 97 forks source link

Add markers and regions to Animation #8414

Closed lapspider45 closed 3 weeks ago

lapspider45 commented 11 months ago

Describe the project you are working on

Action game with a combo system.

Describe the problem or limitation you are having in your project

Right now if I want to have a combo consisting of 6 inputs (for example, XYXXXY), there's really no better way than to have a separate animation for each action.

1. You can't have the whole combo in one animation. This type of system is a branching state machine of sorts. You don't know if the player pressed X which action will come next (if any) 2. Using another AnimationPlayer with an Animation track might help organize the smaller animations, but the individual animations are still disjointed, and this doesn't help when it comes to the actual game code, only with visualization. I don't find Animation tracks useful in general. 3. Using smaller sections of one animation is what this proposal is about. A workflow like this isn't well supported by Godot at the moment.

Dealing with so many animations is a headache, not to mention all the transitions between them. It would be so much easier if you could have the entire combo in one animation (timeline) and handle transitions to/from specific points in that animation (not just the start).

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add a new animation track type that can contain markers or regions. Markers let you tag certain points in time, and are drawn with a vertical line through the timeline editor. Regions are the same, but let you tag an interval of time.

Markers and regions can be accessed through script, and maybe there will be integration with signals or custom behaviours you can define for markers/regions. With the right API and editor integration, you could subdivide and tag longer animations and precisely control the playback through code. This would greatly ease making more complex animations that have to interact with the game code.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Inspiration

Let's start with the inspiration for this proposal. Picture the timeline of a DAW like REAPER: image

Here you have a similar concept of "tracks" as the Godot animation timeline. You also have a strip at the top where you can put markers and regions. Regions can be used for looping. Both are helpful when making a song for annotating different sections and beats of interest. They can also be used with scripting to control script behaviour. For example, there's a script called "Region Playlist" that lets you create playlists that let you play regions in a different order, with different looping behaviours. If only Godot had this capability!

I don't have a mockup screenshot yet, but I think you can imagine how it'd look inside the animation timeline editor.

Marker track

I think this would be best implemented as a new track type. Let's say we call it "Marker". This track would support two types of keys:

It would make sense that you'd be able to create multiple marker tracks. However when processing markers, you shouldn't have to worry about tracks, as markers and regions are metadata specific to the whole animation. Methods such as get_markers() should return markers from all marker tracks.

Suggested script API

For simplicity, there should be API methods to get all the markers from an animation, or find the position (in time) of a particular marker. For example:

# part of the Animation class

## Return all marker names in an array
func get_markers() -> Array[String]

## Return all region names in an array
func get_regions() -> Array[String]

## Get the position of a marker (in seconds)
func get_marker(marker: String) -> float

## Same thing but for regions
func get_region_start(region: String) -> float
func get_region_end(region: String) -> float

## Return the marker that appears soonest after `time`, as a String
func get_next_marker(time: float) -> String 

## Return all regions where start < time < end
func get_active_regions(at_time: float) -> Array[String]
# etc.

Low hanging fruit (not part of this proposal)

The aforementioned would cover my use case, but if implemented you could easily have the following:

Custom loop regions

This would greatly expand the flexibility of looping, which currently is restricted to looping between the start and end points of the animation. Imagine a laser beam attack, first you have the windup and then the animation of firing the beam. With loop segments, you can loop the firing part without needing to sequence several animations (which have to be edited separately)

Animation slices

So you made all animations for your character in a sequence on the same timeline for convenience? Not to worry, you can slice an animation using regions, and treat each slice as its own animation! How convenient!

More flexibility with transitions, AnimationTree, etc.

You can transition from animation A to animation B, but starting animation B from marker C. This would make the "Animation tracks" more useful. You could play a slice from another animation, and chop it up however you want. You can do this with some hacks already, but that has its usability issues.

Pause points

Maybe you're animating a cutscene but need to pause it at a certain point because the player needs to select a dialogue option. This can be done today but it would be simpler using markers.

If this enhancement will not be used often, can it be worked around with a few lines of script?

The workaround (i.e. current workflow) involves either hand coding a bunch of magic numbers and offsets, or maybe making a value track for your markers, parsing the animation a certain way by hand, to then control the playback through a script. It's clunky to say the least.

If you want the UI, which is the main usability feature here, you'd need to make an EditorPlugin (see below)

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

It could maybe be done with an editor plugin. I looked into it though, and it would not be simple to write. In theory you could inject custom UI nodes into the Animation editor, but it would probably break if and when the UI changes. You'd also need to hack on the existing value or method tracks and probably parse a lot of things back and forth. It wouldn't integrate neatly with the rest of the engine.

As this seems like a very useful feature expanding the capabilities of AnimationPlayer I think it would be a great addition to core.

lapspider45 commented 11 months ago

Related proposals I could find:

2159 (similar to regions)

6896 (subset of loop regions)

5934 (related to AnimatedSprite)

7476 (markers could be used as part of the implementation for this feature)

chocola-mint commented 11 months ago

This feature is similar to Unreal's Notify States. I would like to see something similar in Godot as well. Perhaps Unreal's implementation can serve as a reference for how it might look in Godot. NotifyState2

RobProductions commented 10 months ago

Would definitely love to see markers/regions for this kind of stuff as it would help a lot, but couldn't your use case also be satisfied with a more advanced animation import process? In Unity you have the ability to import the "in-engine animations" by selecting a subregion of the actual animation data from Blender, Maya, etc. This allows you to create variations of an animation with different import settings/start and end points. If this was possible in Godot, you could use one source animation to create multiple individual action animations that would become your combo moves.

I definitely see uses for both options though and someone can correct me if what I mentioned above is already possible. Personally I think that would be even easier than trying to customize region positions since you can keep your regular transition workflow. Would that be appreciated as a separate proposal?

lapspider45 commented 10 months ago

@RobProductions That sounds useful for 3D workflows, but as importing animations from another program is not the only workflow where regions/markers would be useful, especially for 2D, I'd like this feature to be available for editing inside the animation editor. We're already missing root motion or any viable alternative in 2D, so I wouldn't want the gap to grow any further. I don't see a problem with what you're suggesting coexisting with this proposal though. You could probably implement both proposals together using a common system.

Arnklit commented 3 months ago

Closed my own proposal that was similar. But wanted to suggest that a comment field be included on each marker that can then pop up when hovering over a marker.

Mockup of a comment field from my proposal: 349033896-16e4c800-a16f-4e0b-b2f7-fa9486f30d35