2021SC@SDUSC
之前六次博客我们分析了flaxEngine游戏引擎中actor中的刚体(RigidBody)和载具(WheelVehicles),本次我们将针对flax Engine中actor的另一个SplineRopeBody(用样条线表示的绳索、链条和缆绳的物理模拟)进行源码的分析。
按照管理来本次我们将针对flaxengine游戏引擎中物理引擎的SplineRopeBody.h文件但由于SplineRopeBody内容相对较少所以本次我将针对SplineRopeBody.h文件和SplineRopeBody.c文件一起进行源代码的分析。
一:SplineRopeBody.h文件
API_CLASS() class FLAXENGINE_API SplineRopeBody : public Actor
{
API_AUTO_SERIALIZATION();
DECLARE_SCENE_OBJECT(SplineRopeBody);
private:
struct Mass
{
Vector3 Position;
float SegmentLength;
Vector3 PrevPosition;
bool Unconstrained;
};
Spline* _spline = nullptr;
float _time = 0.0f;
Array<Mass> _masses;
首先是SplineRopeBody的构造函数继承自Actor。定义了struct结构体mass保存有关SplineRopeBody大小的相关数据结构和变量。
public:
API_FIELD(Attributes="EditorOrder(0), DefaultValue(null), EditorDisplay(\"Rope\")")
ScriptingObjectReference<Actor> AttachEnd;
上述函数的功能是目标actor把绳子的一端系在绳子上。如果未设置,绳端将自由运行。
API_FIELD(Attributes="EditorOrder(10), EditorDisplay(\"Rope\")")
float GravityScale = 1.0f;
上述函数是应用于绳索的世界重力比例尺。可用于调整重力或禁用重力。
API_FIELD(Attributes="EditorOrder(20), EditorDisplay(\"Rope\")")
Vector3 AdditionalForce = Vector3::Zero;
上述函数是施加在绳索(世界空间)上的附加外力。这可能是风力。
API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Rope\")")
bool EnableStiffness = false;
上述是代码是选择框,如果选中,物理解算器将对绳索使用刚度约束。它将不太可能弯曲,并将保持更多的形状。
API_FIELD(Attributes="EditorOrder(40), Limit(0, 0.1f, 0.0001f), EditorDisplay(\"Rope\")")
float SubstepTime = 0.02f;
上述是绳索模拟更新子步骤(以秒为单位)。定义物理更新的频率。
private:
void Tick();
public:
// [Actor]
void OnEnable() override;
void OnDisable() override;
void OnTransformChanged() override;
void OnParentChanged() override;
上述代码 tick函数在SplineRopeBody.c文件中将进行详细的讲解。
下面的定义了几个成员方法,需要在c文件或子类中重写的方法。
二:SplineRopeBody.c文件:
SplineRopeBody::SplineRopeBody(const SpawnParams& params)
: Actor(params)
{
}
首先是SplineRopeBody的构造函数构造一个含有相关物理参数的actor,有关物理参数的内容可以在我最早的有关刚体的文章中找到。
void SplineRopeBody::Tick()
{
Tick函数的具体实现。
if (!_spline || _spline->GetSplinePointsCount() < 2)
return;
PROFILE_CPU();
首先判断绳子是否存在,如果不存在或者绳子数量小于2个则直接返回。
const Vector3 gravity = Physics::GetGravity() * GravityScale;
auto& keyframes = _spline->Curve.GetKeyframes();
const Transform splineTransform = _spline->GetTransform();
const int32 keyframesCount = keyframes.Count();
const float substepTime = SubstepTime;
const float substepTimeSqr = substepTime * substepTime;
bool splineDirty = false;
缓存数据:gravity:重力 keyframe:关键帧 substep time子步骤时间
if (_masses.Count() > keyframesCount)
_masses.Resize(keyframesCount);
else
{
_masses.EnsureCapacity(keyframesCount);
while (_masses.Count() < keyframesCount)
{
const int32 i = _masses.Count();
auto& mass = _masses.AddOne();
mass.PrevPosition = splineTransform.LocalToWorld(keyframes[i].Value.Translation);
if (i != 0)
mass.SegmentLength = Vector3::Distance(mass.PrevPosition, _masses[i - 1].PrevPosition);
else
mass.SegmentLength = 0.0f;
}
}
上述代码是将样条线关键帧与模拟质量同步
auto& mass = _masses.First();
mass.Position = mass.PrevPosition = GetPosition();
mass.Unconstrained = false;
if (splineTransform.LocalToWorld(keyframes.First().Value.Translation) != mass.Position)
splineDirty = true;
上述代码是绳子头的位置(初始化的位置)
auto& mass = _masses.Last();
mass.Position = mass.PrevPosition = AttachEnd->GetPosition();
mass.Unconstrained = false;
if (splineTransform.LocalToWorld(keyframes.Last().Value.Translation) != mass.Position)
splineDirty = true;
上述代码是绳子尾的位置
for (int32 i = 1; i < keyframesCount; i++)
{
auto& massA = _masses[i - 1];
auto& massB = _masses[i];
Vector3 offset = massB.Position - massA.Position;
const float distance = offset.Length();
const float scale = (distance - massB.SegmentLength) / Math::Max(distance, ZeroTolerance);
if (massA.Unconstrained && massB.Unconstrained)
{
offset *= scale * 0.5f;
massA.Position += offset;
massB.Position -= offset;
}
else if (massA.Unconstrained)
{
massA.Position += scale * offset;
}
else if (massB.Unconstrained)
{
massB.Position -= scale * offset;
}
}
上述代码是距离的约束:其绳子最长连接距离和两个绳子的规模有关(mass)
if (EnableStiffness)
{
for (int32 i = 2; i < keyframesCount; i++)
{
auto& massA = _masses[i - 2];
auto& massB = _masses[i];
Vector3 offset = massB.Position - massA.Position;
const float distance = offset.Length();
const float scale = (distance - massB.SegmentLength * 2.0f) / Math::Max(distance, ZeroTolerance);
if (massA.Unconstrained && massB.Unconstrained)
{
offset *= scale * 0.5f;
massA.Position += offset;
massB.Position -= offset;
}
else if (massA.Unconstrained)
{
massA.Position += scale * offset;
}
else if (massB.Unconstrained)
{
massB.Position -= scale * offset;
}
}
}
上述代码是绳子连接的硬度。
if (splineDirty)
{
for (int32 i = 0; i < keyframesCount; i++)
keyframes[i].Value.Translation = splineTransform.WorldToLocal(_masses[i].Position);
_spline->UpdateSpline();
}
在完成以上操作后更新样条线和相关组件。
void SplineRopeBody::OnEnable()
{
GetScene()->Ticking.FixedUpdate.AddTick<SplineRopeBody, &SplineRopeBody::Tick>(this);
Actor::OnEnable();
}
void SplineRopeBody::OnDisable()
{
Actor::OnDisable();
GetScene()->Ticking.FixedUpdate.RemoveTick(this);
}
void SplineRopeBody::OnParentChanged()
{
Actor::OnParentChanged();
_spline = Cast<Spline>(_parent);
}
void SplineRopeBody::OnTransformChanged()
{
Actor::OnTransformChanged();
_box = BoundingBox(_transform.Translation);
_sphere = BoundingSphere(_transform.Translation, 0.0f);
}
上述四个方法就是头文件中的定义的四个方法。
统一绳子的规格和不可取的部分。