RayStartに沿ってenumerateBodiesで光線をバウンスします

クリス

SpriteKitGameSceneで弾丸が移動するパスを追跡したいと思います。「enumerateBodies(alongRayStart」を使用しているので、物理ボディとの最初の衝突を簡単に計算できます。

接触点と接触法線を考えると、反射角を計算する方法がわかりません。

5回の反射/バウンスを超えるパスを計算したいので、最初に次のことを行います。

  1. 光線を投げ、それが交差するすべての物体を取得し、最も近いものを取得します。
  2. 次に、その接点を次のリフレクション/バウンスの開始点として使用します。しかし、終点を何に設定するかで苦労しています。

私は何だと思う私は、通常の接点と接点との間の角度を取得していること、そしてそれに新しいポイントの反対を計算する必要があります...

    var points: [CGPoint] = []
    var start: CGPoint = renderComponent.node.position
    var end: CGPoint = crossHairComponent.node.position

    points.append(start)

    var closestNormal: CGVector = .zero

    for i in 0...5 {

        closestNormal = .zero
        var closestLength: CGFloat? = nil
        var closestContact: CGPoint!

        // Get the closest contact point.
        self.physicsWorld.enumerateBodies(alongRayStart: start, end: end) { (physicsBody, contactPoint, contactNormal, stop) in

            let len = start.distance(point: contactPoint)

            if closestContact == nil {
                closestNormal = contactNormal
                closestLength = len
                closestContact = contactPoint
            } else {
                if len <= closestLength! {
                    closestLength = len
                    closestNormal = contactNormal
                    closestContact = contactPoint
                }
            }
        }


        // This is where the code is just plain wrong and my math fails me.
        if closestContact != nil {

            // Calculate intersection angle...doesn't seem right?
            let v1: CGVector = (end - start).normalized().toCGVector()
            let v2: CGVector = closestNormal.normalized()
            var angle = acos(v1.dot(v2)) * (180 / .pi)

            let v1perp = CGVector(dx: -v1.dy, dy: v1.dx)
            if(v2.dot(v1perp) > 0) {
                angle = 360.0 - angle
            }

            angle = angle.degreesToRadians


            // Set the new start point
            start = closestContact

            // Calculate a new end point somewhere in the distance to cast a ray to, so we can repeat the process again
            let x = closestContact.x + cos(angle)*100
            let y = closestContact.y + sin(-angle)*100
            end = CGPoint(x: x, y: y)  

            // Add points to array to draw them on the screen
            points.append(closestContact)
            points.append(end)
        }

    }
ルカアンジェレッティ

私はあなたがこのようなものを探していると思いますか?

ここに画像の説明を入力してください

1.作業コード

まず、完全に機能するコードを投稿しましょう。SpriteKitベースの新しいXcodeプロジェクトを作成して

  1. GameViewController.swiftセットで

    scene.scaleMode = .resizeFill

  2. で見つけた通常のラベルを削除します GameScene.sks

  3. Scene.swift次のコードに置き換えます

>>

import SpriteKit

class GameScene: SKScene {

    override func didMove(to view: SKView) {
        self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
    }

    var angle: CGFloat = 0

    override func update(_ currentTime: TimeInterval) {
        removeAllChildren()
        drawRayCasting(angle: angle)
        angle += 0.001
    }

    private func drawRayCasting(angle: CGFloat) {
        let colors: [UIColor] = [.red, .green, .blue, .orange, .white]

        var start: CGPoint = .zero
        var direction: CGVector = CGVector(angle: angle)

        for i in 0...4 {
            guard let result = rayCast(start: start, direction: direction) else { return }
            let vector = CGVector(from: start, to: result.destination)

            // draw
            drawVector(point: start, vector: vector, color: colors[i])

            // prepare for next iteration
            start = result.destination
            direction = vector.normalized().bounced(withNormal: result.normal.normalized()).normalized()
        }
    }

    private func rayCast(start: CGPoint, direction: CGVector) -> (destination:CGPoint, normal: CGVector)? {

        let endVector = CGVector(
            dx: start.x + direction.normalized().dx * 4000,
            dy: start.y + direction.normalized().dy * 4000
        )

        let endPoint = CGPoint(x: endVector.dx, y: endVector.dy)

        var closestPoint: CGPoint?
        var normal: CGVector?

        physicsWorld.enumerateBodies(alongRayStart: start, end: endPoint) {
            (physicsBody:SKPhysicsBody,
            point:CGPoint,
            normalVector:CGVector,
            stop:UnsafeMutablePointer<ObjCBool>) in

            guard start.distanceTo(point) > 1 else {
                return
            }

            guard let newClosestPoint = closestPoint else {
                closestPoint = point
                normal = normalVector
                return
            }

            guard start.distanceTo(point) < start.distanceTo(newClosestPoint) else {
                return
            }

            normal = normalVector
        }
        guard let p = closestPoint, let n = normal else { return nil }
        return (p, n)
    }

    private func drawVector(point: CGPoint, vector: CGVector, color: SKColor) {

        let start = point
        let destX = (start.x + vector.dx)
        let destY = (start.y + vector.dy)
        let to = CGPoint(x: destX, y: destY)

        let path = CGMutablePath()
        path.move(to: start)
        path.addLine(to: to)
        path.closeSubpath()
        let line = SKShapeNode(path: path)
        line.strokeColor = color
        line.lineWidth = 6
        addChild(line)
    }
}


extension CGVector {

    init(angle: CGFloat) {
        self.init(dx: cos(angle), dy: sin(angle))
    }

    func normalized() -> CGVector {
        let len = length()
        return len>0 ? self / len : CGVector.zero
    }

    func length() -> CGFloat {
        return sqrt(dx*dx + dy*dy)
    }

    static func / (vector: CGVector, scalar: CGFloat) -> CGVector {
        return CGVector(dx: vector.dx / scalar, dy: vector.dy / scalar)
    }

    func bounced(withNormal normal: CGVector) -> CGVector {
        let dotProduct = self.normalized() * normal.normalized()
        let dx = self.dx - 2 * (dotProduct) * normal.dx
        let dy = self.dy - 2 * (dotProduct) * normal.dy
        return CGVector(dx: dx, dy: dy)
    }

    init(from:CGPoint, to:CGPoint) {
        self = CGVector(dx: to.x - from.x, dy: to.y - from.y)
    }

    static func * (left: CGVector, right: CGVector) -> CGFloat {
        return (left.dx * right.dx) + (left.dy * right.dy)
    }

}

extension CGPoint {

    func length() -> CGFloat {
        return sqrt(x*x + y*y)
    }

    func distanceTo(_ point: CGPoint) -> CGFloat {
        return (self - point).length()
    }

    static func - (left: CGPoint, right: CGPoint) -> CGPoint {
        return CGPoint(x: left.x - right.x, y: left.y - right.y)
    }

}

2.どのように機能しますか?

このコードが何をするのか見てみましょう。下から始めましょう。

3.CGPointおよびCGVector拡張機能

これらは、実行する幾何学的操作を単純化するための単純な拡張機能です(主にGitHubのRay Wenderlichのリポジトリから取得)。

4.4。 drawVector(point:vector:color)

これは、vector与えられたcolorから始まる与えられを描く簡単な方法pointです。

ここには何も派手なものはありません。

private func drawVector(point: CGPoint, vector: CGVector, color: SKColor) {

    let start = point
    let destX = (start.x + vector.dx)
    let destY = (start.y + vector.dy)
    let to = CGPoint(x: destX, y: destY)

    let path = CGMutablePath()
    path.move(to: start)
    path.addLine(to: to)
    path.closeSubpath()
    let line = SKShapeNode(path: path)
    line.strokeColor = color
    line.lineWidth = 6
    addChild(line)
}

5.5。 rayCast(start:direction) -> (destination:CGPoint, normal: CGVector)?

このメソッドはレイキャスティングを実行し、光線が物理ボディと衝突して入るALMOSTの最も近いポイントを返します。

private func rayCast(start: CGPoint, direction: CGVector) -> (destination:CGPoint, normal: CGVector)? {

    let endVector = CGVector(
        dx: start.x + direction.normalized().dx * 4000,
        dy: start.y + direction.normalized().dy * 4000
    )

    let endPoint = CGPoint(x: endVector.dx, y: endVector.dy)

    var closestPoint: CGPoint?
    var normal: CGVector?

    physicsWorld.enumerateBodies(alongRayStart: start, end: endPoint) {
        (physicsBody:SKPhysicsBody,
        point:CGPoint,
        normalVector:CGVector,
        stop:UnsafeMutablePointer<ObjCBool>) in

        guard start.distanceTo(point) > 1 else {
            return
        }

        guard let newClosestPoint = closestPoint else {
            closestPoint = point
            normal = normalVector
            return
        }

        guard start.distanceTo(point) < start.distanceTo(newClosestPoint) else {
            return
        }

        normal = normalVector
    }
    guard let p = closestPoint, let n = normal else { return nil }
    return (p, n)
}

クローゼットのほとんどはどういう意味ですか?

これは、目的地が始点から少なくとも1ポイント離れている必要があることを意味します

guard start.distanceTo(point) > 1 else {
    return
}

いいけどなんで?

このルールがないと、光線は物理体に突き刺さり、外に出ることはできません。

6.6。 drawRayCasting(angle)

この方法は基本的に、ローカル変数を最新の状態に保ち、5つのセグメントを適切に生成します。

private func drawRayCasting(angle: CGFloat) {
    let colors: [UIColor] = [.red, .green, .blue, .orange, .white]

    var start: CGPoint = .zero
    var direction: CGVector = CGVector(angle: angle)

    for i in 0...4 {
        guard let result = rayCast(start: start, direction: direction) else { return }
        let vector = CGVector(from: start, to: result.destination)

        // draw
        drawVector(point: start, vector: vector, color: colors[i])

        // prepare next direction
        start = result.destination
        direction = vector.normalized().bounced(withNormal: result.normal.normalized()).normalized()
    }
}

最初のセグメントの開始点はゼロに等しく、方向は角度パラメーターをダイビングします。

セグメント2から5は、前のセグメントの最終点と「ミラーリングされた方向」を使用します。

update(_ currentTime:TimeInterval)

ここでは、現在の角度値と増加する角度を0.001通過させるすべてのフレームをdrawRayCastingと呼んでいます。

var angle: CGFloat = 0
override func update(_ currentTime: TimeInterval) {
    removeAllChildren()
    drawRayCasting(angle: angle)
    angle += 0.001
}

6.6。 didMove(to view: SKView)

最後に、ここで、光線が境界を越えて跳ね返るようにするために、シーンの周りに物理ボディを作成します。

override func didMove(to view: SKView) {
    self.physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
}

7.まとめ

説明が明確であることを願っています。疑問がある場合はお知らせください。

更新

バウンスされた関数にバグがありました。反射光線の適切な計算を妨げていました。修正されました。

ここに画像の説明を入力してください

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

CSSまたはキャンバスで曲線を描き、それに沿って円を移動します

分類Dev

WPFのx軸に沿ってキャンバスを回転します

分類Dev

キャンバスの高さ全体に沿って垂直線を引きますか?

分類Dev

曲線のUIBezierPathに沿ってグラデーションを描画します

分類Dev

行列の対角線に沿って値を設定します

分類Dev

キャンバスの曲線パスに沿って図形を描く

分類Dev

プログレスバーを円に沿って均等に配置します

分類Dev

pytorchの軸に沿ってゼロ以外の要素をカウントします

分類Dev

長方形の外側の端に沿ってキャンバステキストを描画します

分類Dev

Seaborn / Matplotlibは、ラインに沿ってカスタムエラーバンドを作成します

分類Dev

Unity GameObjectを更新してスプライン曲線に沿って移動するにはどうすればよいですか?

分類Dev

tensorflow:2次元に沿ってテンソルをスライスします

分類Dev

ポリゴンと最初に交差するまでの線に沿ったパスを見つける方法は?

分類Dev

konvaの線またはパスに沿ってシェイプをアニメーション化する

分類Dev

Rの(ポリ)線に沿って等距離の点を作成します

分類Dev

オフの対角線に沿って文字列を連結します

分類Dev

transform.position = hit.pointを設定すると、キャストしている光線に沿ってGameObjectが移動するのはなぜですか?Unity 3D

分類Dev

プロトタイプを使用してウィンドウをy軸に沿って中央にスクロールしますか?

分類Dev

Rでミリ秒に沿って一連のタイムスタンプを作成します

分類Dev

Numpy:異なるインデックスを持つ入力配列で軸に沿って適用します

分類Dev

PostGISでポリラインに沿ってラスターピクセル値を抽出します

分類Dev

HTMLキャンバス上でマウスを使用して線を描画すると、正しい位置に線が描画されません

分類Dev

行と列に沿って接続されている非ゼロの数をカウントしますが、シェルスクリプトのマトリックスでは斜めにはカウントしません

分類Dev

RaphaelSVGはオブジェクトを直線の水平線に沿って移動します

分類Dev

np.polyfitを使用して多項式回帰によって生成された曲線に沿って接線を計算してプロットします

分類Dev

CSS Flexbox:交差軸に沿って縮小し、スクロールバーを表示します

分類Dev

Googleスプレッドシートのトップバーに沿ってスクリプトをトリガーするボタンを追加します

分類Dev

ナビゲーションバーがスクロールに沿って移動します

分類Dev

レンダリングせずにSVGパスに沿ってポイントを取得できますか?

Related 関連記事

  1. 1

    CSSまたはキャンバスで曲線を描き、それに沿って円を移動します

  2. 2

    WPFのx軸に沿ってキャンバスを回転します

  3. 3

    キャンバスの高さ全体に沿って垂直線を引きますか?

  4. 4

    曲線のUIBezierPathに沿ってグラデーションを描画します

  5. 5

    行列の対角線に沿って値を設定します

  6. 6

    キャンバスの曲線パスに沿って図形を描く

  7. 7

    プログレスバーを円に沿って均等に配置します

  8. 8

    pytorchの軸に沿ってゼロ以外の要素をカウントします

  9. 9

    長方形の外側の端に沿ってキャンバステキストを描画します

  10. 10

    Seaborn / Matplotlibは、ラインに沿ってカスタムエラーバンドを作成します

  11. 11

    Unity GameObjectを更新してスプライン曲線に沿って移動するにはどうすればよいですか?

  12. 12

    tensorflow:2次元に沿ってテンソルをスライスします

  13. 13

    ポリゴンと最初に交差するまでの線に沿ったパスを見つける方法は?

  14. 14

    konvaの線またはパスに沿ってシェイプをアニメーション化する

  15. 15

    Rの(ポリ)線に沿って等距離の点を作成します

  16. 16

    オフの対角線に沿って文字列を連結します

  17. 17

    transform.position = hit.pointを設定すると、キャストしている光線に沿ってGameObjectが移動するのはなぜですか?Unity 3D

  18. 18

    プロトタイプを使用してウィンドウをy軸に沿って中央にスクロールしますか?

  19. 19

    Rでミリ秒に沿って一連のタイムスタンプを作成します

  20. 20

    Numpy:異なるインデックスを持つ入力配列で軸に沿って適用します

  21. 21

    PostGISでポリラインに沿ってラスターピクセル値を抽出します

  22. 22

    HTMLキャンバス上でマウスを使用して線を描画すると、正しい位置に線が描画されません

  23. 23

    行と列に沿って接続されている非ゼロの数をカウントしますが、シェルスクリプトのマトリックスでは斜めにはカウントしません

  24. 24

    RaphaelSVGはオブジェクトを直線の水平線に沿って移動します

  25. 25

    np.polyfitを使用して多項式回帰によって生成された曲線に沿って接線を計算してプロットします

  26. 26

    CSS Flexbox:交差軸に沿って縮小し、スクロールバーを表示します

  27. 27

    Googleスプレッドシートのトップバーに沿ってスクリプトをトリガーするボタンを追加します

  28. 28

    ナビゲーションバーがスクロールに沿って移動します

  29. 29

    レンダリングせずにSVGパスに沿ってポイントを取得できますか?

ホットタグ

アーカイブ