如何用一根手指“触摸并拖动”来旋转 Sprite 节点
How do I rotate a SpriteNode with a one finger “touch and drag”
如何用一根手指“触摸并拖动”旋转 SpriteNode,以便:
- 它不会乱动
- 它在一个圆圈中移动(我已经多次成功完成这部分 - 都使用纯代码解决方案和 SKS 文件)
- 它产生一个有意义的值(就像一个物理控制旋钮)
- 当我的手指放在上面时它会移动,但当我的手指离开时它不会移动
我尝试过的事情:
使用 CGAffineT运行sform 的 CGAffineT运行sformMakeRotation 来实现 SpriteKit 中旋钮的旋转。但是我不知道如何在 SpriteNode 上使用 CGAffineT运行sformMakeRotation。我可以将不同类型的对象放入我的场景中或放在它上面,但那是不对的。
例如,Matthijs Hollemans 的 MHRotaryKnob https://github.com/hollance/MHRotaryKnob
.我 t运行 将 Hollemans 旋钮从 Objective C 调整为 Swift 4,但 运行 尝试在 SpriteKit 中使用它来旋转精灵时遇到了麻烦。我没有明白,因为我不知道如何在 SpriteKit 中使用 knobImageView.transform = CGAffineTransformMakeRotation (newAngle * M_PI/180.0);
in Swift。我知道我可以使用 Hollemans Objective C class 并将 UIImage 推到我的场景顶部,但这似乎不是最好也不是最优雅的解决方案。
我也 t运行 将 Wex 的解决方案从 Objective C 改为 Swift
Rotate image on center using one finger touch
使用 Allan Weir 关于处理 CGAffineT运行sform 部分代码的建议 但这不起作用。
我试过直接在我的精灵上设置 zRotation 而没有使用 .physicalBody 无济于事。它具有相同的颠簸运动,并且不会在您希望它停止的地方停止。并沿手指拖动的相反方向移动-即使您将“-”放在弧度角的前面。
我也试过 0x141E 在 Stack Overflow 上的解决方案:
这是下面使用 .sks 文件发布的解决方案(有些修改 - 我试过 un 修改版本,但也好不到哪儿去)。这个解决方案会晃动,不能顺畅地跟随我的手指,不能始终如一地将旋钮移动到特定点。如果我设置 physicsBody 属性来创建摩擦力、质量或 angularDamping 和 linearDamping 或降低 SKSpriteNode 的速度,都没有关系。
我也在网上搜索过 Swift 4 中使用 SpriteKit 的好的解决方案,但到目前为止无济于事。
import Foundation
import SpriteKit
class Knob: SKSpriteNode
{
var startingAngle: CGFloat?
var currentAngle: CGFloat?
var startingTime: TimeInterval?
var startingTouchPoint: CGPoint?
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
self.setupKnob()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupKnob()
}
func setupKnob() {
self.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.size.height))
self.physicsBody?.pinned = true
self.physicsBody?.isDynamic = true
self.physicsBody?.affectedByGravity = false
self.physicsBody?.allowsRotation = true
self.physicsBody?.mass = 30.0
//self.physicsBody?.friction = 0.8
//self.physicsBody?.angularDamping = 0.8
//self.physicsBody?.linearDamping = 0.9
//self.speed = 0.1
self.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in:self)
let node = atPoint(location)
startingTouchPoint = CGPoint(x: location.x, y: location.y)
if node.name == "knobHandle" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
startingAngle = atan2(dy, dx)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in:self)
let node = atPoint(location)
guard startingAngle != nil else {return}
if node.name == "knobHandle" {
let dx:CGFloat = location.x - node.position.x
let dy:CGFloat = location.y - node.position.y
var angle: CGFloat = atan2(dy, dx)
angle = ((angle) * (180.0 / CGFloat.pi))
let rotate = SKAction.rotate(toAngle: angle, duration: 2.0, shortestUnitArc: false)
self.run(rotate)
startingAngle = angle
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
var touch: UITouch = touches.first!
var location: CGPoint = touch.location(in: self)
self.removeAllActions()
startingAngle = nil
startingTime = nil
}
}
编辑:如果我取消转换为度数并将 SKAction 的持续时间更改为 SKAction.rotate(toAngle:duration: 1.0, shortestUnitArc:)
中的 1.0,那么它几乎可以工作:不是 因为 生涩,但仍然混蛋;杠杆不会很好地改变方向 - 这意味着有时如果你试图将它移动到与它行进的方向相反的方向,它会继续沿着锚点周围的旧方向而不是你拖动它的新方向。
编辑 2:GreatBigBore 我讨论了 SKAction 旋转和 self.zRotation- 上面的代码和下面的代码。
编辑 3:sicvayne 为 SKScene 建议了一些代码,我已经适应了 SKSpriteNode(如下)。它不会始终如一地移动或允许您停在特定位置。
import Foundation
import SpriteKit
class Knob: SKSpriteNode {
var fingerLocation = CGPoint()
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
self.setupKnob()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupKnob()
}
func setupKnob() {
self.isUserInteractionEnabled = true
}
func rotateKnob(){
let radians = atan2(fingerLocation.x - self.position.x, fingerLocation.y - self.position.y)
self.zRotation = -radians
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
fingerLocation = touch.location(in: self)
}
self.rotateKnob()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
/*override func update(_ currentTime: TimeInterval) { //this is a SKScene function
rotateKnob()
}*/
}
我经常做这样的事情,没有任何抖动或抽搐的问题。
var fingerLocation = CGPoint()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
fingerLocation = touch.location(in: self)
}
}
func rotatePlayer(){
let radians = atan2(fingerLocation.x - playerNode.position.x, fingerLocation.y - playerNode.position.y)
playerNode.zRotation = -radians//this rotates the player
}
override func update(_ currentTime: TimeInterval) {
rotatePlayer()
}
根据图像的朝向,您可能不得不调整弧度。就我而言,我的 "player" 图像朝上。希望这对您有所帮助。
数学错了。这是我了解到您需要的:
这在 swift 中看起来如何:
if point.x.sign == .minus {
angle = atan(point.y/point.x) + CGFloat.pi/2
} else {
angle = atan(point.y/point.x) + CGFloat.pi/2 + CGFloat.pi
}
另外,因为整个坐标系都随着物体旋转,所以还得获取场景中另一个物体的坐标:
let body = parent?.childNode(withName: "objectInScene")
let point = touch.location(in: body!)
如何用一根手指“触摸并拖动”旋转 SpriteNode,以便:
- 它不会乱动
- 它在一个圆圈中移动(我已经多次成功完成这部分 - 都使用纯代码解决方案和 SKS 文件)
- 它产生一个有意义的值(就像一个物理控制旋钮)
- 当我的手指放在上面时它会移动,但当我的手指离开时它不会移动
我尝试过的事情:
使用 CGAffineT运行sform 的 CGAffineT运行sformMakeRotation 来实现 SpriteKit 中旋钮的旋转。但是我不知道如何在 SpriteNode 上使用 CGAffineT运行sformMakeRotation。我可以将不同类型的对象放入我的场景中或放在它上面,但那是不对的。
例如,Matthijs Hollemans 的 MHRotaryKnob https://github.com/hollance/MHRotaryKnob
.我 t运行 将 Hollemans 旋钮从 Objective C 调整为 Swift 4,但 运行 尝试在 SpriteKit 中使用它来旋转精灵时遇到了麻烦。我没有明白,因为我不知道如何在 SpriteKit 中使用 knobImageView.transform = CGAffineTransformMakeRotation (newAngle * M_PI/180.0);
in Swift。我知道我可以使用 Hollemans Objective C class 并将 UIImage 推到我的场景顶部,但这似乎不是最好也不是最优雅的解决方案。
我也 t运行 将 Wex 的解决方案从 Objective C 改为 Swift Rotate image on center using one finger touch 使用 Allan Weir 关于处理 CGAffineT运行sform 部分代码的建议 但这不起作用。
我试过直接在我的精灵上设置 zRotation 而没有使用 .physicalBody 无济于事。它具有相同的颠簸运动,并且不会在您希望它停止的地方停止。并沿手指拖动的相反方向移动-即使您将“-”放在弧度角的前面。
我也试过 0x141E 在 Stack Overflow 上的解决方案:
我也在网上搜索过 Swift 4 中使用 SpriteKit 的好的解决方案,但到目前为止无济于事。
import Foundation
import SpriteKit
class Knob: SKSpriteNode
{
var startingAngle: CGFloat?
var currentAngle: CGFloat?
var startingTime: TimeInterval?
var startingTouchPoint: CGPoint?
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
self.setupKnob()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupKnob()
}
func setupKnob() {
self.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.size.height))
self.physicsBody?.pinned = true
self.physicsBody?.isDynamic = true
self.physicsBody?.affectedByGravity = false
self.physicsBody?.allowsRotation = true
self.physicsBody?.mass = 30.0
//self.physicsBody?.friction = 0.8
//self.physicsBody?.angularDamping = 0.8
//self.physicsBody?.linearDamping = 0.9
//self.speed = 0.1
self.isUserInteractionEnabled = true
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in:self)
let node = atPoint(location)
startingTouchPoint = CGPoint(x: location.x, y: location.y)
if node.name == "knobHandle" {
let dx = location.x - node.position.x
let dy = location.y - node.position.y
startingAngle = atan2(dy, dx)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
let location = touch.location(in:self)
let node = atPoint(location)
guard startingAngle != nil else {return}
if node.name == "knobHandle" {
let dx:CGFloat = location.x - node.position.x
let dy:CGFloat = location.y - node.position.y
var angle: CGFloat = atan2(dy, dx)
angle = ((angle) * (180.0 / CGFloat.pi))
let rotate = SKAction.rotate(toAngle: angle, duration: 2.0, shortestUnitArc: false)
self.run(rotate)
startingAngle = angle
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
var touch: UITouch = touches.first!
var location: CGPoint = touch.location(in: self)
self.removeAllActions()
startingAngle = nil
startingTime = nil
}
}
编辑:如果我取消转换为度数并将 SKAction 的持续时间更改为 SKAction.rotate(toAngle:duration: 1.0, shortestUnitArc:)
中的 1.0,那么它几乎可以工作:不是 因为 生涩,但仍然混蛋;杠杆不会很好地改变方向 - 这意味着有时如果你试图将它移动到与它行进的方向相反的方向,它会继续沿着锚点周围的旧方向而不是你拖动它的新方向。
编辑 2:GreatBigBore 我讨论了 SKAction 旋转和 self.zRotation- 上面的代码和下面的代码。
编辑 3:sicvayne 为 SKScene 建议了一些代码,我已经适应了 SKSpriteNode(如下)。它不会始终如一地移动或允许您停在特定位置。
import Foundation
import SpriteKit
class Knob: SKSpriteNode {
var fingerLocation = CGPoint()
override init(texture: SKTexture?, color: UIColor, size: CGSize) {
super.init(texture: texture, color: color, size: size)
self.setupKnob()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupKnob()
}
func setupKnob() {
self.isUserInteractionEnabled = true
}
func rotateKnob(){
let radians = atan2(fingerLocation.x - self.position.x, fingerLocation.y - self.position.y)
self.zRotation = -radians
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
fingerLocation = touch.location(in: self)
}
self.rotateKnob()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
/*override func update(_ currentTime: TimeInterval) { //this is a SKScene function
rotateKnob()
}*/
}
我经常做这样的事情,没有任何抖动或抽搐的问题。
var fingerLocation = CGPoint()
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
fingerLocation = touch.location(in: self)
}
}
func rotatePlayer(){
let radians = atan2(fingerLocation.x - playerNode.position.x, fingerLocation.y - playerNode.position.y)
playerNode.zRotation = -radians//this rotates the player
}
override func update(_ currentTime: TimeInterval) {
rotatePlayer()
}
根据图像的朝向,您可能不得不调整弧度。就我而言,我的 "player" 图像朝上。希望这对您有所帮助。
数学错了。这是我了解到您需要的:
这在 swift 中看起来如何:
if point.x.sign == .minus {
angle = atan(point.y/point.x) + CGFloat.pi/2
} else {
angle = atan(point.y/point.x) + CGFloat.pi/2 + CGFloat.pi
}
另外,因为整个坐标系都随着物体旋转,所以还得获取场景中另一个物体的坐标:
let body = parent?.childNode(withName: "objectInScene")
let point = touch.location(in: body!)