AR示教遇到的问题
前提:
工作中经常遇到示教应用开发,特点是功能简单但需要反复与该领域专业团队反复打磨示教内容
1. 问题1:反复修改个模块,用状态机与协同函数往往牵一发动全身,浪费时间重复工作
再在公司混日子倒是一个不错的解决工作量不准的工作
解决办法
  1. 解耦:将音频、视频、模型、动画代码解耦,让各个模块的修改不在影响其他模块。
  2. 替换:将老式繁琐的状态机,事件处理器与不便于修改的携程函数替换为Timeline系统
步骤:
  1. 开发TimeLine自定义轨道——3D示教播放器。
  2. 开发UI系统实现对3D示教播放器的控制
假设需求
将一个盒子依次向左右前后运动内容,制作成可上一步下一步,分段连续切换,重复当前步骤,重新开始暂停。
如何做
  1. 制作方块前后左右的动画
  2. 制作自定义Timeline轨道——Section:套餐 Default Playables插件可以方便创建,大智有开过大话TimeLine的课
  3. 根据需要通过MarkClip记录每段起始点。
  4. 添加UI。
  5. 写每个UI需要响应的逻辑
  6. 大功告成,随意推荐装饰
实现时需要掌握的知识点
制作方块前后左右的动画
制作自定义Timeline轨道——Section:套餐
Default Playables——扩展插件可方便的生成TimeLine自定义轨道所需的基础脚本
  1. 定义自定义轨道
  2. 每次定义自定义轨道都要创建很多脚本。一个快捷的方法,添加DefaultPlayables插件,再在Window->Timeline Playable Wizard里面创建就可以了。 很简单,只需要输入轨道名字,点击创建。它就会为我们自动创建自定义轨道所需要的脚本,放在一个文件夹里
Behaviour :里面主要是Clip里的逻辑
using System; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; [Serializable] //用来将变量呈现在编辑器里 public class TestTrackBehaviour : PlayableBehaviour { public override void OnPlayableCreate (Playable playable) { } }
Clip :是一个我们可以用来放在轨道上的一个个剪辑片段
using System; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; [Serializable] public class TestTrackClip : PlayableAsset, ITimelineClipAsset //这个脚本的功能是让一个我们可以用来放在轨道上的一个个行为片段 { public TestTrackBehaviour template = new TestTrackBehaviour (); //在片段类里声明一个行为样板 public ClipCaps clipCaps { get { return ClipCaps.None; } } public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) { var playable = ScriptPlayable<TestTrackBehaviour>.Create (graph, template); TestTrackBehaviour clone = playable.GetBehaviour (); return playable; } }
MixerBehaviour :用来处理轨道中两个段落混合的关系,可用此脚本处理其权重,此脚本通常是和Track脚本关联的,他可以控制整个Track上的事情
using System; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; public class TestTrackMixerBehaviour : PlayableBehaviour { // NOTE: This function is called at runtime and edit time. Keep that in mind when setting the values of properties. public override void ProcessFrame(Playable playable, FrameData info, object playerData) { int inputCount = playable.GetInputCount (); for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); //这里我们能获取到每一个input的权重, //如果当前的权重大于0,说明当前Clip处于一个在执行的状态,我们就可以根据这个属性来处理 ScriptPlayable<TestTrackBehaviour> inputPlayable = (ScriptPlayable<TestTrackBehaviour>)playable.GetInput(i); TestTrackBehaviour input = inputPlayable.GetBehaviour (); // Use the above variables to process each frame of this playable. } } }
Track :新定义一个轨道 ——Section轨道
using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; [TrackColor(0.855f, 0.8623f, 0.87f)] //轨道颜色 [TrackClipType(typeof(TestTrackClip))] public class TestTrackTrack : TrackAsset //功能:新定义一个轨道 { public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) //创建轨迹混合器 { return ScriptPlayable<TestTrackMixerBehaviour>.Create (graph, inputCount); } }
根据需要通过MarkClip记录每段起始点在Timeline中的位置,便于我们跳转。
SectionBehaviour
[Serializable] //用来将变量呈现在编辑器里 public class SectionBehaviour : PlayableBehaviour { public MarkerType Type; //1.定义一个标记点类型,便于Timeline在这些标记点之间跳转 public int SectionNumber; //用来区分每个Mark是第几部 public override void OnPlayableCreate (Playable playable) {} } public enum MarkerType //2.为其标记点创建一个枚举,用来存放标记点 { Mark, }
SectionMixerBehaviour
public class SectionMixerBehaviour : PlayableBehaviour { public Dictionary<int, double> markerClips = new Dictionary<int, double>(); //定义此字典,第一个是key记录第几部。第二个double是Mark开始的时间,记录下来,因为我们之后要跳到这里 internal static int newSectionNumber = 1; //实时更新,走过第几部就记到第几部的数字 // NOTE: This function is called at runtime and edit time. Keep that in mind when setting the values of properties. public override void ProcessFrame(Playable playable, FrameData info, object playerData) { int inputCount = playable.GetInputCount(); var director = playable.GetGraph().GetResolver() as PlayableDirector; //这一步相当获得了一个playableDirector Timelint的控制经理 for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); //这里我们能获取到每一个input的权重, //如果当前的权重大于0,说明当前Clip处于一个在执行的状态,我们就可以根据这个属性来处理 ScriptPlayable<SectionBehaviour> inputPlayable = (ScriptPlayable<SectionBehaviour>)playable.GetInput(i); SectionBehaviour input = inputPlayable.GetBehaviour(); // Use the above variables to process each frame of this playable. //······ } } }
SectionTrack
public class SectionTrack : TrackAsset //功能:新定义一个轨道 { public static int clipnumb; //用于动态获取(当前动画段落数字) public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) //创建轨迹混合器 { var scriptPlayable = ScriptPlayable<SectionMixerBehaviour>.Create(graph, inputCount); var b = scriptPlayable.GetBehaviour(); //我们先来获取到MixerBehaviour foreach (var c in GetClips()) { SectionClip clip = c.asset as SectionClip; //将其转换为同类型的 if (clip.template.Type == MarkerType.Mark) { b.markerClips.Add(clip.template.SectionNumber, c.start); //将片段的(动画段落数字)与片段在轨道上的开始时间 以键值对的形式记录在MixerBehavior字典里 if (clip.template.SectionNumber > clipnumb) clipnumb = clip.template.SectionNumber; } } return scriptPlayable; ///在轨道这个脚本中,我们将需要的Clip的相关信息记录在MixerBehavior的字典里。 } }
添加UI
细节暂时不展示了 比较丑陋,等以后用GPT帮我优化一下在亮出来

展示

大功告成,随意推荐装饰
这时候在有需求就可以直接在时间轴上直接加了
音频什么的添加新的人物动画都可以在轨道上加。
如UI这四个轨道或多个也可以通过写一个自定义轨道,每个Clip里改一下Text的文本内容就可以了
还可以做什么
  1. 舞蹈分步学习
  2. 检修分步操作等等吧
待解决问题
是一种思路代码还需优化,
Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐