├── DataNormalization ├── pictures │ ├── UE4标准化.png │ ├── UE5标准化.png │ └── Z-score Normalization.png └── 游戏开发中的DataNormalization.md ├── README.md ├── UE ├── 【UE4】GAS技能系统介绍 │ ├── 【UE4】GAS技能系统介绍(一).md │ ├── 【UE4】GAS技能系统介绍(一).pdf │ └── 【UE4】GAS技能系统介绍(一).png ├── 【UE4】一图看懂移动同步(上) │ ├── input_axis.png │ ├── input_bind.png │ ├── input_function.png │ ├── start_ds.png │ ├── start_ds_show.png │ ├── 【UE4】一图看懂移动同步(上).md │ ├── 【UE4】一图看懂移动同步(上).pdf │ ├── 【UE4】一图看懂移动同步(上).png │ └── 【UE4】一图看懂移动同步(上).vsdx ├── 【UE4】一图看懂移动同步(下) │ ├── 【UE4】一图看懂移动同步(下).md │ ├── 【UE4】一图看懂移动同步(下).pdf │ ├── 【UE4】一图看懂移动同步(下).png │ └── 【UE4】一图看懂移动同步(下).vsdx ├── 【UE4】图解动画系统源码 │ ├── pictures │ │ ├── 3-ParallelGameThread.png │ │ ├── 3-PreUpdateAnimation.png │ │ ├── 3-SwapContext.png │ │ ├── 3-TickAnimation.png │ │ ├── 3-UpdateMontage.png │ │ ├── 4-Collision.png │ │ ├── 4-Mesh.png │ │ ├── 4-MeshComponent坐标更新.png │ │ ├── 4-MeshComponent层次结构.png │ │ ├── 4-MeshTick.png │ │ ├── 4-Skeleton.png │ │ ├── 4-刚性层阶动画.png │ │ └── 【UE4】图解动画系统源码.png │ ├── 【UE4】图解动画系统源码.md │ └── 【UE4】图解动画系统源码.pdf ├── 【UE5】基于物理的角色动画(基础篇) │ ├── pictures │ │ ├── AnimDynamics-1.png │ │ ├── Animation-1.gif │ │ ├── AnimationUpdate.png │ │ ├── Collision.png │ │ ├── Constraint.png │ │ ├── PA-1.png │ │ ├── PA-2.png │ │ ├── PA-3.png │ │ ├── PA-4.png │ │ ├── PA-5.png │ │ ├── PhysicalAnimation.drawio │ │ ├── PhysicsAnimationUpdate.png │ │ ├── PhysicsAsset-1.png │ │ ├── PhysicsAsset-2.png │ │ ├── RigidBody-1.png │ │ ├── Setting-1.png │ │ ├── SkeletalMesh-1.png │ │ ├── UML-1.png │ │ └── 人类一败涂地.jpg │ └── 【UE5】基于物理的角色动画(基础篇).md ├── 【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent │ ├── pictures │ │ ├── Damping.png │ │ ├── Hit1.png │ │ ├── Hit2.png │ │ └── Physics.drawio │ └── 【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent.md ├── 【UE5】物理系统源码分析 │ ├── Physics.drawio │ ├── pictures │ │ ├── AABBTree.png │ │ ├── ActorMove.png │ │ ├── BVH.png │ │ ├── BoundingVolume.png │ │ ├── BroadPhase.png │ │ ├── CoreFunction.png │ │ ├── CreateConstraintGraph.png │ │ ├── DestroyPhysicsState.png │ │ ├── DuringSim.png │ │ ├── FParallelBlendPhysicsCompletionTask.png │ │ ├── FParallelBlendPhysicsTask.png │ │ ├── MidPhase.png │ │ ├── PushPhysicsData.png │ │ ├── RegisterObject.png │ │ ├── SkeletalMeshAddToPhysicsScene.png │ │ ├── SpatialAccelerationCollection.png │ │ ├── StartSim.png │ │ ├── StaticMeshAddToPhysicsScene.png │ │ ├── TickFunction.png │ │ ├── TickGroupShow.png │ │ ├── UpdateAABB.png │ │ ├── WorldTick.png │ │ ├── 创建物理场景.png │ │ ├── 创建物理状态.png │ │ └── 加入物理场景接口.png │ └── 【UE5】物理系统源码分析.md └── 图解Chaos源码.pdf ├── 【游戏开发】动画技术文章合集.md ├── 服务器 └── 【记录】游戏服务器开发知识 │ ├── book.jpg │ ├── run.jpeg │ └── 【记录】游戏服务器开发知识.md └── 逆向运动学(IK)详解 ├── pictures ├── CCDIK一次推演步骤.png ├── other.png ├── 可达与不可达.png ├── 解的个数.png └── 雅可比矩阵-线性模拟.png └── 【游戏开发】逆向运动学(IK)详解.md /DataNormalization/pictures/UE4标准化.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/DataNormalization/pictures/UE4标准化.png -------------------------------------------------------------------------------- /DataNormalization/pictures/UE5标准化.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/DataNormalization/pictures/UE5标准化.png -------------------------------------------------------------------------------- /DataNormalization/pictures/Z-score Normalization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/DataNormalization/pictures/Z-score Normalization.png -------------------------------------------------------------------------------- /DataNormalization/游戏开发中的DataNormalization.md: -------------------------------------------------------------------------------- 1 | 在看Daniel大佬关于MotionMatching的文章时,看到了一篇关于神经网络的文章,上面提到了Data Normalization,原文链接在这里:https://theorangeduck.com/page/neural-network-not-working,然后就利用业余时间学习整理了下~ 2 | 3 | # 1 概念 4 | 5 | 由于归一化和标准化这两个中文概念在网络上争议很大,所以下面的内容不再细分,全部使用Data Normalization来代替。可移步[标准化和归一化什么区别?](https://www.zhihu.com/question/20467170) 6 | 7 | Feature scaling(特征缩放)是一种用于归一化数据的自变量或特征范围的方法。在数据处理中,它也被称为Data Normalization,通常在数据预处理步骤中执行。 8 | 9 | 使用Data Normalization的原因:由于原始数据的取值范围变化很大,在一些机器学习算法中,如果不进行Data Normalization,目标将无法正常工作。在统计中中,样本数据都是多个维度的,一个样本是用多个特征来表征的,这些特征的单位也不一致,如果不进行Normalization,那么这些特征对最终结果的影响很可能是不一样的。而使用Data Normalization后,每个特征具有相同的尺度,也对最终结果的贡献大约成比例。 10 | 11 | 方法: 12 | 13 | 1、**Rescaling (min-max normalization)** 14 | 15 | - 最简单的方法,把特征数据缩放在指定范围内。 16 | 17 | - [0,1]缩放的公式为:$x'=\frac{x-min(x)}{max(x)-min(x)}$(x是原始值,x'是Normalization后的值) 18 | 19 | - [a,b]缩放的公式为:$x'=a+\frac{(x-min(x))(b-a)}{max(x)-min(x)}$(x是原始值,x'是Normalization后的值,a为最小值,b为最大值) 20 | 21 | 2、**Mean normalization** 22 | 23 | - 公式为:$x'=\frac{x-average(x)}{max(x)-min(x)}$(x是原始值,x'是Normalization后的值) 24 | 25 | 3、**Standardization (Z-score Normalization)** 26 | 27 | - 在机器学习中,可以处理各种类型的数据,这些数据可以包含多个维度。特征标准化使数据中每个特征的值具有均值为0和标准差为1的服从标准正态分布的特点。 28 | 29 | - 公式为:$x'=\frac{x-\bar{x}}{\sigma}$(x是原始值,x'是Normalization后的值, $\overline{x}$ = average(x)是特征向量的均值,分母是标准差) 30 | 31 | - 更适用于正态分布的数据: 32 | 33 | ![](pictures\Z-score Normalization.png) 34 | 35 | 4、**Scaling to unit length** 36 | 37 | - 缩放特征向量的分量,使完整向量的长度为1。 38 | 39 | - 公式为:$x'=\frac{x}{||x||}\\$ 40 | 41 | # 2 基础数学知识 42 | 43 | 表征数据的离散程度:离差平方和、(总体)方差和(总体)标准差,它们的共同性质:最小值为0。数据的离散程度越大,它们的值也越大。 44 | 45 | 1、**平均数**:严格来说,应该称为算数平均数或均值,其他还有几何平均、调和平均等平均数。 46 | 47 | 2、**离差平方和**: 48 | 49 | - 离差:距离平均值的远近状况。 50 | 51 | - 离差平方和公式:$SS=\Sigma(x_{i} - \bar{x})^{2}$ 52 | 53 | - 离差平方和缺点:数据的个数越多时,它的值也会变得越大,所以实际很少使用它作为表征离散程度的指标。 54 | 55 | 3、**(总体)方差**: 56 | 57 | - 解决了离差平方和的缺点。 58 | 59 | - 公式:$\sigma^{2}=\frac{\Sigma(X-\mu)^{2}}{N}\\$( $\sigma^{2}$ 为总体方差,X为变量, $\mu$为总体均值,N为总体倒数。) 60 | 61 | 4、**标准差**:表现离散程度。表示一组数据平均离散程度的指标。标准差最小值为0(完全不离散,全为相同数据),而数据的离散程度越大,标准差的值就越大。并且消除了负偏差。 62 | 63 | - 总体标准差:从本质上讲与(总体)方差是相同的。总体是真正想调查的对象的集合。$SD=\sqrt{\frac{1}{N}\sum_{i=1}^N(x_{i}-\mu)^{2}}$($\mu$为平均值$\bar{x}$) 64 | 65 | - 样本标准差:样本是从总体中被选出来的人所形成的集合,n < N。$s=\sqrt{\frac{1}{N-1}\sum_{i=1}^N(x_{i}-\bar{x})^{2}}\\$ 66 | 67 | # 3 UE4/UE5应用 68 | 69 | 在UE4/UE5引擎中,我们常用的标准化方式是min-max normalization和Z-score Normalization。 70 | 71 | 1、在UE4中: 72 | 73 | ![](pictures\UE4标准化.png) 74 | 75 | 2、在UE5中,Pose Search插件使用了Z-score Normalization: 76 | 77 | (PoseSearch插件可用于MotionMatching功能。在MotionMatching中,要从Pose数据库中取出最合适的Pose,所以会有很多特征来进行查找对比,这些特征也都影响最终的运行效果,比如trajectory的位置、Pose的速度等,而每个特征带来的Cost范围可能会不一样,也就是度量不同,如果不对特征数据进行Normalization,后期将要反复调整权重,最终效果可能也不好。所以在MotionMatching中,Normalization是很重要的一步。) 78 | 79 | ![](pictures\UE5标准化.png) 80 | 81 | 这个函数使用矩阵进行计算,是一个不错的方法。 82 | 83 | 与Z-score算法不一样的是,这里没有使用标准方差,而是使用的平均绝对偏差。关于原因,注释也解释了一下,并且放了paper的名字。注释:标准偏差更强调异常值,因为离差平方会以指数方式增加方差,而不是以相加的方式增加方差,而平方和的平方根不会增加方差消除这种偏见。 84 | 85 | paper内容:介绍了标准方差(SD)和平均绝对偏差(MD)的概念,从历史原因分析为什么大多数采用SD而不是MD,为什么采用MD,最后总结概括。也就是说,在某些情况下,MD更优于SD。MD在数据包含微小误差或者不能形成完全的正态分布时效率更好,而且它还更容易使用,更能容忍极端值。SD优越性在于随机选取样本,更依赖于正态分布的数据,SD更强调较大的偏差,SD和MD的相对效率取决于是否存在在观察中没有任何错误。但是正态分布具有数据污染小的相对优势。所以MD实际上在观察和测量中会出现小错误的情况下比SD更有效率(比SD的效率高出一倍以上)。 86 | 87 | 3、其他:这样当然要提一下Daniel Holden大佬的MotionMatching代码了,也做了Normalization。https://github.com/orangeduck/Motion-Matching 88 | 89 | 在database.h文件中,这个函数使用的是标准的Z-score Normalization,有兴趣的同学可以去看看~ 90 | 91 | ```c++ 92 | void normalize_feature( 93 | slice2d features, 94 | slice1d features_offset, 95 | slice1d features_scale, 96 | const int offset, 97 | const int size, 98 | const float weight = 1.0f) 99 | { 100 | // First compute what is essentially the mean 101 | // value for each feature dimension 102 | for (int j = 0; j < size; j++) 103 | { 104 | features_offset(offset + j) = 0.0f; 105 | } 106 | 107 | for (int i = 0; i < features.rows; i++) 108 | { 109 | for (int j = 0; j < size; j++) 110 | { 111 | features_offset(offset + j) += features(i, offset + j) / features.rows; 112 | } 113 | } 114 | 115 | // Now compute the variance of each feature dimension 116 | array1d vars(size); 117 | vars.zero(); 118 | 119 | for (int i = 0; i < features.rows; i++) 120 | { 121 | for (int j = 0; j < size; j++) 122 | { 123 | vars(j) += squaref(features(i, offset + j) - features_offset(offset + j)) / features.rows; 124 | } 125 | } 126 | 127 | // We compute the overall std of the feature as the average 128 | // std across all dimensions 129 | float std = 0.0f; 130 | for (int j = 0; j < size; j++) 131 | { 132 | std += sqrtf(vars(j)) / size; 133 | } 134 | 135 | // Features with no variation can have zero std which is 136 | // almost always a bug. 137 | assert(std > 0.0); 138 | 139 | // The scale of a feature is just the std divided by the weight 140 | for (int j = 0; j < size; j++) 141 | { 142 | features_scale(offset + j) = std / weight; 143 | } 144 | 145 | // Using the offset and scale we can then normalize the features 146 | for (int i = 0; i < features.rows; i++) 147 | { 148 | for (int j = 0; j < size; j++) 149 | { 150 | features(i, offset + j) = (features(i, offset + j) - features_offset(offset + j)) / features_scale(offset + j); 151 | } 152 | } 153 | } 154 | ``` 155 | 156 | # 4 参考 157 | 158 | Feature scaling:https://en.wikipedia.org/wiki/Feature_scaling 159 | 160 | Standard score:https://en.wikipedia.org/wiki/Standard_score 161 | 162 | Normalization (statistics):https://en.wikipedia.org/wiki/Normalization_(statistics) 163 | 164 | Motion-Matching:https://github.com/orangeduck/Motion-Matching 165 | 166 | 数据什么时候需要做中心化和标准化处理?:https://www.zhihu.com/question/37069477 167 | 168 | Revisiting a 90-Year-Old Debate: The Advantages of the Mean Deviation:https://emilkirkegaard.dk/en/wp-content/uploads/Revisiting-a-90-year-old-debate-the-advantages-of-the-mean-deviation.pdf 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 学而不思则罔,思而不学则殆。 2 | 3 | 游戏开发笔记~ 4 | 5 | 知乎:https://www.zhihu.com/people/xia-wen-09 6 | -------------------------------------------------------------------------------- /UE/【UE4】GAS技能系统介绍/【UE4】GAS技能系统介绍(一).md: -------------------------------------------------------------------------------- 1 | # 1 介绍 2 | 3 | 其实介绍技能系统的文章、视频已经很多了,但是由于之前学习过一段时间,所以还是想提笔总结记录一下。 4 | 5 | GAS(GameplayAbilitySystem)是虚幻自带的一个插件。该插件易扩展、支持网络复制和预测等。 6 | 7 | 本文将介绍GAS的核心概念以及源码触发调用同步流程,重点是第4章节( •̀ ω •́ )✧。 8 | 9 | 使用的虚幻引擎版本为4.26。 10 | 11 | (由于是之前学习的,所以有些内容可能来自于网络,但是我又忘记出处,侵删~) 12 | 13 | # 2 学习资源 14 | 15 | 官方文档:https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/GameplayAbilitySystem/ 16 | 17 | GAS文档:https://github.com/tranek/GASDocumentation 18 | 19 | - 最详细的文档了,还有项目进行演示查看。 20 | 21 | Gameplay Abilities插件入门教程:https://www.cnblogs.com/JackSamuel/tag/GameplayAbility%E6%8F%92%E4%BB%B6/ 22 | 23 | - 该教程介绍了如何在编辑器中使用Gameplay Abilities插件,如何编写技能蓝图,如何使用技能标签,如何触发技能,如何配置GameplayEffect等。 24 | 25 | GAS插件介绍(入门篇) | 伍德 大钊:https://www.bilibili.com/video/BV1X5411V7jh 26 | 27 | - 介绍了GAS提供了哪些功能,GAS核心概念,最后通过演示一个项目来帮助理解GAS。项目和PPT可下载。 28 | 29 | 深入GAS架构设计:https://www.bilibili.com/video/BV1zD4y1X77M 30 | 31 | - 可以帮助理解GAS核心概念,源码架构。 32 | 33 | 官方项目及视频介绍:https://docs.unrealengine.com/4.26/en-US/Resources/SampleGames/ARPG/、https://www.bilibili.com/video/BV18J411M7jg 34 | 35 | # 3 概念 36 | 37 | GAS涉及的概念比较多,需要先理解核心概念,才能用好该系统。 38 | 39 | ## 3.1 ASC(UAbilitySystemComponent) 40 | 41 | 技能系统组件,是技能系统运行的核心。 42 | 43 | 负责管理协调其他部件:AbilityEffect、Attribute、Task、Event... 44 | 45 | 主要函数: 46 | 47 | - GiveAbility():赋予角色技能,返回技能处理器,可用于激活技能 48 | - TryActivateAbility():激活给定的技能 49 | - TryActivateAbilityByClass():通过类激活给定的技能 50 | - TryActivateAbilitiesByTag():通过Tag激活给定的技能 51 | 52 | ## 3.2 GA(UGameplayAbility) 53 | 54 | 定义一个技能的主体逻辑,可触发GE、GC。 55 | 56 | 主要函数: 57 | 58 | - ActivateAbility():激活技能 59 | - EndAbility():技能结束 60 | 61 | FGameplayAbilitySpec:GA学习后的实例,定义了GA的等级等 62 | 63 | FGameplayAbilitySpecContainer:GA实例集合 64 | 65 | FGameplayAbilitySpecHandle:实例化GA后的处理器,可以用于获取GA实例、激活Ability等 66 | 67 | 流程:ASC通过FGameplayAbilitySpecContainer变量来管理GA实例,每一个GA实例都有一个FGameplayAbilitySpecHandle处理器成员和GA模板成员。 68 | 69 | ## 3.3 GE(UGameplayEffect) 70 | 71 | 定义游戏里一个技能的效果,可以说成Buff系统。 72 | 73 | 一般用来修改自己或别人的属性、触发GC(UGameplayCueNotify) 74 | 75 | 主要结构: 76 | 77 | - UGameplayEffect:GE模板,用来配置数据,不会在应用到角色后进行实例化。 78 | - 主要变量: 79 | - TArray< FGameplayModifierInfo> Modifiers; // 属性修改器 80 | - TArray< FGameplayEffectCue> GameplayCues; // GE触发的Cue 81 | 82 | 83 | - FGameplayEffectSpec:GE的实例,定义了GE等级、属性修改变量等 84 | 85 | 86 | - FActiveGameplayEffect:激活GE的实例 87 | 88 | 89 | - FActiveGameplayEffectsContainer:激活GE的实例集合 90 | 91 | 流程:ASC通过FActiveGameplayEffectsContainer变量来管理GE实例,每次调用UGameplayEffect都会产生一个FGameplayEffectSpec实例 92 | 93 | ## 3.4 GC(UGameplayCueNotify) 94 | 95 | 定义游戏中的特效部分,如:爆炸、燃烧等 96 | 97 | 主要结构: 98 | 99 | - UGameplayCueManager:生成FGameplayCueObjectLibrary实例,以供后续调用,减少触发时候的延迟 100 | 101 | 102 | - UGameplayCueSet:Cue集合 103 | 104 | 流程: 105 | 106 | - Cue在GE配置中触发或GA里调用Execute/Add触发,触发后生成实例UGameplayCueManager 107 | 108 | 109 | - Cue触发的两种方式: 110 | - UGameplayCueNotify_Static 111 | - 不可实例化 112 | 113 | - static的Cue直接在CDO上调用 114 | - 不要在Cue里保存状态,因为它不会生成新的对象 115 | 116 | - AGameplayCueNotify_Actor 117 | - 实例化 118 | 119 | - 在使用的时候有时会触发Spawn生成新的实例,这些生效的Cue最终也都是在ASC里面有引用保存 120 | 121 | ## 3.5 Attribute(FGameplayAttribute) 122 | 123 | 游戏属性,如:生命值、攻击力、防御力等 124 | 125 | 主要类: 126 | 127 | - FGameplayAttribute:描述属性数据 128 | 129 | 130 | - UAttributeSet:属性集 131 | 132 | 流程:将UAttributeSet变量挂在Actor类中来使用 133 | 134 | ## 3.6 Tag(FGameplayTag) 135 | 136 | 可用于描述和归类一个对象的状态。可以用于判断各种状态等。 137 | 138 | 核心代码在Source\Runtime\GameplayTags\文件夹下,GameplayTags是一个单独的模块,可被单独引用。 139 | 140 | 主要类: 141 | 142 | - UGameplayTagsManager:标签管理类,构造了标签树,描述了标签之间的关系 143 | 144 | 145 | - FGameplayTag:Tag类,定义了标签名等 146 | 147 | 148 | - FGameplayTagContainer:FGameplayTag集合 149 | 150 | 151 | - FGameplayTagQuery:FGameplayTag查询 152 | 153 | 流程:该模块的入口为IGameplayTagsModule接口,通过该接口可以获取到UGameplayTagsManager(单例)类以及FGameplayTag类 154 | 155 | ## 3.7 Task(FGameplayTask) 156 | 157 | 异步操作,如:当蒙太奇播放结束后,异步结束一个技能 158 | 159 | 核心代码在AbilityTask.h、Source\Runtime\GameplayTasks\文件夹下,GameplayTasks是一个单独的模块,可被单独引用。 160 | 161 | 主要结构:UGameplayTask:GA调用 162 | 163 | 流程: 164 | 165 | - ASC继承于UGameplayTasksComponent 166 | 167 | 168 | - UAbilityTask继承于UGameplayTask,并添加了GA引用 169 | 170 | ## 3.8 Event(FGameplayEventData) 171 | 172 | 发送事件通知对方。可触发技能。事件靠Tag识别。 173 | 174 | 主要结构:FGameplayEventData:事件信息。 175 | 176 | 流程:注册回调,通过Tag来映射识别。当外部别的GA调用SendGameplayEvent时,会用EventTag在ASC的这个映射表里搜索,如果搜索到,就会触发回调。 177 | 178 | ## 3.9 预测(Prediction) 179 | 180 | 客户端不必等待服务器的许可就可以激活一个GameplayAbility并应用Effects。 181 | 182 | # 4 同步流程 183 | 184 | 同步流程比较复杂,依然是以往的风格:图析~ 185 | 186 | 演示项目是GASDocument,pdf版在github上https://github.com/xiaxia9/Game-Development-Notes:流程图节点是函数,方便查看。 187 | 188 | 我总结的流程:技能激活预测流程、服务器收到消息后的处理流程、客户端收到服务器预测激活成功后的流程、GE调用流程、GE移除流程、GC流程。 189 | 190 | 流程图很清晰,这里就不分开介绍了~ 191 | 192 | ![](【UE4】GAS技能系统介绍(一).png) 193 | 194 | # 5 结语 195 | 196 | GAS系统还有很多细节需要挖掘,留个坑~ 197 | 198 | 水平有限,如有错误,烦请指正,谢谢吼~ 199 | -------------------------------------------------------------------------------- /UE/【UE4】GAS技能系统介绍/【UE4】GAS技能系统介绍(一).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】GAS技能系统介绍/【UE4】GAS技能系统介绍(一).pdf -------------------------------------------------------------------------------- /UE/【UE4】GAS技能系统介绍/【UE4】GAS技能系统介绍(一).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】GAS技能系统介绍/【UE4】GAS技能系统介绍(一).png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/input_axis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/input_axis.png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/input_bind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/input_bind.png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/input_function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/input_function.png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/start_ds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/start_ds.png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/start_ds_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/start_ds_show.png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).md: -------------------------------------------------------------------------------- 1 | 本文仅介绍普通移动,RootMotion相关的移动会在下篇文章中介绍。 2 | 3 | # 1 流程图 4 | 5 | ![](【UE4】一图看懂移动同步(上).png) 6 | 7 | 可以下载pdf、visio版本查看修改,链接在https://github.com/xiaxia9/blogs/tree/master/UE4/ 8 | 9 | 可以结合大佬们的文章、官方文档以及下面的补充一起看~ 10 | 11 | - https://zhuanlan.zhihu.com/p/114341957 12 | - https://zhuanlan.zhihu.com/p/34257208 13 | - https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/Networking/CharacterMovementComponent/ 14 | 15 | # 2 补充 16 | 17 | ## 2.1 开启DedicatedServer(ds) 18 | 19 | 在红框中设置玩家数量和网络模式: 20 | 21 | ![](start_ds.png) 22 | 23 | 开启后的效果如下,编辑器充当ds,ROLE_AutonomousProxy(客户端)、ROLE_Authority(服务器)、ROLE_SimulatedProxy(模拟端)的区别这里就不多介绍啦: 24 | 25 | ![](start_ds_show.png) 26 | 27 | ## 2.2 移动输入设置 28 | 29 | 以GASDocumentation项目演示,下载地址在这里哦:https://github.com/tranek/GASDocumentation 30 | 31 | 流程可以分为三步: 32 | 33 | - 第一步:在Project Setting -> Input -> Bindings -> Axis Mappings设置输入映射: 34 | 35 | ![](input_axis.png) 36 | 37 | - 第二步:在代码中(也可以在蓝图中编写),使用BindAxis()将我们在上一步设置的值与函数绑定起来: 38 | 39 | ![](input_bind.png) 40 | 41 | - 第三步:上一步绑定的函数里,会调用AddMovementInput()和AddControllerYawInput(),具体信息请参考《Input》图片里的流程: 42 | 43 | ![](input_function.png) 44 | 45 | ## 2.3 主要结构 46 | 47 | 这里介绍一下,网络同步中涉及到的一些重要的结构体,理解这些能够在我们有扩展移动同步数据需求时受益。 48 | 49 | **FSavedMove_Character**:描述客户端一次移动的信息。 50 | 51 | - TimeStamp:使用FNetworkPredictionData_Client_Character::CurrentTimeStamp作为自己的时间戳。服务器如果判断某个move需要bUpdatePosition,会将该值重新发给客户端,客户端调用ClientUpdatePositionAfterServerUpdate(),并遍历SavedMoves数组。 52 | 53 | - 客户端把该结构发送给服务器进行模拟,同步加速度等,并不同步input值等。 54 | 55 | **FNetworkPredictionData_Client_Character**:客户端move预测数据 56 | 57 | - ClientUpdateTime:上次向服务器发送ServerMove()的时间戳。 58 | - CurrentTimeStamp:每次累加DeltaTime,超过一定的数值后会重置。 59 | - SavedMoves:客户端本地维护的移动缓存数组,按从最旧到最新顺序排列。里面存储的是客户端已模拟,但还没收到服务器ack的move数据。 60 | - LastAckedMove:上次确认被发送的移动指针,FSavedMove_Character类型。 61 | - PendingMove:用于存储移动。等待与下一个移动结合以减少客户端到服务器的带宽。(比如:一些没变化的移动包,会合并发送,以减少带宽) 62 | - IsImportantMove():如果未收到服务器ack时,需要再次发送时,返回true;否则,返回false。(也就是说,一些比较重要的移动包,需要再次发送的,会在这里判断。MovementMode或Acceleration不同,都会标记为重要的move) 63 | 64 | **FCharacterServerMovePackedBits**:客户端和服务器在网络上传输的数据结构。 65 | 66 | **FCharacterNetworkMoveData:**客户端发送到服务器的移动同步包。 67 | 68 | **FCharacterNetworkMoveDataContainer**:FCharacterServerMovePackedBits反序列化得到的结构数据。 69 | 70 | - 包含NewMoveData、PendingMoveData、OldMoveData 71 | 72 | 73 | **FCharacterMoveResponseDataContainer**:与上面的结构体相对应,这是服务器发送给客户端的同步包。 74 | 75 | ## 2.4 扩展移动同步 76 | 77 | 这里仅提供一种方法,具体实践请遵守项目需求。 78 | 79 | 客户端数据扩展: 80 | 81 | - 扩展移动快照(FSavedMove_Character):重写PostUpdate()对变量进行赋值,有时候还需要重写Clear()清理变量。 82 | - 由于我们使用新的移动快照,所以需要重写FNetworkPredictionData_Client_Character::AllocateNewMove()来new我们自定义的移动快照。 83 | - 扩展网络上一次move的数据包FCharacterNetworkMoveData。重写ClientFillNetworkMoveData(),如果用到自定义的移动快照,可以转换传入的参数,然后取出数据。重写Serialize()进行序列化。 84 | - 扩展网络同步的数据包容器FCharacterNetworkMoveDataContainer。它包括NewMoveData、PendingMoveData、OldMoveData,这里简单的与FCharacterNetworkMoveData扩展包关联起来即可。还有,要在移动组件构造函数中调用SetNetworkMoveDataContainer()进行设置。 85 | 86 | 服务器数据扩展: 87 | 88 | - 扩展网络同步的数据包容器FCharacterMoveResponseDataContainer。这里更像是上面说的FCharacterNetworkMoveData扩展结构包,重写ServerFillResponseData()、Serialize()即可。还有,要在移动组件构造函数中调用SetMoveResponseDataContainer()进行设置。 89 | 90 | 模拟端数据扩展: 91 | 92 | - 扩展其他数据:新建自己的Character类。重写GetLifetimeReplicatedProps()方法,添加自己想要同步的变量及标识。重写PreReplication()方法,对同步变量赋值。 93 | 94 | 95 | 96 | 97 | 98 | 最后,如有错误,烦请指正!谢谢~ 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).pdf -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(上)/【UE4】一图看懂移动同步(上).vsdx -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).md: -------------------------------------------------------------------------------- 1 | 在UE4中,驱动移动的方式可以分为两类: 2 | 3 | - 一类是Code driven。胶囊体推动,播放的动画仅做表现层,并不对移动结果产生影响。由于动画和移动各行各的,所以当移动模型和动画不匹配的时候,会产生滑步现象。(详细同步流程请参考上篇文章) 4 | - 一类是Data driven。从动画中提取位移,然后用动画中的位移驱动移动,也就是本篇要介绍的内容——RootMotion。该方式会让游戏表现更自然。 5 | 6 | 首先需要阅读官方文档:https://docs.unrealengine.com/4.27/en-US/AnimatingObjects/SkeletalMeshAnimation/RootMotion/,对RootMotion有一个大概的了解。 7 | 8 | 由于Root Motion From Everything目前不支持网络同步,所以下面的内容主要介绍Root Motion From Montages Only这个模式。 9 | 10 | # 1 流程 11 | 12 | Montage的同步主要由上层处理,比如UAbilitySystemComponent中就提供了一定的支持。也可以使用RPC的方式播放montage。 13 | 14 | 流程:客户端从动画中提取位移并转换成Velocity,之后调用StartNewPhysics()进行模拟。 15 | 16 | ![](【UE4】一图看懂移动同步(下).png) 17 | 18 | 有些游戏使用MotionMatching技术时,在做网络同步时支持了Data Driven方案,所以了解该同步流程是有一定的帮助的。 19 | 20 | RootMotionSource相关的内容,有空的话再补充~ 21 | 22 | 最后,如果错误,烦请指正~ 23 | 24 | 25 | 26 | 参考: 27 | 28 | - https://zhuanlan.zhihu.com/p/74554876 29 | 30 | -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).pdf -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).png -------------------------------------------------------------------------------- /UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】一图看懂移动同步(下)/【UE4】一图看懂移动同步(下).vsdx -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/3-ParallelGameThread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/3-ParallelGameThread.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/3-PreUpdateAnimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/3-PreUpdateAnimation.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/3-SwapContext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/3-SwapContext.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/3-TickAnimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/3-TickAnimation.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/3-UpdateMontage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/3-UpdateMontage.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-Collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-Collision.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-Mesh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-Mesh.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-MeshComponent坐标更新.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-MeshComponent坐标更新.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-MeshComponent层次结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-MeshComponent层次结构.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-MeshTick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-MeshTick.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-Skeleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-Skeleton.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/4-刚性层阶动画.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/4-刚性层阶动画.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/pictures/【UE4】图解动画系统源码.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/pictures/【UE4】图解动画系统源码.png -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/【UE4】图解动画系统源码.md: -------------------------------------------------------------------------------- 1 | # 1 前言 2 | 3 | 在了解动画系统之前,笔者有很多问题,比如:动画是如何动起来?我们看到的角色的位置是如何更新的?动画是如何更新的?等等。在看源码的时候,又遇到新的问题,比如:Skeleton和Mesh之间的关系是什么?。所以决定看看源码~ 4 | 5 | 接下来,介绍下本文的结构,本文一共分为四节,分别是: 6 | 7 | - 基本概念:列举了一些结构及解释。 8 | - 流程讲解:本节结合源码分析了组件注册、动画更新和渲染流程。不过,不包括Sequence等类型的动画资源、蒙太奇同步、蒙太奇触发等的具体分析。 9 | - 问题及回答:由于一些问题不方便在源码流程中讲解,所以单独出了一节。比如:Mesh坐标在何时何处设置的?Skeleton与Mesh之间的关系等等~ 10 | - 总图:这一节是本篇文章的精华部分,也映照了本文的标题。本节将动画系统流程以图绘的方式展示,使用函数节点表示,可以更方便的定位到函数并查看。老地方,图片及pdf版在github上哦:https://github.com/xiaxia9/Game-Development-Notes 11 | 12 | 注:使用UE4.26,且本文仅讨论骨骼动画。 13 | 14 | # 2 基本概念 15 | 16 | 下面的内容部分参考于:https://docs.unrealengine.com/4.27/zh-CN/ProgrammingAndScripting/Rendering/Overview/ 17 | 18 | | 结构 | 解释 | 19 | | :-------------------------------: | ------------------------------------------------------------ | 20 | | USkeletalMesh | 绑定到可以为使网格变形而设置动画的骨骼层级骨架的几何体。 由两部分组成:构造网格表面的多边形和可用于为多边形设置动画的层级骨架。 | 21 | | FSkeletalMeshRenderData | 渲染数据,包括LOD渲染数据等。 | 22 | | FSkeletalMeshLODRenderData | 骨骼蒙皮LOD渲染数据,包括蒙皮权重、皮肤权重配置文件数据等。 | 23 | | USceneComponent | 需要添加到 FScene 中的任意对象的基础类,如光照、网格体、雾等。 | 24 | | UPrimitiveComponent | 基元组件,是可视性和相关性确定的基本单位。可渲染或进行物理交互(碰撞检测)的任意资源的基础类。也可以作为可视性剔除的粒度和渲染属性规范(投射阴影等)。与所有 UObjects 一样,游戏线程拥有所有变量和状态,渲染线程不应直接对其进行访问。 是一些可见几何体的父类,比如: ShapeComponents (Capsule, Sphere, Box):用于碰撞检测但不渲染。 StaticMeshComponent、SkeletalMeshComponent:包含渲染的预构建几何体,也可用于碰撞检测。 | 25 | | UMeshComponent | 继承自UPrimitiveComponent,是所有具有可渲染的三角形集合实例的组件的抽象基类,如:UStaticMeshComponent、USkeletalMeshComponent。 | 26 | | UStaticMeshComponent | 继承自UMeshComponent,用于创建UStaticMesh实例,静态网格是由一组静态多边形组成的几何体。 | 27 | | USkinnedMeshComponent | 继承自UMeshComponent,支持骨骼蒙皮网格渲染,不支持动画。提供USkeletalMesh接口等。 | 28 | | USkeletalMeshComponent | 继承自USkinnedMeshComponent,创建带动画的SkeletalMesh资源的实例。 支持动画。 | 29 | | FScene | UWorld 的渲染器版本。对象仅在其被添加到 FScene(注册组件时调用)后才会存在于渲染器中。渲染线程拥有 FScene 中的所有状态,游戏线程无法直接对其进行修改。 主要操作是添加或删除图元(Primitive)和灯光。 | 30 | | FPrimitiveSceneProxy | UPrimitiveComponent 的渲染器版本,为渲染线程映射 UPrimitiveComponent 状态。存在于引擎模块中,用于划分为子类以支持不同类型的基元(骨架、刚体、BSP 等)。实现某些非常重要的函数,如 GetViewRelevance、DrawDynamicElements 等。 | 31 | | FPrimitiveSceneInfo | 内部渲染器状态(FRendererModule 实现专有),对应于 UPrimitiveComponent 和 FPrimitiveSceneProxy。存在于渲染器模块中,因此引擎看不到它。单个UPrimitiveComponent的内部渲染器状态。 | 32 | | FDynamicSkelMeshObjectDataGPUSkin | 存储顶点蒙皮所需的更新矩阵。 | 33 | | FSkeletalMeshObjectCPUSkin | CPU蒙皮网格渲染数据。 | 34 | | FSkeletalMeshObjectStatic | 静态网格渲染数据。 | 35 | | FSkeletalMeshObjectGPUSkin | GPU蒙皮渲染数据。包含FDynamicSkelMeshObjectDataGPUSkin* DynamicData,它用于动态更新和渲染。 | 36 | | FGPUBaseSkinVertexFactory | 存储皮肤处理所需的骨基质,以及对 GPU 皮肤顶点工厂着色器代码需要用作输入的各种顶点缓冲区的引用。 | 37 | # 3 流程讲解 38 | 39 | 先梳理一下大致的流程,流程可以分为三个部分,分别是组件注册、动画更新和渲染。 40 | 41 | 组件注册:在Game线程中执行。在游戏启动时,需要先注册USkeletalMeshComponent组件,然后调用执行注册事件。注册事件做了以下内容:初始化Anim、更新Animation、刷新骨骼变换、创建渲染状态并分配MeshObject、创建物理状态。 42 | 43 | 动画更新:判断是否需要执行蒙太奇更新优化;进行预更新——重置动画通知队列和RootMotionBlendQueue,赋值;蒙太奇更新、蒙太奇同步组更新、蓝图更新。分发并行执行和并行执行结束两个任务。在并行执行Task中,更新动画蓝图中的节点和TickAssetPlayer();在并行执行结束Task中,更新数据,以便之后在创建渲染命令时可以判断。 44 | 45 | 渲染:游戏线程创建渲染命令,渲染线程执行渲染命令,最后更新骨骼数据。 46 | 47 | 下面是详细介绍: 48 | 49 | ## 3.1 组件注册 50 | 51 | 在介绍具体流程前,首先放一下官方文档https://docs.unrealengine.com/4.26/zh-CN/ProgrammingAndScripting/Rendering/ThreadedRendering/中的两段话,了解了之后再介绍流程: 52 | 53 | - **UPrimitiveComponent** 是属于可被渲染、可投射阴影资源的基础游戏线程类,拥有其自身的可视状态等属性。渲染线程无法直接触及 UPrimitiveComponent 的内存,因为游戏线程可能随时写入其构件中。渲染线程自身拥有代表此功能的类 - **FPrimitiveSceneProxy**。游戏线程被创建和注册后,无法触及 FPrimitiveSceneProxy 的内存构件。**UActorComponent::RegisterComponent** 将一个组件添加到场景,并创建一个 FPrimitiveSceneProxy 使其对渲染器可见。组件注册后,如其为可见,将为所需的每次通路调用 **FPrimitiveSceneProxy::DrawDynamicElements**。 54 | - 动态资源更新的一个最佳范例是游戏线程动画每帧生成的骨骼网格体骨骼变形。目的是:在每个动画更新进渲染线程上(在此可将变形设为着色器常数)的一个阵列后,从游戏线程中获取变形。如在每帧更新索引或顶点缓冲,结果相同。以下是操作顺序: 55 | - **USkinnedMeshComponent::CreateRenderState_Concurrent** 分配 **USkinnedMeshComponent::MeshObject**。从此时起,游戏线程只可写入 **MeshObject** 指示器,但不可写入 **FSkeletalMeshObject** 的内存。 56 | - **USkinnedMeshComponent::UpdateTransform** 每帧被调用至少一次,更新组件的移动。在 GPU 蒙皮中将调用 **FSkeletalMeshObjectGPUSkin::Update**。现在游戏线程上拥有最新的变形,需要将它们转移到渲染线程中。操作方法:首先在堆(**FDynamicSkelMeshObjectData**)上分配内存,然后将骨骼变形复制进去,再使用 ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER 将此拷贝传到渲染线程。渲染线程现在拥有此拷贝,并负责删除。ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER 宏包含复制变形到最终目的地的代码,因此变形可被设为着色器常数。如更新顶点位置,这就是锁定和更新顶点缓冲的位置。 57 | - 在一些情况下,组件会被分离。游戏线程使渲染命令入列,以释放所有动态 FRenderResources,现在可将 MeshObject 指示器设为 NULL;然而实际内存仍被渲染线程引用,无法删除。此时延迟删除机制即可发挥作用。从 **FDeferredCleanupInterface** 派生的类可按对线程无害的异步方式进行删除。FSkeletalMeshObject 应用此接口。游戏线程需要开始 FSkeletalMeshObject 的延迟删除,因此它调用了 **BeginCleanup(MeshObject)**。安全且完成清理后,内存将被逐步删除。 58 | 59 | 下面是具体流程: 60 | 61 | 1、首先是序列化FSkeletalMeshRenderData。从UObject::ConditionalPostLoad()开始,最后调用到FSkeletalMeshRenderData::Serialize()进行序列化。 62 | 63 | 2、然后是游戏启动,从UActorComponent::RegisterComponent()开始,主要是将组件添加到场景中,并创建一个FPrimitiveSceneProxy使其对渲染器可见。流程如下: 64 | 65 | - 调用ExecuteRegisterEvents()执行注册事件; 66 | - 调用ComputeRequiredBones()使用RenderData(渲染数据)、RequiredVirtualBones(虚拟骨骼)、PhysAssetBones(物理骨骼)填充OutRequiredBones,之后清除不可见的Bones,然后添加Mirror需要的Bones、SocketBones、ShadowShapeBones; 67 | - 调用TickAnimation()主动更新一次动画; 68 | - 然后调用RefreshBoneTransforms()刷新Bone位置; 69 | - 调用CreateRenderState_Concurrent()创建渲染状态,并分配MeshObject:FSkeletalMeshObjectStatic、FSkeletalMeshObjectCPUSkin or FSkeletalMeshObjectGPUSkin;其解释是负责将骨骼变换、变形目标状态等发送到渲染线程的对象。这一步和渲染建立关系。 70 | - 调用UpdateBounds()更新边界; 71 | - 调用CreateSceneProxy()创建一个FPrimitiveSceneProxy使其对渲染器可见; 72 | - 调用FSkeletalMeshObjectGPUSkin::Update()进行GPU渲染更新; 73 | - 调用UpdatePreviousRefToLocalMatrices()初始化FDynamicSkelMeshObjectDataGPUSkin结构。 74 | 75 | ## 3.2 动画更新 76 | 77 | ### 3.2.1 TickComponent 78 | 79 | 1、从USkeletalMeshComponent::TickComponent()中开始,调用USkinnedMeshComponent::TickComponent(),再调用USkeletalMeshComponent::TickPose(),最后在USkeletalMeshComponent::TickAnimation()中对LinkedInstances(链接的动画蓝图)、AnimScriptInstance(动画蓝图)、PostProcessAnimInstance(从SkeletalMesh的PostPhysicsBlueprint属性创建的实例)三种类型的动画实例执行UpdateAnimation()更新。 80 | 81 | ![](pictures\3-TickAnimation.png) 82 | 83 | 2、动画更新做了哪些内容?UpdateAnimation 84 | 85 | - 在UpdateAnimation()函数中,当我们设置了OnlyTickMontagesWhenNotRendered且未被渲染时,会调用UpdateMontage()更新,然后返回。这一步其实是在进行优化。 86 | 87 | - 如果不是,则往下执行。先调用**PreUpdateAnimation()**进行预更新,在预更新函数里,会重置动画通知队列和RootMotionBlendQueue,并调用FAnimInstanceProxy::PreUpdate()进行代理更新。PreUpdate()其实就是进行常规赋值操作,比如给RootMotionMode、SkelMeshCompLocalToWorld、UngroupedActivePlayers、SyncGroups、ComponentTransform、ComponentRelativeTransform、ActorTransform等赋值。 88 | 89 | ![](pictures\3-PreUpdateAnimation.png) 90 | 91 | - 之后来到UpdateMontage(),这里主要是调用Montage_UpdateWeight()根据DeltaTime进行动画权重更新和调用Montage_Advance()处理一些RootMotion逻辑——提取位移(具体可以参照5 总图或上篇关于RootMotion的文章)。 92 | 93 | ![](pictures\3-UpdateMontage.png) 94 | 95 | - 然后调用UpdateMontageSyncGroup()更新蒙太奇同步组,计算同步组Leader。 96 | 97 | - 然后调用UpdateMontageEvaluationData()更新蒙太奇data,重置计算数据,然后将之前计算的权重等数据放在Proxy中。之后会用到。 98 | 99 | - 然后调用BlueprintUpdateAnimation()进行蓝图更新。 100 | 101 | - 如果动画需要立即更新,还需要调用ParallelUpdateAnimation()和PostUpdateAnimation()进行并行更新,这些在后面会详细介绍。 102 | 103 | 3、之后回到上层USkinnedMeshComponent::TickComponent()中继续执行,如果ShouldUpdateTransform(bLODHasChanged) 则执行RefreshBoneTransforms()刷新骨骼坐标,收集动画数据。这里主要做了以下三件事: 104 | 105 | - 给FAnimationEvaluationContext AnimEvaluationContext赋值。 106 | 107 | - 预并行处理:调用PreEvaluateAnimation()(和上面的一样,对三种类型进行操作:AnimScriptInstance、LinkedInstance、PostProcessAnimInstance),这个函数是给FAnimInstanceProxy结构中的MainInstanceProxy、Skeleton赋值。 108 | 109 | - 分发并行执行任务: 110 | 111 | - 如果bDoParallelEvaluation并行执行为true,则调用DispatchParallelEvaluationTasks(),在这个函数里面调用SwapEvaluationContextBuffers()将我们在上面操作的AnimEvaluationContext与本地缓存进行交换,然后创建FParallelAnimationEvaluationTask执行任务和FParallelAnimationCompletionTask完成任务。 112 | 113 | ![](pictures\3-SwapContext.png) 114 | 115 | - 反之,调用DoParallelEvaluationTasks_OnGameThread(),正如函数名所示,这里在Game线程中主动调用。这里先调用SwapEvaluationContextBuffers()交换,然后调用ParallelAnimationEvaluation(),最后再调用SwapEvaluationContextBuffers()交换回来。最后回到上层调用PostAnimEvaluation()。 116 | 117 | ![](pictures\3-ParallelGameThread.png) 118 | 119 | 4、之后回到上层USkinnedMeshComponent::TickComponent()中,如果我们设置了AlwaysTickPose,则调用DispatchParallelTickPose()。 120 | 121 | ### 3.2.2 Task 122 | 123 | 在上面的DispatchParallelEvaluationTasks()中,我们创建了FParallelAnimationEvaluationTask和FParallelAnimationCompletionTask两个Task,这两个Task也都是在任务线程中执行的。 124 | 125 | 1、首先先看FParallelAnimationEvaluationTask。 126 | 127 | - 调用DoTask(),一路执行到USkeletalMeshComponent::PerformAnimationProcessing,该函数首先调用**UAnimInstance::ParallelUpdateAnimation()**进行动画代理更新和Tick,分别是下面两个函数: 128 | 129 | - UpdateAnimation():动画蓝图节点更新。从根节点依次遍历更新。 130 | 131 | - TickAssetPlayerInstances():主要执行TickAssetPlayer()来更新FAnimAssetTickContext,RootMotion在此提取并更新。我们也可以自己实现TickAssetPlayer()来适配项目需求。 132 | 133 | - 然后调用EvaluateAnimation():执行动画蓝图更新。 134 | 135 | - 之后调用EvaluatePostProcessMeshInstance():更新PostProcessMeshInstance。 136 | 137 | - 之后调用FinalizePoseEvaluationResult():填充OutBoneSpaceTransforms和OutRootBoneTranslation。 138 | 139 | - 最后调用FillComponentSpaceTransforms():给RootBone赋值,将InBoneSpaceTransforms转换为OutComponentSpaceTransforms 140 | 141 | 2、最后是FParallelAnimationCompletionTask。调用DoTask(),执行到CompleteParallelAnimationEvaluation(),先调用SwapEvaluationContextBuffers()交换回来,然后调用PostAnimEvaluation(),最后发送给渲染线程。 142 | 143 | ## 3.3 渲染 144 | 145 | 在上面TickAnimation之后,渲染线程需要渲染动画数据,以便我们可以看到角色。 146 | 147 | 在FSkeletalMeshObjectGPUSkin::Update()中我们可以看到有ENQUEUE_RENDER_COMMAND这样一个宏,这个宏是处理两个线程间通讯的问题。它的作用是创建一个渲染命令,游戏线程将该命令插入渲染命令队列,渲染线程在开始时调用执行函数。 148 | 149 | 游戏线程:调用DoDeferredRenderUpdates_Concurrent(),然后调用SendRenderTransform_Concurrent(),判断动画更新流程中FParallelAnimationCompletionTask任务标记的bRenderDynamicDataDirty是否为True,如果为Ture,则需要将组件变换发送到渲染器。然后判断是否需要Update(),最后调用FSkeletalMeshObjectGPUSkin::Update()创建渲染命令,以便渲染线程执行。 150 | 151 | 渲染线程:从FRenderingThread::Run()开始,之后调用FSkeletalMeshObjectGPUSkin::Update()更新FDynamicSkelMeshObjectDataGPUSkin数据并执行渲染命令,最后调用FGPUBaseSkinVertexFactory::FShaderDataType::UpdateBoneData()更新骨骼数据。 152 | 153 | # 4 问题及回答 154 | 155 | ## 4.1 角色动画的类型 156 | 157 | 下面的内容来自于《游戏引擎架构》11.1 角色动画的类型 158 | 159 | 游戏引擎中有3种最常用的动画技术,下面将分开介绍: 160 | 161 | - 赛璐璐动画:赛璐璐是透明的塑料片,可在上面绘画。把一连串含动画的赛璐璐放置于固定的手绘背景之上,就能产生动感,而无须不断重复绘制静态的背景。赛璐璐动画的电子版本是称为精灵动画的技术。所谓精灵,其实是一张小位图,叠在全屏的背景影响之上而不会扰乱背景,通常由专门的图形硬件绘制。精灵是二维游戏时代最主要的技术。 162 | 163 | - 刚性层阶式动画:角色由一堆刚性部分建模而成,这些刚性部分以层阶形式彼此约束,类似于哺乳类动物以关节连接骨骼,这样能使角色自然地移动。但是,这种技术的最大问题在于,角色的身体会在关节位置产生碍眼的裂缝。 164 | 165 | ![](pictures\4-刚性层阶动画.png) 166 | 167 | - 蒙皮动画:是当今最流行的技术,也是本篇文章介绍的重点。在蒙皮动画中,骨骼(skeleton)是由刚性的骨头(bone)所建构而成的,这与刚性层阶动画是一样的。然而,这些刚性的部件并不渲染显示,始终是隐藏起来的。称为皮肤(skin)的圆滑三角形网格会绑定于骨骼上,其顶点会追踪关节(joint)的移动。蒙皮上每个顶点可按权重绑定至多个关节,因此当关节移动时,蒙皮可以自然地拉伸。 168 | 169 | ## 4.2 胶囊体、Skeleton(骨骼、骨架)、Bone(骨头、joint)、Mesh(皮)之间的关系 170 | 171 | 在了解动画系统之前,我们需要了解下一些用语之间的关系~ 172 | 173 | 1、对于移动来说,无论是Code Driven还是Data Driven,都是先调用StartNewPhysics()修改USceneComponent* UpdatedComponent的位置,然后在FScopedMovementUpdate析构时更新Mesh(作为子组件更新)的位置。这个UpdatedComponent一般默认是根组件,也就是我们所说的胶囊体。我们在游戏中是无法直观的看到胶囊体的,可以使用show Collision查看。如下所示: 174 | 175 | ![](pictures\4-Collision.png) 176 | 177 | 2、而Skeleton是由Bone构成的一种层级结构。如下面所示的USkeleton资源,我们选择一个Skeleton中的一个Bone,图中显示的是pelvis,在右方Details框中可以看到坐标。(所以位移信息是存储在Skeleton里面的哦,不是Mesh~) 178 | 179 | ![](pictures\4-Skeleton.png) 180 | 181 | 3、接下来是Mesh,Mesh仅仅是一层皮而已,我们所说的修改Mesh坐标其实是修改的根骨骼的位置和朝向,因为坐标等信息是保存在骨骼里的。当然,RootMotion动画中的位移也是写在骨骼中的。Mesh是依附于骨骼存在的。如下面所示的USkeletonMesh资源,可以看到编辑器右上角显示的标签是Mesh。 182 | 183 | ![](pictures\4-Mesh.png) 184 | 185 | ## 4.3 SkeletalMeshComponent坐标是在何时何处被设置的? 186 | 187 | 在游戏中,我们看到的玩家其实是Mesh,那它的坐标是在哪设置的呐? 188 | 189 | 1、首先介绍一下层级关系,如下图所示(角色蓝图),可以看到组件之间的层级关系,Mesh是CapsuleComponent的子组件,而CapsuleComponent也是根组件。 190 | 191 | ![](pictures\4-MeshComponent层次结构.png) 192 | 193 | 2、其次,在移动组件中的PerformMovement()函数里面可以看到FScopedMovementUpdate这样一个结构,在这个结构体析构的时候,会调用EndScopedMovementUpdate(),然后继续执行;当位置有改变时,调用PropagateTransformUpdate(),最后一路调用到UpdateComponentToWorldWithParent()更新坐标,这里是直接设置的坐标。如下图所示: 194 | 195 | ![](pictures\4-MeshComponent坐标更新.png) 196 | 197 | 3、**所以默认来说,主控端SkeletalMeshComponent的坐标是相对于父组件的,其坐标的变换随着父组件相对变化。**从移动角度来说,移动组件Tick时,执行PerformMovement(),然后就会判断是否需要更新SkeletalMeshComponent,也就是说SkeletalMeshComponent的坐标每帧都在跟随着父组件变化。在之前的移动同步流程中,我们发现,模拟端是不执行PerformMovement()的,也没调用到FScopedMovementUpdate结构,所以它重写了一套Mesh插值平滑流程。 198 | 199 | ## 4.4 USkeletalMesh资源与USkeleton资源介绍 200 | 201 | 在第一个问题中,解释了Skeleton和Mesh之间的关系。之后,我们发现UE4提供了两种资源,分别是USkeletalMesh和USkeleton,现在介绍一下它们~ 202 | 203 | 1、USkeleton:维护了一个骨骼内部的层级关系及Bone信息。主要成员如下: 204 | 205 | - TArray< struct FBoneNode> BoneTree成员:维护骨骼层级结构。存储名字和父节点id。 206 | 207 | - FReferenceSkeleton ReferenceSkeleton成员:这个结构记录了Bone Info(名字和父节点id)、Bone Transform、Virtual Bone Data等,是操作的基础。也印证了之前所说的坐标信息保存在骨骼里这句话。 208 | - TArray< FVirtualBone> VirtualBones成员:虚拟骨骼的基本信息,记录SourceBoneName、TargetBoneName和VirtualBoneName。 209 | 210 | 2、USkeletalMesh:绑定到骨架后的网格体。由两部分组成,构造网格表面的多边形和可用于为多边形设置动画的层级骨架。主要成员如下: 211 | 212 | - TSharedPtr< FSkeletalMeshModel> ImportedModel:模型的几何信息 213 | 214 | - **TUniquePtr< FSkeletalMeshRenderData> SkeletalMeshRenderData成员:渲染数据。** 215 | - USkeleton* Skeleton成员:同上,所属骨架。 216 | - TArray< FSkeletalMaterial> Materials:材质信息 217 | - TArray< struct FSkeletalMeshLODInfo> LODInfo:LOD信息 218 | - class UPhysicsAsset* PhysicsAsset:物理资源设置 219 | - TArray< FTransform> RetargetBasePose:重定向pose的缓冲区 220 | - TArray MeshClothingAssets:布料资源配置 221 | - TArray< FSkinWeightProfileInfo> SkinWeightProfiles:mesh的蒙皮权重配置 222 | 223 | ## 4.5 USkinnedMeshComponent介绍 224 | 225 | USkinnedMeshComponent是USkeletalMeshComponent的父组件,也是我们要深入了解的骨骼蒙皮技术的核心内容。继承于UMeshComponent,可渲染。主要成员如下: 226 | 227 | - **class USkeletalMesh* SkeletalMesh:Skeleton Mesh资源** 228 | - TArray< ESkinCacheUsage> SkinCacheUsage:皮肤缓存功能 229 | - TArray< FVertexOffsetUsage> VertexOffsetUsage:顶点偏移功能 230 | 231 | USkeletalMeshComponent继承于USkinnedMeshComponent,用于创建SkeletalMesh资源的实例。支持动画,并多了如下的主要成员: 232 | 233 | - UAnimInstance* AnimScriptInstance:动画实例 234 | - FVector RootBoneTranslation:根骨骼位置 235 | - FBlendedHeapCurve AnimCurves:Curve临时存储 236 | - TArray LinkedInstances:关联的动画实例 237 | - TArray< FTransform> CachedBoneSpaceTransforms:BoneSpace下的缓存 238 | - TArray < FTransform> CachedComponentSpaceTransforms:ComponentSpace下的缓存 239 | - uint16 CachedAnimCurveUidVersion:Curve缓存,用于识别是否需要更新 240 | 241 | ## 4.6 Mesh Tick选项优化 242 | 243 | 在角色蓝图中,可以对Mesh组件设置Visibility Based Anim Tick Option,可选的选项分别是: 244 | 245 | - AlwaysTickPoseAndRefreshBones:无论是否渲染,总是tick和Refresh Bone transforms。 246 | - AlwaysTickPose:总是tick,只在渲染时Refresh Bone transforms。 247 | - OnlyTickMontagesWhenNotRendered:只在渲染时tick pose和Refresh Bone transforms。 不渲染时,只更新montage,跳过其他操作。 248 | - OnlyTickPoseWhenRendered:只在渲染时tick和Refresh Bone transforms。 249 | 250 | ![](pictures\4-MeshTick.png) 251 | 252 | (动画优化时,应该会用到这部分的内容叭~ 253 | 254 | ## 4.7 动画中的位移提取流程 255 | 256 | 首先需要动画师写入位移。 257 | 258 | 以RootBone为例。 259 | 260 | 在ParallelUpdateAnimation()并行执行的过程中,调用了FAnimInstanceProxy::TickAssetPlayerInstances(),如果是UAnimSequence会最终调用到ExtractRootMotion()进行提取,UAnimMontage会调用ExtractRootMotionFromTrackRange()提取,UAnimCompositeBase会调用ExtractRootMotionFromTrack()提取。 261 | 262 | 以UAnimSequence为例,会调用到TickAssetPlayer()设置Time;之后调用ExtractRootMotionFromRange()来提取一段范围内的StartTransform和EndTransform;然后会调用GetBoneTransform(),这个函数里会根据FCompressedAnimSequence.CompressedDataStructure(压缩骨骼数据流)等信息生成FAnimSequenceDecompressionContext(封装骨骼压缩编解码器使用的解压相关数据)DecompContext,DecompContext会把Time作为参数传递给Seek(),在这里为RelativePos赋值,最后调用DecompressBone()提取FTransform,该函数里面调用的GetBoneAtomTranslation()、GetBoneAtomRotation()均用到了RelativePos。所以是使用动画中的Time作为参数,然后进行提取。 263 | 264 | ## 4.8 蒙皮介绍 265 | 266 | 下面的内容部分来自于《游戏引擎架构》11.5 蒙皮及生成矩阵调色板 267 | 268 | 蒙皮:把三维网格顶点联系至骨骼的过程。蒙皮用的网格是通过其顶点系上骨骼的。每个顶点可绑定至一个或多个关节。若某顶点只绑定至一个关节,它就会完全跟随该关节移动。若绑定至多个关节,则该顶点的位置就等于把它逐一绑定至个别关节后的位置,再取其加权平均。 269 | 270 | 要把网格蒙皮至骨骼,三维建模师必须替每个顶点提供以下额外信息: 271 | 272 | - 该顶点要绑定到的(一个或多个)关节索引。 273 | - 对于每个绑定的关节,提供一个权重因子,以表示该关节对最终顶点位置的影响力。 274 | 275 | 如同计算其他加权平均时的习惯,每个顶点的权重因子之和为1。 276 | 277 | 蒙皮矩阵:能把网格顶点从原来位置(绑定姿势)变换至骨骼的当前姿势。 278 | 279 | 书上重点的内容介绍完了,那么上面的概念又对应于虚幻引擎中的哪部分内容呐? 280 | 281 | 这里先介绍几个结构体及概念: 282 | 283 | - FSkinWeightDataVertexBuffer:存储骨骼索引/权重数据的顶点缓冲区。该结构继承于FVertexBuffer,FVertexBuffer又继承于FRenderResource,也就是说这是渲染线程拥有的渲染资源。 284 | - FSkinWeightLookupVertexBuffer:存储皮肤权重流偏移/影响计数的查找顶点缓冲区。和上面的FSkinWeightDataVertexBuffer结构一样,也是渲染资源。 285 | 286 | - FSkinWeightVertexBuffer:皮肤权重数据顶点缓冲区和查找顶点缓冲区的容器。也就是说,包含FSkinWeightDataVertexBuffer和FSkinWeightLookupVertexBuffer成员。 287 | - FSkinWeightProfileInfo:存储面向用户的属性的结构,用于识别SkeletalMesh级别的配置文件。 288 | - FSkeletalMeshLODRenderData:骨骼蒙皮LOD渲染数据,主要包括下面内容: 289 | - FSkinWeightVertexBuffer SkinWeightVertexBuffer:蒙皮权重 290 | - FSkinWeightProfilesData SkinWeightProfilesData:皮肤权重配置文件数据 291 | - TArray< FBoneIndexType> ActiveBoneIndices:活动骨骼 292 | 293 | FSkeletalMeshRenderData里有FSkeletalMeshLODRenderData成员,FSkeletalMeshLODRenderData里有FSkinWeightVertexBuffer记录了蒙皮权重。在编辑器启动时,首先从USkeletalMesh::PostLoad()开始,然后调用FSkeletalMeshRenderData::Serialize(),然后调用FSkeletalMeshLODRenderData::Serialize()进行序列化,之后再调用SerializeStreamedData(),然后执行Ar << SkinWeightVertexBuffer,再执行Ar << VertexBuffer.DataVertexBuffer,最后调用VertexBuffer.WeightData->Serialize(Ar)完成FSkinWeightDataVertexBuffer中WeightData和Data的赋值;之后回到上层PostLoad(),然后调用FSkeletalMeshRenderData::InitResources(),最后调用FSkeletalMeshLODRenderData::InitResources()进行初始化。 294 | 295 | # 5 总图 296 | 297 | ![](pictures\【UE4】图解动画系统源码.png) 298 | 299 | # 6 参考 300 | 301 | 《游戏引擎架构》第11章 动画系统 302 | 303 | [图形编程](https://docs.unrealengine.com/4.26/zh-CN/ProgrammingAndScripting/Rendering/) 304 | 305 | [Skinned Mesh原理解析和一个最简单的实现示例](https://happyfire.blog.csdn.net/article/details/3105872) 306 | 307 | [UE4动画系统更新源码分析](https://zhuanlan.zhihu.com/p/405437842) 308 | 309 | [UE4 动画系统 源码及原理剖析](https://blog.csdn.net/qq_23030843/article/details/109103433) 310 | 311 | 312 | 313 | 动画系统的学习之路仅仅开了一个头,还有很多东西需要去学习,本文也有很多细节没有涉及到,以后慢慢学习慢慢补充~ 314 | 315 | 水平有限,如有错误,烦请指正,谢谢吼~ 316 | 317 | -------------------------------------------------------------------------------- /UE/【UE4】图解动画系统源码/【UE4】图解动画系统源码.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE4】图解动画系统源码/【UE4】图解动画系统源码.pdf -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/AnimDynamics-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/AnimDynamics-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/Animation-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/Animation-1.gif -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/AnimationUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/AnimationUpdate.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/Collision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/Collision.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/Constraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/Constraint.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-2.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-3.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-4.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PA-5.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicalAnimation.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAnimationUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAnimationUpdate.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAsset-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAsset-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAsset-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/PhysicsAsset-2.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/RigidBody-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/RigidBody-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/Setting-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/Setting-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/SkeletalMesh-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/SkeletalMesh-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/UML-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/UML-1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/pictures/人类一败涂地.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(基础篇)/pictures/人类一败涂地.jpg -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(基础篇)/【UE5】基于物理的角色动画(基础篇).md: -------------------------------------------------------------------------------- 1 | ![](pictures\人类一败涂地.jpg) 2 | 3 | 人类一败涂地挺有意思的。 4 | 5 | # 1. 前言 6 | 7 | 基于物理的动画(Physically Based Animation)是计算机图形学中的一个有趣的领域,它包括刚体模拟(Rigid Body Simulation)、柔体模拟(Soft Body Simulation)、流体模拟(Fluid Simulation)、粒子系统(Particle Systems)、蜂拥行为(Flocking)、基于物理的角色动画(Physically Based Character Animation)等。 8 | 9 | 在动画中添加物理,会使动画更丰富,与场景交互也会更真实。本文介绍角色物理动画最常见的实现之一——布娃娃模拟,本文包括概念、驱动物理跟随动画的方式、UE5中的物理动画代码介绍等。 10 | 11 | 和之前的文章一样,本文依然只介绍骨骼,且为基础篇,所以**有些内容可以适当跳过**~ 12 | 13 | # 2. 一分钟实现最简单的角色物理动画模拟 14 | 15 | 开头先实现一个最简单的物理动画模拟,按照Physical Animation / Ragdoll TUTORIAL in Unreal Engine 4 latest version:https://www.youtube.com/watch?v=_Xnx9L6DgfE流程来。 16 | 17 | 打开角色蓝图,添加PhysicalAnimation组件,并设置Bone的值为pelvis,然后按下图操作: 18 | 19 | - 选择所有刚体,可以添加一个Physical Animation Profiles,然后选择Assign,可以在Details面板中,看到PhysicalAnimation配置,这里可以为每个刚体单独配置数据。 20 | 21 | ![](pictures\PA-1.png) 22 | 23 | - 添加PhysicalAnimation组件,然后选择Mesh,修改Collision,修改胶囊体的大小。 24 | 25 | ![](pictures\PA-2.png) 26 | 27 | - 然后编写下面的逻辑,这里创建了一个用于WorldSpace中的PhysicalAnimationData。 28 | 29 | ![](pictures\PA-3.png) 30 | 31 | 运行结果如下所示(效果与配置有关,不代表最终结果): 32 | 33 | ![](pictures\Animation-1.gif) 34 | 35 | (这个表现...哈哈哈哈,emm...只是为了引出后面的内容... 36 | 37 | # 3. 布娃娃物理系统(Ragdoll Physics System) 38 | 39 | Ragdoll将物理和动画结合起来,是一种由物理引擎驱动的程序式动画(procedural Animation)。它是多个刚体(rigid)的集合(在骨骼动画系统中,每个刚体通常与骨骼绑定在一起),通过限制骨骼的移动来实现。当角色死亡时,每个joint的运动都受到了限制,表现也会更真实。 40 | 41 | # 4 前置概念及介绍 42 | 43 | 游戏引擎所指的“角色物理”,更精确地说应称为刚体动力学(rigid body dynamics)模拟。 44 | 45 | 关于物理资产编辑器的介绍,请见官方文档:https://docs.unrealengine.com/5.0/en-US/physics-asset-editor-in-unreal-engine/ 46 | 47 | ## 4.1 刚体(Rigid Bodies) 48 | 49 | 在理想状态下,刚体在运动中和受力作用后,形状和大小不变。 50 | 51 | 在UE5中,FBodyInstance继承于FBodyInstanceCore,它描述了刚体,是物理数据描述的基本单位,包括: 52 | 53 | - TWeakObjectPtr< UBodySetupCore> BodySetup:共享的资源信息 54 | 55 | - CollisionEnabled:碰撞类型 56 | - LinearDamping、AngularDamping:线性阻尼、角速度阻尼 57 | - DOFConstraint:自由度约束 58 | - GetBodyMass():质量 59 | - GetBodyInertiaTensor():惯性张量,张量描述了围绕任何给定轴旋转物体的难度,惯性张量越高越稳定。 60 | - UpdatePhysicalMaterials():更新材质的摩擦、恢复系数等。 61 | 62 | UBodySetup继承于UBodySetupCore,它包含与单个资源关联的碰撞信息,单个UBodySetup实例可以在多个FBodyInstance之间共享。它包括: 63 | 64 | - BoneName:骨骼名 65 | 66 | - PhysicsType:物理类型 67 | - Default:继承父组件的设置 68 | - Kinematic:不受物理影响,但可以与物理模拟的物体交互 69 | - Simulated:使用物理模拟 70 | 71 | USkeletalBodySetup继承于UBodySetup,在父类的基础上增加了一个bSkipScaleFromAnimation开放参数。 72 | 73 | ![UML](pictures\UML-1.png) 74 | 75 | 选择物理资产编辑器中的body,可以在Details面板看到FBodyInstance含有的参数: 76 | 77 | ![](pictures\PhysicsAsset-1.png) 78 | 79 | ## 4.2 约束(Constraints) 80 | 81 | 无约束的刚体有6个自由度(degree of freedom, DOF):可以在3个维度上平移,并可以绕3个笛卡尔轴旋转。 82 | 83 | 在物理引擎中,约束用于模拟物体间的连接情况,如连杆、布娃娃、吊灯等。在一定程度上削减了DOF。 84 | 85 | **常见的约束类型**: 86 | 87 | - point-to-point:点对点约束,刚体中某个指定的点与其他刚体的指定点对齐,比如球窝关节(ball and socket joint)。 88 | - hinge:铰链约束,将连接物理的刚体的运动约束在某一个轴上,限制关节中的旋转,比如膝盖I(knees)、肘部(elbows)。有限制铰链只能在预设的角度内旋转,无限制铰链则允许物体旋转无线圈。 89 | - ragdoll:身体上的约束,为模仿真实人体的关节运动而特别设计的,并使用约束链提高模拟的稳定性。 90 | - powered constraint:富动力约束,这个将在后文介绍。 91 | 92 | 骨骼关节的数量通常比布娃娃刚体的数量多,刚体与骨骼之间存在着映射关系,UBodySetupCore中的BoneName即表示刚体与skeletal mesh中的bone相关联。UE5中的刚体与骨骼之间的关系可以在物理资产中的Skeleton Tree中查看: 93 | 94 | ![](pictures\PhysicsAsset-2.png) 95 | 96 | 在UE5中,选择Constraint,可以配置右边的参数,对应的类是FConstraintInstance: 97 | 98 | ![](pictures\Constraint.png) 99 | 100 | ## 4.3 刚体状态(Rigid Body States) 101 | 102 | 动画表现可以分为三种类型: 103 | 104 | - 关键帧(KeyFramed):完全由动画驱动。通常禁用刚体上的碰撞,与其他物体碰撞时动画表现不能被修改。 105 | - 动力/动画(Powered/Animated):由动画驱动,但可以使用脉冲(impulses)或其他碰撞修改动画表现。物理跟随动画。 106 | - 无动力/模拟(Unpowered/Simulated):不能由动画驱动,只受重力和约束影响。 107 | 108 | **神秘海域4的GDC演讲**(https://www.youtube.com/watch?v=7S-_vuoKgR4)中介绍了两种控制器,分别用于Local Space和World Space控制,来实现物理跟随动画。两种控制分别是: 109 | 110 | - 富动力约束控制器(Powered Constraint Controller),也称为Motor。 111 | - 控制Local Space下的Rotation。Rotation Matching。 112 | - 难以匹配Animation Pose。 113 | - Havok物理引擎(他们使用的物理引擎)建议在死亡或垂死角色制作动画时使用Motor,因为Motor在Local Space中工作(控制joint,可以产生非常逼真的结果)。 114 | - 刚体控制器(Rigid Body Controller),也称为关键帧控制器(Keyframe Controller)。 115 | 116 | - 控制World Space下的Position。Position Matching。 117 | 118 | - 操纵单个刚体的速度以匹配动画。线性的。 119 | 120 | - Havok物理引擎建议将此控制器用于实时角色,因为它在World Space中工作。 121 | 122 | - 代码如下:(角速度也同样) 123 | 124 | ```c++ 125 | currentLinearVelocity = getRigidBodyLinearVelocity(myRB) 126 | currentLinearVelocity += Limit(poseLocalAcceleration, localAccelerationLimit) * localAccelerationGain 127 | currentLinearVelocity += Limit(poseParentAcceleration, parentAccelerationLimit) * parentAccelerationGain 128 | desiredVelocity = (currentRBPosition - desiredPosePosition) / deltaTime 129 | 130 | currentLinearVelocity += (desiredVelocity - currentLinearVelocity) * positionGain 131 | havokBody->setLinearVelocity(currentLinearVelocity) 132 | ``` 133 | 134 | **EA的GDC演讲**(https://www.gdcvault.com/play/1025210/Physics-Driven-Ragdolls-and-Animation)中也介绍了驱动物理跟随动画的三种方式: 135 | 136 | - 约束跟随: 137 | - 控制普通人类的极限,跟随动画有问题,但是互动好。 138 | - “变壮”之后,跟随动画好一点,但是互动不好。 139 | - 速度跟随: 140 | - 速度作为辅助:寻找动画希望我们在WorldSpace中到达的物理位置,根据所需时间,计算速度并应用。 141 | - 另外一种使用速度的方式:查看当前所处的动画姿势,然后查看下一个动画姿势,计算两者之间的delta并应用delta。 142 | - 约束和速度结合使用 143 | 144 | **星战的GDC演讲**(https://www.youtube.com/watch?v=TmAU8aPekEo)中也介绍了驱动物理跟随动画的三种方式: 145 | 146 | - Motors:布娃娃身体部位之间的关节约束部分,它们使用指定的动画目标来计算之间的局部受力,这个局部受力会驱动body跟随动画目标。 147 | - Velocities:计算在给定帧时间内,将body从一个点移动到另外一个点所需的速度,然后驱动body移向动画目标。在World Space下工作。 148 | 149 | - Constraints:在布娃娃的动态身体和动画目标之间创建一个约束,这个约束的参数可以驱动身体跟随动画目标。 150 | 151 | 神秘海域4的演讲其实可以概括为Rotation Matching和Position matching。在《游戏引擎架构》书中提到了富动力约束(powered constraint, 外部引擎系统(如动画系统)可以间接地控制布娃娃刚体的平移及定向),也就是神秘海域4中的控制器。可以瞅瞅https://github.com/hairibar/Hairibar.Ragdoll这个项目。 152 | 153 | 结合上面的内容,**驱动物理跟随动画(Drive Physics Follow Animation)**有四种方式,分别是Rotation Matching、Position Matching、Velocity Matching、Constraint Matching。而且这四种方式是相互独立的,所以可以结合起来使用,并且可以使用权重来控制占比。比如:神秘海域中的Motor Controller和Keyframe Controller可以一起使用,EA的速度和约束也可以一起使用。(注:可以说是四种方式,也可以说是两种、三种,这个不重要,重要是里面的方案.....) 154 | 155 | ## 4.4 碰撞(Collision) 156 | 157 | 在实现物理动画的时候,有必要对碰撞进行配置,这里简单介绍一下。 158 | 159 | 打开角色蓝图,点击Mesh,可以看到Collision配置: 160 | 161 | ![](pictures\Collision.png) 162 | 163 | **bGenerateOverlapEvents**:生成重叠事件 164 | 165 | **CanCharacterStepUpOn**, ECanBeCharacterBase:撞上的时候是否可以踩上去 166 | 167 | - No:角色无法站立其上 168 | - Yes:角色可以站立其上 169 | - Owner:父级决定是否站立其上,默认为true 170 | 171 | **碰撞类型(Collision Enabled)**,ECollisionEnabled: 172 | 173 | - NoCollision:不会在物理引擎中创建。不能用于空间查询(raycasts, sweeps, overlaps)或模拟(rigid body, constraints)。 174 | - QueryOnly:仅用于空间查询。无法用于模拟。 175 | - PhysicsOnly:仅用于模拟,不能用于空间查询。 176 | - QueryAndPhysics:可以用于空间查询和模拟。 177 | 178 | **物体类型(Object Type)**,ECollisionChannel:物体碰撞类型, 179 | 180 | **碰撞响应(Collision Responses)**,ECollisionResponse,每个参与碰撞的对象可以分为三类: 181 | 182 | - Ignore:可穿透,无事件通知。 183 | - Overlap:可穿透,有事件通知。 184 | - Block:不可穿透,有事件通知。 185 | 186 | 概括上面的内容,也就是:只有两个物体同时选择了Block,才会不可穿透。两个物体之间有一个Ignore,就是Ignore,无事件通知。 187 | 188 | 在物理资产界面,选择刚体,Details面板还有一个Use CCD参数,**Continuous Collision Detection**(CCD/连续碰撞检测):物体碰撞检测更精确,但是性能会下降。 189 | 190 | ## 4.5 配置文件(Profile) 191 | 192 | 物理动画配置文件(Physical Animation Profile) 193 | 194 | - 源码及注释已经讲得很清楚了,存储Bone需要的Motor信息。可以查看FPhysicalAnimationProfile类。 195 | 196 | ![](pictures\PA-4.png) 197 | 198 | 物理约束配置文件(Constraint Profiles) 199 | 200 | - 可以查看FConstraintProfileProperties类。 201 | 202 | ![](pictures\PA-5.png) 203 | 204 | # 5 UE5 物理动画介绍 205 | 206 | ## 5.1 动画更新流程 207 | 208 | 常规动画更新流程:输入状态,进行逻辑处理,控制器更新,最后输出动画姿势。 209 | 210 | ![](pictures\AnimationUpdate.png) 211 | 212 | 物理动画更新流程:输入和逻辑处理不变,控制器更新,然后把动画系统中得到的姿势发送给物理系统,告诉物理这是目标姿势,并将此姿势输入到物理模拟中,然后与环境一起求解,最后输出姿势。 213 | 214 | ![](pictures\PhysicsAnimationUpdate.png) 215 | 216 | ## 5.2 骨骼物理动画实现方式 217 | 218 | 在前面也介绍了很多基础概念,UE5中也提供了一些接口。点开上面的角色蓝图,我们可以对胶囊体和USkeletalMeshComponent进行物理设置,可以通过编写代码对UPhysicalAnimationComponent进行物理动画设置。与骨骼有关的是USkeletalMeshComponent和UPhysicalAnimationComponent,在动画蓝图中还有FAnimNode_RigidBody和FAnimNode_AnimDynamics节点,接下来会介绍这四个。 219 | 220 | 首先调用UWorld::CreatePhysicsScene()创建物理场(FPhysScene_Chaos)。如果组件设置bSimulatePhysics为true,则组件的位置由物理场景更新到游戏世界,否则,使用kinematic更新位置。 221 | 222 | ### 5.2.1 USkeletalMeshComponent 223 | 224 | 很多博主也对USkeletalMeshComponent做了很多介绍。 225 | 226 | 从代码上看,该组件定义了一个`TArray Bodies`,所以该组件可以有多个FBodyInstance。 227 | 228 | 在游戏开始运行时,首先调用USkeletalMeshComponent::OnCreatePhysicsState()创建物理状态,当bEnablePerPolyCollision为false时,调用InitArticulated()进行物理初始化。在该函数中调用InstantiatePhysicsAsset(),使用物理资产中的数据创建FBodyInstance数组类型的Bodies,具体实现可以看USkeletalMeshComponent::InstantiatePhysicsAsset_Internal()函数。 229 | 230 | 接下来打开PhysAnim.cpp文件。 231 | 232 | - 在游戏运行时,在USkeletalMeshComponent::PostAnimEvaluation()函数中,会调用UpdateKinematicBonesToAnim()根据动画的变换去更新物理数据,之后调用UpdateRBJointMotors()迭代物理中的关节。 233 | - 在UpdateRBJointMotors()中,首先需要将Mesh组件中的UpdateJointsFromAnimation参数设置成true,然后才会继续往下走。在该函数中,也是通过约束去限制物理模拟,最后会调用FConstraintInstance::SetAngularOrientationTarget()设置目标角朝向。所以这个只是在Rotation上做了匹配,单纯用这个组件,可能并不会达到特别好的效果,所以还需要叠加4.4章节中的其他方案。 234 | 235 | ![](pictures\SkeletalMesh-1.png) 236 | 237 | 在UE4 Advanced Locomotion System中也有Ragdoll的存在,在角色死亡时会触发Ragdoll。流程是,在Start时调用SetAllBodiesBelowSimulatePhysics()函数,开启Mesh的物理模拟。在Update时,调用SetAllMotorsAngularDriveParams()设置Angular驱动,之后更新Actor的位置(不然的话,Mesh和胶囊体的位置可能会分开)。 238 | 239 | SkeletalMeshComponent还提供了很多接口用于Motor驱动,比如:SetAllMotorsAngularPositionDrive()、SetAllMotorsAngularVelocityDrive()、SetAllMotorsAngularDriveParams()、SetNamedMotorsAngularPositionDrive()、SetNamedMotorsAngularVelocityDrive()等。 240 | 241 | ### 5.2.2 UPhysicalAnimationComponent 242 | 243 | 该组件用于处理物理动画,代码并不多。官方文档为https://docs.unrealengine.com/5.0/zh-CN/physics-driven-animation-in-unreal-engine/。 244 | 245 | 在上面的例子已经看到了,需要设置SkeletalMeshComponent,所以其实还是离不开上面的SkeletalMesh组件。 246 | 247 | FPhysicalAnimationData内部还有很多参数: 248 | 249 | - bIsLocalSimulation:控制驱动器目标是WorldSpace还是LocalSpace。也就是在神秘海域4 GDC中提到的两种控制器。 250 | - OrientationStrength:修正朝向的强度。 251 | - AngularVelocityStrength:修正角速度的强度。 252 | - PositionStrength:WorldSpace下,修正线性位置的强度。 253 | - VelocityStrength:WorldSpace下,修正线性速度的强度。速度匹配。 254 | - MaxLinearForce:修正线性的最大力。 255 | - MaxAngularForce:修正角度的最大力。 256 | 257 | UPhysicalAnimationComponent的一些参数: 258 | 259 | - StrengthMultiplyer:Active Motor强度系数。 260 | - TArray< FPhysicalAnimationInstanceData> RuntimeInstanceData:运行时数据的FPhysicalAnimationData数据。 261 | 262 | - TArray< FPhysicalAnimationData> DriveData:保存每个刚体的FPhysicalAnimationData数据。 263 | - bPhysicsEngineNeedsUpdating:物理引擎是否需要更新。 264 | 265 | 流程: 266 | 267 | - 首先需要调用SetSkeletalMeshComponent()设置Mesh组件,在该函数中会调用InitComponent(),之后调用UpdatePhysicsEngine(),给bPhysicsEngineNeedsUpdating赋值为true。 268 | - 调用ApplyPhysicalAnimationProfileBelow()应用物理数据配置文件,遍历刚体,调用UpdatePhysicalAnimationSettings(),应用PhysicalAnimationData数据,保存到**DriveData**中。然后调用UpdatePhysicsEngine()更新物理引擎(刚体)。 269 | 270 | - 调用SetAllBodiesBelowSimulatePhysics()设置要模拟的骨骼。 271 | - 组件Tick: 272 | - 当bPhysicsEngineNeedsUpdating为ture时会调用UpdatePhysicsEngineImp(),在该函数中会给**RuntimeInstanceData**赋值。 273 | - 调用UpdateTargetActors()更新位置。调用SetKinematicTarget_AssumesLocked()把RuntimeInstanceData.TargetActor(Kinematic Actor)移动到TargetTransform(动画数据)。 274 | 275 | 仅仅设置这些参数,效果还不是很好,会有些僵硬,所以还需要添加约束控制(在物理资产界面,添加Constraint Profiles, Constraint Matching)。在SetMotorStrength()函数内,会调用FConstraintInstance::SetAngularDriveParams()设置angular motor参数,会调用FConstraintInstance::SetLinearDriveParams()设置linear motor参数。在这里,可以通过参数传递定义知道,PositionStrength和OrientationStrength为Spring(Stiffness),VelocityStrength和AngularVelocityStrength为Damping, MaxLinearForce和MaxAngularForce为ForceLimit,使用的是物理引擎中的概念。 276 | 277 | ### 5.2.3 FAnimNode_RigidBody 278 | 279 | 在动画蓝图中,还提供了FAnimNode_RigidBody这样一个节点,该节点也是用于模拟含有物理资产的SkeletalMeshComponent。该节点用于轻量级物理模拟,模拟角色的SkeletalMesh上的辅助结构的运动,比如:马尾辫,链子或其他骨骼。具体使用可以参照官方文档https://docs.unrealengine.com/5.0/zh-CN/animation-blueprint-rigid-body-in-unreal-engine/。 280 | 281 | ![](pictures\RigidBody-1.png) 282 | 283 | ### 5.2.4 FAnimNode_AnimDynamics 284 | 285 | AnimDynamics也是一种轻量级的物理模拟方案,比RigidBody节点更轻量,可以让角色的部分骨骼网格体实现基于物理的附属动画。该节点可用于角色的项链、手镯、背包等物品。可以参考官方文档:https://docs.unrealengine.com/5.0/zh-CN/animation-blueprint-animdynamics-in-unreal-engine/。 286 | 287 | ![](pictures\AnimDynamics-1.png) 288 | 289 | ## 5.3 参数设置 290 | 291 | Edit -> Project Setting -> Engine -> Physics,这里可以对物理进行一些基本的配置,比如迭代次数等: 292 | 293 | ![](pictures\Setting-1.png) 294 | 295 | # 6 后记 296 | 297 | 物理很有趣!~ (●'◡'●) 298 | 299 | 在游戏中,加入物理能让我们和周边环境互动得更好,从而提升沉浸感。国外大厂在这方面花了很多时间,做了很多工作,在细节上打磨了很久,也建立了流程管线。 300 | 301 | 上面的三个GDC演讲里,还有很多干货,比如特殊情况下(攀爬、平衡木行走、伴随机器人)的物理处理、工具流、FreeRegion、SoftRegion、稳定模拟的方案、数值经验等,篇幅有限,就不在此一一介绍了,推荐去看看。EA的GDC演讲中也提到了在一些情况下使用MotionMathing来匹配动画(其实我觉得准确说应该是PoseMatching),然后叠加物理使效果更好,狂喜。 302 | 303 | 虽然给游戏添加物理,会使游戏更有趣,但是物理参数相互影响,需要反复调整,所以需要耐心,且需要物理工程师和动画师一起配合。在复杂情况下,做好优化变得更困难了。 304 | 305 | 物理动画,顾名思义,需要对物理、动画都要熟悉,至少对引擎中的物理和动画的相关实现熟悉。而且物理模拟也需要很多基础性知识与原理,想调好用好物理动画,物理、数学要好! 306 | 307 | Cascadeur是一款基于物理的动画制作软件,可以辅助动画师制作。 308 | 309 | 310 | 311 | 写于2022年9月25日。 -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Damping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Damping.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Hit1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Hit1.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Hit2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Hit2.png -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/pictures/Physics.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /UE/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent/【UE5】基于物理的角色动画(应用篇)之PhysicsControlComponent.md: -------------------------------------------------------------------------------- 1 | 本文包括上层流程、参数含义、示例分析、性能等。 2 | 3 | # 1. 基本概念 4 | 5 | 在上一篇文章中已经介绍过,动画表现可以分为三种类型:关键帧(KeyFramed)、动力/动画(Powered/Animated)、无动力/模拟(Unpowered/Simulated)。实现物理跟随动画,需要实现两种控制器,分别是Local Space控制和World Space控制。也提到了三篇GDC。 6 | 7 | 详见:[【UE5】基于物理的角色动画(基础篇)](https://zhuanlan.zhihu.com/p/568049696)4.3 刚体状态。 8 | 9 | UE 5.1的PhysicsControlComponent也是基于上面提到的概念,官方提供的示例工程也把GDC里面的案例实现了。好耶ヽ(✿゚▽゚)ノ 10 | 11 | # 2. PCC介绍 12 | 13 | 本文提到的PCC为PhysicsControlComponent的简称。 14 | 15 | ## 2.1 上层流程 16 | 17 | PCC可以控制管理多个部位,开放了更多的参数,比如:力、扭矩等,可以动态调整,把参数计算好,通过调用ConstraintInstance的接口实现。 18 | 19 | PCC使用EPhysicsMovementType来控制动画驱动类型,Static表示不使用动力学也不使用物理模拟控制,Kinematic表示使用动画控制,Simulated表示使用物理模拟控制。 20 | 21 | 流程:首先调用MakeControl()创建ControlRecord,用于记录目标点以及ControlData;调用MakeBodyModifier()创建Body修改器,用于控制EPhysicsMovementType;运行时通过修改ControlRecord和BodyModifier来控制运动。PCC组件提供了很多接口,都是在上层控制FPhysicsControlRecord和FPhysicsBodyModifier。ControlRecord控制WorldSpace和LocalSpace下的移动表现,BodyModifier控制上面提到的MovementType。 22 | 23 | ## 2.2 参数含义 24 | 25 | 只有理解每个参数的含义,才能更好的控制结果。 26 | 27 | 首先看一下UPhysicsControlComponent代码结构: 28 | 29 | - FPhysicsControlComponentImpl: 30 | - TMap PhysicsControlRecords;【Name的命名格式为`{ParentBoneName}_{ChildBoneName}_{Index}`】 31 | - FPhysicsControl PhysicsControl;控制的所有信息 32 | - FPhysicsControlData ControlData;弹簧阻尼等相关信息 33 | - FPhysicsControlMultipliers ControlMultipliers;FPhysicsControlData的倍数 34 | - FPhysicsControlTarget ControlTarget;目标点的位置、方向、速度等 35 | - FPhysicsControlSettings ControlSettings;常规参数,是否开启碰撞等 36 | - ...... 37 | - FPhysicsControlState PhysicsControlState;约束等 38 | - TMap PhysicsBodyModifiers:设置动力学目标点、重力影响、EPhysicsMovementType。【Name的命名格式`{BoneName}_{index}`或`Body_{index}`】 39 | 40 | 从上面的代码结构中,可以看到里面比较重要的结构体,接下来详细介绍一下这些结构体中的参数具体含义: 41 | 42 | 1、**FPhysicsControl**:Control结构,控制的Body为SkeletalMesh或StaticMesh。 43 | 44 | | 参数名 | 含义 | 45 | | ------------------- | ------------------------------------------------------------ | 46 | | ParentMeshComponent | 将进行驱动的父级Mesh,如果未设置,则表示此次控制在WorldSpace中进行。 | 47 | | ParentBoneName | 将进行驱动的父级SkeletalMesh的骨骼名或StaticMesh的名称。 | 48 | | ChildMeshComponent | 要控制的Mesh。 | 49 | | ChildBoneName | 要控制的SkeletalMesh的骨骼名或StaticMesh的名称。 | 50 | | ControlData | FPhysicsControlData类型,设置运动强度等参数,可在运行时动态修改。 | 51 | | ControlMultipliers | FPhysicsControlMultipliers类型,ControlData的倍数,通常会被动态修改。 | 52 | | ControlTarget | FPhysicsControlTarget类型,控制目标移动的位置和方向,可相对于ParentMesh。 | 53 | | ControlSettings | FPhysicsControlSettings类型,其他常规参数设置。 | 54 | 55 | 2、**FPhysicsControlData**:使用弹簧阻尼实现控制body朝着目标。 56 | 57 | | 参数名 | 含义 | 58 | | ------------------- | ------------------------------------------------------------ | 59 | | LinearStrength | 驱动线性运动的强度。也可称之为frequency,是一个伪频率值,是`w=2*PI*f`中的f,f值越小,周期时间越长,表示到达目标的时间越长。 | 60 | | LinearDampingRatio | 线性运动阻尼比。=0时表示无阻尼;=1时表示临界阻尼,会以最平稳的速度,最短的时间达到稳定平衡;>1时为过阻尼;<1时为欠阻尼,达成稳定平衡的时间较久。 | 61 | | LinearExtraDamping | 附加线性运动阻尼的值。如果LinearStrength为零,却想要添加阻尼,那么就需要此参数。(注:可以阅读ConvertStrengthToSpringParams()函数的实现,很清晰。) | 62 | | MaxForce | 驱动线性运动的最大力。零表示无限制。 | 63 | | AngularStrength | 驱动角运动的强度。同驱动线性运动的强度的作用。 | 64 | | AngularDampingRatio | 角运动阻尼比。同LinearDampingRatio的作用。 | 65 | | AngularExtraDamping | 附加角阻尼值。同LinearExtraDamping的作用。 | 66 | | MaxTorque | 驱动角运动的最大扭矩。零表示无限制,零扭矩。 | 67 | 68 | 3、**FPhysicsControlMultipliers**:用于修改FPhysicsControlData中的参数,优点在于:可以对每个轴分开设置(比如:水平驱动物体,然后让其在重力作用下下降);可以为例子设置默认的FPhysicsControlData参数,然后通过该结构进行缩放控制。 69 | 70 | | 参数名 | 含义 | 71 | | ----------------------------- | ------------------------------------------------------------ | 72 | | LinearStrengthMultiplier | 驱动线性运动的强度的各方向的倍率。 | 73 | | LinearExtraDampingMultiplier | 线性附加阻尼值的各个方向的倍率。 | 74 | | MaxForceMultiplier | 驱动线性运动的最大力的各方向的倍率。ResultMaxForce = MaxForce * MaxForceMultiplier。 | 75 | | AngularStrengthMultiplier | 驱动角运动的强度的倍率。 | 76 | | AngularExtraDampingMultiplier | 角附加阻尼值的倍率。 | 77 | | MaxTorqueMultiplier | 驱动角运动的最大扭矩的倍率。ResultMaxTorque = MaxTorque * MaxTorqueMultiplier。 | 78 | 79 | 4、**FPhysicsControlTarget**:定义目标位置、方向、速度和角速度。可以指定目标速度,也可以自动计算速度。速度会受FPhysicsControlData参数影响。 80 | 81 | | 参数名 | 含义 | 82 | | -------------------------- | ------------------------------------------------------------ | 83 | | TargetPosition | child body相对于parent body的目标位置。World Space下的parent body为Null。 | 84 | | TargetVelocity | child body相对于parent body的目标速度。 | 85 | | TargetOrientation | child body相对于parent body的目标方向。 | 86 | | TargetAngularVelocity | child body相对于parent body的目标角速度(每秒转数)。 | 87 | | bApplyControlPointToTarget | 是否将FPhysicsControlSettings.ControlPoint用作Target Transform以及physical body的偏移。计算公式为TargetPosition = TargetPosition + TargetOrientation * ControlPoint。 | 88 | 89 | 5、**FPhysicsControlSettings**:常规设置。 90 | 91 | | 参数名 | 含义 | 92 | | ----------------------------------- | ------------------------------------------------------------ | 93 | | ControlPoint | 相对于Child Mesh的位置。 | 94 | | bUseSkeletalAnimation | 如果为True且Skeletal Animation存在,则目标速度的结果依赖于骨骼动画顶部的速度。可见CalculateControlTargetData()函数。 | 95 | | SkeletalAnimationVelocityMultiplier | 骨骼动画速度倍率。 | 96 | | bDisableCollision | 约束中的碰撞状态设置。 | 97 | | bAutoDisable | 在Tick结束时是否需要设置PhysicsControlState.bEnabled为False。 | 98 | 99 | 6、**FPhysicsControlState**:每个FPhysicsControlRecord都有一个FPhysicsControlState,用来设置基础状态。 100 | 101 | | 参数名 | 含义 | 102 | | ------------------ | ------------------------------------------------------------ | 103 | | ConstraintInstance | 每个FPhysicsControlRecord对应一个约束。 | 104 | | bEnabled | 是否开启物理控制。 | 105 | | bPendingDestroy | 是否在下一刻删除该FPhysicsControlState,初始化时设置为false。 | 106 | 107 | 7、**FPhysicsBodyModifier**:为每个需要控制的BodyInstance都创建一个PhysicsBodyModifier。 108 | 109 | | 参数名 | 含义 | 110 | | -------------------------- | ------------------------------------------------------------ | 111 | | MeshComponent | 要修改Mesh。 | 112 | | BoneName | 要修改的SkeletalMesh的骨骼名或StaticMesh名字。 | 113 | | MovementType | Body的移动方式。Static表示不使用动力学也不使用物理模拟控制,Kinematic表示使用动画控制,Simulated表示使用物理模拟控制。 | 114 | | CollisionType | 碰撞类型。 | 115 | | GravityMultiplier | 重力系数。如果禁用了重力,则不会生效。 | 116 | | KinematicTargetPosition | EPhysicsMovementType为Kinematic,Body移动的目标位置。如果设置了bUseSkeletalAnimation,则应用于BoneName对应的骨骼。 | 117 | | KinematicTargetOrientation | EPhysicsMovementType为Kinematic,Body移动的目标方向。如果设置了bUseSkeletalAnimation,则应用于BoneName对应的骨骼。 | 118 | | bUseSkeletalAnimation | 如果为true,则目标将应用于SkeletalMesh的Root骨骼或BoneName对应的骨骼。 | 119 | | bPendingDestroy | 是否在下一刻删除该FPhysicsBodyModifier,初始化时设置为false。 | 120 | 121 | (建议还是过一遍代码,上层流程也不是很多......(●'◡'●) 122 | 123 | ## 2.3 示例分析 124 | 125 | 理解了参数,示例就能看懂了。 126 | 127 | 首先看一下父类BP_PhysicsControlCharacter流程: 128 | 129 | - 设置Profile:调用SetConstraintProfileForAll()设置约束Profile为HingesOnly。 130 | - 设置骨骼名链:设置LimbSetupData(Array,LimbName表示Key,StartBone表示开始骨骼名,用于创建从StartBone到末端的骨骼名链,IncludeParentBone表示骨骼名链是否包含父骨骼名),这里设置的骨骼链为左臂、右臂、头部、左腿、右腿和spine。然后调用GetLimbBonesFromSkeletalMesh()创建约束骨骼名链(Map,Key为LimbName,Value为FPhysicsControlLimbBones,即骨骼名链和SkeletalMeshComponent)。 131 | - 设置ControlRecord:调用MakeControlsFromLimbBones()为四肢创建WorldSpace下的ControlRecord和ParentSpace下的ControlRecord(注:这里的ParentSpace和GDC中提到的LocalSpace是一个概念。)。WorldSpace下设置LinearStrength和 AngularStrength,ParentSpace下设置AngularStrength,其他都是默认参数。 132 | - 设置BodyModifier:调用MakeBodyModifiersFromLimbBones()创建,设置默认MovementType为Static。 133 | 134 | 1、第一个例子最简单,该例子是控制WorldSpace下的方块运动。每次隔一段事件随机生成TargetPosition和TargetOrientation,之后通过调整ControlTarget的TargetPosition和TargetOrientation、ControlMultipliers、PhysicsBodyModifier的KinematicTarget和MovementType来实现移动。结果是,物理控制的方块会平滑移动到Target,无物理的效果是直接设置目标点。 135 | 136 | 2、CompoundBox:可使用PhysicsConstraintComponent来设置约束。 137 | 138 | 3、Driver:双腿设置为Kinematic。然后所有部位开启ParentSpace模拟,Spine开启WorldSpace模拟,控制世界空间的位置。之后设置Multiplier值。 139 | 140 | 4、HitReact:死亡的时候,设置的是默认Profile,身体受重力影响,所以表现起来躯体比较柔软,如果追求比较好的效果,还是需要修改profile参数。神秘海域中的示例,更自然。普通受击,可以设置冲量强度,调整起来比较方便。需要考虑碰撞。 141 | 142 | ![剪秋,我的头好痛?(⊙o⊙)?](pictures\Hit1.png) 143 | 144 | 5、Hanger:左边的角色开启了物理模拟,右边则是普通动画。开启物理模拟的角色也能更好的与环境碰撞产生反应。手部使用的是Kinematic类型,其他部位使用Simulated。手臂设置为WorldSpace控制,腿部则设置为ParentSpace控制。 145 | 146 | 6、Carry:使用MakeControl将被抱者和抱人者连接起来,将Strength设置得大一些即可实现。 147 | 148 | ## 2.4 代码流程 149 | 150 | TickComponent()流程: 151 | 152 | - 首先遍历FPhysicsControlRecord,如果没有约束,则根据FPhysicsControl信息创建一个ParentBody与ChildBody之间的约束;如果有约束,则调用ApplyControl()应用我们记录的FPhysicsControlRecord信息。在ApplyControl()中: 153 | 154 | - 调用SetDisableCollision()设置碰撞是否禁用。 155 | - 调用ApplyControlStrengths()计算约束需要的Spring、Damping参数并应用给约束。(注:Spring的计算推导可参考小木子大佬的文章:[游戏开发中的阻尼器和阻尼弹簧](https://zhuanlan.zhihu.com/p/461813778) The Half-life and the Frequency章节 ,Damping的计算没看懂,感觉少了点东西.....如果有同学知道,还请告诉我~) 156 | 157 | ![](pictures\Damping.png) 158 | 159 | - 调用CalculateControlTargetData()计算目标位置和目标速度。 160 | 161 | - 之后遍历FPhysicsBodyModifier,调用SetInstanceSimulatePhysics()设置不同的运动状态配置。对于Kinematic类型,还需调用ApplyKinematicTarget()应用Modifier,使用KinematicTarget给Body直接设置TargetTransform。然后调用SetShapeCollisionEnabled()为刚体设置碰撞类型。如果开启了物理模拟,还需要计算重力,并给BodyInstance设置重力影响。 162 | 163 | 然后就交给物理引擎去模拟了。 164 | 165 | # 3. 物理资产设置 166 | 167 | 好的表现是依托于物理资产的。物理资产调整是很复杂的,也直接影响最终效果。这个调整起来也是很麻烦,神秘海域制作团队也在Maya中建立了测试环境。 168 | 169 | 从示例工程中的物理资产中可以看到,只设置了三种约束类型,分别是: 170 | 171 | - RagdollNoDrives:关节设置为Limit。 172 | - NoLimits:不限制关节自由度。 173 | - HingesOnly:铰链(Hinge)约束,只有一个自由度,用于限制膝盖(thigh_x与calf_x之间的约束)和肘部(upperarm_x与lowerarm_x之间的约束)的一个轴(Twist)的常规约束。(人体模型或机器人学中有介绍)。 174 | 175 | 仔细观察示例,会发现有时候肢体表现得有些柔软。比如:Walker例子中,左臂碰到碰撞物时,左臂旋转的角度太大,肩胛部分还存在扭曲现象。所以,如果需要在项目中使用,物理资产也还需要进一步调整。 176 | 177 | 这个调整起来好复杂......(在学了在学了.jpg,研究完了再补充叭) 178 | 179 | # 4. 性能 180 | 181 | 在特定情况下使用物理动画,会给游戏带来更多的趣味性,所以如何灵活地控制物理动画的开启关闭以及Profile切换也是一个很重要的问题。 182 | 183 | 本机测试对比:DebugGame模式下,空地图是120帧左右;PhysicsControl地图是80帧左右。 184 | 185 | 部分参数设置: 186 | 187 | - Solver iterations(解算器的迭代次数):UE默认 8(position)和1(velocity),可根据项目情况调整,比如星球大战绝地设置的是64(position)和32(velocity)。 188 | - 在同一个布娃娃中禁用关节之间的CCD更好。 189 | 190 | 把这些例子单独拿出来测试后,问题不大,主要重心还是放在效果优化上。 191 | 192 | 使用物理动画的目标是,让模拟尽可能稳定,并且还要有尽可能好的表现。所以不能为了让求解器收敛,而在物理求解器上进行大量的迭代,因此要通过一些设置减少这些情况的发生。比如:沿着某个方向减少质量、提高惯性、、减少实体数量等。 193 | 194 | # 5. GDC其他例子介绍 195 | 196 | 由于自己的demo还在制作中以及资源限制,所以暂时无法实现,以后有条件了再实现。所以本节只总结GDC提到的内容。 197 | 198 | 1、抓取:hip使用Kinematic,躯干等其他部分由Motor驱动的。 199 | 200 | 2、死亡布娃娃: 201 | 202 | - 如果只在死亡动画的最后开启物理,当周围环境狭窄的时候,播放动画时就可能会产生穿模,动画也不自然。 203 | - 解决方案:身体所有部位都参与物理模拟,但是对Hip创建一个新的约束,约束设置在hip和给定的动画目标之间,这个新约束会拖动hip的物理身体去跟随动画位移,并且锁定新约束的自由度,这样能更好得匹配动画。如果遇到障碍物,动画目标依然在向前运动时,检测一下Root的实际位置和动画目标之间的距离,如果新约束无法抵达目标,则让布娃娃进入自由落体模式。 204 | 205 | 3、特殊移动:在动画姿势里将纯动画运动和Motor驱动的身体运动进行局部空间混合(local space blend)。 206 | 207 | - 滑动:混合物理姿态的50%。 208 | - 攀爬:混合物理姿态的40%。 209 | - 风原场景:混合物理姿态的45%。 210 | - 绳索:混合物理姿态的40%。 211 | - 平衡木悬挂:混合物理姿态的50%。 212 | 213 | 4、Locomotion: 214 | 215 | - 起步、停步和转身时,加入物理能够表现出惯性的感觉。 216 | 217 | - 只限制手臂在一定范围内自由摆动,当距离目标太远时,会被约束强制带回来。 218 | 219 | 5、伴随机器人: 220 | 221 | - 机器人腿部设置为Kinematic。 222 | 223 | - 主角速度很快时,限制机器人的运动,所以需要在机器人头部和动画目标之间添加新约束。当主角移动慢时,约束允许大范围的移动;当主角移动较快时,允许自由移动的距离就会变成接近零,从而确保头部接近动画目标;当主角移动速度过高时,则把限制自由移动的距离设置为0,约束切换到Lock模式。[注:应该可以通过设置MakeControl,然后调整Strength等参数来实现。] 224 | - 任何不需要碰撞检测的物理动画,比如机器人和大部分主角的动画,都会在自己独立的物理场景中进行处理。 225 | - 先运行主角身上的物理动画,然后根据主角动画的结果,再去配置机器人身上的物理动画。 226 | 227 | 6、水下:手部和腿部添加Damping用于模拟水的阻力。 228 | 229 | # 6. 其他 230 | 231 | Maya to DC Script Export:该工具可导出常规刚体的主要参数及其约束,Python脚本是一个好选择~脚本优点如下: 232 | 233 | - 不用接触资产管理器,默认规律依然存储在骨架上。 234 | - 方便添加和调整更多的参数。 235 | - 不需要重建游戏资产。 236 | - 快速原型制作(copy & paste FTW)。 237 | 238 | 更好地管理Profile,同一骨骼层次结构的角色,可以共享Profile。(如果能进行Profile文件之间的Blend,那就更好了~) 239 | 240 | 需要测试场景,需要美术先在DCC中测试并调整好物理资产,然后导入引擎,与物理工程师一起在工程测试场景中调整优化。 241 | 242 | 需要Debug工具,可以显示当前刚体和动画。 243 | 244 | 物理参数是相互影响,所以需要花很多时间去调整,请保持耐心~ 245 | 246 | 游戏运行需要注意物理动画和普通动画逻辑顺序。 247 | 248 | 尽可能支持AnimNotify控制。 249 | 250 | 支持向前模拟,看看是否即将发生任何碰撞,然后再接上我们的动画。比如:前方有墙的时候,不会撞到墙,或者用手撑着墙。这样看起来会更智能。 251 | 252 | 问题: 253 | 254 | - 物理模拟中提到了PhysicsBlendWeight,注释写的是0表示只使用动画,1表示只使用物理,不知道能不能设置成其他值来做纯动画姿势和物理姿势的融合? 255 | - GDC2018提到的肌肉状态工具? 256 | 257 | 写于2022年12月31日。 -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/Physics.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/AABBTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/AABBTree.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/ActorMove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/ActorMove.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/BVH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/BVH.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/BoundingVolume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/BoundingVolume.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/BroadPhase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/BroadPhase.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/CoreFunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/CoreFunction.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/CreateConstraintGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/CreateConstraintGraph.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/DestroyPhysicsState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/DestroyPhysicsState.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/DuringSim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/DuringSim.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/FParallelBlendPhysicsCompletionTask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/FParallelBlendPhysicsCompletionTask.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/FParallelBlendPhysicsTask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/FParallelBlendPhysicsTask.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/MidPhase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/MidPhase.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/PushPhysicsData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/PushPhysicsData.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/RegisterObject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/RegisterObject.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/SkeletalMeshAddToPhysicsScene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/SkeletalMeshAddToPhysicsScene.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/SpatialAccelerationCollection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/SpatialAccelerationCollection.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/StartSim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/StartSim.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/StaticMeshAddToPhysicsScene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/StaticMeshAddToPhysicsScene.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/TickFunction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/TickFunction.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/TickGroupShow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/TickGroupShow.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/UpdateAABB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/UpdateAABB.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/WorldTick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/WorldTick.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/创建物理场景.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/创建物理场景.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/创建物理状态.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/创建物理状态.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/pictures/加入物理场景接口.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/【UE5】物理系统源码分析/pictures/加入物理场景接口.png -------------------------------------------------------------------------------- /UE/【UE5】物理系统源码分析/【UE5】物理系统源码分析.md: -------------------------------------------------------------------------------- 1 | # 1. 前言 2 | 3 | 和之前的文章一样,主要还是以SkeletalMesh为例。本文包括物理初始化、物理更新、物理销毁、碰撞检测系统、以SkeletalmeshComponent例子分析物理场景与游戏场景交互、Chaos、碰撞检测等流程。 4 | 5 | 补充: 6 | 7 | - 物理相关的部分概念及刚体物理初始化流程可以先参考我的另一篇文章[【UE5】基于物理的角色动画(基础篇)](https://zhuanlan.zhihu.com/p/568049696)。 8 | - 如果有想了解动画系统的朋友,可以参考我的另一篇文章[【UE4】图解动画系统源码](https://zhuanlan.zhihu.com/p/446851284)。 9 | - 本文篇幅较长,是因为介绍了大量的流程代码,内容是从零开始介绍,然后逐渐深入一丢丢,结合代码阅读本文更佳。 10 | 11 | # 2. 流程介绍 12 | 13 | ## 2.1 物理初始化 14 | 15 | **创建物理场景:**首先在世界初始化时,调用UWorld::CreatePhysicsScene()创建物理场景FPhysScene_Chaos。物理场景提供了一些功能接口,包括碰撞事件通知、物理同步、添加力等,接口实现会调用chaos底层,其实也就是游戏场景和物理场景之间的一个交互,具体交互调用流程会在后面分析。 16 | 17 | ![创建物理场景流程](pictures\创建物理场景.png) 18 | 19 | **创建物理状态:**然后在组件注册时,调用USkeletalMeshComponent::OnCreatePhysicsState()创建物理状态。在该函数中,创建物理状态分为两种方案: 20 | 21 | - 一种方案是不启用逐三角面碰撞(EnablePerPolyCollision),这也是引擎默认的方案。在该方案下,会使用物理资产中的数据进行物理初始化。首先创建物理资产中的USkeletalBodySetup数量的Aggregate,之后使用物理资产中的SkeletalBodySetups和ConstraintSetup数据给SkeletalMeshComponent的**TArray Bodies**和**TArray Constraints** 进行赋值,最后初始化物理资产内部的实体之间的物理碰撞关系。 22 | - 另外一种方案则是启用逐三角面碰撞。在该情况下,使用SkeletalMesh的BodySetup数据进行初始化,此方案开销较大。 23 | 24 | ![创建物理状态流程](pictures\创建物理状态.png) 25 | 26 | 向物理引擎中的FPBDRigidsSolver中注册初始化刚体: 27 | 28 | ![](pictures\RegisterObject.png) 29 | 30 | **加入物理场景:**我们需要将对象传递给物理线程才可以进行模拟,该流程在创建物理状态时调用。有两种函数接口用于加入物理场景,分别是AddObject()和AddToComponentMaps(),在AddToComponentMaps()中做了物理代理和组件的一个映射。IPhysicsProxyBase类为物理代理基类,所有物理代理需要继承于该类。ComponentToPhysicsProxyMap存储组件到物理代理的映射,有的Actor组件可能会存在多组物理代理,比如SkeletalMeshComponent。PhysicsProxyToComponentMap存储物理代理到组件的映射。流程如下图所示: 31 | 32 | - 加入物理场景接口:FSkeletalMeshPhysicsProxy等继承于TPhysicsProxy, TPhysicsProxy继承于IPhysicsProxyBase。 33 | 34 | ![](pictures\加入物理场景接口.png) 35 | 36 | - StaticMesh: 37 | 38 | ![](pictures\StaticMeshAddToPhysicsScene.png) 39 | 40 | - SkeletalMesh: 41 | 42 | ![SkeletalMesh加入物理场景](pictures\SkeletalMeshAddToPhysicsScene.png) 43 | 44 | ## 2.2 物理更新 45 | 46 | UE5中有TickGroup概念,TickGroup指定在引擎的一帧中,该Tick在该帧中的执行顺序。具体解释请见官网:https://docs.unrealengine.com/5.0/zh-CN/actor-ticking-in-unreal-engine/。 47 | 48 | TickGroup可以分为以下几类: 49 | 50 | - TG_PrePhysics:在物理模拟开始前执行。 51 | - TG_StartPhysics:物理模拟开始执行。 52 | - TG_DuringPhysics:物理模拟并行执行。 53 | - TG_EndPhysics:物理模拟结束执行。 54 | - TG_PostPhysics:在刚体和布料模拟结束后执行。 55 | - TG_PostUpdateWork:帧内所有更新完成之后执行。 56 | - TG_LastDemotable:被延迟到最后执行。 57 | - TG_NewlySpawned:所有Tick组执行完毕后,对所有世界中新生成的物体Tick 58 | 59 | 流程如下所示: 60 | 61 | ![](pictures\WorldTick.png) 62 | 63 | 具体代码流程为,在每次**UWorld::Tick()**时: 64 | 65 | - **UWorld::SetupPhysicsTickFunctions()** 66 | - 设置StartPhysicsTickFunction和EndPhysicsTickFunction的bCanEverTick参数为true,启动Tick。 67 | 68 | - 如果未注册,则调用RegisterTickFunction()将StartPhysicsTickFunction注册到TG_StartPhysics组,将EndPhysicsTickFunction注册到TG_EndPhysics组。 69 | - 调用DeferredPhysResourceCleanup()执行物理引擎资源清理,目前仅对PHYSICS_INTERFACE_PHYSX处理。 70 | - 调用FChaosScene::SetUpForFrame()设置重力和FPhysicsSolver的MMaxDeltaTime、MMaxSubSteps(物理子步)、MMinDeltaTime。 71 | - **FTickTaskManager::StartFrame()** 72 | - 设置FTickContext Context。 73 | 74 | - 调用FTickTaskSequencer::StartFrame()重置数据,包括CleanupTasks、TickCompletionEvents、TickTasks、HiPriTickTasks、WaitForTickGroup。 75 | 76 | - 调用FTickTaskManager::FillLevelList()设置TArray LevelList。流程为遍历传入的参数Levels,将有效的Level添加至LevelList中。 77 | - 遍历LevelList,调用FTickTaskLevel::StartFrame()。 78 | - 设置FTickContext Context。 79 | - 调用FTickTaskLevel::ScheduleTickFunctionCooldowns()。根据Cooldown对TickFunctionsToReschedule进行升序排序,然后遍历TickFunctionsToReschedule,设置TickState为CoolingDown,然后添加至AllCoolingDownTickFunctions列表中,最后重置TickFunctionsToReschedule。 80 | - 遍历AllCoolingDownTickFunctions,确定此帧哪些CoolingDownTickFunction可以执行。 81 | - 遍历LevelList,调用FTickTaskLevel::QueueAllTicks()。 82 | - 遍历AllEnabledTickFunctions。 83 | - 调用FTickFunction::QueueTickFunction()。 84 | - 在该函数中遍历该TickFunction的前置条件TArray< struct FTickPrerequisite> Prerequisites,递归调用FTickFunction::QueueTickFunction()。然后使用FTickFunction调用GetCompletionHandle()获取FGraphEventRef对象,然后添加至前置任务TaskPrerequisites中。 85 | - 调用FTickTaskSequencer::QueueTickTask()。在该函数中调用StartTickTask()创建Task,然后调用AddTickTaskCompletion()将该Task添加到HiPriTickTasks或TickTasks数组中,将FGraphEventRef对象添加到TickCompletionEvents数组中。 86 | - 如果调用TickInterval>0, 则调用RescheduleForInterval()重新设置时间间隔。 87 | - 遍历AllCoolingDownTickFunctions。如果TickState为Enabled,则调用FTickFunction::QueueTickFunction()和FTickTaskLevel::RescheduleForInterval(),细节同上。 88 | - **UWorld::RunTickGroup(TG_PrePhysics)** 89 | - 调用UWorld::RunTickGroup() 90 | - 调用FTickTaskManager::RunTickGroup()。 91 | - 调用FTickTaskSequencer::ReleaseTickGroup()释放该TickGroup。在该函数中,调用FTickTaskSequencer::DispatchTickGroup(),遍历HiPriTickTasks\[WorldTickGroup][IndexInner]和TickTasks\[WorldTickGroup][IndexInner],并调用TGraphTask< FTickFunctionTask>::Unlock(),然后重置数组。回到上一级函数,阻塞等待,如果TickCompletionEvents[Block]数量大于0,调用WaitUntilTasksComplete()直到任务完成,然后调用ResetTickGroup()重置数据 92 | - 如果bBlockTillComplete为True,查询是否有新生成的Tick,如果无,则设置bFinished为true。 93 | - TickGroup更新。 94 | - 上面提到了将**StartPhysicsTickFunction**注册到TG_StartPhysics组,这里介绍一下该TickFunction的流程:入口为FStartPhysicsTickFunction::ExecuteTick(),然后调用UWorld::StartPhysicsSim(),再调用FChaosScene::StartFrame()发起物理模拟 95 | - 调用FPhysScene_Chaos::OnStartFrame()。 96 | - 调用FPhysicsReplication::Tick()。根据复制目标,更新body的状态。 97 | - 调用FPhysScene_Chaos::UpdateKinematicsOnDeferredSkelMeshes()。收集需要移动的Actor和Bodies的transform,并批量处理。 98 | - 广播OnPhysScenePreTick。 99 | - 广播OnPhysSceneStep。 100 | - 调用FChaosSolversModule::GetSolversMutable(),获取SolverList。如果GetSolver()存在,则将该值添加到SolverList中。 101 | - 遍历SolverList,调用Chaos::FPhysicsSolverBase::AdvanceAndDispatch_External()获取FGraphEventRef,然后将FGraphEventRef添加到CompletionEvents中。在AdvanceAndDispatch_External()函数中会调用SetSolverSubstep_External()设置物理子步,在后面会介绍。 102 | 103 | 104 | - **UWorld::RunTickGroup(TG_StartPhysics)** 105 | - 同上。 106 | - **UWorld::RunTickGroup(TG_DuringPhysics, false)** 107 | - 不阻塞等待,其余同上。 108 | - **UWorld::RunTickGroup(TG_EndPhysics)** 109 | - 同上。 110 | - 上面提到了将**EndPhysicsTickFunction**注册到TG_EndPhysics组,这里介绍一下该TickFunction的流程:入口为FEndPhysicsTickFunction::ExecuteTick() 111 | - 调用GetPhysicsScene()获取物理场景FPhysScene。 112 | - 调用GetCompletionEvents()获取物理完成事件FGraphEventArray。 113 | - 调用IsCompletionEventComplete()判断事件是否完成。如果未完成,则调用DontCompleteUntil()延迟事件。如果已完成,则调用FinishPhysicsSim(),最后调用FChaosScene::EndFrame()结束该帧的执行。 114 | - **UWorld::RunTickGroup(TG_PostPhysics)** 115 | - 同上。 116 | 117 | - **FTickTaskManager::EndFrame()** 118 | - 调用FTickTaskSequencer::EndFrame(),是否打印日志。 119 | - 设置bTickNewlySpawned为false。 120 | - 遍历LevelList数组,调用FTickTaskLevel::EndFrame()。在该函数中,调用FTickTaskLevel::ScheduleTickFunctionCooldowns(),细节同上。 121 | - 重置LevelList。 122 | 123 | 除了上面的介绍之外,还有很多的TickFunction,我们可以直接在代码中全局搜索来看到各TickGroup有哪些操作;也可以在代码执行时,通过查看FTickTaskLevel的AllEnabledTickFunctions、AllCoolingDownTickFunctions、AllDisabledTickFunctions、NewlySpawnedTickFunctions参数来获取。 124 | 125 | ![](pictures\TickGroupShow.png) 126 | 127 | AActor通过控制成员变量PrimaryActorTick来控制Tick的执行时序,这里总结一下各TickGroup常见的模块执行: 128 | 129 | - **TG_PrePhysics:**APawn、AActor、APlayerController、UMovementComponent、URadialForceComponent、USkeletalMeshComponent、USkeletalMeshComponent::ClothTickFunction、UNiagaraComponent、FSkinWeightProfileManager、USkinnedMeshComponent、UCharacterMovementComponent::PrePhysicsTickFunction、AGameMode、UTimelineComponent 130 | - **TG_StartPhysics:**UWorld::StartPhysicsTickFunction 131 | - **TG_DuringPhysics:**ALandmassActor、UUIFrameworkPlayerComponent、UNiagaraComponent、UActorComponent、ULensComponent、USceneCaptureComponentCube、UParticleSystemComponent、FParticleSystemWorldManager、AHUD、UGameplayTasksComponent、ALandscapeBlueprintBrushBase 132 | - **TG_EndPhysics:**UWorld::EndPhysicsTickFunction、USkeletalMeshComponent::EndPhysicsTickFunction 133 | - **TG_PostPhysics:**UPointCloudComponent、UDisplayClusterSyncTickComponent、UCharacterMovementComponent::PostPhysicsTickFunction、USkeletalMeshComponent的EndTickGroup、USpringArmComponent、UParticleSystemComponent的EndTickGroup、UPhysicsSpringComponent、UChaosEventListenerComponent、AChaosSolverActor、 134 | - **TG_PostUpdateWork:**AControlRigControlActor、AGameplayAbilityTargetActor_Trace、FSkinWeightProfileManager、UChaosDebugDrawComponent 135 | 136 | 继承TickFunction的结构体如下所示: 137 | 138 | ![](pictures\TickFunction.png) 139 | 140 | 141 | 142 | ## 2.3 物理结束 143 | 144 | 如果开启了物理动画,且SkeletalMeshComponent结束物理模拟后,会调用FSkeletalMeshComponentEndPhysicsTickFunction::ExecuteTick() -> USkeletalMeshComponent::EndPhysicsTickComponent(),在该函数中调用BlendInPhysicsInternal()进行物理Pose混合或调用SyncComponentToRBPhysics()使用物理Pose更新骨骼Pose。 145 | 146 | 当游戏结束运行时,会调用UWorld::FinishDestroy() -> UWorld::ReleasePhysicsScene()来释放World拥有的PhysicsScene。销毁Actor时会调用DestroyPhysicsState()。 147 | 148 | ![](pictures\DestroyPhysicsState.png) 149 | 150 | ## 2.4 物理子步 151 | 152 | 物理子步的开启及其他设置请参考官方文档https://docs.unrealengine.com/5.0/zh-CN/physics-sub-stepping-in-unreal-engine/。UE5的物理子步的流程也和UE4有一些不同。 153 | 154 | 由于UWorld::StartPhysicsSim()是由UWorld::Tick()调用的,所以物理更新也受World的DeltaTime影响。物理子步可以把一帧拆成多个SubStep来更新,这样提升了物理模拟的精确度,但也损耗了性能。 155 | 156 | 在UWorld::Tick() -> UWorld::SetupPhysicsTickFunctions() -> FChaosScene::SetUpForFrame()时进行物理子步参数的计算: 157 | 158 | - 计算物理子步模拟的总时间:MDeltaTime = FMath::Min(InDeltaSeconds, InMaxSubsteps * InMaxSubstepDeltaTime); 159 | - 调用SetMaxDeltaTime_External(InMaxSubstepDeltaTime)设置一次SubStep的最大时间。 160 | - 调用SetMaxSubSteps_External(InMaxSubsteps)设置物理子步的最大次数。 161 | - 调用SetMinDeltaTime_External(InMinPhysicsDeltaTime)设置最小物理时间。 162 | 163 | 在UWorld::StartPhysicsSim() -> FChaosScene::StartFrame() -> Chaos::FPhysicsSolverBase::AdvanceAndDispatch_External()中分发SubStep任务: 164 | 165 | - 调用SetSolverSubstep_External()设置是否启用物理子步。 166 | - 如果NumSteps大于0,则调用FPBDRigidsSolver::PushPhysicsState()函数设置物理状态。在该函数中,调用MarshallingManager.GetProducerData_External()获取FPushPhysicsData* ProducerData数据,进行部分设置,然后调用FChaosMarshallingManager::Step_External()继续设置ProducerData数据,之后在Step_External()函数中将ProducerData添加到TArray ExternalQueue数组中。一个SubStep对应一个ProducerData数据。 167 | - 在While循环中,不断调用MarshallingManager.StepInternalTime_External()获取我们在上一步设置的ProducerData。然后调用TGraphTask< FPhysicsSolverProcessPushDataTask>::CreateTask()创建任务,并添加到Prereqs中。之后调用TGraphTask< FPhysicsSolverAdvanceTask>::CreateTask()创建任务。 168 | 169 | 上面已经为每一个SubStep创建完任务,接下来便是任务的调度,后面会详细介绍: 170 | 171 | - FPhysicsSolverProcessPushDataTask::ProcessPushData() 172 | - FPhysicsSolverAdvanceTask::AdvanceSolver() 173 | 174 | ## 2.5 物理交互 175 | 176 | 其实前面也能总结出来物理场景与游戏场景交互,这里用USkeletalMeshComponent例子帮助理解。流程如下: 177 | 178 | - 物理模拟前。将几何数据发送到PhysicsScene。在上面的物理初始化流程中,已经知道USkeletalMeshComponent从调用OnCreatePhysicsState(),到调用FPhysScene_Chaos::AddToComponentMaps()加入物理场景。 179 | 180 | - 物理模拟开始。动画更新后,将骨骼数据发送给物理场景。调用MarkForPreSimKinematicUpdate()标记更新,DeferredKinematicUpdateSkelMeshes存储组件数据,流程如下: 181 | 182 | ![](pictures\StartSim.png) 183 | 184 | - 物理模拟中。在StartPhysicsSim()时,会调用UpdateKinematicsOnDeferredSkelMeshes()进行更新,在该函数中:(PS:这一块的内容较多,会在后面详细介绍......) 185 | 186 | - 遍历DeferredKinematicUpdateSkelMeshes,如果没有开启逐三角面碰撞,将每一个有效的Body的IPhysicsProxyBase存储在ProxiesToDirty数组中;如果开启了,则调用UpdateKinematicBonesToAnim()直接更新。 187 | - 当ProxiesToDirty数组的数量大于0时,获取ProxiesToDirty[0]的解算器,然后将代理数组作为参数传递给解算器。 188 | - 获取SkelComp的数据,设置Chaos::FRigidBodyHandle_External。当我们添加一个力的时候,FBodyInstance::AddForce() -> FPhysScene_Chaos::AddForce_AssumesLocked() -> TThreadedSingleParticlePhysicsProxyBase(FRigidBodyHandle_External的父类)::AddForce()。 也就是说,该Handle也提供了一些接口用于响应游戏场景的调用,直到最后影响PBDRigidParticles。该Handle是游戏场景和物理场景数据交互的重要角色。 189 | 190 | - 调用UpdateActorsInAccelerationStructure()更新加速结构。 191 | 192 | - 重置DeferredKinematicUpdateSkelMeshes。 193 | 194 | ![](pictures\DuringSim.png) 195 | 196 | - 物理模拟结束。会触发FSkeletalMeshComponentEndPhysicsTickFunction执行物理Pose与骨骼Pose的处理。以调用的BlendInPhysicsInternal()为例,当bParallelBlend为true时候,调用SwapEvaluationContextBuffers()交换AnimEvaluationContext和组件缓存的动画数据,FAnimationEvaluationContext即缓存数据,然后创建FParallelBlendPhysicsTask、FParallelBlendPhysicsCompletionTask任务。 197 | 198 | - FParallelBlendPhysicsTask::DoTask() -> ParallelBlendPhysics() -> PerformBlendPhysicsBones()。更新Pose。在PerformBlendPhysicsBones()函数中,遍历InRequiredBones,调用GetUnrealWorldTransform_AssumesLocked()获取物理姿势PhysTM(该函数最终调用获取的是Handle的数据),当物理权重大于0时,然后调用UpdateWorldBoneTM()递归更新WorldBoneTMs而获取ParentWorldTM,计算PhysTM相对父骨骼的Transform,然后Blend Transform,并存放到AnimEvaluationContext中。 199 | 200 | ![](pictures\FParallelBlendPhysicsTask.png) 201 | 202 | - FParallelBlendPhysicsCompletionTask::DoTask() -> CompleteParallelBlendPhysics()。SwapEvaluationContextBuffers()会交换回AnimEvaluationContext和缓存的动画数据,AnimEvaluationContext现在存储的是更新后的组件缓存的动画数据。调用FinalizeAnimationUpdate()完成动画更新,推送给渲染线程。 203 | 204 | ![](pictures\FParallelBlendPhysicsCompletionTask.png) 205 | 206 | - 还有,当角色移动时,调用OnMovementUpdated()时,最后会调用SendPhysicsTransform()将组件的世界变换发送给物理场景。流程如下: 207 | 208 | ![](pictures\ActorMove.png) 209 | 210 | # 3. Chaos模块 211 | 212 | ps:本节不会提及过多的理论知识,相关理论知识会标注来源,可自行选择查看。仅介绍Chaos底层的一些重要的数据结构及接口。 213 | 214 | ## 3.1 核心数据 215 | 216 | 前面的章节阅读完之后,已经了解了游戏场景和物理场景的上层交互流程。 217 | 218 | Chaos模拟是基于位置的,PBD的理论知识请阅读[【深入浅出 Nvidia FleX】(1) Position Based Dynamics](https://zhuanlan.zhihu.com/p/48737753)。 219 | 220 | 通过上文,可以知道游戏世界和物理引擎的交互通过Task实现了多线程。 221 | 222 | 在Chaos引擎中,刚体是使用粒子来描述的。以刚体为例,其继承顺序为: 223 | 224 | - TParticles:粒子父类。 225 | 226 | - TGeometryParticlesImp:包含几何参数的粒子。 227 | 228 | - TKinematicGeometryParticlesImp:包含运动参数的粒子。 229 | 230 | - TRigidParticles:刚体。 231 | 232 | 和粒子相对应的还有Handle,通过Handle来获取粒子,从而对粒子的数据进行处理。以刚体为例,其继承顺序为: 233 | 234 | - TParticleHandleBase 235 | - TGeometryParticleHandleImp 236 | - TKinematicGeometryParticleHandleImp 237 | - TPBDRigidParticleHandleImp 238 | 239 | 在Handle里还可以看到Proxy成员,Proxy连接着游戏世界和物理世界。以刚体为例,其继承顺序为: 240 | 241 | - IPhysicsProxyBase 242 | - FSingleParticlePhysicsProxy:(using FPhysicsActorHandle = Chaos::FSingleParticlePhysicsProxy*;) 243 | - TThreadedSingleParticlePhysicsProxyBase 244 | - FRigidBodyHandle_External,FRigidBodyHandle_Internal 245 | 246 | 还有很重要的一个概念是解算器。FPBDRigidsSolver继承于FPhysicsSolverBase,是物理计算的核心,从命名也可以看出来是基于PBD的。 247 | 248 | 在前面的物理子步章节中提到了物理的Task流程,我们提到了FPhysicsSolverProcessPushDataTask::ProcessPushData(),这个Task会传递FPushPhysicsData参数。FPushPhysicsData保存着需要更新到物理场景的数据以及一些Callback。 249 | 250 | ![](pictures\PushPhysicsData.png) 251 | 252 | 我们还提到了FPhysicsSolverAdvanceTask::AdvanceSolver(),在该函数中,会调用到Chaos的核心函数AdvanceOneTimeStepImpl()。 253 | 254 | ![](pictures\CoreFunction.png) 255 | 256 | FChaosMarshallingManager:从成员变量就可以看出,它管理着FPushPhysicsData。 257 | 258 | ## 3.2 碰撞检测系统 259 | 260 | 游戏引擎的碰撞系统紧密地和物理引擎整合。相关理论知识可查看《游戏引擎架构》12.3节,本节不会提及过多的理论知识。 261 | 262 | 从AdvanceOneTimeStepImpl()来看: 263 | 264 | - Chaos::FPBDRigidsEvolutionGBF::Integrate():对FPBDRigidsSOAs这块内存中的粒子进行操作,在每次迭代时更新刚体的位置、形状、加速度、AABB等。 265 | 266 | ![update AABB](pictures\UpdateAABB.png) 267 | 268 | - 加速结构类型: 269 | 270 | - AABBTree: 271 | 272 | ![](pictures\AABBTree.png) 273 | 274 | - BoundingVolume: 275 | 276 | ![](pictures\BoundingVolume.png) 277 | 278 | 279 | 280 | - BoundingVolumeHierarchy: 281 | 282 | ![](pictures\BVH.png) 283 | 284 | - SpatialAccelerationCollection: 285 | 286 | ![](pictures\SpatialAccelerationCollection.png) 287 | 288 | - 碰撞检测阶段: 289 | 290 | - 粗略阶段(broad phase):使用轴对齐包围盒(AABB)粗略判断哪些物体有机会碰撞。 291 | 292 | ![](pictures\BroadPhase.png) 293 | 294 | - 中间阶段(midphase):检测复合形状的逼近包围体。在下一级FSingleShapePairCollisionDetector::GenerateCollisionImpl()函数中,会调用Collisions::UpdateConstraint()更新narrow phase的约束。生成碰撞的流程如下所示: 295 | 296 | ![](pictures\MidPhase.png) 297 | 298 | - 精确阶段(narrow phase):通过精确计算接触点,穿透距离等检测两个物体是否真的相交,并创建出对应的碰撞约束。 299 | 300 | - 在GeometryQueries.h文件中可以看到两种查询方式,分别是OverlapQuery和SweepQuery。以UCharacterMovementComponent::ComputeFloorDist()为例,然后调用UWorld::SweepSingleByChannel(),然后调用物理层接口,最后调用到GJKRaycast2()执行检测。 301 | - 其他没看.... 302 | 303 | - 创建约束图:描述粒子之间的关系。Edge为约束,Node为粒子。遍历约束图,把解算会受到影响的物体划分在一个island间,不会影响的物体在不同的island中,不同island可以并行。使用bIsSleeping设置island是否处于休眠状态,来决定island是否参与后续的解算。 304 | 305 | ![](pictures\CreateConstraintGraph.png) 306 | 307 | FPBDIslandGroupManager:SolveGroupConstraints():解算约束,包括ApplyPositionConstraints()、ApplyVelocityConstraints()、ApplyProjectionConstraints()。在ApplyPositionConstraints()时,会调用ApplyFrictionCone()对摩擦进行处理,当超过静摩擦(static friction cone)会clip to动摩擦(dynamic friction cone)。 308 | 309 | 310 | 311 | 物理模块的东西还有好多好多好多没有写,比如:破碎、更详细的碰撞检测流程等,后面详细看了再更~ 312 | 313 | 如有误,还请指正~ 314 | 315 | 写于2022年11月6日 316 | -------------------------------------------------------------------------------- /UE/图解Chaos源码.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/UE/图解Chaos源码.pdf -------------------------------------------------------------------------------- /【游戏开发】动画技术文章合集.md: -------------------------------------------------------------------------------- 1 | # 前言 2 | 3 | 最近在研究游戏角色动画技术,发现动画相关的介绍很多,所以整理下,也方便自己进一步的学习~ 4 | 5 | 强烈建议先看一下《游戏引擎架构》中的第11章动画系统。 6 | 7 | 本文搜集的内容主要来源于GDC、SIGGRAPH、知乎、游戏编程精粹系列等。(大部分应用于UE4/UE5)。 8 | 9 | # ALS(Advanced Locomotion System) 10 | 11 | 技术简介:搞动画方向的应该没人不知道ALS叭?(→_→) ALS是UE商城的一个第三方插件,支持行走、奔跑、原地转身、奔跑急停转向、IK、Ragdoll等功能。非常适合新手学习~ 12 | 13 | [刘舒畅:UE4高级运动系统(Advanced Locomotion System V3)插件分析](https://zhuanlan.zhihu.com/p/101477611) 14 | 15 | [呆晒晒:UE4 Advanced Locomotion System V3中的动画表现设计](https://zhuanlan.zhihu.com/p/117405363) 16 | 17 | [Vivera Gu:UE4 Advanced Locomotion System V4学习笔记01](https://zhuanlan.zhihu.com/p/159646345) 18 | 19 | [Vivera Gu:UE4 Advanced Locomotion System V4学习笔记02](https://zhuanlan.zhihu.com/p/160789836) 20 | 21 | [Vivera Gu:UE4 Advanced Locomotion System V4学习笔记03](https://zhuanlan.zhihu.com/p/165218480) 22 | 23 | [Vivera Gu:UE4 Advanced Locomotion System V4学习笔记04](https://zhuanlan.zhihu.com/p/172330735) 24 | 25 | [bluerose:AdvancedLocomotionV4学习笔记(1)——动画节点](https://zhuanlan.zhihu.com/p/141266454) 26 | 27 | [bluerose:AdvancedLocomotionV4学习笔记(2)——AnimModifier](https://zhuanlan.zhihu.com/p/141825438) 28 | 29 | [bluerose:AdvancedLocomotionV4学习笔记(3)——思路整理](https://zhuanlan.zhihu.com/p/158516073) 30 | 31 | [bluerose:AdvancedLocomotionV4学习笔记(4)——8方向运动](https://zhuanlan.zhihu.com/p/158516073) 32 | 33 | [bluerose:AdvancedLocomotionV4学习笔记(5)——FootIK实现](https://zhuanlan.zhihu.com/p/192473710) 34 | 35 | # Motion Matching 36 | 37 | 技术简介:Motion Matching是新的动画技术,2016年GDC时由育碧提出,在荣耀战魂、最后生还者2、Control等游戏中应用,现在已经成为很多3A游戏的标配。使用该方案可以减少动画师的工作,程序维护状态机也比较方便。UE商城里提供了一个第三方插件,为MotionSymphony,是付费的。UE5-main分支也提供了两个插件,分别是MotionTrajectory插件和PoseSearch插件,可以用于实现Motion Matching功能。育碧后来又把神经网络和传统的Motion Matching结合起来,提出了Learned Motion Matching。 38 | 39 | [网易游戏雷火事业群:Motion Matching 技术介绍和实现](https://zhuanlan.zhihu.com/p/136971426) 40 | 41 | [洛城:Motion Matching回答](https://www.zhihu.com/question/405291121/answer/1342723194) 42 | 43 | [徐凯鸣:【UE4】给Animation加一点料 - Motion Matching篇](https://zhuanlan.zhihu.com/p/315908910) 44 | 45 | [sincezxl:UE4实现MotionMatching(上)](https://zhuanlan.zhihu.com/p/370138470) 46 | 47 | [sincezxl:UE4实现MotionMatching(下)](https://zhuanlan.zhihu.com/p/371201080) 48 | 49 | [小木子:《荣耀战魂》 中的MotionMatching](https://zhuanlan.zhihu.com/p/401890149) 50 | 51 | [小木子:《最后生还者2》 中的MotionMatching](https://zhuanlan.zhihu.com/p/403923793) 52 | 53 | [小木子:《Control》 中的MotionMatching](https://zhuanlan.zhihu.com/p/405873194) 54 | 55 | [小木子:Motion Matching 中的代码驱动移动和动画驱动移动](https://zhuanlan.zhihu.com/p/432663486) 56 | 57 | [奶帆:简单聊聊Motion Matching](https://zhuanlan.zhihu.com/p/378189446) 58 | 59 | [【GDC 2016】Motion Matching and The Road to Next-Gen Animation](https://www.gdcvault.com/play/1023280/Motion-Matching-and-The-Road) 60 | 61 | [【GDC 2016】Motion Matching, The Future of Games Animation... Today](https://www.youtube.com/watch?v=KSTn3ePDt50) 62 | 63 | [[UnrealCircle深圳]《黑神话:悟空》的Motion Matching | 游戏科学 招文勇](https://www.bilibili.com/video/BV1GK4y1S7Zw) 64 | 65 | [Learned Motion Matching](https://montreal.ubisoft.com/en/learned-motion-matching-2/) 66 | 67 | [Introducing Learned Motion Matching](https://montreal.ubisoft.com/en/introducing-learned-motion-matching/) 68 | 69 | [冰淇淋狗:Learned Motion Matching](https://zhuanlan.zhihu.com/p/266755281) 70 | 71 | # Motion Warping 72 | 73 | 技术简介:可以动态调整角色的根运动,以便对其目标位置,解决动画适配问题。 74 | 75 | [虚幻引擎文档:Motion Warping](https://docs.unrealengine.com/5.0/en-US/AnimationFeatures/MotionWarping/) 76 | 77 | [刘舒畅:UE5 Motion Warping(运动扭曲) 原理剖析及UE4适配](https://zhuanlan.zhihu.com/p/378948277) 78 | 79 | # Distance Matching 80 | 81 | 技术简介:在Paragon里面使用的技术,主要解决起步、停步、急转向、原地转弯适配。 82 | 83 | [Zero:UE4 Locomotion 距离适配(Distance Matching)](https://zhuanlan.zhihu.com/p/357650112) 84 | 85 | [Paragon Feature Examples: Animation Techniques | Feature Highlight | Unreal Engine](https://www.youtube.com/watch?v=1UOY-FMm-xo) 86 | 87 | [[Nucl.ai 2016] Bringing a Hero from Paragon to Life with UE4](https://www.youtube.com/watch?v=YlKA22Hzerk) 88 | 89 | [Distance Matching Locomotion](https://www.youtube.com/playlist?list=PL8-kndpxmv9iH0t3lnAobGuIKdb7UnwKS) 90 | 91 | # Speed Warping 92 | 93 | 技术简介:解决移动速度和动画步伐(跨度)适配。 94 | 95 | [Zero:UE4 Locomotion 速度适配(Speed Warping)](https://zhuanlan.zhihu.com/p/363656339) 96 | 97 | [Paragon Feature Examples: Animation Techniques | Feature Highlight | Unreal Engine](https://www.youtube.com/watch?v=1UOY-FMm-xo) 98 | 99 | # Slope Warping 100 | 101 | [Paragon Feature Examples: Animation Techniques | Feature Highlight | Unreal Engine](https://www.youtube.com/watch?v=1UOY-FMm-xo) 102 | 103 | # Seam Carving 104 | 105 | [Image Resizing by Seam Carving](https://www.youtube.com/watch?v=6NcIJXTlugc) 106 | 107 | # IK(Inverse kinematics) 108 | 109 | 技术简介:IK(逆向运动学)的算法比较多,包括Two Bone IK、CCDIK、FABRIK等。 110 | 111 | [爱吃菠萝不吃萝卜:【游戏开发】逆向运动学(IK)详解](https://zhuanlan.zhihu.com/p/499405167) 112 | 113 | [架狙只打脚:[UE4蓝图]虚幻4中完整实现脚部IK(一)](https://zhuanlan.zhihu.com/p/84399021) 114 | 115 | [大侠刘茗:UE4[C++]IK(逆向运动学)的实现方法](https://zhuanlan.zhihu.com/p/42140318) 116 | 117 | [Simple Two Joint](https://theorangeduck.com/page/simple-two-joint) 118 | 119 | [奶帆:UE4动画系统的那些事(二):使用Two Bone IK处理多根骨骼的手臂](https://zhuanlan.zhihu.com/p/142091001) 120 | 121 | [sincezxl:UE4实现基于预测的FootIK](https://zhuanlan.zhihu.com/p/380222928) 122 | 123 | [虚幻引擎文档:FullBodyIK](https://docs.unrealengine.com/5.0/en-US/AnimationFeatures/PBIK/) 124 | 125 | [sincezxl:UE5 FullBodyIK学习](https://zhuanlan.zhihu.com/p/381469750) 126 | 127 | [开发游戏的老王:[玩转UE4/UE5动画系统>Control Rig篇] 之 Control Rig + Fullbody IK版的足部IK实现(附项目代码)](https://zhuanlan.zhihu.com/p/412251528) 128 | 129 | [Full Body IK: Procedural Dragon Animations](https://www.youtube.com/watch?v=Z8eqaFG7lZQ) 130 | 131 | [左加右:UE动画后处理IK之CCDIK算法分析](https://zhuanlan.zhihu.com/p/469221237) 132 | 133 | [连冠荣(Mac):逆运动学(六): FABRIK](https://zhuanlan.zhihu.com/p/361520144) 134 | 135 | [Melody:UE4 C++手部扶墙IK](https://zhuanlan.zhihu.com/p/335357497) 136 | 137 | [pdf - IK survey](http://www.andreasaristidou.com/publications/papers/IK_survey.pdf) 138 | 139 | [【GDC 2016】Fitting the World: A Biomechanical Approach to Foot IK](https://www.gdcvault.com/play/1023316/Fitting-the-World-A-Biomechanical) 140 | 141 | 《游戏编程精粹3》:2.5 受限的逆运动学 142 | 143 | 《游戏编程精粹4》:2.6 应用于反向运动的雅可比转置方法 144 | 145 | 《游戏编程精粹4》:3.5 在动力学模拟中的快速接触消除法 146 | 147 | 《游戏编程精粹8》:2.3 Non-Iterative, Closed-Form, Inverse Kinematic Chain Solver (NCF IK) 148 | 149 | # Control Rig 150 | 151 | [虚幻引擎文档:Control Rig](https://docs.unrealengine.com/5.0/en-US/AnimationFeatures/ControlRig/) 152 | 153 | [Yene:介绍一下Control Rig方案](https://zhuanlan.zhihu.com/p/353524801) 154 | 155 | [Melody:UnrealEngine 程序化对白——ControlRig表情绑定](https://zhuanlan.zhihu.com/p/374378385) 156 | 157 | # 骨骼 158 | 159 | [启思:浅谈骨骼动画技术原理(一):基本介绍](https://zhuanlan.zhihu.com/p/431446337) 160 | 161 | [n5:Skinned Mesh原理解析和一个最简单的实现示例](https://happyfire.blog.csdn.net/article/details/3105872) 162 | 163 | [pdf - Skinned Mesh Character Animation With Directx9.0c](https://www.gamedevs.org/uploads/skinned-mesh-and-character-animation-with-directx9.pdf) 164 | 165 | [爱吃菠萝不吃萝卜:【UE4】图解动画系统源码](https://zhuanlan.zhihu.com/p/446851284) 166 | 167 | 《游戏编程精粹3》:4.8 改良的骨节变换计算 168 | 169 | 《游戏编程精粹3》:4.9 针对真实人物运动的架构 170 | 171 | 《游戏编程精粹4》:5.14 基于骨骼的有关节的3D角色的快速碰撞检测 172 | 173 | 《游戏编程精粹7》:减少骨骼动画中的累积误差 174 | 175 | # 优化 176 | 177 | [Zero:虚幻4的服务器动画优化指北](https://zhuanlan.zhihu.com/p/382058353) 178 | 179 | [玄冬Wong:[UE4]性能优化指南(美术向)](https://zhuanlan.zhihu.com/p/55335653) 180 | 181 | # 压缩 182 | 183 | [【GDC 2017】Simple and Powerful Animation Compression](https://www.youtube.com/watch?v=85uOa2m_kBc) 184 | 185 | 《游戏编程精粹4》:5.13 动作捕捉数据的压缩 186 | 187 | 《游戏引擎架构》:11.8 压缩技术 188 | 189 | # 渲染 190 | 191 | [chenjd:利用GPU实现大规模动画角色的渲染](https://zhuanlan.zhihu.com/p/28159739) 192 | 193 | 《GPU精粹3》:第2章 群体动画渲染 194 | 195 | # 动作捕捉 196 | 197 | [【GDC 2015】The Motion Capture Pipeline of The Last of Us](https://www.youtube.com/watch?v=2GoDlM1Z7BU):动作捕捉管道 198 | 199 | [【GDC 2017】Motion Capture Performance: An Actor's Approach](https://www.youtube.com/watch?v=er4SgnjDYMI):动作捕捉演员、提高动作捕捉性能 200 | 201 | # 物理 && 人工智能 202 | 203 | [【GDC 2020】Machine Learning Summit: Ragdoll Motion Matching](https://www.youtube.com/watch?v=JZKaqQKcAnw) 204 | 205 | [呆晒晒:GDC 2020技术导览-Ragdoll Motion Matching](https://zhuanlan.zhihu.com/p/129009008) 206 | 207 | [Supertrack Motion Tracking For Physically Simulated Characters Using Supervised Learning/](https://montreal.ubisoft.com/en/supertrack-motion-tracking-for-physically-simulated-characters-using-supervised-learning/) 208 | 209 | [【GDC 2020】Physical Animation in Star Wars Jedi: Fallen Order](https://www.youtube.com/watch?v=TmAU8aPekEo):物理动画、碰撞、速度驱动 210 | 211 | [【GDC 2017】Physics Animation in Uncharted 4: A Thief's End](https://www.youtube.com/watch?v=7S-_vuoKgR4):物理对象、物理模拟、Rigid Bodies 212 | 213 | [左丘六校:基于物理的角色运动系统-Unity实现](https://zhuanlan.zhihu.com/p/64416755) 214 | 215 | # 程序化动画 216 | 217 | [Yene:程序化动画流水帐](https://zhuanlan.zhihu.com/p/279750480) 218 | 219 | 《游戏引擎架构》:11.7.1 程序式动画 220 | 221 | # GDC 222 | 223 | 前面动画技术章节里没有列举到的在这里列举,以关键词介绍...... 224 | 225 | [【GDC 2018】Animation Isn't the Answer](https://www.youtube.com/watch?v=i2Vgq0RVG7I):动画师技术化 226 | 227 | [【GDC 2018】A Practical Approach to Developing Forward-Facing Rigs, Tools and Pipelines](https://www.youtube.com/watch?v=3l5tgk8hRn4):工具、管道、自动化、插件化、Maya 228 | 229 | [【GDC 2018】Animation Bootcamp: Bringing Life to the Machines of Horizon Zero Dawn](https://www.youtube.com/watch?v=50mIKB-NACU):动画制作过程、AI行为 230 | 231 | [【GDC 2014】Animation Bootcamp: Animation Prototyping for Games](https://www.youtube.com/watch?v=a-zKMzboOec):动画原型制作、重定向 232 | 233 | [【GDC 2017】Animation Bootcamp: The First Person Animation of Overwatch](https://www.youtube.com/watch?v=7t0hLZd_8Z4):守望先锋、IK、弹簧 234 | 235 | [【GDC 2014】Animation Bootcamp: An Indie Approach to Procedural Animation](https://www.youtube.com/watch?v=LNidsMesxSE):程序化动画、弹簧阻尼、动画过渡、IK、物理、Ragdolls、通过程序方式减少动画师重复性工作 236 | 237 | [【GDC 2014】Animating the Spy Fantasy in Splinter Cell: Blacklist](https://www.youtube.com/watch?v=bgwqaFKgbZI):动画管道、工具 238 | 239 | [【GDC 2017】Animating Dauntless: Slaying AAA Animation on the Indie Scale](https://www.youtube.com/watch?v=6TB62YKHwRQ):动作捕捉、预算、管道、四足运动关节匹配、Maya动画绑定 240 | 241 | [【GDC 2016】Animating Quadruped Characters in The Flame in The Flood](https://www.youtube.com/watch?v=Bs046-TWMhA):四足动画、线性混合 242 | 243 | [【GDC 2017】A Study of Creature Animation in Film and Games](https://www.youtube.com/watch?v=tBz7LlbvXFI):动画师 244 | 245 | [【GDC 2011】AI & Animation in Assassin's Creed Brotherhood](https://www.youtube.com/watch?v=HzhDjbsXA9s):AI、物理动作、数据驱动 246 | 247 | [【GDC 2017】Bringing Hell to Life: AI and Full Body Animation in DOOM](https://www.youtube.com/watch?v=3lO1q8mQrrg):AI控制角色动画、Full Body、Delta Correction、Focus Tracking、Foot Phase Synchronization、Bad Zone Avoidance 248 | 249 | [【GDC 2018】Cloth Self Collision with Predictive Contacts](https://www.youtube.com/watch?v=XUsD3xrNJH0):物理、布料模拟、碰撞 250 | 251 | [【GDC 2018】Character Control with Neural Networks and Machine Learning](https://www.youtube.com/watch?v=o-QLSjSSyVk):神经网络、机器学习、使用数据驱动系统来降低角色控制动画系统的复杂性和人力投入、标签标记动画状态、Motion Matching 252 | 253 | [【GDC 2017】Can You See Me Now? Building Robust AI Sensory Systems](https://www.youtube.com/watch?v=pUXS9snrZE4):AI感知、更好的响应玩家输入 254 | 255 | [【GDC 2018】Expanding Animation and Design Capabilities in Post-Launch Battlefield 1](https://www.youtube.com/watch?v=p9i-iqs_CO4):子弹、射击、第一人称 256 | 257 | [【GDC 2016】Embracing Animation Complexity in EA UFC 2 Through Procedural Techniques](https://www.youtube.com/watch?v=1nSshgQ3HM4):动画复杂性、管道-生成的动画烘焙成可以在MotionBuilder中编辑的动画 258 | 259 | [【GDC 2020】Freeform Animation Rigging: Evolving the Animation Pipeline](https://www.youtube.com/watch?v=XjMKbElVNmg):动画管道、Unity、Control Rig、Uber-Rig、runtime rigging 260 | 261 | [【GDC 2016】Honey, Where is My Wingsuit? - Building the Just Cause 3 Animation and Rigging Pipeline](https://www.youtube.com/watch?v=Tfw21ciEyZE):Rigging Pipeline、Rig Creation、Maya 262 | 263 | [【GDC 2017】Huddle up! Making the [SPOILER] of INSIDE](https://www.youtube.com/watch?v=gFkYjAKuUCE):物理动画 264 | 265 | [【GDC 2016】IK Rig: Procedural Pose Animation](https://www.youtube.com/watch?v=KLjTU0yKS00):IK Rig、在任何角色之间共享任何动画,并改变基础动画,为角色产生新的动作和动作类型、重定向 266 | 267 | [【GDC 2015】Inside The UFC Animation System](https://www.youtube.com/watch?v=Xw47OUed9vE):程序化动画、动画流程、locomotion工作流、Motion Builder 268 | 269 | [【GDC 2018】Inertialization: High-Performance Animation Transitions in Gears of War](https://www.youtube.com/watch?v=BYyv4KTegJI):高性能动画转换 270 | 271 | [【GDC 2019】Maximizing Animation and Cinematic Content Workflows for 'Just Cause 4'](https://www.youtube.com/watch?v=MQjCYQb_pFU):流程向、动画工作流程 272 | 273 | [【GDC 2018】Middle-earth: Shadow of War: Creating Multi-Character Combat Animations](https://www.youtube.com/watch?v=j0Bl_nBonos):动作同步、多角色动作、战斗挑战 274 | 275 | [【GDC 2018】Parkour: How to Improve Freedom of Movement in First-Person Games in 20 Simple Steps](https://www.youtube.com/watch?v=uikbLyi-cug):攀爬、移动、跑步 276 | 277 | [【GDC 2015】Realizing Responsive High Fidelity Character Movement in Just Cause 3](https://www.youtube.com/watch?v=RI09nSVG-ic):物理系统、高响应、Game Driven Movement、Animation Driven Movement、Motion Correction、朝向 278 | 279 | [【GDC 2021】Take 'Control' of Animation](https://www.youtube.com/watch?v=JH69g7yA7QM):Motion Matching、工具集 280 | 281 | [【GDC 2017】The Animation Pipeline of Overwatch](https://www.youtube.com/watch?v=cr7oO8kDu8g):动画管道 282 | 283 | [【GDC 2015】Technical Artist Bootcamp: The Elder Scrolls Online's Character Tools and Pipeline](https://www.youtube.com/watch?v=EdDotYUpydE):动画管道、角色工具 284 | 285 | [【GDC 2015】Tools-Based Rigging in Bungie's Destiny](https://www.youtube.com/watch?v=U_4u0kbf-JE):Rigging、工具集 286 | 287 | [【GDC 2016】The Best Animation Tricks of the Trade (For 2016)](https://www.youtube.com/watch?v=_1j5Tf6ulII):动画师 288 | 289 | [【GDC 2017】The Illusion of Motion: Making Magic with Textures in the Vertex Shader](https://www.youtube.com/watch?v=EUTE1SoOGrk):动画管道技术、动画师增加骨骼数量 290 | 291 | [【GDC 2015】Tech Art in Tamriel: The Elder Scrolls Online's Character Tools and Pipeline](https://www.youtube.com/watch?v=5YBJaXHFoSA):角色管道 292 | 293 | [【GDC 2015】No Man's Sky: How I Learned to Love Procedural Art](https://www.youtube.com/watch?v=vcEA41eBOGs):程序化 294 | 295 | # 其他 296 | 297 | [奶帆:UE4动画系统的那些事(一):UE4动画系统基础](https://zhuanlan.zhihu.com/p/62401630) 298 | 299 | [Yene:CEDEC 2020 FF7Remake的动画技术](https://zhuanlan.zhihu.com/p/249114618) 300 | 301 | [呆晒晒:Gears of War 4中的高性能动画技术](https://zhuanlan.zhihu.com/p/73284882) 302 | 303 | [小木子:Twitter上一些关于Game Animation的精彩内容](https://zhuanlan.zhihu.com/p/418962707) 304 | 305 | [小木子:GDC无缝过程动画制作经验分享](https://zhuanlan.zhihu.com/p/376234221) 306 | 307 | [浅谈Rigging未来可能的技术走向](https://blog.csdn.net/hcud024/article/details/52190495) 308 | 309 | [破晓:【翻译】过程化动画的简介](https://zhuanlan.zhihu.com/p/33943499) 310 | 311 | [谌嘉诚:【动画入门01课】](https://www.bilibili.com/video/BV164411Y732?p=60) 312 | 313 | 《游戏编程精粹3》:4.7 针对人物运动的表面细分 314 | 315 | 《游戏编程精粹4》:3.7 用多层物理模拟快速变形 316 | 317 | 《游戏编程精粹5》:2.3 动画中基于样条的时间控制 318 | 319 | 《游戏编程精粹5》:4.5 使用反馈控制系统让布娃娃活起来 320 | 321 | 《游戏编程精粹6》:5.1 交互角色真实的静止动作合成 322 | 323 | 《游戏编程精粹6》:7.1 3D动画角色数据的动态自适应流 324 | 325 | 《游戏编程精粹7》:5.5 用径向基函数纹理来替代动画浮雕 326 | 327 | 《游戏编程精粹8》:2.2 Curved Paths for Seamless Character Animation 328 | 329 | 《DirectX游戏开发终极指南》:第13章 模型动画 330 | 331 | 《GPU精粹1》:第4章 Dawn演示中的动画 332 | 333 | 《GPU精粹1》:6.2 动画精灵的实现 334 | 335 | 《GPU精粹1》:7.4 动画 336 | 337 | 《GPU精粹2》:第23章 Nalu Demo的头发动画和渲染 338 | 339 | 《Fundamentals of Computer Graphics》:Chapter 16 Computer Animation 340 | 341 | 342 | 343 | 344 | 345 | 有点混乱....以后边学边整理呗..... 346 | 347 | 348 | 349 | 写于2022年4月3日 350 | 351 | -------------------------------------------------------------------------------- /服务器/【记录】游戏服务器开发知识/book.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/服务器/【记录】游戏服务器开发知识/book.jpg -------------------------------------------------------------------------------- /服务器/【记录】游戏服务器开发知识/run.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/服务器/【记录】游戏服务器开发知识/run.jpeg -------------------------------------------------------------------------------- /服务器/【记录】游戏服务器开发知识/【记录】游戏服务器开发知识.md: -------------------------------------------------------------------------------- 1 | 由于现在在做客户端,整理下之前做服务器开发时的一些知识记录和经验。以链接形式为主,大佬们写得很好,篇幅有限,不做太多复述,点进去查看即可。 2 | 3 | 本文介绍的是客户端和服务器分离的架构。如有误,烦请指正~ 4 | 5 | # 1. Linux 6 | 7 | 游戏服务器一般部署在Linux上,服务器开发其实也可以说是Linux后台开发,所以需要熟悉Linux系统。需要熟悉如下: 8 | 9 | - linux常见命令的使用:ls(list)、cd、grep、cat、top、lscpu、getconf、find等 10 | - [linux系统查看服务内存,cpu,硬盘命令](https://blog.csdn.net/weixin_40720301/article/details/80103763) 11 | - [Linux系统查看CPU使用率、内存使用率、磁盘使用率](https://blog.csdn.net/wujizhishui/article/details/89333957) 12 | - [Linux中top命令](https://www.cnblogs.com/loved-wangwei/p/8986287.html) 13 | - [Linux下4个查找命令which、whereis、locate、find的总结](https://blog.csdn.net/u010625000/article/details/44455023) 14 | 15 | - GDB的使用:断点、watch、p、x、bt、list、c、r、set等 16 | - [GDB十分钟教程](https://blog.csdn.net/liigo/article/details/582231) 17 | 18 | - vim 19 | 20 | # 2. 服务器架构及功能 21 | 22 | ## 2.1 架构 23 | 24 | 不同类型的游戏的架构是不一样的,但是同类型的游戏架构其实还是比较相似的,比如:MMO服务器有登陆服、网关服、日志服、场景管理服、逻辑服、数据库等。除此之外,还需要知道服与服之间的通信、每个服负责管理的事件,单服内部是如何处理数据的,像MMO中数据很重要,需要知道什么时候存储数据,什么时候做数据缓存,负载均衡的实现,多线程操作数据时的安全性,数据库操作的瓶颈,容错容灾的处理等。服务器中还需要对事件进行管理,事件是用什么结构存储的,事件回调的接口。熟悉服务器的架构,有助于定位重大问题,也有助于分析服务器的性能瓶颈。 25 | 26 | 所以工作中除了做需求外,熟悉服务器架构以及服务器核心的技术实现是很有必要的。 27 | 28 | [端游、手游服务端常用的架构是什么样的?](https://www.zhihu.com/question/29779732/answer/45791817) 29 | 30 | [网络游戏服务器架构中,网关服务器的存在有什么优势,它的存在能给服务器带来明显的好处吗?](https://www.zhihu.com/question/21038610) 31 | 32 | [如何从事游戏后台开发](https://www.zhihu.com/question/62386941/answer/197990865) 33 | 34 | [游戏服务器与普通服务器有什么区别?](https://www.zhihu.com/question/23508968) 35 | 36 | ## 2.2 日志系统 37 | 38 | 日志系统在服务器系统里很重要,日志也并不是越多越好,所以需要知道在什么时候输出日志以及使用哪个日志等级。一个好的日志系统会在定位问题时提升效率。 39 | 40 | 日志等级(Trace、DEBUG、INFO、WARNING、FATAL/CRITICAL)使用:[When to use the different log levels](https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels) 41 | 42 | [如何实现日志打点?](https://baijiahao.baidu.com/s?id=1672021251772560895) 43 | 44 | ## 2.3 热更新 45 | 46 | 热更新是一个很重要的功能,其实就是打补丁,在不停机的前提下去修改。客户端和服务器都会用到热更。解释型语言(Lua和Python)热更相对简单,编译型语言(C++)热更相对复杂一些,但也是能实现的。虽然热更新方案公司基本上都会有,但是学习一下热更新的原理及实现,也是很有帮助的。 47 | 48 | [为什么解释型的脚本语言(如Lua、Python)可以热更新,而编译型的语言(如C、C++)不能呢?](https://www.zhihu.com/question/29603593) 49 | 50 | ## 2.4 SVN && GIT管理 51 | 52 | 开发项目肯定要会版本工具,需要会一些基本命令。这里不再详细介绍,上网搜一下即可~ 53 | 54 | ## 2.5 寻路 55 | 56 | 游戏项目寻路判断方式也有区别,比如:客户端先行服务器验证,客户端表现服务器执行,客户端服务器都执行并同步。因为我没做过这方面的内容,只是学习了解过,所以这里仅仅列举一些寻路算法的介绍: 57 | 58 | [A星寻路算法介绍](https://www.cnblogs.com/zhoug2020/p/3468167.html) 59 | 60 | [【洛谷日报#213】另类寻路算法——B*算法浅谈](https://zhuanlan.zhihu.com/p/86433957) 61 | 62 | 多边形(NavMesh)寻路算法 63 | 64 | [NavMesh生成原理](https://zhuanlan.zhihu.com/p/40177186) 65 | 66 | [即时战略游戏中实用的寻路算法都有哪些,比较如何?](https://www.zhihu.com/question/20298134) 67 | 68 | ## 2.6 AOI 69 | 70 | MMO游戏中非常重要的一个功能,我学习了解过九宫格法。AOI也会成为性能瓶颈,需要对AOI进行很多优化,比如: 71 | 72 | - 网格的大小 73 | 74 | - 数据同步频率 75 | - 移动包 76 | - 同步关键值,如战斗状态下的玩家武力值,目标角色的生命值等;其他的,如安全区内的NPC状态,可降低同步频率。 77 | - 兴趣列表 78 | 79 | [游戏服务器AOI的实现](https://www.cnblogs.com/coding-my-life/p/14256640.html) 80 | 81 | [深入探索AOI算法](https://zhuanlan.zhihu.com/p/201588990) 82 | 83 | # 3. 测试及优化 84 | 85 | 在游戏上线前,会经过多轮测试,除了逻辑测试之外,压力测试和性能测试也很重要。自己也做了很多这方面的工作。 86 | 87 | ## 3.1 测试工具 88 | 89 | 首先测试需要工具,这里分为机器人、内存检测工具和性能检测工具。 90 | 91 | ### 3.1.1 机器人 92 | 93 | 机器人可用于模拟玩家操作,比如:移动、释放技能、做任务、与NPC交互、击杀Boss等。可以单独测试某个用例,也可以混合测试(尽可能模拟玩家的真实操作,比如:10%的机器人在交易,40%做任务,20%砍怪,10%与NPC交互,20%释放技能)。 94 | 95 | 机器人不仅用于服务器测试,客户端测试也会用到机器人,比如多个机器人同屏,测试同屏性能。 96 | 97 | 机器人工具需要支持多线程,可以同时操作N个机器人,可以用行为树控制测试节点。机器人工具是进行压力测试和性能测试的必要工具。机器人可以用纯脚本编写,也可以用高级语言编写,只要不崩溃,数据对,能跑就好。公司也大都会提供机器人框架,然后按照自己的项目需求,修改一下即可。 98 | 99 | ### 3.1.2 内存检测工具 100 | 101 | Windows下的Visual Leak Detector 102 | 103 | Linux下的Valgrind工具集 104 | 105 | ### 3.1.3 性能检测工具 106 | 107 | gperftools的CPU profiler 108 | 109 | perf:采样并生成火焰图 110 | 111 | ### 3.1.4 统计工具 112 | 113 | 在进行测试的时候,我们需要观察服务器的运行情况,查看一些网络流量、CPU占用、收发量、TPS等。我们不可能一直盯着数据看,所以需要一个可视化监控平台来帮助我们统计数据,然后不定时分析数据即可。可视化数据监控平台市面上也有很多,比如:Prometheus+Grafana等。 114 | 115 | ## 3.2 压力测试 116 | 117 | 顾名思义,压力测试就是测试服务器的承载压力,比如:单服可以容纳的最多人数量。而且,进行压力测试也可以暴露一些在开发中没有出现的问题,比如:内存泄漏。 118 | 119 | 压测需要考虑的指标:同时在线人数、容易引起服务器负载过高的功能如登录等、单组服务器压力、数据量等。 120 | 121 | ## 3.3 性能测试 122 | 123 | 首先需要知道可能影响服务器性能的因素:内存、磁盘I/O、网络、CPU、数据库等。 124 | 125 | 首先分析性能瓶颈在哪,然后再具体优化,比如:AI、广播频率、角色池大小、在网游中数据库可能会成为服务器的性能瓶颈,如大量创角时;在线角色过多,定时存盘时。 126 | 127 | 网络延迟判断: 128 | 129 | - 1~30ms:极快,几乎察觉不出有延迟,玩任何游戏速度都特别顺畅 130 | - 31~50ms:良好,可以正常游戏,没有明显的延迟情况 131 | - 51~100ms:普通,对抗类游戏能感觉出明显延迟,稍有停顿 132 | - 100ms:差,无法正常游戏,有卡顿,丢包并掉线现象 133 | 134 | [Linux C++怎么做框架的性能调优?](https://www.zhihu.com/question/64382354) 135 | 136 | [服务器性能瓶颈分析方法](https://www.cnblogs.com/preacher/p/6347185.html) 137 | 138 | [浅谈服务器性能测试的全生命周期——从测试、结果分析到优化策略](https://wetest.qq.com/labs/102) 139 | 140 | [如何回答性能优化的问题,才能打动阿里面试官?](https://developer.aliyun.com/article/727675) 141 | 142 | [游戏服务端高并发优化](https://blog.csdn.net/r1037/article/details/78372566) 143 | 144 | # 4. 宕机 145 | 146 | 测试之后,上线时,如果**不小心**还是宕机了怎么办? 147 | 148 | ![](run.jpeg) 149 | 150 | 服务器宕机的常见原因: 151 | 152 | - 内存泄漏导致内存增长太多,进程被杀死 153 | - 流量负载过大 154 | - 数据库设计有问题 155 | 156 | [记录一次服务器宕机分析过程(1)-排查问题](https://www.jianshu.com/p/5ef24617cf09) 157 | 158 | [服务器又宕机了,怎么办?](https://blog.csdn.net/lfhfut/article/details/2738109) 159 | 160 | [作为一名游戏服务器端程序员,是一种怎样的体验? ](https://www.zhihu.com/question/41498780/answer/1537480110) 161 | 162 | [coredump产生的几种可能情况](https://www.cnblogs.com/baiduboy/p/6830464.html) 163 | 164 | # 5. 知识储备 165 | 166 | 下面的很多内容大学学过,学校开的这些课程都很重要,当然,学校领入门,还得自己多思考多深入。服务器开发很重视性能和内存,而且谁也不想写出有宕机风险的代码。 167 | 168 | 虽然有时候下面的部分内容不一定会直接用到需求上,但是它可以帮助我们看源码,找到优化的切入点。多看源码多写代码本就是技术提升的方法之一,会一定量的基础可以更好的理解源码。 169 | 170 | 所以这里把知识储备分为数据库、操作系统、网络、编程语言、设计模式。 171 | 172 | ## 5.1 数据库 173 | 174 | 需要会一些常用的SQL语句,在做需求时,也需要考虑SQL优化等。服务器会对热数据进行缓存。 175 | 176 | [在一个千万级的数据库查寻中,如何提高查询效率?](https://blog.csdn.net/xlgen157387/article/details/44156679) 177 | 178 | [mysql数据库引擎常用面试总结](https://blog.csdn.net/sjyttkl/article/details/76176836) 179 | 180 | [MySQL索引实现原理分析](https://blog.csdn.net/u013308490/article/details/83001060) 181 | 182 | [mysql索引优化面试题](https://www.cnblogs.com/hephec/p/4557053.html) 183 | 184 | ## 5.2 网络 185 | 186 | 现在网络游戏很多,网络也是很重要的一部分,谁也不想玩个手游,连不上或流量哗哗的跑叭~ 187 | 188 | 游戏服务器内部也需要处理很多情况,比如:断网重连、可靠传输、网络攻击的处理方式、弱网处理等。 189 | 190 | 网络攻击很常见: 191 | 192 | - [TCP SYN洪泛攻击的原理及防御方法](https://blog.csdn.net/shixin_0125/article/details/78829069) 193 | - [什么是 DDoS 攻击?](https://www.zhihu.com/question/22259175) 194 | 195 | [MOBA类游戏是如何解决网络延迟同步的?](https://www.zhihu.com/question/36258781/answer/98944369) 196 | 197 | [某百万大作的弱网处理及断线重连方案](https://www.gameres.com/766067.html) 198 | 199 | [TCP快速重传为什么是三次冗余ack,这个三次是怎么定下来的?](https://blog.csdn.net/u010202588/article/details/54563648) 200 | 201 | [面试官,不要再问我三次握手和四次挥手](https://blog.csdn.net/hyg0811/article/details/102366854) 202 | 203 | [socket中的函数遇见EINTR的处理](http://blog.chinaunix.net/uid-21501855-id-4490453.html) 204 | 205 | [IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)](https://blog.csdn.net/historyasamirror/article/details/5778378) 206 | 207 | [使用epoll时需要将socket设为非阻塞吗? ](https://www.zhihu.com/question/23614342/answer/184513538) 208 | 209 | [完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三](https://blog.csdn.net/PiggyXP/article/details/6922277) 210 | 211 | [select、poll、epoll之间的区别(搜狗面试)](https://www.cnblogs.com/aspirant/p/9166944.html) 212 | 213 | [Windows IOCP模型与Linux EPOLL模块之比较](https://blog.csdn.net/educast/article/details/15500349 ) 214 | 215 | ## 5.3 操作系统 216 | 217 | [无锁队列是否不适用于大容量应用场景?](https://www.zhihu.com/question/25960605) 218 | 219 | [Linux内核中经典链表 list_head 常见使用方法解析](https://blog.csdn.net/wanshilun/article/details/79747710) 220 | 221 | [深入浅出:操作系统之虚拟内存 ](https://zhuanlan.zhihu.com/p/171784987) 222 | 223 | [进程间通信——共享内存(Shared Memory)](https://blog.csdn.net/ypt523/article/details/79958188) 224 | 225 | [死锁面试题(什么是死锁,产生死锁的原因及必要条件)](https://blog.csdn.net/hd12370/article/details/82814348) 226 | 227 | [进程间的八种通信方式](https://blog.csdn.net/weixin_44653395/article/details/99686244) 228 | 229 | [C++11中的线程是系统级的还是用户级的呢 比如说thread?](https://www.zhihu.com/question/365982400) 230 | 231 | [进程、线程和协程之间的区别和联系](https://blog.csdn.net/daaikuaichuan/article/details/82951084) 232 | 233 | [开源Boost内存池的使用与测试](http://tech.it168.com/a2011/0726/1223/000001223399_all.shtml) 234 | 235 | [C++ 实现高性能内存池](https://www.jianshu.com/p/18c7e8a900a5 ) 236 | 237 | [开发内功修炼CPU篇汇总,值得收藏](https://zhuanlan.zhihu.com/p/82368982) 238 | 239 | [让 CPU 告诉你硬盘和网络到底有多慢](https://cizixs.com/2017/01/03/how-slow-is-disk-and-network/) 240 | 241 | ## 5.4 编程语言 242 | 243 | 常见的服务器框架有C++&&Lua和C++&&Python。 244 | 245 | ### 5.4.1 C++ 246 | 247 | C++要学的东西很多,其实标准库文档(https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5)可以解决我们平时开发的大部分需求。现在很多服务器支持跨平台,所以需要了解一些基础类型在不同平台下的位数,以避免移植的时候出现问题。 248 | 249 | [malloc一次性最大能申请多大内存空间?](https://www.zhihu.com/question/20836462/answer/16341442) 250 | 251 | [空类的大小为什么是1?](https://www.zhihu.com/question/266041176) 252 | 253 | [深度解析C/C++中的宏是什么?有什么作用呢?](https://blog.csdn.net/dreamispossible/article/details/80686858 ) 254 | 255 | [内存释放free函数如何知道内存大小](https://blog.csdn.net/bdss58/article/details/94005337) 256 | 257 | [【c++编程思想学习笔记】operator=为何要检查自赋值的解答](https://blog.csdn.net/ZongYinHu/article/details/47445081) 258 | 259 | [局部静态变量只能初始化一次?它是怎么实现的](https://zhuanlan.zhihu.com/p/87213810) 260 | 261 | [如何保证申请内存的地址是4的倍数](https://blog.csdn.net/kai8wei/article/details/78701531?utm_source=copy) 262 | 263 | [strcpy函数为什么要返回指针?](http://2wdragon.blog.sohu.com/95488072.html) 264 | 265 | ### 5.4.2 STL 266 | 267 | 这个非常重要,有的需求的时候,我们需要考虑下数据的存储结构,减少数据的遍历,因为服务器开发过程中处理的数据真的非常多。可以看《STL源码剖析》。 268 | 269 | [map和unordered_map的差别和使用](https://blog.csdn.net/BillCYJ/article/details/78985895) 270 | 271 | [STL vector list deque区别与实现](https://blog.csdn.net/ithomer/article/details/6121890) 272 | 273 | [迭代器失效的几种情况总结](https://blog.csdn.net/lujiandong1/article/details/49872763) 274 | 275 | ### 5.4.3 Lua 276 | 277 | [Lua string(字符串)(源码解析)](https://blog.csdn.net/u013781447/article/details/107080103) 278 | 279 | [lua数据结构之table的内部实现](https://blog.csdn.net/zr339361504/article/details/52432163) 280 | 281 | [深入理解Lua的闭包一:概念、应用和实现原理](https://blog.csdn.net/MaximusZhou/article/details/44280109) 282 | 283 | [Lua的upvalue和闭包](https://blog.csdn.net/chenjiayi_yun/article/details/25219937) 284 | 285 | [Lua 面向对象实现](https://blog.csdn.net/ring0hx/article/details/16341261) 286 | 287 | [深入理解lua的协程coroutine](https://blog.csdn.net/yzf279533105/article/details/79982139/) 288 | 289 | [如何让 lua 做尽量正确的热更新](https://blog.codingnow.com/2016/11/lua_update.html) 290 | 291 | ### 5.4.4 C++和Lua交互 292 | 293 | [Lua和C++交互总结(很详细)](https://blog.csdn.net/shun_fzll/article/details/39120965) 294 | 295 | ## 5.5 数据结构及算法 296 | 297 | 需要知道栈、队列、链表、哈希表、堆、优先队列、二叉树的原理及使用,虽然有些知识并不一定需要自己实现,但是看源码和分析服务器核心技术的实现的时候还是要用到的吖(如果能自己实现,那就最好不过了)。 298 | 299 | [十大经典排序算法(动图演示)](https://www.cnblogs.com/onepixel/p/7674659.html) 300 | 301 | [快速排序的稳定化实现](https://blog.csdn.net/liuchenjane/article/details/72902325) 302 | 303 | [链表翻转的图文讲解(递归与迭代两种实现)](https://blog.csdn.net/FX677588/article/details/72357389) 304 | 305 | [数据结构 Hash表(哈希表)](https://blog.csdn.net/u011109881/article/details/80379505) 306 | 307 | [HashMap底层实现原理及面试问题](https://blog.csdn.net/suifeng629/article/details/82179996) 308 | 309 | [详细图文——AVL树](https://blog.csdn.net/qq_25343557/article/details/89110319) 310 | 311 | [海量数据处理 - 10亿个数中找出最大的10000个数(top K问题)](https://blog.csdn.net/zyq522376829/article/details/47686867) 312 | 313 | [LRU算法四种实现方式介绍](https://blog.csdn.net/elricboa/article/details/78847305) 314 | 315 | [如何给100亿个数字排序?](https://www.jianshu.com/p/8dc11152f178) 316 | 317 | 跳跃表 318 | 319 | ## 5.6 设计模式 320 | 321 | 游戏中的设计模式运用的还是挺多的,比如单例模式。之前在网上看过很多零零散散的知识,后来发现《设计模式与游戏完美开发》这本书有详细介绍,作者写的浅显易懂,挺好的。 322 | 323 | [C++实现线程安全的单例模式](https://www.cnblogs.com/myd620/p/6133420.html) 324 | 325 | # 6. 推荐学习 326 | 327 | 酷壳:https://www.coolshell.cn/ 328 | 329 | 云风的BLOG:http://codingnow.com/ 330 | 331 | 开源的服务端并发框架skynet:https://github.com/cloudwu/skynet 332 | 333 | 网络库muduo:https://github.com/chenshuo/muduo 334 | 335 | 高性能WebServer:https://github.com/linyacool/WebServer 336 | 337 | 基于C++的操作系统微内核FreeNOS:https://github.com/Neirth/FreeNOS,代码风格很好,注释良好,doxygen 338 | 339 | 高效内存池分配器MemoryPool:https://github.com/cacay/MemoryPool 340 | 341 | 推荐书: 342 | 343 | ![](book.jpg) 344 | 345 | # 7. 后记 346 | 347 | 拿到策划案的时候,需要考虑服务器是否能负载,比如:背包内道具叠加的数量等,一定要做好预估,不能随便接需求,这样能减少很多返工情况。而且还要注意安全性。 348 | 349 | CMake、bat、shell的基本命令也要会,当一件事情需要重复做的时候,就要思考能不能让机器做,而这些工具也会提供一些帮助,比如批量执行命令。 350 | 351 | 防止过度设计,技术并不是堆得越多越好,承载的用户量也决定了水平的提升空间。 352 | 353 | 服务器开发其实也是综合性开发。 354 | 355 | 写于2022年8月17日。 -------------------------------------------------------------------------------- /逆向运动学(IK)详解/pictures/CCDIK一次推演步骤.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/逆向运动学(IK)详解/pictures/CCDIK一次推演步骤.png -------------------------------------------------------------------------------- /逆向运动学(IK)详解/pictures/other.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/逆向运动学(IK)详解/pictures/other.png -------------------------------------------------------------------------------- /逆向运动学(IK)详解/pictures/可达与不可达.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/逆向运动学(IK)详解/pictures/可达与不可达.png -------------------------------------------------------------------------------- /逆向运动学(IK)详解/pictures/解的个数.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/逆向运动学(IK)详解/pictures/解的个数.png -------------------------------------------------------------------------------- /逆向运动学(IK)详解/pictures/雅可比矩阵-线性模拟.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaxia9/Game-Development-Notes/f9c8b8c6a6e29db5ed2c6a69fc487ceb87baa921/逆向运动学(IK)详解/pictures/雅可比矩阵-线性模拟.png -------------------------------------------------------------------------------- /逆向运动学(IK)详解/【游戏开发】逆向运动学(IK)详解.md: -------------------------------------------------------------------------------- 1 | # 1 前言 2 | 3 | 开发游戏的同学应该对IK不陌生,但是对于我这个菜鸡而言,看到FABRIK、Two Bone IK、Full Body IK 、CCD IK“这么多的IK”总是一头雾水,所以总结下~ 4 | 5 | 本文为原理加应用,将介绍IK概念、IK算法、使用限制等原理,然后介绍“这么多的IK”在游戏开发(主要是UE4/UE5引擎)中的应用。(应用还不太完善,抽空会补演算过程图、应用图......还有,理论知识比较多,文章大部分总结[Inverse Kinematics Techniques in Computer Graphics: A Survey](http://www.andreasaristidou.com/publications/papers/IK_survey.pdf)这篇论文里的内容,翻译多) 6 | 7 | # 2 概念 8 | 9 | **正向运动学(Forward kinematics, FK)**:利用机器人运动学方程,根据关节的特定参数计算末端受动器(end effector)的位置。正向运动学要求用户为所有涉及的关节设置参数。 10 | 11 | **逆向运动学(Inverse kinematics, IK)**:流程与FK相反,利用机器人运动学方程来确定机械手的关节参数,使末端受动器移动到期望的位置。末端受动器可以是关节(如手和脚),也可以是内部关节(如手肘和膝盖),不一定位于末端。IK最早出现在机器人学技术中,现在在许多领域都有应用,比如:工程学、计算机图形学、电子游戏、CG动画。 12 | 13 | 关于末端受动器:机械手臂抓取末端受动器的位置、角色开门时手的位置、角色行走时脚的位置,这些可以称为末端受动器。 14 | 15 | # 3 IK问题分析 16 | 17 | IK可能有一个解、多个解或无解。 18 | 19 | IK问题解决有两步比较重要: 20 | 21 | - 一:分析目标是可达还是不可达。检查目标是否在可达范围内是很重要的,因为避免寻找不存在的解决方案,可以节省大量的处理时间。末端受动器能够到达的目标空间称为可达工作空间。 22 | - 二:当存在解决方案且没有达到解决方案时,需要添加终止条件来避免在迭代过程中进入无限循环的情况。比如:限制迭代次数、当末端受动器在前一次迭代和当前迭代时的位置差小于指定的公差时终止IK解算器(IK solvers)的计算。 23 | 24 | 要令IK发挥最好的效果,开始时骨骼最好摆出接近目标的姿势。这样有助于算法专注最接近的解,并能在合理的时间内完成计算。可用解的数量取决于目标位置或运动链的自由度。目标不可达的IK问题被称为**过度约束问题**。当目标可达时,两个或多个链可能存在多个解决方案。这使得IK问题不受约束(或冗余)——它可能有无限多的解决方案来满足期望的目标。 25 | 26 | 举个栗子~:可达与不可达如下图所示:$d_{1}$、$d_{2}$、$d_{3}$分别为三个杆,红色区域为不可达区域,绿色区域为可达区域。 27 | 28 | ![](pictures\可达与不可达.png) 29 | 30 | # 4 IK求解算法 31 | 32 | IK解算器主要分为四大类,分别是解析法(the Analytic family)、数值法(the Numerical family)、数据驱动法(Data-driven methods)和混合法(the Hybrid family of solvers)。 33 | 34 | 如何选取最合适的IK解算器呐?这取决于许多标准,包括给定的解决方案的平滑性以及选择该解决方案的计算成本。而且,IK解算器的效率和有效性通常是根据产生的运动的平滑度、可扩展性和产生的姿态所需的计算成本来衡量的。 35 | 36 | 接下来将分别介绍几类IK解算器: 37 | 38 | ## 4.1 解析法 39 | 40 | ![](pictures\解的个数.png) 41 | 42 | 如上图所示:$l_{1}$、$l_{2}$为两个杆,$\theta_{1}$、$\theta_{2}$ 表示旋转角度(图有误,$\theta_{1}$为$\angle$ joint base target、$\theta_{2}$ 为$\angle$ base joint target),可以看到图中两个杆可以有两种方案到达target点。由数学知识可知,其中一组解是:$\theta_{1}=cos^{-1}(\frac{l_{1}^{2}+x^{2}+y^{2}-l_{2}^{2}}{2l_{1}\sqrt{x^{2}+y^{2}}})$、$\theta_{2}=cos^{-1}(\frac{l_{1}^{2}+l_{2}^{2}-(x^{2}+y^{2})}{2l_{1}l_{2}})$ 43 | 44 | 由上述的解分析可知,当在三维空间中,面临关节更多更复杂的情况时,求解会变得复杂。 45 | 46 | 还有,对于平滑运动,我们希望解θ是稳定的。以及,当末端受动器微微修改时,其他关节不会变化太大。 47 | 48 | ### 4.1.1 应用之TwoBoneIK 49 | 50 | TwoBoneIK正如其名两个骨骼(不包括根骨骼),使用起来简单,应用也较多,比如:UE4.26的SolveBasicTwoBoneIK、FAnimNode_TwoBoneIK、Advanced Locomotion System V4插件。 51 | 52 | 由于三个点确定一个平面,所以需要增加一个控制点(也就是UE4中的PoleVector 极坐标点)来解三维空间内的旋转角度。但是,应该如何确定极坐标点呐?Zero大佬在[虚幻引擎的TwoboneIK心得 -- Joint Target](https://zhuanlan.zhihu.com/p/457339033)中有说,请移步查看~ 53 | 54 | ## 4.2 数值法 55 | 56 | 数值族一般可以分为三类:雅可比矩阵法(Jacobian)、牛顿法(Newton)、启发式法(Heuristic)。 57 | 58 | ### 4.2.1 雅可比逆矩阵法 59 | 60 | (大量翻译,逃ε=ε=ε=┏(゜ロ゜;)┛ 61 | 62 | 介绍:雅可比矩阵方法通过反复改变一个完整链的构型,使得末端受动器的位置和方向在每一步都更接近于目标位置和方向,从而迭代地解决IK问题。雅可比矩阵的解是运动链实际运动的线性近似。 63 | 64 | 雅可比矩阵是整个链系统对角度参数θ的偏导数矩阵。雅可比矩阵方法通过反复改变一个完整链的构型,使得末端受动器的位置和方向在每一步都更接近于目标位置和方向,从而迭代地解决IK问题。如下图所示: 65 | 66 | ![](pictures\雅可比矩阵-线性模拟.png) 67 | 68 | 雅可比矩阵$J$可以描述为$\theta$值的函数:$J(\theta)_{ij}=(\frac{\partial s_i}{\partial \theta_j})$(i=1,...,k,j=1,...,n,k为末端受动器的个数,n为关节个数),$J$是一个带有向量元素的$k \times n$矩阵。在实践中,它会转换成一个$3k \times n$的标量矩阵。可以用$v_j$来计算$J$的分量,$v_j$是指向第$J$个关节旋转轴的单位向量:$\frac{\partial s_i}{\partial \theta_j} = v_j\times(s_i-P_j)$($P_j$是关节的位置)。 69 | 70 | 现在,假设末端受动器$i$的目标位置为$t_i$,然后我们试图找到使末端受动器的实际位置和目标位置之间的误差最小的$\theta$值:$e_i=t_i-s_i(\theta)$ 71 | 72 | 为了做到这一点,我们在关节角上做一个小的改变$\Delta\theta$,然后将末端受动器位置的随之改变近似为:$\Delta s \approx J\Delta\theta$ 73 | 74 | $J$可以由$s$和$\theta$的当前值计算出来。由于我们正在寻找一个$\Delta s$的值,它尽可能接近误差$e$(误差项$e$应该被clamp,以避免收敛不稳定),我们可以估计$\theta的$变化为$\Delta\theta \approx J^{-1}e$。 75 | 76 | 然而,$J$可能既不是方阵也不可逆,除此之外,还可能存在奇异性问题。我们在这里注意到,当一个关节发生变化时,雅可比独立于其他关节来考虑每个关节的影响(这是一阶近似),它的所有子关节都被视为单刚体。随着时间的推移,人们提出了各种不同的方法来解决IK问题,其目的是避免奇异性问题,并提高解的收敛性和稳定性。一些方法更多的是通过适当定义的映射将任务空间与关节空间关联起来,来对奇异点附近ill-conditioned病态的逆微分运动映射进行局部修正,而另一些方法则是针对整体运动的平滑。接下来,我们总结这些技术。 77 | 78 | PS:奇异性:从数学角度而言,奇异性是指函数的不连续或导数不存在,表现出奇异性的点。 79 | 80 | #### 4.2.1.1 雅可比矩阵转置(Jacobian transpose) 81 | 82 | 用转置代替逆。因此:$\Delta\theta=\alpha J^Te$。对于一些合适的标量$\alpha$可以计算为$\alpha=\frac{}{}$,其中$$表示向量$a$,$b$的点积。 83 | 84 | 雅可比矩阵转置法在几何奇异点附近并不是定义不清的,但它需要多次迭代才能收敛。还有,可以通过确定雅可比矩阵的行是否为零来验证是否存在奇异性问题。还有,雅可比转置可能会有不稳定的运动,从而导致不良的姿态,尤其是在末端受动器位置与目标之间有显著差异的情况下更明显。此外,转置近似没有考虑关节变量的相对贡献,也不支持约束维度之间的严格优先级。还需要注意的是,由于直接运动学模型的非线性原因,$\alpha$应该很小,否则,可能出现波动和不连续。 85 | 86 | 在《游戏编程精粹4》2.6 应用于反向运动的雅可比转置方法 中也提到了该方法。 87 | 88 | #### 4.2.1.2 雅可比矩阵伪逆(Jacobian pseudo-inverse) 89 | 90 | 雅可比矩阵的伪逆,也称为雅可比矩阵的Moore-Penrose逆,设$\Delta\theta=J^\dagger e$,其中$J^\dagger为$$n \times m$矩阵,称为$J$的伪逆。伪逆的主要优点是它适用于所有的矩阵$J$,即使是那些不是方阵或者不是满秩矩阵。伪逆具有这样的性质:矩阵$(I-J^\dagger J)$在$J$的零空间上进行投影。雅可比矩阵伪逆的计算方法:$\Delta\theta=J^T(JJ^T)^{-1}e$。 91 | 92 | 当$J$不是满秩时,另一个估计伪逆的公式给出。一些作者已经使用零空间方法来帮助避免奇异构型。此外,使用雅可比矩阵伪逆可以来解决两足人类的运动问题。然而,雅可比矩阵伪逆也不是没有缺点;如:当链的位形接近奇异点时,伪逆方法会导致关节角度的变化非常大,而到目标的运动可能非常小,从而导致运动中的波动和不连续性。 93 | 94 | #### 4.2.1.2 阻尼最小平方(Damped least squares) 95 | 96 | 雅可比的另一种变化是阻尼最小二乘(DLS)方法,也被称为Levenberg-Marquardt算法。使用DLS可以让$\Delta\theta$稳定,避免伪逆方法存在的奇异性问题。DLS解可以表示为$\Delta\theta=J^T(JJ^T+\lambda^2I)^{-1}e$,其中$\lambda\in R$是一个非零阻尼常数。为了使上述式子数值稳定,阻尼常数必须谨慎选择。DLS比伪逆和转置方法更好。然而,它的优越性能取决于阻尼常数$\lambda$。大的阻尼常数使得$\Delta\theta$解在奇异点附近表现良好,但同时也降低了收敛速度,降低了跟踪目标的精度,并产生波动。 97 | 98 | #### 4.2.1.3 奇异值分解(Singular value decomposition) 99 | 100 | 奇异点的存在使雅可比矩阵的反演过程相当复杂。为此,奇异值分解(SVD)被提出作为雅可比方法的另一种变体,它利用伪逆矩阵。SVD特别有用,因为它为矩阵的基本子空间提供了标准正交基。形式上,$m \times n$的雅可比矩阵的奇异值分解$J$是式子$J = UDV^T$的因式分解, $U$是一个$m \times m$酉正交矩阵,$D$是一个$m \times n$的对角线上有非负实数的矩形对角矩阵,$V^T$($V$的转置)是一个$n \times n$酉正交矩阵。$D$矩阵$\sigma_i = d_{ii}$的对角项称为$J$的奇异值。注意,$\sigma_i$可以是0,而$J$的秩等于使$\sigma_i$= 0的$r$的最大值。雅可比伪逆可以用SVD表示为$J^\dagger = VD^\dagger U^\dagger$。其中,伪逆$J^\dagger$由$J^\dagger=\sum_{i=1}^{r}{\sigma_i^{-1}v_iu_i^T}$表示。 101 | 102 | 还有奇异值滤波(SVF)方法,该方法是对雅可比矩阵进行滤波的另一种方法,使其始终是满秩矩阵;新的替代伪逆具有lower-bounded奇异值,当奇异值远离0时趋于$J^\dagger$。 103 | 104 | #### 4.2.1.4 阻尼最小二乘伪逆(Pseudo-inverse damped least squares) 105 | 106 | 伪逆DLS方法是DLS方法的扩展,在DLS方法下使用SVD。因此,它可以表示为$J^{T}(JJ^{T}+\lambda^{2}I)^{-1} = \sum_{i=1}^{r}{\frac{\sigma_{i}}{\sigma_{i}^{2}+\lambda^{2}}v_{i}u_{i}^{T}}$。 107 | 108 | 伪逆DLS方法在奇异点附近的表现与简单伪逆方法相似,但在奇异点附近的表现较为平滑。更具体地说,这两种方法的雅可比矩阵都由表达式$\sum_{i=1}^{n}{\tau_{i}v_{i}u_{i}^T}$求逆。但在伪逆DLS情况下,$\tau_{i} = \sigma_{i}/(\sigma_{i}^{2}+\lambda^{2})$,而在简单的伪逆方法中,$\tau_{i} = \sigma_{i}^{-1}$,当$\sigma_{i}$趋近于0时,$\tau_{i}$不稳定。 109 | 110 | #### 4.2.1.5 选择性阻尼最小二乘(Selectively damped least squares) 111 | 112 | 选择性SDLS方法是伪逆DLS方法的扩展。SDLS根据达到目标位置的难度,对雅可比矩阵奇异值分解的每个奇异向量分别调整阻尼因子。SDLS的阻尼常数不仅与多体关节的当前构型有关,还与末端受动器与目标位置的相对位置有关。SDLS需要更少的迭代来收敛,不需要特别的阻尼常数,并返回在缺乏波动方面的最佳结果。该方法的性能优于其他任何一种反雅可比矩阵方法,但其缺点是由于奇异值分解的计算,其计算成本较高(在所有雅可比矩阵方法中性能时间最慢)。 113 | 114 | 一个被用来计算拟人化机器人手的IK的扩展的SDLS方法被提出。通过只计算相关的最小奇异值和相应的向量来降低原方法的高昂代价,这是由于计算扩展雅可比矩阵的奇异值分解而产生的。并且为每个估计的奇异值关联一个特定的阻尼因子,并使用Cholesky分解计算关节角。 115 | 116 | #### 4.2.1.6 其他雅可比矩阵解决方案 117 | 118 | 方案如下所示: 119 | 120 | - 使用Gauss-Seidel算法来解决角色IK问题:构造一个线性方程组形式的雅可比矩阵$\Delta S\approx J\Delta\theta$,并利用Gauss-Seidel迭代法估计了未知的$\Delta\theta$;$J=J^{T}J+\delta I$,其中加入阻尼值$\delta I$以防止奇异性,使最终的方法更加稳定和鲁棒。 121 | - 使用"半雅可比矩阵"替代完整的雅可比矩阵解,从而降低了应用IK时的计算成本。 122 | - 扩展雅可比矩阵技术,在雅可比矩阵上增加了额外的行。 123 | - 计算一阶雅可比矩阵,该矩阵将关节速度映射为任务空间速度,并将其反转,将误差映射为关节状态更新。 124 | - 通过整合质量分布信息来控制关节的重心位置,从而扩大了IK的范围。 125 | 126 | - 大多数数值方法可以通过适当实施特定约束的优先级来进一步加强。考虑到人的结构高度冗余,可能会导致多个任务之间的冲突,通用的多优先级DLS框架被提出,以避免任务之间的这种冲突。如: 127 | 128 | - 基于雅可比伪逆的在线运动重定向(OMR)方法,使用两个级别的优先级来控制人形机器人的姿态,并处理多个末端受动器。 129 | 130 | - 使用增强雅可比矩阵来解决具有优先级的IK问题(同样是两级),后来该方案在一种称为优先级IK (PIK)的方案中进行了扩展,以处理full-body操作。PIK允许将约束与优先级相关联,以便先强制重要属性。基于任务的雅可比矩阵及其零空间投影算子能够处理具有多优先级和高度冗余结构的运动学问题。然而,雅可比矩阵的简单逆没有考虑如:关节范围、速度和加速度限制等约束。 131 | 132 | - feedback IK(FIK)方法,该方法从控制的角度解决IK问题,使所需速度和实际笛卡尔速度之间的差异最小化。在feedback循环中,通过控制灵敏度函数得到所需的关节参数。该算法作为一个滤波器,不需要矩阵操作(如逆)。奇异点的处理不需要阻尼因子,这使得它比基于伪逆的方法计算效率更高。作者还描述了如何应用操纵器约束,将关节和末端受动器加权为一组更可行的姿势。与其他基于雅可比矩阵的算法一样,它可以轻松地处理带有多个末端受动器的问题。 133 | 134 | #### 4.2.1.7 合并约束(Incorporating constraints) 135 | 136 | 在雅可比矩阵族方法中实现约束,以提高性能并增加重建位姿的真实感,并不简单。多年来已经为处理关节限制做出了一些努力。如: 137 | 138 | - 将无约束解简单投影到可行位姿上的方法。然而,这并不能保证结果会接近一个最优的解决方案。 139 | - 基于惩罚的增加移动限制的方法,缺点是这种方法通常会收敛到较差的结果。 140 | - 其他一些在关节约束下控制运动链的常用方法有任务优先级法和雅可比矩阵的零空间投影法。 141 | - 在机器人领域,SNS(零点空间饱和)和IKTC (带任务校正的IK)方法被提出。在使用最小范数解时,这些方法通常会放弃使用超出其运动边界的关节,将其重新引入到合适的零空间中。 142 | 143 | 合并约束的最简单方法是通过加权单个关节的运动来实现。使用$\Delta\theta=WJ^{-1}e$方程来预测在配置姿势时自由度变化的能力,其中W是一个权重向量,值介于0和1之间。最后通过修改update scheme加入一种迭代投影技术(称为projected Gauss–Seidel),从而引入关节限制;角度的边界限制是通过clamping强制执行的。 144 | 145 | #### 应用 146 | 147 | UE4.26中FullBodyIK采用的是基于雅可比矩阵。FullBodyIk是基于ControlRig的一个插件。配置起来好多参数....... 148 | 149 | ### 4.2.2 牛顿法 150 | 151 | 使用基于Hessian矩阵计算或使用基于函数梯度值的近似Hessian方法计算。 152 | 153 | 特点:不会遇到求雅可比矩阵时的奇异性问题,但复杂、难以实现、高计算成本的每次迭代。计算机建模和动画工具常使用牛顿法解决非线性运动学方程。 154 | 155 | 这里不再详细介绍..... 156 | 157 | ### 4.2.3 启发式法 158 | 159 | 启发式算法子家族实现了解决IK问题的简单方法,而不使用复杂的方程和计算。这些算法通常由简单的操作组成,以一种迭代的方式,逐渐推出IK解决方案。启发式IK算法具有较低的计算成本,因此通常很快就能得到最终的姿态,并且非常适合于简单的问题,特别是对于非人体测量骨骼(如蜘蛛、昆虫)。它们的主要局限之一是,即使满足了所有的关节约束,它们也会产生非自然或生物力学上不可行的动作和手势。启发式解算器不考虑附近关节之间的时空修正,因为它们独立地处理每个关节的约束,而不考虑全局约束。下面的小节将介绍并讨论最流行的启发式IK解算器,以及克服当前局限性的方法。 160 | 161 | #### 4.2.3.1 Cyclic coordinate descent(循环坐标推演) 162 | 163 | 原理:一次转换一个关节变量来最小化位置和姿态误差。 164 | 165 | 实现:每一步将每个关节位置与末端受动器和目标对齐;从末端受动器开始,向内侧移动,对每个关节角进行变换,使链条的最后一根骨头更接近目标。设运动链由$n$个关节组成,其中$p_{1}$为根关节,$p_{n}$为末端受动器,$t$为目标位置。首先,求由目标位置、$p_{n-1}$和末端受动器定义的角$\theta_{n-1}$。然后,通过旋转$p_{n}$更新末端受动器的位置,使$\theta_{n-1}$设为零。类似地,求由目标位置、$p_{n-2}$和末端受动器定义的角度$\theta_{n-2}$,并更新$p_{n-1}$和$p_{n}$位置使$\theta_{n-2}$为零。当所有关节都更新时,迭代就完成了。重复这一过程,直到末端受动器满意地接近目标位置。 166 | 167 | 解释:CCD的实现非常简单,只需要点积和叉积;因此,每次迭代的计算成本较低。它提供了一个数值稳定的解,并且在自由度的数量上具有线性时间的复杂度。CCD可以很容易地扩展到包括局部约束,如允许的角度变换在每一步都有上下限。根据定义,**CCD只处理串行链**,在这个方向上,也有一种处理树形关节结构和多个末端受动器的扩展。所提出的多链方法可连续应用于多个关节链;它将关节结构分成更小的串联链,并对每个链独立处理。类似的,建议将骨架划分子类别,然后分层迭代地应用CCD。 168 | 169 | CCD存在的问题: 170 | 171 | - 它的运动分布不佳,它倾向于过分强调靠近末端受动器的关节的运动,这可能会导致姿势不自然。 172 | - CCD可能产生大角度旋转,经常产生不稳定的不连续性和振荡运动。 173 | - 在某些情况下,特别是当目标位于接近base时,它会导致链条形成一个环,在到达目标之前滚动和展开自己。同样,对于特定的目标位置(特别是要求较高精度时),算法会进行大量的迭代,导致末端受动器运动缓慢。 174 | 175 | IBK:通常情况下,即使加入了约束条件,也可能产生不现实的姿势,特别是在高度清晰的角色中,因为很难执行全局操纵限制。在这种方式下,逆分支运动学(IBK)的CCD的变化出现,旨在通过分配一个连续的旋转范围来控制预定义的全局过渡成本阈值,来处理非自然姿态的产生。此外,在每次迭代中引入一个偏置因子,以便在每次迭代中对旋转进行校正。还有,可以通过增加一个反馈常数(末端受动器与目标之间的距离)来改善CCD的收敛性,该反馈常数随后与欠阻尼因子相乘,以产生更接近目标的更小的值。不过,这只允许在必须完成给定任务的关节链中移动。 176 | 177 | 变化和扩展:CCD算法还有很多的变化和扩展来提高其性能。例如: 178 | 179 | - the circular alignment algorithm(CAA) 将给定的关节链沿base与目标位置之间的圆弧放置。因此,可以确保有一个可用的解决方案,并且不存在链本身交叉的可能性(CCD中的一个常见问题)。然而,CAA要求所有链都具有相同的长度,并且只在包含运动链base和目标的二维平面上工作。 180 | - inductive IK(IIK)算法是CCD的另一种扩展。它使用一个uniform posture map(UPM)来控制一个类人的3D角色的姿势,同时学习算法防止产生无效的输出神经元。IIK算法形成包含每个输出神经元FK值的正运动学表,并搜索离期望位置最近的FK点,以获得更自然的姿态 181 | 182 | ##### 应用 183 | 184 | UE4.26中的FAnimNode_CCDIK和上面的思想步骤一样,这里不再过多阐述。 185 | 186 | 一次推演步骤: 187 | 188 | ![](pictures\CCDIK一次推演步骤.png) 189 | 190 | 还有《游戏编程精粹3》2.5 受限的逆运动学提到,限制角速度来生成真实效果动作。 191 | 192 | #### 4.2.3.2 Forward and backward reaching inverse kinematics(前向和后向到达IK) 193 | 194 | 思想:类似于用于绳索模拟的跟随领先(FTL)算法,该算法不使用角度旋转,而是将关节沿直线的新位置更新到下一个关节。然而,与只进行一次迭代的FTL不同,FABRIK采用的是向前和向后的迭代模式,每次都最小化目标与末端受动器之间的距离。 195 | 196 | 实现:设运动链由$n$个关节组成,其中$p_{1}$为根关节,$p_{n}$为末端受动器,$t$为目标位置。每对关节之间的距离记为$d_{i}=|p_{i+1}-p_{i}|$,$i$ = 1,…,n-1。在前向阶段,该算法从末端受动器估计每个关节的位置,从末端受动器$p_{n}$开始,向内移动到manipulator base $p_{1}$。末端受动器的新位置被指定为目标位置$p_{n}^{'}=t$。然后,线$l_{n-1}$是$p_{n-1}$和$p_{n}^{'}$两点之间的连线,将 $p_{n-1}^{'}$放到位于这条线上的距离$p_{n}^{'}$的距离是$d_{n-1}$的位置上。同样,线$l_{n-2}$是$p_{n-2}$和$p_{n-1}^{'}$两点之间的连线,将$p_{n-2}^{'}$设置为距离$p_{n-1}^{'}$的距离是$d_{n-2}$的位置上。算法继续执行,直到所有关节位置$p_{1}$,…,$p_{n}$都被更新。在算法的后一阶段,重复同样的过程,但这一次从the manipulator’s base向后移动到末端受动器,这完成了一个完整的迭代。然后重复上述过程,根据需要进行多次迭代,直到末端受动器完全相同或足够接近预期目标。当目标在可及范围内时,FABRIK总是收敛到任何给定的目标位置。 197 | 198 | 优点:FABRIK的主要优点是简单性,容易适配不同的问题的灵活性,低计算成本和处理闭环或多个末端受动器问题的有效性。后者可以通过将算法划分为更小的sub-sections,并使用sub-bases作为intermediate stops来实现。FABRIK还支持大多数人体测量和机器人关节约束,通过重新定位和重新定向目标,使其在允许的范围内。FABRIK到达目标非常快速,即使误差容忍值为零也能收敛到目标,并且在奇点附近很好地平衡。FABRIK的另一个特点是,与CCD过分强调接近末端受动器的运动不同,它将运动分布到所有关节。 199 | 200 | 扩展FABRIK的方法,比如: 201 | 202 | - 从能量转移的角度来解决IK问题:使用mass-spring模型,通过使弹簧中守恒的力能最小化来调整关节位置。 203 | - 处理目标优先级,通过调整初始算法来处理含有两个以上节段的关节。 204 | - 采用具有优先级的分层顺序方式的FABRIK算法的约束版本来控制类人模型的运动。 205 | - 热启动前使用data-driven的FABRIK来生成更自然的人体姿势。扩展FABRIK处理无碰撞的任务。 206 | 207 | FABRIK在 position space而不是orientation space中解决IK问题,而关节朝向则在一个单独的步骤中处理(这也增加了额外的复杂度)。因此,在方向约束下,其连续性较小。特别是当运动链尺寸较小且接近末端受动器的关节有严格约束时,算法的约束版本会出现死锁情况。在这种情况下,FABRIK无法找到解决方案,因为运动链的弯曲程度不足以达到目标。这是每个关节独立处理的的算法结构导致的。在算法的每一步中,目标位置都在圆锥截面表面局部投影并重新定向。因此,为了在可能的情况下推动运动链在当前关节中进一步弯曲,该算法不考虑对前(父)关节或后(子)关节的限制。作者使用随机扰动技术来处理死锁情况,将父关节推至其极限,允许子关节有更多的弯曲。此外,与CCD类似,FABRIK也遇到了无法整合满足附近关节之间时空相关性的全局模型约束的问题。 208 | 209 | ##### 应用 210 | 211 | UE4.26中的FAnimNode_Fabrik和上面的思想步骤一样。 212 | 213 | ## 4.3 数值驱动法 214 | 215 | 主要应用在动作捕捉(motion capture)技术中。数据驱动方法背后的主要思想是使用预先学习的姿势将末端受动器的位置与从数据库中学习到的可行姿势相匹配。在机器人领域中,这通常通过基于神经网络和人工智能的方法来完成。 216 | 217 | 现有的IK学习方法可以分为两类:基于误差的方法(the error-based methods)和基于实例的方法(the example-based methods)。基于误差的方法改进了用于到达目标位置的IK估量,而基于实例的方法使用实例配置来学习IK估量。计算机图形学中最常见的策略是基于实例的,即基于预先记录的动作数据库重建姿势。在这个方向上,可以通过一种插入实例运动和位置方法来完成给定的人体任务。每个实例姿势都由特定骨骼的位置参数化,并根据期望位置的参数,使用径向基函数混合几个姿势。 218 | 219 | 数据驱动法通常用于确定从一组可行的解决方案中选择自然运动。它们的效率通常取决于数据库的大小。基于实例的方法需要一个离线的训练过程,并且结果与训练数据高度相关。因此,数据驱动的IK解算器通常会出现姿态的不连续性;如果期望姿势与数据库中的姿势相差太远,那么结果可能在时间上是不连贯的。这里,可以使用一些特征参数来确保姿势平滑过渡,之后构建kd-tree,用于快速选择运动。 220 | 221 | 这里,可以看Motion Matching技术和Learned Motion Matching技术。 222 | 223 | ## 4.4 混合法 224 | 225 | 为了降低优化问题的复杂性,可以将IK问题分解为解析部分和数值部分。 226 | 227 | ### 4.4.1 统计方法 228 | 229 | 序贯蒙特卡罗方法(SMCM,人工智能方向)也被用于求解IK问题。通过采用采样方法,用FK算法求解IK问题,因此避免了逆矩阵的计算。该问题被视为隐马尔可夫模型(HMM),其隐式状态由定义关节图形的所有参数给出。状态空间由关节图形的所有可能的构型组成,然后在过滤框架中重新制定逆运动学。SMCM IK解算器不需要显式数值反演,并且可以以直观的方式将关节约束添加到系统中。这些可以很容易地实现,而不需要复杂的优化算法。 230 | 231 | Particle IK使用一个身体姿势目标集,并试图通过在角色身体上形成一个约束系统来满足目标。在雅可比矩阵框架内使用概率密度函数(PDF)作为软约束来控制关节的自由度,以确保局部关节空间中运动的平滑性和连贯性。 232 | 233 | ### 4.4.2 并行计算 234 | 235 | parallel IK算法允许求解具有多个约束的复杂关节体问题。比如:依赖于雅可比逆矩阵的多次数值估计的计算;将内部DLS步骤映射到数据并行GPU架构来扩展DLS方法以利用最大并行度;变化SDLS方法,关节参数从存储在八叉树中的动捕数据中自动学习,以便快速访问。给定末端受动器轨迹,通过对关节信息的并行过滤实现平滑动画,允许动态学习约束并减少所需的计算时间。 236 | 237 | ### 4.4.3 连续的逆运动学 238 | 239 | SIK(Sequential inverse kinematics)是一种解析迭代的IK方法,可以实时重建人体的三维全身运动。该方法的输入是末端受动器的位置,如手腕、脚踝、头部和骨盆,然后使用这些受动器寻找人体姿势。 240 | 241 | SIK的主要思想是用简单的解析IK算法在身体不同部位按特定顺序依次求解重建。SIK使用混合IK方法从spine的配置开始,该方法使用root和head的位置;然后,利用spine和已知的末端受动器位置确定clavicles的位置和方向;最后,利用已知的末端受动器位置,结合解析IK方法来定位每个limb的位置和方向。除此之外,使用生物力学(见下一节)来约束关节和防止产生不自然的运动,以确保视觉上人体姿势合理。 242 | 243 | ## 4.5 生物力学约束(Biomechanical constraints) 244 | 245 | 在冗余系统中,寻求IK解决方案时,结合关节约束来选择满足用户/模型约束的解决方案是必要的。有很多关节和模型约束已经被提出。 246 | 247 | - 第一组约束基于高级控制,如位置、方向、特定视觉和平衡。 248 | - 第二组约束,目标由用户定义或依赖于环境,如在交互任务(地板、到达、抓取)中或在碰撞避免中。同时执行多项任务可能会导致目标冲突,所以IK结算器可以使用优先级或加权方法来控制,然后在不同的情况下找到不同的解决方案。 249 | 250 | 关节由位置和方向定义,在普通情况下,它有3个自由度。关节允许连接在一起的两个肢体之间进行相对运动。人体测量关节也有多种,比如:球窝、铰链等。大多数现有的结构模型使用的技术是限制关节的旋转和平移限制。通过限制关节运动,可以保持运动在可行的范围内,以防止出现不现实、非自然的运动。 251 | 252 | 即使考虑到关节的极限,仍然可能得到非自然的姿势。还需要下面的方案来保证动作自然性: 253 | 254 | - 由于IK不能直接解决动力学约束,如弹道运动中的动量守恒问题,所以人们进行了很多努力。使用基于物理的角色动画来为IK结算器提供一个更自然的运动,保持在一个可行的运动集里。物理相关的技术有很多,比如:利用动量和惯性来改善动画平衡;利用某些动态物理特性,如质心和角动量,来改善不现实的运动;使用动量和力约束;通过控制质量中心和角色惯性张量的大小,在特定的动态活动中尊重物理。 255 | 256 | - 以肌肉为基础的控制方法。 257 | - 结合数据驱动的方法。代价是训练学习大量复杂和动态的动作,这是很耗时的。 258 | 259 | ## 4.6 物理模拟 260 | 261 | UE5采用全新的解算器,性能得到改进,求值速度也更快,并能提供确定性的姿势。UE5引用的是最新的基于XPBD的刚体模拟论文。 262 | 263 | sincezxl大佬的文章写得很详细了,请移步[UE5 FullBodyIK学习](https://zhuanlan.zhihu.com/p/381469750)~ 264 | 265 | # 5 讨论 266 | 267 | IK起源于机器人技术,其目的是确定将机器人的每个末端受动器移动到期望位置的关节参数。然而,在计算机图形学和游戏编程中,它被引入来处理完全不同的问题,包括有效地为3D关节主体设置动画、将游戏角色与世界连接起来、让虚拟角色完成特定的任务(避障,运动合成,运动重定向,接触约束等)。在本节中,我们将讨论众多IK解算器的性能,并建议哪种解算器最适合解决特定问题,例如:类人或非拟人化角色、实时交互、多链问题等。讨论将评估解算器在可扩展性、计算成本以及所产生运动的平滑度等方面的性能,还有他们的局限性。 最后,我们提出了未来的研究方向,包括IK使用和性能的潜在发展。 268 | 269 | IK解算器的选择主要取决于问题的定义和特性,并包括几个参数,如计算成本(性能速度,如对于实时交互应用程序)、期望的最终姿态的平滑(取决于应用程序,如人类、动物或机器人)、可扩展性(例如,需要易于扩展到不同的模型并支持多个末端受动器)以及应用优先级、关节和模型限制的可能性。例如,解析法解算器最适合于简单的 IK 问题,而迭代方法更通用,但由于系统的非线性,需要多个步骤向解收敛。在生物力学定律、优先级和加权约束很重要的情况下,雅可比方法可能是一个不错的选择,而如果计算成本比较重要,则启发式方法更具吸引力。下面的小节将讨论每种方法对特定应用程序的性能,并建议哪种方法更适合不同的任务。 270 | 271 | ## 5.1 类人角色(Human-like characters) 272 | 273 | 类人角色是计算机图形学中最常用的模型。一般来说,这种特征由大量的关节(至少 24 个)组成,这些关节受到生物力学和生理学的限制,最多有 70个DoFs。人们已经提出了许多解决方案来处理人类运动的high articulation。 274 | 275 | **雅可比矩阵方法**:用于类人动画的第一方法是基于雅可比矩阵的方法。雅可比矩阵方法可以轻松处理人体骨骼的high articulation,但它们通常会产生不连续的抖动和不平稳的运动,尤其是当线性近似的clamping允许目标和末端受动器之间有较大的步幅时,或者当四肢接近单一姿势。减小clamping步骤的振幅提高了它们的稳定性,但代价是较高的计算时间。总而言之,在没有急速收敛的情况下跟踪目标所需的时间是有限制的;例如,DLS和SDLS方法在抖动和奇异性问题方面表现良好,但计算量大,因此在控制或与类人角色交互方面不太受欢迎。 276 | 277 | **启发式法**:在启发式迭代方法中,通过跟踪附加在特定位置(如末端受动器)的多个控制点来为类人角色设置动画。启发式方法的问题是在确保附近关节之间的时空相关性得到满足方面的能力有限。即使应用了关节约束,并非所有生成的姿势都符合自然人体运动的生理约束。这主要是由于启发式方法在每次迭代时局部应用约束,每个关节都是独立处理的,不提供整合全局约束的可能性。从全局角度处理不自然姿势产生的一种方法是将骨架划分为sub-chains,并根据优先级按层次和顺序工作,以及研究关节之间的时间相关性,然后允许通过拉力进一步控制运动。 278 | 279 | **混合方法**:在层次结构模型中结合数值和解析技术的混合方法可用于需要低计算成本和寻求约束良好的类人模型的任务。CCD和混合方法(SIK)在计算上比其他方法更有效,而在重建质量方面,SIK方法在评估中使用的方法中给出了最好的结果。然而,即使对于定义明确的模型,也很难理解如何应用全局限制(如基于物理或生理的限制)。因此,不能保证解算器返回的解会在一组可行的人类自然运动范围内。 280 | 281 | **数值驱动法**:当前控制和动画类人角色的趋势是使用data-driven的学习或深度学习方法。学习方法选择从数据库获得的满足给定约束的最接近的候选姿势;它们的优点是不需要将约束集成到解算器中,因为重建的姿势仅与可行的姿势匹配。这确保了生成的姿势是自然的,并满足人体骨骼的解剖和生理约束。然而,数据驱动的方法需要一个离线学习阶段,结果的质量取决于所使用的运动数据库的大小。在真实姿势与存储的姿势范围不同的情况下,可能会产生异常解决方案。在数据库中建立大量可用的motion clips克服了这个问题,但从可用的姿势中选择最合适的姿势并非易事。还有许多其他因素会影响数据驱动方法的性能,例如确保时间一致性和所选姿态之间平滑过渡的机制。此外,不能保证从匹配的姿势中检索到的最佳候选不包含由于错误捕获而导致的错误。另一方面,深度学习方法可以处理错误问题;他们使用预先捕获的运动,通过卷积学习运动流形,平滑误差,从而重建自然和合理的运动。然而,与学习方法类似,学习CNN模型非常耗时,所产生运动的质量与用于训练的数据量相关。当通过卷积过程平滑运动时,它会插入运动的精细细节。尽管如此,学习和深度学习方法似乎都是目前最流行的方法之一,用于控制人体姿势和从稀疏数据中重建运动以及为高度关节化的人体部位(例如手)设置动画。 282 | 283 | 即使在处理类人角色时,数据驱动的方法也并不总是合适的。在某些情况下,导演希望他们的角色拥有卡通形象和行为或拥有超级英雄的能力。无法大规模捕获此类运动,来建立一个方便的运动库以用于检索所需的姿势。在这种情况下,有必要通过定义以分层和顺序方式运行的良好约束的混合解决方案来替代方法。此外,数据驱动的方法在计算上不够高效,无法实现实时交互。 284 | 285 | ## 5.2 非拟人化的角色或物体( Non-anthropomorphic characters or objects) 286 | 287 | 非拟人化角色包含难以与人类数据匹配的关节(例如蛇、昆虫、多腿动物、龙),以及允许某种程度关节的其他物体(例如绳索)。创建包含每个生物或物体的所有可行动作的大型数据库是极其困难的;在某些情况下,捕捉对象(例如昆虫)具有挑战性,或者自然界中没有类似的真实生物(例如卡通中的虚拟人物)。这也适用于具有额外关节部分(例如尾巴)的虚拟类人生物。因此,在这些情况下,数据驱动的方法似乎不太方便。 288 | 289 | 处理非拟人对象的最佳方法是针对具体问题集成最合适的方法;根据运动链的特性,可以对不同的关节进行不同的处理。在适用的情况下,可以将问题分成几部分,并以分层和顺序的方式应用最合适的方法。可以调整混合方法以处理卡通和怪物般的角色。另一方面,启发式方法在更简单的问题中提供平滑的运动。例如,由于FABRIK计算效率高、足够灵活以适应不同的模型,并且能够控制多个末端受动器,因此它似乎是实时动画具有有限约束或无约束的单个运动链(例如尾巴)或具有多个末端受动器解算器(例如昆虫)的运动链。 CCD由于其效率也非常适用于单运动链,但是它的运动分布较差,链条在到达目标之前的rolling和unrolling会导致姿势不自然。Jacobian方法也可以有效地为单个和多个末端受动器设置动画,然而它们通常收敛缓慢,因为它们使用小步长的线性近似(改变步长可能会导致不希望的抖动和不连续性)。SVD-DLS 中使用的近似收敛速度略快于其雅可比矩阵,但雅可比矩阵转置和SDLS返回最平滑的运动。 290 | 291 | 对于非人体特征的自然动画和运动的制作,模型的骨骼设计和IK选择应考虑到生物的形态和肌肉骨骼结构。有很多论文处理这种复杂生物的动画,如双足动物、四足动物、昆虫 或给定关节生物的游泳行为,它们主要是通过结合基于物理的雅可比IK解算器来设计的。对于多关节组成的更复杂的结构,建议使用混合法。 292 | 293 | 对于非拟人化角色来说,可扩展性和对多个末端受动器的处理也是很重要的因素。可扩展性通常通过系统的通用性以及可调整和定制以应对不同问题的能力来衡量;求解大型运动链问题所需的计算时间以及产生的精度,对这种评估起着重要的作用。具体来说,解析方法往往具有较差的可扩展性;当问题或使用的模型发生微小变化时需要完全重新设计解决方案。同样,即使data-driven的方法可以有效地缩小到分区,它们被放大或适应具有不同骨架结构的模型的能力也是有限的,并且高度依赖于训练数据。对于不同结构的模型,必须进行新的训练。另一方面,雅可比法和牛顿法可以很容易地适应不同运动链的问题,但收敛所需的时间会着关节和DoFs数量的增加而增加。同样,FABRIK很容易适应具有不同结构和多个末端受动器的模型;与性能和收敛速度低的CCD不同,FABRIK的计算时间在运动链增加时变化不大。最后,缩放混合方法可以被用于re-defining和re-adjusting模型的结构、优先级和层次结构,即使并不简单。 294 | 295 | ## 5.3 未来发展方向 296 | 297 | Motion capture技术有一个限制是只能捕捉到逼真的动作,而导演有时希望角色具有非现实的外观和行为。在这种情况下,可以将IK技术用于部分身体运动合成,从而从现有运动中创建新动作。此外,它们可以结合起来有效地合成动作,避免常见的问题,如滑步和抖动。虽然动作捕捉是对3D动画而言的一个非常有用工具,但有时它并不能让导演对动画的细微之处进行足够的控制。例如,不可能为预先捕捉的动作添加更多表情。自动将捕捉到的动作转换成不同风格、行为、性别、年龄等角色的能力是一个需要进一步研究的重要方面;因此,需要改进 IK 算法,为导演提供更大程度的灵活性和控制动画风格变化的能力。 298 | 299 | IK最重要的应用之一是实时骨骼重定向,这对于将从一个角色捕捉到的动作映射另一个具有不同比例(更高、更小、更长的腿等)的角色至关重要。由于骨骼结构和/或骨骼长度的差异,大多数重定向技术都会受到飞行、穿透和滑行的影响。此外,运动重定向必须确保满足接触约束。在这个方向上,有必要研究自动将经历复杂变形的骨架适应不同的输入网格的方法,自然地保留运动的本质并且没有抖动。他们还应该能够重新建立可能发生的约束违反。未来的研究方向应侧重于实现具有完全不同的关节约束(如两足到四足)、完全不同的骨骼结构、不同的身体比例、不同的运动和行为方式的骨骼之间的平滑过渡。另一种解决设计高效运动控制器的是深度学习,它只需要生物的机械结构,不需要其他先验信息。 300 | 301 | 还应该考虑结合更多解剖学和生理学约束的方法,旨在将最终姿势限制在一个可行的集合中并允许基于物理的动画。除了生物力学定律(例如完成任务所需的力和能量)之外,还需要进一步研究考虑肢体大小的微小变化(例如通过添加spring/mass模型),目的是在人体动画中添加额外的真实感。然而,还有一个问题是,在用户不感知变化的情况下,链可以扩大或缩小多少(保持人体解剖结构和约束)。 302 | 303 | Komura通过利用最小肌肉力变化的标准解决了IK冗余;然而,肌肉、不同被动扭矩等的影响尚未得到充分研究。考虑到解剖学原理和肌肉骨骼模型,必须改进 IK 方法以处理人体、身体形状。最近关于骨骼肌和皮下脂肪生长的研究,揭示了需要应对的新挑战。肌肉和脂肪的建模需要计算和解剖的努力,以保持身体形状的适当变形;当肌肉和脂肪量发生变化时,不仅会影响体积和表面模型,还会影响人体的骨骼结构和运动方式。 304 | 305 | # 6 其他 306 | 307 | Dragon IK:为不同类型的动物提供IK,如:四足动物、两足动物 308 | 309 | LockIk:脚锁定 310 | 311 | Power IK:UE的一个插件,结合射线检测开发。应用场景如:将生物与不平的地形对齐、在运行时动态修改姿势。 312 | 313 | 基于预测的IK:参考[UE4实现基于预测的FootIK](https://zhuanlan.zhihu.com/p/380222928) 314 | 315 | Final IK:unity插件 316 | 317 | - Limb IK:专注于三段手和腿特征肢体类型。 318 | - Biped IK:复制和增强Unity内置角色IK设置的行为,比如头部控制。 319 | - Look At IK:可用于任何角色或其他骨骼层次结构,以旋转一组骨骼以面向目标。 320 | - Aim IK:修改的CCDIK,设定目标,关节末端始终朝向目标,一般做头部的朝向。 321 | 322 | 323 | 324 | 325 | 326 | 从3月开始看,拖延好久了......(*  ̄︿ ̄) 应用后面补! 327 | 328 | 发布于2022年3月15日 329 | 330 | --------------------------------------------------------------------------------