首页 > 解决方案 > 如何防止触摸位置处理的 SKShapeNode 超出圆形路径?

问题描述

我正在将 SpriteKit 用于带有 Swift 的 iOS 应用程序。

我做了一个小的 SKShapeNode(“球”)和一个大的圆形路径(“房间”),我希望 SKShapeNode 留在圆圈内。

这是我的代码:


import SpriteKit
import GameplayKit

class GameScene: SKScene {

    var isFingerOnBall = false

    var ball: SKShapeNode!
    var room: SKShapeNode!

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        let touchLocation = touch!.location(in: self)

        if let body = physicsWorld.body(at: touchLocation) {
            if body.node!.name == "ball" {
                isFingerOnBall = true
            }
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch = touches.first
        let touchLocation = touch!.location(in: self)

        if isFingerOnBall {
            ball.position = touchLocation
        }

    }

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

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {

    }

    override func didMove(to view: SKView) {

        self.physicsWorld.contactDelegate = self
        self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
        self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)

        let radius:CGFloat = 60

        // BALL
        ball = SKShapeNode(circleOfRadius: radius)
        ball.name = "ball"
        ball.fillColor = .red
        ball.strokeColor = .clear
        ball.position = CGPoint(x: 0, y: 150)
        ball.physicsBody = SKPhysicsBody(circleOfRadius: radius)
        ball.physicsBody?.isDynamic = true
        scene?.addChild(ball)

        // ROOM
        room = SKShapeNode(circleOfRadius: radius * 5)
        room.name = "room"
        room.strokeColor = .white
        room.lineWidth = 10
        room.position = CGPoint(x: 0, y: 0)
        room.physicsBody = SKPhysicsBody(edgeLoopFrom: room.path!)
        room.physicsBody?.isDynamic = false
        scene?.addChild(room)

    }

}


我预计房间的 SKPhysicsBody 会限制球超出路径, OK image

但是当我的手指将球拖出圆圈(房间)时,它也会熄灭。 没有好图

提前致谢。

标签: swiftsprite-kit

解决方案


有几种方法可以做到这一点,尽管我能想到的最简单的方法是将你的 didMove(to view: SKView) 方法替换为:

    override func didMove(to view: SKView) {

    self.physicsWorld.contactDelegate = self
    self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)

    // BALL
    let ball = SKShapeNode(circleOfRadius: 10)
    ball.fillColor = .yellow
    ball.strokeColor = .yellow
    ball.lineWidth = 5
    ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
    ball.physicsBody?.isDynamic = true
    ball.physicsBody?.affectedByGravity = true
    ball.physicsBody?.categoryBitMask = 1
    ball.physicsBody?.collisionBitMask = 2
    ball.physicsBody?.contactTestBitMask = 0
    addChild(ball)


    // ROOM
    let room = SKShapeNode(circleOfRadius: 200)
    room.fillColor = .clear
    room.strokeColor = .red
    room.lineWidth = 5
    room.physicsBody = SKPhysicsBody(edgeLoopFrom: room.path!)
    room.physicsBody?.isDynamic = false
    room.physicsBody?.affectedByGravity = false
    room.physicsBody?.categoryBitMask = 2
    room.physicsBody?.collisionBitMask = 0
    room.physicsBody?.contactTestBitMask = 0
    addChild(room)

}

基本上你只需要设置 PhysicsBody CategoryBitMask 和 Collision Bit 掩码。也不要指定实体物理体,注意使用“edgeFromLoop”来使房间成为物理体。

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first
    let touchLocation = touch!.location(in: self)

    if isFingerOnBall {
        let roomRadius: CGFloat = 200
        let hyp = sqrt(touchLocation.x * touchLocation.x + touchLocation.y * touchLocation.y)
        if abs(hyp) > roomRadius {
            ball.position = ball.position
        } else {
            ball.position = touchLocation
        }
    }

}

在这里,您将获得新接触点的斜边并检查它是否大于房间的半径。如果是,则球的位置保持不变,如果不是,则照常更新球的位置。

希望这会有所帮助并享受


推荐阅读