41

下の画像を元に(円と平面が分かるように色を変えてみましたが、最終的には同じ色になります)、SwiftとSpritekitを使って、円状の物体が入ったような効果を出そうとしています。濃厚な物質(必ずしも粘着性である必要はありません)と、濃厚な物質からの分離。基本的に、円形のオブジェクトが分離するときは、円を形成しながら平面から引き離されます。イメージ アニメーション フレームを使用したかったのですが、オブジェクトが物理ボディを持つ SKSpriteNode であるため、オブジェクトとアニメーションの衝突のタイミングが非常に難しくなります。もう 1 つのアプローチは CAAnimation を使用することですが、物理ボディを持つ SKSpriteNodes とこれを組み合わせる方法がわかりません。上記のアプローチのいずれかまたは別のアプローチを使用して、この分離効果を作成するにはどうすればよいですか?

ここに画像の説明を入力

アップデート

下の画像は、厚い物質に円形の物体が入り、沈むまでの、厚い物質の表面の変化を示しています。

ここに画像の説明を入力

4

2 に答える 2

2

流体シミュレーターを探しています

これは、最新のハードウェアで実際に可能です。ここで何を構築するのか見てみましょう。

ここに画像の説明を入力

コンポーネント

それを達成するためには、

  • 物理体とぼやけた画像を持ついくつかの分子を作成する
  • シェーダーを使用して、アルファ > 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")
    }
}

シェーダー

次に Fragment Shader が必要です。次の名前のファイルを作成しましょう。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で入手できます。

于 2018-08-20T16:13:59.417 に答える
1

高レベルの理解から、これを行うには 2 つの方法があります。

  1. 悪い方法 (ただし、流体にテクスチャがある場合はより適切に機能します): 事前にスプライト シートを作成してから、SKSpriteNode オブジェクトの追加の子をオーバーレイします。アニメーション スプライトのフレームは、ボールとサーフェスの間の距離が一定量未満の場合、ボールからサーフェスまでの距離の関数になります。目的の距離範囲 (範囲) は、スプライトのフレーム番号 (frameIndex) にマップする必要があります。f(範囲) = フレームインデックス。ここでは線形補間が役立ちます。補間については後で詳しく説明します。

  2. 正しい方法: 流体を曲線オブジェクトにしてから、初期状態、中間状態、および最終状態の間で線形補間を使用して曲線上のポイントをアニメートします。これには、それぞれ同じ数のポイントを持つ 3 つのカーブが必要です。初期の流体状態を 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;
    

次に、プロジェクトが 2D であっても、3D ベクトルでベクトルの外積を使用して、ボールが流体の外側にあることを検出します。

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

V = Vb x Vs

次に、Vz と呼ばれる V の Z 成分を見てください。(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

これは、補間を使用して任意の 2 つの形状をブレンドできるためです。パラメータ「t」は、2 つの形状をブレンドするパーセンテージとして機能します。

ポイントの数が同じである限り、実際には任意の 2 つのシェイプ間でシームレスなブレンドを作成できます。これは、ハリウッド映画で男性がオオカミに変身する方法、または男性が液体の水たまりに変身する方法です。これらの効果の唯一の原則は補間です。補間は非常に強力なツールです。次のように定義されています。

    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の記事です:

曲線の入門書

あなたの進歩に幸運を祈ります:)

于 2016-06-17T20:19:07.230 に答える