从高层次的理解来看,有两种方法可以做到这一点。
不好的方式(但当流体有纹理时效果更好):提前创建精灵表,然后覆盖 SKSpriteNode 对象的另一个子对象。当它们之间的距离小于某个量时,动画精灵中的帧将是从球到表面的距离的函数。所需的距离范围(范围)必须映射到精灵帧号(frameIndex)。f(范围) = 帧索引。线性插值将在这里有所帮助。稍后将详细介绍插值。
正确的方法:使流体成为曲线对象,然后在初始、中间和最终状态之间使用线性插值对曲线上的点进行动画处理。这将需要三条曲线,每条曲线具有相同的点数。设初始流体状态为 F1。模型 F1 点为静态流体。当球被淹没一半时,流体状态为 F2。模型 F2 指向看起来像浸没在其最大宽度处的球。当球被淹没 75% 时,流体状态为 F3。请注意,当球完全浸没时,流体看起来没有变化。这就是为什么当球被淹没 75% 时,它具有最大的表面张力来抓球。就 SpriteKit 而言,您可以使用这些对象:
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddQuadCurveToPoint(path, NULL, 50, 100, 100, 0);
CGPathAddLineToPoint(path, NULL, 50, -100);
CGPathCloseSubpath(path);
SKShapeNode *shape = [[SKShapeNode alloc]init];
shape.path = path;
然后使用带有 3D 矢量的矢量叉积来检测球何时位于流体外部,即使您的项目是 2D 也是如此。
Ball Vector (Vb)
^
|
(V) O---> Closest Fluid Surface Vector (Vs)
V = Vb x Vs
然后查看 V 的 Z 分量,称为 Vz。如果 (Vz < 0),球在流体之外: 创建一个变量 t:
t = distOfBall/radiusOfBall
然后对流体形状中的每个有序点执行以下操作:
newFluidPointX = F1pointX*(t-1) + F2pointX*t
newFluidPointY = F1pointY*(t-1) + F2pointY*t
如果 Vz > 0),球在流体内部:
t = -(((distOfBall/radiusOfBall) + 0.5)^2) *4 + 1
newFluidPointX = F2pointX*(t-1) + F3pointX*t
newFluidPointY = F2pointY*(t-1) + F3pointY*t
这是可行的,因为可以使用插值将任何两个形状混合在一起。参数“t”作为两个形状之间混合的百分比。
只要点数相同,您实际上可以在任何两个形状之间创建无缝混合。这就是好莱坞电影中一个人如何变身为狼的方式,或者一个人如何变身为液体水坑。这些效果的唯一原理是插值。插值是一个非常强大的工具。它被定义为:
L = A*(t-1) + B*t
where t is in between 0.0 and 1.0
and A and B is what you are morphing from and to.
有关插值的更多信息,请参见:
Wiki 文章
供进一步研究。如果您正在考虑为任何动态形状制作动画,我会考虑理解贝塞尔曲线。Pomax 有一篇关于这个主题的精彩文章。尽管许多框架中都有曲线,但大致了解它们的工作原理将使您能够广泛地操纵它们或在框架缺乏的地方滚动您自己的功能。她是 Pomax 的文章:
曲线入门
祝你进步顺利:)