Skip to content

Commit

Permalink
Add AnimatorAspect
Browse files Browse the repository at this point in the history
`AnimatorAspect` is high level API for changing animation

+ remove unused component `FrameGrid`
+ naming changes
+ update Animation.md
  • Loading branch information
Antoshidza committed Apr 2, 2023
1 parent bfdb604 commit c0b0096
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 27 deletions.
34 changes: 30 additions & 4 deletions About/Animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ Provides components / systems / authorings / data structures to simulate sprite
It just shifts UVs over time which looks like different sprite rendered.
While it just UV shifting animation sprite sequence should be a solid texture sheet.

## How it works
All animation data lives in provided `ScriptableObject`s (mentioned below).
In baking phase it bakes all immutable animation data into blob (see `SpriteAnimationAuthoring`).
Then in runtime it changes `UVAtlas` component value over time to perform flipbook animation.

## [`SpriteAnimationAuthoring`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Authoring/SpriteAnimationAuthoring.cs)
Inherited from [`SpriteRenderAuthoring`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Base/Authoring/SpriteRendererAuthoring.cs) it also bakes all needed animation data provided as [`SpriteAnimationSet`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Data/SpriteAnimationSet.cs).
If you want to implement your own authoring you can still use static methods provided by this class to perform same baking.

## Prepare assets
To work with animation part you will also need to create [`SpriteAnimationSet`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Data/SpriteAnimationSet.cs) and bunch of [`SpriteAnimation`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Data/SpriteAnimation.cs).
You can create them like most of `ScriptableObjects` by calling context menu in project and selecting `Create/NSprites/Animation Set` or `Create/Nsprites/Animation (sprite sequence)`.
To work with animation part you need to create [`SpriteAnimationSet`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Data/SpriteAnimationSet.cs) and bunch of [`SpriteAnimation`](https://github.com/Antoshidza/NSprites-Foundation/blob/main/Animation/Data/SpriteAnimation.cs).
You can create them like most of `ScriptableObjects` by calling context menu in project and selecting `Create/NSprites/Animation Set` / `Create/Nsprites/Animation (sprite sequence)`.

### `SpriteAnimation`
A `ScriptableObject` containing sprite sequence (as solid sprite sheet) where each sprite has duration of how long it stays before switch to next frame. This class also requires additional data like frame count resolution.
Expand All @@ -18,6 +23,29 @@ A `ScriptableObject` containing set of `SpriteAnimation`s with string names (whi
### `SpriteAnimationBlobData`
A `struct` blob to contain runtime immutable animation data. Contains `int` ID which corresponds to animation name with help of `Animator.StringToHash`, atlas UVs of sprite sheet, it's frame resolution, `BlobArray<float>` of frame durations and sum of all frames duration.

## Change animation during runtime
### Using `AnimatorAspect`
You can use this [aspect](https://docs.unity3d.com/Packages/[email protected]/manual/aspects-intro.html) to change animation by it's ID in a simple way.
```csharp
// this example is from Age-of-Sprites project https://github.com/Antoshidza/Age-of-Sprites/blob/main/Assets/Sources/Rome/Systems/MovableAnimationControlSystem.cs
[BurstCompile]
private partial struct ChangeAnimationJob : IJobEntity
{
public int SetToAnimationID;
public double Time;

private void Execute(ref AnimatorAspect animator)
{
animator.SetAnimation(SetToAnimationID, Time);
}
}
```

### Manually
Basically to change animation we should set `AnimationIndex` to another index and reset `AnimationTimer` and `FrameIndex` and also calculate proper `UVAtlas` of 1st frame of animation you choosed. Though the short way is to just change `AnimationIndex`, set `FrameIndex` to last frame index in choosed animation and set `AnimationTimer` to current `Time.ElapsedTime` which will trigger `SpriteUVAnimationSystem` to calculate next frame which should be the 1st frame of choosed animation.

You can inspect example of doing this by looking at `AnimatorAspect` source code.

## Components
|Type|Description|
|----|-----------|
Expand All @@ -26,5 +54,3 @@ A `struct` blob to contain runtime immutable animation data. Contains `int` ID w
|`FrameIndex`|Currently displayed frame from sprite sheet texture|
|`AnimationTimer`|Timer which has value of global time + frame duration. When timer excided (timer value equals current global time) frame index increases|

## How to change animations during runtime
Basically to change animation we should set `AnimationIndex` to another index and reset `AnimationTimer` and `FrameIndex` and also calculate proper `UVAtlas` of 1st frame of animation you choosed. Though the short way is to just change `AnimationIndex`, set `FrameIndex` to last frame index in choosed animation and set `AnimationTimer` to current `Time.ElapsedTime` which will trigger `SpriteUVAnimationSystem` to calculate next frame which should be the 1st frame of choosed animation.
3 changes: 3 additions & 0 deletions Animation/Aspects.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 51 additions & 0 deletions Animation/Aspects/AnimatorAspect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Unity.Entities;

namespace NSprites
{
public readonly partial struct AnimatorAspect : IAspect
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
private readonly Entity _entity;
#endif
private readonly RefRW<AnimationIndex> _animationIndex;
private readonly RefRW<AnimationTimer> _animationTimer;
private readonly RefRW<FrameIndex> _frameIndex;
private readonly RefRO<AnimationSetLink> _animationSetLink;

public void SetAnimation(int toAnimationIndex, in double worldTime)
{
// find animation by animation ID
ref var animSet = ref _animationSetLink.ValueRO.value.Value;
var setToAnimIndex = -1;
for (int i = 0; i < animSet.Length; i++)
if (animSet[i].ID == toAnimationIndex)
{
setToAnimIndex = i;
break;
}

if (setToAnimIndex == -1)
throw new NSpritesException($"{nameof(AnimatorAspect)}.{nameof(SetAnimation)}: incorrect {nameof(toAnimationIndex)} was passed. {_entity} has no animation with such ID ({toAnimationIndex}) was found");

if (_animationIndex.ValueRO.value != setToAnimIndex)
{
ref var animData = ref animSet[setToAnimIndex];
_animationIndex.ValueRW.value = setToAnimIndex;
// here we want to set last frame and timer to 0 (equal to current time) to force animation system instantly switch
// animation to 1st frame after we've modified it
_frameIndex.ValueRW.value = animData.FrameDurations.Length - 1;
_animationTimer.ValueRW.value = worldTime;
}
}

public void SetToFrame(int frameIndex, in double worldTime)
{
ref var animData = ref _animationSetLink.ValueRO.value.Value[_animationIndex.ValueRO.value];
_frameIndex.ValueRW.value = frameIndex;
_animationTimer.ValueRW.value = worldTime + animData.FrameDurations[frameIndex];
}

public void ResetAnimation(in double worldTime) =>
SetToFrame(0, worldTime);
}
}
3 changes: 3 additions & 0 deletions Animation/Aspects/AnimatorAspect.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 0 additions & 7 deletions Animation/Components/FrameGrid.cs

This file was deleted.

11 changes: 0 additions & 11 deletions Animation/Components/FrameGrid.cs.meta

This file was deleted.

8 changes: 4 additions & 4 deletions Animation/Systems/SpriteUVAnimationSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ public partial struct SpriteUVAnimationSystem : ISystem
[WithNone(typeof(CullSpriteTag))]
private partial struct AnimationJob : IJobEntity
{
public double time;
public double Time;

private void Execute(ref AnimationTimer animationTimer,
ref FrameIndex frameIndex,
ref UVAtlas uvAtlas,
in AnimationSetLink animationSet,
in AnimationIndex animationIndex)
{
var timerDelta = time - animationTimer.value;
var timerDelta = Time - animationTimer.value;

if (timerDelta >= 0f)
{
Expand All @@ -44,7 +44,7 @@ private void Execute(ref AnimationTimer animationTimer,
nextFrameDuration -= extraTime;
}

animationTimer.value = time + nextFrameDuration;
animationTimer.value = Time + nextFrameDuration;

var frameSize = new float2(animData.UVAtlas.xy / animData.GridSize);
var framePosition = new int2(frameIndex.value % animData.GridSize.x, frameIndex.value / animData.GridSize.x);
Expand All @@ -56,7 +56,7 @@ private void Execute(ref AnimationTimer animationTimer,
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
var animationJob = new AnimationJob { time = SystemAPI.Time.ElapsedTime };
var animationJob = new AnimationJob { Time = SystemAPI.Time.ElapsedTime };
state.Dependency = animationJob.ScheduleParallelByRef(state.Dependency);
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.tonymax.nsprites.foundation",
"version": "3.0.0",
"version": "3.1.0",
"displayName": "NSprites Foundation",
"description": "Contains solutions based on NSprites package such as authoring sprite data, animation, sorting, culling systems.",
"unity": "2022.2",
Expand Down

0 comments on commit c0b0096

Please sign in to comment.