问题
我在一个GameScene中有一组SKSpriteNodes
,它们以有序的方式放置,即它们的位置恒定地间隔开。(0,0),(0,150),(0,300)。我希望能够旋转所有的船只的质心的位置,并有他们的航向指向的方向,他们正在旋转。请看下面的图表来理解我的意思:
What I've Tried
我尝试将所有的精灵节点放在一个容器SKNode
中,并旋转SKNode
。这将导致子节点根据需要旋转,因为它们都相对于父节点定位。然而,我想避免这样做,因为我想控制每个单独的精灵节点的zRotation
和position
,而不是“作弊”,让一个容器节点为我做这件事。
为了解决这个问题,我尝试将标准的2D旋转矩阵应用于SKSpriteNode
s的位置。下面的代码不是一个精确的副本,但它大致描述了我尝试做的事情:
@objc func sliderValueDidChange(_ sender: UISlider) {
// just assume I have access to the GameScene from the GameViewController
guard let scene = view.scene as? GameScene else { return }
let nodes = scene.nodes
// prevSliderValue is the value of the slider from the previous call to
// this function. the slider has a range of values from 0 to 359
let angleChange = (prevSliderValue - CGFloat(sender.value)) / 180 * .pi
// calculating the centroid
var cenX = CGFloat.zero
var cenY = CGFloat.zero
// nodes is a list of [SKSpriteNodes]
for node in nodes {
cenX += node.position.x
cenY += node.position.y
}
cenX = cenX / CGFloat(nodes.count)
cenY = cenY / CGFloat(nodes.count)
// applying the rotation matrix to each node's position
for node in nodes {
// calculating the new position
let newX = node.position.x * cos(angleChange) - node.position.y * sin(angleChange)
let newY = node.position.x * sin(angleChange) + node.position.y * cos(angleChange)
node.position = CGPoint(x: newX, y: newY)
// calculating the new angle to point at
// let a = -atan2(node.position.y - cenY, node.position.x - cenX) + .pi/2
// node.zRotation = a
/*
atan2 gives the angle relative to the X axis, but SKSpriteNodes
zRotation are relative to the Y axis, hence I'm adding pi/2
furthermore, I want a positive angle of rotation to be clockwise,
so I'm negating the return value of the atan2 function
*/
}
}
此代码的结果到目前为止尚未成功。节点大致能够沿着相对于质心的弧顺时针旋转约180 °。但是对于任何超过180º的值,节点基本上停止旋转/移动,只是在现场痉挛。尝试反转旋转(即从180º回到0º)确实会导致节点逆时针旋转,但它们不会到达原始位置(即它们的结束位置可以偏离完全垂直的几度)。此外,如果我上下拖动滑块几次,不管出于什么原因,节点最终会越来越近,直到它们会聚在质心上。
我计算最终位置的方法有问题吗?我意识到我对newX
和newY
的计算不是相对于质心,而是相对于原点,这让我的大脑更加混乱。我已经被困在这几天,我已经可以感觉到自己开始燃烧。任何帮助或指针将不胜感激。
编辑
我能够让它在不使用容器节点的情况下工作。代码如下:
@objc func sliderValueDidChange(_ sender: UISlider) {
guard let scene = view.scene as? GameScene else { return }
let nodes = scene.nodes
let angleChange = (prevSliderVal - CGFloat(sender.value)) / 180 * .pi
var cenX = CGFloat.zero
var cenY = CGFloat.zero
for node in nodes {
cenX += node.position.x
cenY += node.position.y
}
cenX = cenX / CGFloat(nodes.count)
cenY = cenY / CGFloat(nodes.count)
for node in nodes {
let translatedPos = CGPoint(x: node.position.x - cenX,
y: node.position.y - cenY)
let newX = translatedPos.x * cos(angleChange) - translatedPos.y * sin(angleChange)
let newY = translatedPos.x * sin(angleChange) + translatedPos.y * cos(angleChange)
node.position = CGPoint(x: newX, y: newY)
let a = CGFloat(sender.value) / 180 * .pi
node.zRotation = -a
}
prevSliderVal = CGFloat(sender.value)
}
看来我把问题想得太复杂了。为了围绕原点以外的点旋转精灵,我只需要平移节点的位置,使它们相对于质心,然后应用旋转矩阵。
1条答案
按热度按时间lstz6jyr1#
您需要
SKSpriteNode
的origin
属性。原点定义旋转、平移和缩放的中心。例如,如果你需要在左上角旋转你的精灵,你可以这样做:
如果你想围绕一个点旋转一组精灵,你可以把它们作为其他空精灵的查尔兹,然后旋转父精灵:
更高级的技术(如一组精灵的群集行为)可以通过使用
GameplayKit
API来实现。阅读GKAgent
和GKBehavior
,以及如何使用它来控制精灵组。