使用的是Entities(0.9.1)版本,渲染sprite动画需要自定义着色器,将动画图集进行分割然后分帧显示在实体位置。
如上图,通过实体控制器创建实体,然后使用Graphics API进行渲染,通过自定义着色器,每帧进行判定切换显示UV范围,实现动画效果。
自定义Shder:支持GPU实例化
Shader "Custom/InstancedShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1,1,1,1)
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
sampler2D _MainTex;
//float4 _MainTex_UV;
// #pragma instancing_options assumeuniformscaling
// 接收实例的颜色和UV数据
UNITY_INSTANCING_BUFFER_START(Props)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _Color)
UNITY_DEFINE_INSTANCED_PROP(fixed4, _MainTex_UV)
UNITY_INSTANCING_BUFFER_END(Props)
v2f vert (appdata v)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_TRANSFER_INSTANCE_ID(v, o);
o.vertex = UnityObjectToClipPos(v.vertex);
//o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv = (v.uv * UNITY_ACCESS_INSTANCED_PROP(Props, _MainTex_UV).xy) + UNITY_ACCESS_INSTANCED_PROP(Props, _MainTex_UV).zw;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
// sample the texture
fixed4 c = tex2D(_MainTex, i.uv) * UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
return c;
}
ENDCG
}
}
}
根据自定义shader和动画图集创建个Player命名的材质球。
脚本:SpriteSheetAnimation_Data
public struct SpriteSheetAnimation_Data : IComponentData
{
public int currentFrame; // 当前帧
public int frameCount; // 帧数量
public float frameTimer; // 当前帧所花费时间
public float frameTimerMax; // 超过计时器最大值 增加帧
public Vector4 uv; //当前UV
public Matrix4x4 matrix4X4; // 矩阵换算
}
脚本:GameHandle
using UnityEngine;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Rendering;
public class GameHandler : MonoBehaviour
{
public static GameHandler Instance;
[SerializeField] public Material unitMaterial;
[SerializeField] public Mesh quadMesh;
private static EntityManager entityManager;
private void Start()
{
Instance = this;
entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
for (int i = 0; i < 10000; i++)
{
var randomPos = new float3(UnityEngine.Random.Range(0, +100), UnityEngine.Random.Range(0, +100), 0);
this.SpawnUnitEntity(randomPos);
}
}
private void SpawnUnitEntity(float3 position)
{
Entity entity = entityManager.CreateEntity(
typeof(Translation),
typeof(SpriteSheetAnimation_Data)
);
// entityManager.SetSharedComponentData<RenderMesh>(entity, new RenderMesh { mesh= quadMesh ,material=unitMaterial});
entityManager.SetComponentData<Translation>(entity, new Translation { Value = position });
entityManager.SetComponentData<SpriteSheetAnimation_Data>(entity, new SpriteSheetAnimation_Data
{
currentFrame = 0,
frameCount = 4,
frameTimer = 0f,
frameTimerMax = 0.1f,
});
}
}
脚本:SpriteSheetAnimation_JobSystem
using UnityEngine;
using Unity.Entities;
using Unity.Jobs;
using Unity.Burst;
using Unity.Mathematics;
using Unity.Transforms;
public class SpriteSheetAnimation_JobSystem : JobComponentSystem
{
[BurstCompile]
public struct AnimationJob : IJobForEach<SpriteSheetAnimation_Data, Translation>
{
public float deltaTime;
public void Execute(ref SpriteSheetAnimation_Data animData,ref Translation translation)
{
animData.frameTimer += deltaTime;
while (animData.frameTimer >= animData.frameTimerMax)
{
animData.frameTimer -= animData.frameTimerMax;
animData.currentFrame = (animData.currentFrame + 1) % animData.frameCount;
float uvWidth = 1f / animData.frameCount;
float uvHeight = 1;
float uvOffsetX = uvWidth * animData.currentFrame;
float uvOffsetY = 0;
animData.uv = new Vector4(uvWidth, uvHeight, uvOffsetX, uvOffsetY);
float3 position = translation.Value;
position.z = position.y * 0.1f;
// 处理画面排序顺序
animData.matrix4X4 = Matrix4x4.TRS(position, Quaternion.identity, Vector3.one);
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
AnimationJob animationJob = new AnimationJob
{
deltaTime = Time.DeltaTime
};
var jobHandler = animationJob.Schedule(this, inputDeps);
return jobHandler;
}
}
脚本:SpriteRenderMeshSystem
注意: UpdateAfter(typeof(SpriteSheetAnimation_JobSystem),必须对当前应该显示帧图片计算完再进行渲染
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
[UpdateAfter(typeof(SpriteSheetAnimation_JobSystem))]
public class SpriteRenderMeshSystem : ComponentSystem
{
protected override void OnUpdate()
{
//通过材质属性块 向 shader 发送数据
MaterialPropertyBlock materialPropertyBlock = new MaterialPropertyBlock();
Vector4[] uv = new Vector4[1];
Entities.ForEach((ref Translation translation,ref SpriteSheetAnimation_Data animData) =>
{
uv[0] = animData.uv;
// uv从左下角进行绘制
materialPropertyBlock.SetVectorArray("_MainTex_UV", uv);
Graphics.DrawMesh(
GameHandler.Instance.quadMesh,
animData.matrix4X4,
GameHandler.Instance.unitMaterial,
0,
Camera.main,
0,
materialPropertyBlock
);
});
}
}
如下图,创建了1万个实体进行渲染动画。