如何创建圆形物体进入和分离厚物质的效果

时间:2016-02-15 09:16:04

标签: swift sprite-kit cadisplaylink

基于下面的图像(我使用不同颜色的圆形和平面使它们可以看到,但最后颜色将是相同的),使用Swift和Spritekit,我试图创建一个效果圆形物体进入厚物质(不一定是粘性的)并与厚物质分离。基本上,当圆形物体分离时,它会在形成圆形时从平坦表面拉开。 我想使用图像动画帧,但由于对象是具有物理主体的SKSpriteNodes,因此这将使得对象与动画的碰撞非常困难。另一种方法是使用CAAnimation,但我不知道如何将这与SKSpriteNodes与物理机构结合起来。如何使用上述任何方法或不同的方法创建这种分离效果?

enter image description here

更新

下面的图片显示了当圆形物体进入厚物质直至其被浸没时,厚物质表面的变化。

enter image description here

2 个答案:

答案 0 :(得分:1)

从高度理解,有两种方法可以做到这一点。

  1. 糟糕的方式(但是当流体具有纹理时效果更好):提前创建精灵表,然后覆盖SKSpriteNode对象的另一个子节点。当它们之间的距离小于某个量时,动画精灵中的帧将是从球到表面的距离的函数。所需的距离范围(范围)必须映射到精灵帧编号(frameIndex)。 f(范围)= frameIndex。线性插值将有助于此处。有关插值的更多信息。

  2. 正确的方法:使流体成为曲线对象,然后在初始,中间和最终状态之间使用线性插值为曲线上的点设置动画。这将需要三条曲线,每条曲线具有相同的点数。让初始流体状态为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;
    
  3. 然后使用带有3D矢量的矢量叉积来检测球何时位于流体外部,即使您的项目是2D。

     Ball Vector (Vb)
        ^
        |
    (V) O--->  Closest Fluid Surface Vector (Vs)
    
    V = Vb x Vs
    

    然后查看V的V分量,称为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
    

    这是有效的,因为任何两个形状都可以使用插值混合在一起。 参数&#34; t&#34;作为两种形状之间混合的百分比。

    实际上,您可以在任意两种形状之间创建无缝混合,只要点数相同即可。这就是一个男人如何在好莱坞电影中变成狼,或者男人如何变成液体水坑。这些效果的唯一原则是插值。插值是一个非常强大的工具。它被定义为:

        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 Article

    进一步研究。如果您正在考虑动画任何动态形状,我会考虑理解Bezier曲线。 Pomax有一篇关于这个主题的精彩文章。虽然许多框架都有曲线,但是对它们如何工作有一般性的了解将允许您可以扩展地操作它们或者在框架缺乏的地方滚动自己的特性。她是Pomax的文章:

    A Primer on Curves

    祝你好运:)

答案 1 :(得分:1)

您正在寻找流体模拟器

使用现代硬件确实可以做到这一点。 让我们看看我们将在这里构建什么。

enter image description here

组件

要实现这一目标,我们需要

  • 创建几个具有物理主体和模糊图像的分子
  • 使用着色器将通用颜色应用于alpha> 0的每个像素

分子

这里是分子班

import SpriteKit

class Molecule: SKSpriteNode {

    init() {
        let texture = SKTexture(imageNamed: "molecule")
        super.init(texture: texture, color: .clear, size: texture.size())

        let physicsBody = SKPhysicsBody(circleOfRadius: 8)
        physicsBody.restitution = 0.2
        physicsBody.affectedByGravity = true
        physicsBody.friction = 0
        physicsBody.linearDamping = 0.01
        physicsBody.angularDamping = 0.01
        physicsBody.density = 0.13
        self.physicsBody = physicsBody

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

着色器

接下来,我们需要一个片段着色器,让我们创建一个名称为Water.fsh的文件

void main() {

    vec4 current_color = texture2D(u_texture, v_tex_coord);

    if (current_color.a > 0) {
        current_color.r = 0.0;
        current_color.g = 0.57;
        current_color.b = 0.95;
        current_color.a = 1.0;
    } else {
        current_color.a = 0.0;
    }

    gl_FragColor = current_color;
}

场景

最后我们可以定义场景

import SpriteKit

class GameScene: SKScene {

    lazy var label: SKLabelNode = {
        return childNode(withName: "label") as! SKLabelNode
    }()

    let effectNode = SKEffectNode()

    override func didMove(to view: SKView) {
        physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
        effectNode.shouldEnableEffects = true
        effectNode.shader = SKShader(fileNamed: "Water")
        addChild(effectNode)
    }

    var touchLocation: CGPoint?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let touchLocation = touch.location(in: self)
        if label.contains(touchLocation) {
            addRedCircle(location: touchLocation)
        } else {
            self.touchLocation = touchLocation
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        touchLocation = nil
    }


    override func update(_ currentTime: TimeInterval) {
        if let touchLocation = touchLocation {
            let randomizeX = CGFloat(arc4random_uniform(20)) - 10
            let randomizedLocation = CGPoint(x: touchLocation.x + randomizeX, y: touchLocation.y)
            addMolecule(location: randomizedLocation)
        }
    }

    private func addMolecule(location: CGPoint) {
        let molecule = Molecule()
        molecule.position = location
        effectNode.addChild(molecule)
    }

    private func addRedCircle(location: CGPoint) {
        let texture = SKTexture(imageNamed: "circle")
        let sprite = SKSpriteNode(texture: texture)
        let physicsBody = SKPhysicsBody(circleOfRadius: texture.size().width / 2)
        physicsBody.restitution = 0.2
        physicsBody.affectedByGravity = true
        physicsBody.friction = 0.1
        physicsBody.linearDamping = 0.1
        physicsBody.angularDamping = 0.1
        physicsBody.density = 1
        sprite.physicsBody = physicsBody
        sprite.position = location
        addChild(sprite)
    }

}
  

可以在我的GitHub帐户https://github.com/lucaangeletti/SpriteKitAqua

上找到完整的项目