1
votes

I would like to apply a rotation to multiple nodes (selected nodes in my game) using the UIRotationGesture, based on the center point of all those nodes. I can already rotate a single node simply changing it's zRotation.

The problem with multiple nodes is that it changes position and zRotation based on a center node, and I cannot seem to understand how to manage that.

I would like something like this: enter image description here enter image description here

What I have to rotate a single node is this: During the rotation gesture

theRotation = CGFloat(sender.rotation) + self.offset
theRotation = theRotation * -1
node.rotation = theRotation

After the rotation gesture

self.offset = theRotation * -1

Would you have an idea on how set the correct position and angle for my nodes during the rotation?


What I tried:

  1. I tried to add a node in the center (where the white dot is in my pictures, which represents the center) and change the parent of my nodes to be this one, then apply the zRotation on this node, and then replace the right parents. This did not work as I cannot seem to change a parent (my nodes disappear), this is another one of my Stack Questions.
  2. I tried to change the anchor point of my nodes to fit the center point and than rotate them using theRotation. It did not work as I cannot seem to set the anchor point at the center position (that I have). I tried changing the coordinates system of the center's position to fit the node's one, but this is still not working. node.convertPoint(center, fromNode: Self) gives me coordinated like -58;-74 when it's about -1;-.5 (or something like that). I do not understand this.

So now I am thinking to calculate the position and rotation myself, as those did not work, but I would need an idea on how to calculate those as I am not very good with trigonometry/linear algebra, sadly enough.

Thank you for you help!


How I calculate my center:

var maxX = nodesSelected[0].position.x
var minX = nodesSelected[0].position.x
var maxY = nodesSelected[0].position.y
var minY = nodesSelected[0].position.y

for node in nodesSelected{
    if node.position.x > maxX{
        maxX = node.position.x
    }
    if node.position.x < minX{
        minX = node.position.x
    }
    if node.position.y > maxY{
        maxY = node.position.y
    }
    if node.position.y > maxY{
        minY = node.position.y
    }
}
return CGPoint(x: (maxX-minX)/2+minX, y: (maxY-minY)+minY/2)

How I calculate the radius of the rotation (distance between a node and the center):

extension CGPoint {
    func distance(point: CGPoint) -> CGFloat {
        return abs(CGFloat(hypotf(Float(point.x - x), Float(point.y - y))))
    }

How I get my rotation:

sender.rotation
1

1 Answers

1
votes

Given a rotationAngle, you can calculate the new position of each node with the code below, you need to know a bit of trigonometry to understand the code.

Here I have an array of SKShapeNode that I called dots (It would be the equivalent of your green nodes in the image). And the centralDot would be your central SKSpriteNode.

    for dot in dots {
        let dx = dot.position.x - centralDot!.position.x // Get distance X from center
        let dy = dot.position.y - centralDot!.position.y // Get distance Y from center
        
        let current_angle = atan(dy / dx) // Current angle is the arctan of dy / dx
        
        let next_angle = current_angle - rotationAngle // Sum how much you want to rotate in radians
        
        // the new x is: center + radius*cos(x)
        // the new y is: center + radius*sin(y)
        // if dx < 0 you need to get the oposite value of the position
        let new_x = dx >= 0 ? centralDot!.position.x + rotationRadius * cos(next_angle) : centralDot!.position.x - rotationRadius * cos(next_angle)
        let new_y = dx >= 0 ? centralDot!.position.y + rotationRadius * sin(next_angle) : centralDot!.position.y - rotationRadius * sin(next_angle)

        let new_point = CGPoint(x: new_x, y: new_y)
        
        let action = SKAction.moveTo(new_point, duration: 0.2)
        
        dot.runAction(action)
    }

Hope this helps

Update:

The first code didn't helped, so I tried another one. This one worked better on my tests.

for i in 0..<dots.count {
    let dot = dots[i]

    let angle = rotationAngle + CGFloat(M_PI_2 * Double(i))
    
    let new_x = rotationRadius * cos(angle) + centralDot!.position.x
    let new_y = rotationRadius * sin(angle) + centralDot!.position.y
    
    let new_point = CGPoint(x: new_x, y: new_y)
    
    let action = SKAction.moveTo(new_point, duration: 1/60)
    
    dot.runAction(action)
}
  • rotationRadius is a constant, the distance you want between the center and the green node.