swift - 如何防止触摸位置处理的 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
但是当我的手指将球拖出圆圈(房间)时,它也会熄灭。 没有好图
提前致谢。
解决方案
有几种方法可以做到这一点,尽管我能想到的最简单的方法是将你的 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
}
}
}
在这里,您将获得新接触点的斜边并检查它是否大于房间的半径。如果是,则球的位置保持不变,如果不是,则照常更新球的位置。
希望这会有所帮助并享受
推荐阅读
- javascript - 如何创建一个带有 4 个按钮的页面来播放 gdrive 中的音频?
- swiftui - SwiftUI TabView Page 如何处理大数据
- graphql - 如何为其他字段组织 GraphQL 解析器
- liferay - Liferay - 使用外部 REST 服务填充文本字段的形式
- ios - UIScrollView 是否会自动调整 contentOffset 以适应安全区域?
- javascript - Vue.js - 访问应用程序实例的辅助函数
- matlab - 使用 Matlab 填充文本字段并单击网页上的按钮
- node.js - 使用 chokidar 限制异步文件观看
- c# - ASP.NET MVC 中每个请求的会话和令牌
- reactjs - HTML Canvas 不能与 React 一起使用,代码没有给出任何错误