Skip to content

Commit

Permalink
Refactor authoring workflow (#4)
Browse files Browse the repository at this point in the history
> **Breaking changes**

* One of the major changes is **splitting current authorings** to independent from each other monobehaviours which using **modules** to bake and author specific data. You can read more in Authoring section. This was made because new features which want to be included to baking process are hard to combine in regular class inheritance. While `MonoBehaviour`s already are responsible for assigning data from inspector they should bake data and be flexible enough to be base class for arbitrary use. From now I recommend construct regular `MonoBehaviour` class and use authoring modules inside it as fields in any combination you want.
* `Size` in authorings no longer contains final value. From now this value used as multiplier. So final size of a sprite (which is `Scale2D` component) calculated as `Sprite.GetSize() * authoringTransformScale * Size. This frees authoring class from maintain size value while user change sprite or do another actions which require size update.
* Authorings no longer breaks **Dependency Inversion** principal and don't contain any unnecessary members for baking purposes.
* All fields in authorings are now public.
* Add `bool IsValid()` method to bunch of types to improve baking process.
* Improve sorting layer assigning through inspector by using `[SortingLayer]` attribute.
  • Loading branch information
Antoshidza authored Jun 7, 2023
1 parent 831551b commit b18a84c
Show file tree
Hide file tree
Showing 45 changed files with 699 additions and 386 deletions.
2 changes: 1 addition & 1 deletion About/2DTransform.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Provides similar to `Unity.Transforms` 2D implementation of local / world position and parent / child relationship.
Contains components / systems / authoring. Also provides a way to remove default 3D transform components from baking.

Existing of this part doesn't mean that NSprites only renders 2D, but for 2D needs we don't want to have unnecessary data.
> **Existing of this part doesn't mean that NSprites only renders 2D, but for 2D needs we don't want to have unnecessary data.**
6 changes: 3 additions & 3 deletions About/Animation.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ 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).
## [`SpriteAnimatedRendererAuthoring`](../Animation/Authoring/SpriteAnimatedRendererAuthoring.cs)
Inherited from [`SpriteRenderAuthoring`](/Base/Authoring/SpriteRendererAuthoring.cs) it also bakes all needed animation data provided as [`SpriteAnimationSet`](/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 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).
To work with animation part you need to create [`SpriteAnimationSet`](/Animation/Data/SpriteAnimationSet.cs) and bunch of [`SpriteAnimation`](/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`
Expand Down
66 changes: 66 additions & 0 deletions About/AuthoringWorkflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Authoring workflow
This package provides flexible authoring possibilities to serve your purposes.

### Regular **Authorings**
The most simple way is to use ready monobehaviour authorings from this package.
Those authorings gives you all data assigning you need to render sprites withing pipeline of this package.

* [`SpriteRendererAuthoring`](../Base/Authoring/SpriteRendererAuthoring.cs) - bakes default render components. Adds [2D transform](2DTransform.md) components / removes unity default 3D transforms / adds sorting components.
* [`SpriteAnimatedRendererAuthoring`](../Animation/Authoring/SpriteAnimatedRendererAuthoring.cs) - same as previous, but also adds animation related components. Though not inherited from previous one (because it hard to keep all in place when you deal with unity serialization)

### Modules
However you may want to implement your own register system or your own shader or some other components, I can never know.
So you may want to use authorings partially, for example you want to use only sorting bake part,
then you can use **Authoring Modules** which is just serialized types with `Bake()` method.
So you can use [`SortingAuthoringModule`](/Sorting/Authoring/Modules/SortingAuthoringModule.cs)
and other modules as a field in your custom authoring `MonoBehaviour` class and call `Bake()` in `Baker<TAuthroing>` like
```csharp
public class FooAuthoring : MonoBehaviour
{
private class Baker : Baker<FooAuthoring>
{
public void override Bake(FooAuthoring authoring)
{
aithoring.Sorting.Bake(this);
}
}

public SortingAuthoringModule Sorting;
}
```

### Baker Extensions
Again however if modules doesn't fit your needs or have extra unnecessary data then you can
still use **Baker Extensions** which is used by modules. So if you still want to use sorting
but you, for example, don't need set any sorting data, because it constant, then you can do something like:
```csharp
public class FooAuthoring : MonoBehaviour
{
private class Baker : Baker<FooAuthoring>
{
public void override Bake(FooAuthoring authoring)
{
this.BakeSpriteSorting
(
GetEntity(TransformUsageFlags.None),
SortingIndex,
SortingLayer,
UseStaticSorting
);
}
}

private const int SortingIndex = 0;
private const int SortingLayer = 0;
private const bool UseStaticSorting = false;
}
```

### You can look at diagram below to see how this parts related to each other.
<img src="NSprites-Foundation-Authoring.drawio.svg" width="800"/>


# Assets used with authoring workflow
* [`PropertiesSet`](/Base/Data/PropertiesSet.cs) - contains properties components names with update strategy type (read more about [properties](https://github.com/Antoshidza/NSprites/wiki/Register-components-as-properties) and [update modes](https://github.com/Antoshidza/NSprites/wiki/Property-update-modes)).
This `ScriptableObject` used by **authoring** / **modules** / **extensions** to bake registration data. You can create it by call context menu in project `Create/NSprites/Properties Set`.
* [`SpriteAnimation`](/Animation/Data/SpriteAnimation.cs) & [`SpriteAnimationSet`](/Animation/Data/SpriteAnimationSet.cs) - scriptable objects, first contains animation data and second contains a set of `SpriteAnimation`
File renamed without changes.
4 changes: 4 additions & 0 deletions About/NSprites-Foundation-Authoring.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions About/NSprites-Foundation-Authoring.drawio.svg.meta

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

14 changes: 0 additions & 14 deletions About/RenderDataAuthoring.md

This file was deleted.

43 changes: 43 additions & 0 deletions Animation/Authoring/AnimationAuthoringModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Linq;
using Unity.Entities;
using UnityEngine;

namespace NSprites.Authoring
{
[Serializable]
public struct AnimationAuthoringModule
{
[SerializeField] public SpriteAnimationSet AnimationSet;
[SerializeField] public int InitialAnimationIndex;

public SpriteAnimation InitialAnimationData => AnimationSet.Animations.ElementAt(InitialAnimationIndex).data;

public bool IsValid()
{
if (AnimationSet == null)
{
Debug.LogWarning(new NSpritesException($"{nameof(AnimationSet)} is null"));
return false;
}

if (InitialAnimationIndex >= AnimationSet.Animations.Count)
{
Debug.LogWarning(new NSpritesException($"{nameof(InitialAnimationIndex)} can't be greater than animations count. {nameof(InitialAnimationIndex)}: {InitialAnimationIndex}, animation count: {AnimationSet.Animations.Count}"));
return false;
}

if (InitialAnimationIndex < 0)
{
Debug.LogWarning(new NSpritesException($"{nameof(InitialAnimationIndex)} can't be lower 0. Currently it is {InitialAnimationIndex}"));
return false;
}

return AnimationSet.IsValid(InitialAnimationData.SpriteSheet.texture);
}

public void Bake<TAuthoring>(Baker<TAuthoring> baker)
where TAuthoring : MonoBehaviour
=> baker.BakeAnimation(baker.GetEntity(TransformUsageFlags.None), AnimationSet, InitialAnimationIndex);
}
}
3 changes: 3 additions & 0 deletions Animation/Authoring/AnimationAuthoringModule.cs.meta

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

81 changes: 81 additions & 0 deletions Animation/Authoring/BakerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace NSprites.Authoring
{
public static partial class BakerExtensions
{
public static void BakeAnimation<T>(this Baker<T> baker, in Entity entity, SpriteAnimationSet animationSet, int initialAnimationIndex = 0)
where T : Component
{
if(baker == null)
{
Debug.LogError(new NSpritesException("Passed Baker is null"));
return;
}
if (animationSet == null)
{
Debug.LogError(new NSpritesException("Passed AnimationSet is null"));
return;
}

baker.DependsOn(animationSet);

if (animationSet == null)
return;

if (initialAnimationIndex >= animationSet.Animations.Count || initialAnimationIndex < 0)
{
Debug.LogError(new NSpritesException($"Initial animation index {initialAnimationIndex} can't be less than 0 or great/equal to animation count {animationSet.Animations.Count}"));
return;
}

#region create animation blob asset
var blobBuilder = new BlobBuilder(Allocator.Temp); //can't use `using` keyword because there is extension which use this + ref
ref var root = ref blobBuilder.ConstructRoot<BlobArray<SpriteAnimationBlobData>>();
var animations = animationSet.Animations;
var animationArray = blobBuilder.Allocate(ref root, animations.Count);

var animIndex = 0;
foreach (var anim in animations)
{
var animData = anim.data;
var animationDuration = 0f;
for (int i = 0; i < animData.FrameDurations.Length; i++)
animationDuration += animData.FrameDurations[i];

animationArray[animIndex] = new SpriteAnimationBlobData
{
ID = Animator.StringToHash(anim.name),
GridSize = animData.FrameCount,
UVAtlas = NSpritesUtils.GetTextureST(animData.SpriteSheet),
Scale2D = new float2(animData.SpriteSheet.bounds.size.x, animData.SpriteSheet.bounds.size.y),
AnimationDuration = animationDuration
// FrameDuration - allocate lately
};

var durations = blobBuilder.Allocate(ref animationArray[animIndex].FrameDurations, animData.FrameDurations.Length);
for (int di = 0; di < durations.Length; di++)
durations[di] = animData.FrameDurations[di];

animIndex++;
}

var blobAssetReference = blobBuilder.CreateBlobAssetReference<BlobArray<SpriteAnimationBlobData>>(Allocator.Persistent);
baker.AddBlobAsset(ref blobAssetReference, out _);
blobBuilder.Dispose();
#endregion

ref var initialAnim = ref blobAssetReference.Value[initialAnimationIndex];

baker.AddComponent(entity, new AnimationSetLink { value = blobAssetReference });
baker.AddComponent(entity, new AnimationIndex { value = initialAnimationIndex });
baker.AddComponent(entity, new AnimationTimer { value = initialAnim.FrameDurations[0] });
baker.AddComponent<FrameIndex>(entity);

baker.AddComponent(entity, new MainTexSTInitial { value = initialAnim.UVAtlas });
}
}
}
3 changes: 3 additions & 0 deletions Animation/Authoring/BakerExtensions.cs.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/Authoring/SpriteAnimatedRendererAuthoring.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using NSprites.Authoring;
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;

namespace NSprites
{
/// <summary>
/// Advanced <see cref="SpriteRendererAuthoring"/> which also bakes animation data as blob asset and adds animation components.
/// </summary>
public class SpriteAnimatedRendererAuthoring : MonoBehaviour
{
private class Baker : Baker<SpriteAnimatedRendererAuthoring>
{
public override void Bake(SpriteAnimatedRendererAuthoring authoring)
{
if(!authoring.IsValid)
return;

var initialAnimData = authoring.AnimationAuthoringModule.InitialAnimationData;
var initialSheetUVAtlas = (float4)NSpritesUtils.GetTextureST(initialAnimData.SpriteSheet);
var initialFrameUVAtlas = new float4(new float2(initialSheetUVAtlas.xy / initialAnimData.FrameCount), initialSheetUVAtlas.zw);
var frameSize = initialAnimData.SpriteSheet.GetSize() / initialAnimData.FrameCount;

authoring.RegisterSpriteData.Bake(this, initialAnimData.SpriteSheet.texture);
authoring.AnimationAuthoringModule.Bake(this);
authoring.RenderSettings.Bake(this, authoring, frameSize, initialFrameUVAtlas);
authoring.Sorting.Bake(this);
}
}

[SerializeField] public AnimationAuthoringModule AnimationAuthoringModule;
[SerializeField] public RegisterSpriteAuthoringModule RegisterSpriteData;
[SerializeField] public SpriteSettingsAuthoringModule RenderSettings;
[SerializeField] public SortingAuthoringModule Sorting;

protected virtual bool IsValid
{
get
{
if (!RegisterSpriteData.IsValid(out var message))
{
Debug.LogWarning($"{nameof(SpriteAnimatedRendererAuthoring)}: {message}" );
return false;
}

return AnimationAuthoringModule.IsValid();
}
}
}
}
Loading

0 comments on commit b18a84c

Please sign in to comment.