当前位置:网站首页>LevelSequence源码分析
LevelSequence源码分析
2022-08-01 07:06:00 【虚幻私塾】
优质资源分享
学习路线指引(点击解锁) | 知识定位 | 人群定位 |
---|---|---|
🧡 Python实战微信订餐小程序 🧡 | 进阶级 | 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。 |
Python量化交易实战 | 入门级 | 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统 |
前言
这篇文章主要讲的是Unreal LevelSequence RunTime的部分。即在游戏中运行Level Sequence的源码解析。(而且抛去Replicated 的Sequence,一般Sequence不会在DS上播,因为比较浪费性能,在DS上播的很少这么使用,所以本篇自动忽略。)
即,本篇主要讲的是单纯的只在客户端运行时的LevelSequence的步骤。
作用
- 我是如何分析LevelSequence 源码过程
- 本篇文章主要讲述LevelSequeence中绑定的Actor是如何在运行游戏时候被运行。
- 可以解决LevelSequence运行时的相关bug。比如楼主接触LevelSequence遇到的一个bug,就是Editor Play运行正常,但是在Shipping(正式发布)版运行,某个被绑定在Sequence中的Actor跟没绑定一样。不起作用…
问题分析
我比较喜欢直接讲述实际的案例,我们就拿一个例子来说吧,就是Sequence中我们可以很简单的控制Actor的隐藏,那么在游戏中运行时,是如何被隐藏的,隐藏步骤是啥样的,这个怎么找纳?下面就来说说具体步骤。
1.Editor Sequence中先将Actor 隐藏
- 从以下,我们知道是通过ActorHiddenInGame实现的
2.堆栈寻找
- 从上述步骤我们知道隐藏一个Actor,Sequence也是通过ActorHiddenInGame来实现的,于是就知道了
UFUNCTION(BlueprintCallable, Category="Rendering", meta=( DisplayName = "Set Actor Hidden In Game", Keywords = "Visible Hidden Show Hide" ))
virtual void SetActorHiddenInGame(bool bNewHidden);
我们是否可以直接在这个方法里直接断点一下,寻找到Sequence在Runtime将Actor隐藏的堆栈。这个办法分析源码必备之技巧。尤其对于这种一开始摸不着头脑,可以反向推理。
3.核心知识
我们知道Sequence的类型是:ALevelSequenceActor*
我们知道Sequence在Runtime怎么播放,是通过下述代码:
ALevelSequenceActor::InitializePlayer()
ALevelSequenceActor->SequencePlayer->Play();
ALevelSequenceActor->SequencePlayer->Update(DeltaSeconds);
显而易见,需要知道这个SequencePlayer,它的类型是:ULevelSequencePlayer*
那么需要了解这两个类之间的关系即可。很显然,ULevelSequencePlayer是控制ALevelSequenceActor管理播放的,比如快进,快退,都是通过ULevelSequencePlayer.
1.FMovieSceneEvaluationRange
时间驱动结构,我们知道动画的运动肯定是基于Tick,那么是如何将DeltaSeconds,传递给SequencePlayer,并且还要支持回放,返回,加速等。所以Sequence这里将时间封装了一层。因为考虑到如此多的功能,所以封装成下述时间,需要了解。
inline FFrameTime ConvertFrameTime(FFrameTime SourceTime, FFrameRate SourceRate, FFrameRate DestinationRate)
{
if (SourceRate == DestinationRate)
{
return SourceTime;
}
//We want NewTime =SourceTime * (DestinationRate/SourceRate);
//And want to limit conversions and keep int precision as much as possible
int64 NewNumerator = static_cast(DestinationRate.Numerator) * SourceRate.Denominator;
int64 NewDenominator = static\_cast(DestinationRate.Denominator) * SourceRate.Numerator;
double NewNumerator\_d = double(NewNumerator);
double NewDenominator\_d = double(NewDenominator);
//Now the IntegerPart may have a Float Part, and then the FloatPart may have an IntegerPart,
//So we add the extra Float from the IntegerPart to the FloatPart and then add back any extra Integer to IntegerPart
int64 IntegerPart = ( (int64)(SourceTime.GetFrame().Value) * NewNumerator ) / NewDenominator;
const double IntegerFloatPart = ((double(SourceTime.GetFrame().Value) * NewNumerator) / NewDenominator) - double(IntegerPart);
const double FloatPart = ((SourceTime.GetSubFrame() * NewNumerator\_d) / NewDenominator\_d) + IntegerFloatPart;
const double FloatPartFloored = FMath::FloorToDouble(FloatPart);
const int64 FloatAsInt = int64(FloatPartFloored);
IntegerPart += FloatAsInt;
double SubFrame = FloatPart - FloatPartFloored;
if (SubFrame > 0)
{
SubFrame = FMath::Min(SubFrame, 0.999999940);
}
//@TODO: FLOATPRECISION: FFrameTime needs a general once over for precision (RE: cast to ctor)
return FFrameTime( (int32)IntegerPart, (float)SubFrame);
}
FMovieSceneEvaluationRange
- 上一帧的时间点
- 下一帧的时间点
- 当前的EPlayDirection:Forwards, Backwards
- 当前速率
2.FMovieSceneContext
FMovieSceneContext(FMovieSceneEvaluationRange InRange)
: FMovieSceneEvaluationRange(InRange)
, Status(EMovieScenePlayerStatus::Stopped)
...
对上述FMovieSceneEvaluationRange,再次的封装,传到
FMovieSceneRootEvaluationTemplateInstance::Evaluate(FMovieSceneContext Context, IMovieScenePlayer& Player)
就是将IMovieScenePlayer数据 和 记录的时间结构体FMovieSceneContext,传给MovieSceneTootEvaluationTemplateInstance中。
3.FMovieSceneRootEvaluationTemplateInstance
FMovieSceneEvaluationTrack 这个数据Info是重点,就是对应到Sequence每一条轨道。。
4.FMovieSceneEvaluationGroup
上述堆栈就是找出当前所需要运行的Track List.
5.FMovieSceneExecutionTokens
这就是对实际的需要Track List进行运行。
比如,我一开始遇到的bug:在Editor运行某轨道我想隐藏某Actor是正常的,但是在Shipping正式包,运行了,某轨道运行没反应,还是没有被隐藏。于是就断点查:
最终是通过在Visibility中的Execute 发现foundboundObject一直找不到,才发现原来是Shipping会将场景中一些static打成一个包,所以通过路径查找obj一直找不到,static的被优化了。所以解决这个问题直接将static改成moveable即可。大部分项目应该都会有此优化。
3.总结
这种Sequence的源码分析,可以采用逆向分析,反向打断点找出堆栈,去除次要逻辑,某些特别难的逻辑,可以抛去,略过。
LavelSequence的源码,主要是FMovieSceneRootEvaluationTemplateInstance::EvaluateGroup 根据当前的时间点,查找出哪些轨道,然后根据每条轨道,做出具体的分别不同的事件。每条轨道的规则很容易理解。其实就只剩下根据时间点查找出轨道List,这段代码其实实在看不懂,其实也不需要太过于纠结了。
这段很难得代码就是下述:
void FMovieSceneRootEvaluationTemplateInstance::EvaluateGroup(const FMovieSceneEvaluationPtrCache& EvaluationPtrCache, const FMovieSceneEvaluationGroup& Group, const FMovieSceneContext& RootContext, IMovieScenePlayer& Player)
{
FPersistentEvaluationData PersistentDataProxy(Player);
FMovieSceneEvaluationOperand Operand;
FMovieSceneContext Context = RootContext;
FMovieSceneContext SubContext = Context;
for (const FMovieSceneEvaluationGroupLUTIndex& Index : Group.LUTIndices)
{
int32 TrackIndex = Index.LUTOffset;
// - Do the above in a lockless manner
for (; TrackIndex < Index.LUTOffset + Index.NumInitPtrs + Index.NumEvalPtrs; ++TrackIndex)
{
//略 ***
Track->Evaluate(
SegmentPtr.SegmentID,
Operand,
SubContext,
PersistentDataProxy,
ExecutionTokens);
}
}
ExecutionTokens.Apply(Context, Player);
}
}
上述代码有删改(是我看不懂的,个人感觉也没必要非要纠结,知道大概意思即可),只要知道 ExecutionTokens,和后面得 ExecutionTokens.Apply(Context, Player) 即可。
比如还有个问题,有人好奇根据时间点Sequence对应的值都不同,这个在哪判断纳,
void FMovieSceneFloatPropertySectionTemplate::Evaluate(const FMovieSceneEvaluationOperand& Operand, const FMovieSceneContext& Context, const FPersistentEvaluationData& PersistentData, FMovieSceneExecutionTokens& ExecutionTokens) const
{
float Result = 0.f;
// Only evaluate if the curve has any data
if (FloatFunction.Evaluate(Context.GetTime(), Result))
{
// Actuator type ID for this property
FMovieSceneBlendingActuatorID ActuatorTypeID = EnsureActuator<float>(ExecutionTokens.GetBlendingAccumulator());
// Add the blendable to the accumulator
const float Weight = EvaluateEasing(Context.GetTime());
ExecutionTokens.BlendToken(ActuatorTypeID, TBlendableToken<float>(Result, BlendType, Weight));
}
}
这就很简单了,显然,在各自的Template中判断。
__EOF__
- **本文作者:** [不三周助](https://blog.csdn.net/biggbang)
- 本文链接:https://blog.csdn.net/u3ddjw/p/16537189.html
- 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角**【[推荐](javascript:void(0)】**一下。
边栏推荐
- 企业员工人事管理系统(数据库课设)
- Json对象和Json字符串的区别
- 2022杭电多校第二场1011 DOS Card(线段树)
- Talk about the bugs in using for in to traverse the array in js
- Windows taskbar icon abnormal solution
- Explosive 30,000 words, the hardest core丨Mysql knowledge system, complete collection of commands [recommended collection]
- 爬虫框架 Scrapy 详解
- Three aspects of Ali: How to solve the problem of MQ message loss, duplication and backlog?
- Golang:go开启web服务
- 头歌MySQL数据库实训答案 有目录
猜你喜欢
NIO programming
Golang:go静态文件处理
I have three degrees, and I have five faces. I was "confessed" by the interviewer, and I got an offer of 33*15.
特殊的日子,值得纪念
Matlab simulink particle swarm optimization fuzzy pid control motor pump
Datagrip error "The specified database userpassword combination is rejected..."Solutions
企业员工人事管理系统(数据库课设)
插入排序—直接插入排序和希尔排序
The BP neural network based on MATLAB voice characteristic signal classification
Image lossless compression software which works: try completely free JPG - C image batch finishing compression reduces weight tools | latest JPG batch dressing tools download
随机推荐
我说过无数遍了:从来没有一种技术是为灵活组合这个目标而设计的
企业员工人事管理系统(数据库课设)
LeetCode240+312+394
Golang:go开启web服务
Guest brush SQL - 2
如何使用Photoshop合成星轨照片,夜空星轨照片后期处理方法
电磁兼容简明教程(6)测试项目
Zero-code website development tool: WordPress
上课作业(7)——#598. 取余运算(mod)
JS的运行原理
my creative day
JVM:运行时数据区-PC寄存器(程序计数器)
mysql的行锁和间隙锁
零代码网站开发利器:WordPress
支付宝如何生成及配置公钥证书
问下 mysql向pg同步多个表的话 有什么好的方案吗?
LeetCode 0149. Maximum number of points on a line
I have three degrees, and I have five faces. I was "confessed" by the interviewer, and I got an offer of 33*15.
Dell PowerEdge Server R450 RAID Configuration Steps
我的创作纪念日