笔者觉得一个好缓动效果的使用,往往会让人觉得这个游戏对象瞬间活泼起来就像有了生命一般,Easing 就是这般神奇。在本文,笔者带大家写一个具有缓动效果的 PingPong 动效。
EasingCore
巧妇难为无米之炊,我们要先整理一些 Ease 算法先,没想人家早早的就整理了一份放到 GitHub 上咯~(其实 Itween里面也有一套 Ease 算法哈)
EasingCore-GitHub
怎么用 EasingCore:
怎么用EasingCore,超简单:
float value = EasingFunction.Get(easetype).Invoke(progress);
这样获得的值就是被算法修正的值啦。
PingPong
用上了这个EasingCore 的 PingPong动效模块,:
using EasingCore;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using zFrame.Extend;
using Ease = EasingCore.Ease;
public class PingPong : MonoBehaviour
{
#region SelfField
float progress = 0;
bool addition = true;
private CoroutineHandler coroutine;
#endregion
#region Configration Parameter
private event Action<float> action;
private Ease ease = Ease.Linear;
private float delay = 0;
#endregion
#region 单例
static PingPong driver;
static PingPong Driver
{
get
{
if (null == driver)
{
GameObject go = new GameObject("[PingPongDriver]");
driver = go.AddComponent<PingPong>();
GameObject.DontDestroyOnLoad(go);
go.hideFlags = HideFlags.HideAndDontSave;
}
return driver;
}
}
private void Awake()
{
driver = this;
}
#endregion
#region PingPongBehaviours
public static PingPong OnUpdate(Action<float> action)
{
if (null == Driver.action)
{
Driver.action = action;
}
else
{
List<Delegate> list = new List<Delegate>(Driver.action.GetInvocationList());
if (!list.Contains(action))
{
Driver.action += action;
}
}
return Driver;
}
public PingPong SetEase(Ease ease)
{
this.ease = ease;
return Driver;
}
public PingPong SetDelay(float delay)
{
this.delay = delay;
return Driver;
}
public void Play()
{
if (null == coroutine)
{
coroutine = Handler().Start();
}
}
private void Finish()
{
coroutine.Stop();
coroutine = null;
action = null;
}
public static void Stop()
{
Driver.Finish();
}
#endregion
IEnumerator Handler()
{
yield return new WaitForSeconds(delay);
while (true)
{
progress += (addition ? 1 : -1) * Time.deltaTime ;
progress = Mathf.Clamp01(progress);
addition = progress == 1 ? false : progress == 0 ? true : addition;
float value = EasingFunction.Get(ease).Invoke(progress);
action?.Invoke(value);
yield return 0;
}
}
}
这个实现比较无聊,仅仅是做的好玩,所以居然使用了单例,哈哈。
然后,使用了链式编程风格以配置参数:
怎么使用:
演示代码,应该没有什么复杂需要特别注解的地方了
using UnityEngine;
public class TestBreath : MonoBehaviour
{
public EasingCore.Ease ease = EasingCore.Ease.InExpo;
public Transform endPoint;
public float delay = 2;
public float factor = 0.5f;
Vector3 v3; //缓存起点
Vector3 ve; //终点
void Start()
{
v3 = transform.position;
Play();
}
[EditorButton]
public void Restart()
{
PingPong.Stop();
transform.position = v3;
Play();
}
private void Play()
{
ve = endPoint.position;
Vector3 dr = ve - v3; //获得起点指向终点的向量
float dis = Vector3.Distance(v3, ve); //获得向量距离,
PingPong.OnUpdate(v =>
{
Vector3? pos = v3 + dr.normalized * dis * v; // 使用可空值接这个数据
transform.position = pos.HasValue ? pos.Value : transform.position;
}).SetEase(ease) //设置 Ease 模式
.SetDelay(delay) //设置延时多久后播放动画
.SetFactor(factor) //等效于设置动画时长
.Play();
}
}
动画演示:
Pingpong + EasingCore总共有30个缓动,动画中几个动效为随机测试。
扩展阅读:
Unity EditorButton - GitHub
[Unity3D] 协程管理CoroutineManager - 简书
[Unity 3d] 如何优雅的写一个PingPong效果 - 简书
网友评论