AR示教遇到的问题
前提:
工作中经常遇到示教应用开发,特点是功能简单但需要反复与该领域专业团队反复打磨示教内容
1
.
问题
1
:反复修改个模块,用状态机与协同函数往往牵一发动全身,浪费时间重复工作
再在公司混日子倒是一个不错的解决工作量不准的工作
解决办法
-
解耦:将音频、视频、模型、动画代码 解耦 ,让各个模块的修改不在影响其他模块。
-
替换:将老式繁琐的状态机,事件处理器与不便于修改的携程函数 替换为Timeline系统
步骤:
-
开发TimeLine自定义轨道——3D示教播放器。
-
开发UI系统实现对3D示教播放器的控制
假设需求
将一个盒子依次向左右前后运动内容,制作成可上一步下一步,分段连续切换,重复当前步骤,重新开始暂停。
如何做
-
制作方块前后左右的动画
-
制作自定义Timeline轨道—— Section:套餐 Default Playables插件可以方便创建,大智有开过大话TimeLine的课
-
根据需要通过MarkClip记录每段起始点。
-
添加UI。
-
写每个UI需要响应的逻辑
-
大功告成,随意推荐装饰
实现时需要掌握的知识点
制作方块前后左右的动画
制作自定义Timeline轨道——
Section:套餐
Default Playables
——扩展插件可方便的生成TimeLine自定义轨道所需的基础脚本
-
定义自定义轨道
-
每次定义自定义轨道都要创建很多脚本。一个快捷的方法,添加 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的文本内容就可以了
还可以做什么
-
舞蹈分步学习
-
检修分步操作等等吧
待解决问题
是一种思路代码还需优化,
文章介绍了在AR示教应用开发中遇到的问题,如反复修改导致的工作效率低下。为了解决这个问题,提出了使用Timeline系统来替代传统状态机和复杂函数,通过解耦音频、视频、模型和动画代码,实现各模块独立修改。详细步骤包括开发自定义Timeline轨道、UI系统以及相应逻辑,特别提到了如何创建方块移动的动画和自定义Track、Clip、MixerBehaviour等组件。文章还提供了实现所需掌握的知识点和具体代码示例,最后提到这种方法可以应用于更多场景,如舞蹈教学和设备检修等。

301

被折叠的 条评论
为什么被折叠?



