最近要做一个动画效果,通过简单的有限状态机可以更好的解决依次播放动画、动画跳过等问题,做了一个简单的表现。
FGUI
动画效果通过FGUI的动效实现;
FGUI代码生成
在发布设置中,打开“允许发布代码”
但默认生成的代码,并不符合使用习惯,也无法适应自己的ui框架,所以需要在plugIn文件夹中添加自定义的lua文件,根据自己的需要修改代码文件;
导入lua文件后,需要重启FGUI项目才能生效;
FGUI动效
动画效果:从中间向四周依次分发卡牌,再依次收回;
根据效果可知,每个卡牌的终点位置是根据顺序变化的,所以不能写死,因此需要在关键帧上添加标签,用于在运行过程中修改参数;
读取FGUI文件
加载对应的byte文件:
GRoot.inst.SetContentScaleFactor(800, 600);
UIPackage.AddPackage("UI/Test");
GComponent component = UIPackage.CreateObject("Test", "TestLayer").asCom;
GRoot.inst.AddChild(component);
执行对应类的方法:
component.asCom.Logic = new UI_TestLayer();
m_TestLayer = (UI_TestLayer)component.asCom.Logic;
m_TestLayer.SetSelfComponent(component);
m_TestLayer.Init();
m_TestLayer.Open();
有限状态机
using System.Collections.Generic;
using FairyGUI;
namespace ClientGame.UI
{
public partial class UI_TestLayer
{
private const int STATE_NONE = 0;
private const int STATE_WAITING = 1;
private const int STATE_FORWARD = 2;
private const int STATE_BACK = 3;
private const int STATE_STOP = 4;
private int m_state = -1;
private bool m_isSkip;
private int m_CurrentImageIndex = -1;
private List<UI_TestItem> m_TestItems;
protected override void OnOpen(object data)
{
m_state = STATE_NONE;
m_TestItems = new List<UI_TestItem>()
{
m_Item1, m_Item2, m_Item3, m_Item4, m_Item5, m_Item6, m_Item7, m_Item8,
};
m_BtnStart.onClick.Set(() =>
{
m_state = STATE_FORWARD;
});
m_BtnSkip.onClick.Set(OnSkipClick);
SetName();
}
private void SetName()
{
int index = 0;
foreach (var item in m_TestItems)
{
item.UpdateView(index++);
}
}
public void Update()
{
switch (m_state)
{
case STATE_FORWARD:
OnForward();
break;
case STATE_BACK:
OnBack();
break;
case STATE_STOP:
OnStop();
break;
}
}
private void OnForward()
{
if (m_isSkip)
{
foreach (var item in m_TestItems)
{
item.StopForwardAnim();
}
m_state = STATE_BACK;
m_isSkip = false;//每次只跳过一个
m_CurrentImageIndex = -1;
}
else
{
//结束上一个
if (0 <= m_CurrentImageIndex && m_CurrentImageIndex < m_TestItems.Count)
{
m_TestItems[m_CurrentImageIndex].StopForwardAnim();
}
m_CurrentImageIndex++;
//播放下一个
if (0 <= m_CurrentImageIndex && m_CurrentImageIndex < m_TestItems.Count)
{
m_state = STATE_WAITING;
m_TestItems[m_CurrentImageIndex].ForwardAnim(() => m_state = STATE_FORWARD);
}
else
{
m_CurrentImageIndex = -1;
m_state = STATE_BACK;
}
}
}
private void OnBack()
{
if (m_isSkip)
{
foreach (var item in m_TestItems)
{
item.StopBackwardAnim();
}
m_state = STATE_STOP;
m_isSkip = false;//每次只跳过一个
}
else
{
//结束上一个
if (0 <= m_CurrentImageIndex && m_CurrentImageIndex < m_TestItems.Count)
{
m_TestItems[m_CurrentImageIndex].StopBackwardAnim();
}
m_CurrentImageIndex++;
//播放下一个
if (0 <= m_CurrentImageIndex && m_CurrentImageIndex < m_TestItems.Count)
{
m_state = STATE_WAITING;
m_TestItems[m_CurrentImageIndex].BackwardAnim(() => m_state = STATE_BACK);
}
else
{
m_CurrentImageIndex = -1;
m_state = STATE_STOP;
}
}
}
private void OnStop()
{
m_state = STATE_NONE;
m_CurrentImageIndex = -1;
}
private void OnSkipClick(EventContext context)
{
m_isSkip = true;
}
}
}
动效控制:
using System;
using UnityEngine;
namespace ClientGame.UI
{
public partial class UI_TestItem
{
private int xPostion;
private int yPostion;
public void UpdateView(int index)
{
UI_TestImage image = m_Image;
image.UpdateView(index);
int row = index % 4;
int col = index / 4;
xPostion = row * 150;
yPostion = col * 500;
}
public void ForwardAnim(Action callback)
{
m_Anim.SetValue("end", xPostion, yPostion);
m_Anim.SetHook("end", () =>
{
callback?.Invoke();
});
m_Anim.timeScale = 1;
m_Anim.Play();
}
public void StopForwardAnim()
{
m_Anim.Stop();
m_Image.Self.position=new Vector2(xPostion, yPostion);
}
public void BackwardAnim(Action callback)
{
m_Anim.SetValue("start", xPostion, yPostion);
m_Anim.SetValue("end", 325, 250);
m_Anim.SetHook("end", () =>
{
callback?.Invoke();
});
m_Anim.timeScale = 2;
m_Anim.Play();
}
public void StopBackwardAnim()
{
m_Anim.Stop();
m_Anim.timeScale = 1;
m_Image.Self.position = new Vector2(325, 250);
}
}
}
最后效果
正常播放,不跳过:
![请添加图片描述](https://i-blog.csdnimg.cn/direct/8c52f1187e2941dc941a178e1d4bacb7.gif
点击跳过: