3
votes

I am trying to create a spinning fortune wheel action via SKAction. I have a SKNode which used as wheel, this SKNode is a circle that divided to four quarters (each quarter in different color). also I set an SKAction (which is repeating for 10 counts) that spin the SKNode around fixed point (the node's center). The problem is that the action is running well but it stops suddenly and not slowing down - like a real wheel. I don't really have an idea how to set this animation, I mean to slow the spinning down before the action is stop. Here is my code so far:

class GameScene: SKScene {

let colors = [SKColor.yellow, SKColor.red, SKColor.blue, SKColor.purple]


override func didMove(to view: SKView) {
    createWheel()

    let sq = CGRect(x: size.width/2, y: size.height/2, width: 300, height: 300)
    let sqx = SKShapeNode(rect: sq)
    sqx.lineWidth = 2
    sqx.fillColor = .clear
    sqx.setScale(1.0)
    addChild(sqx)
    }

func createWheel() {
    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: 0))
    path.addLine(to: CGPoint(x: 0, y: -200))
    path.addArc(withCenter: CGPoint.zero,radius: 200,startAngle: CGFloat(0.0), endAngle: CGFloat(3.0 * Double.pi / 2),clockwise: false)
    path.addLine(to: CGPoint(x: 200, y: 0))

    let obstacle = obstacleByDuplicatingPath(path, clockwise: true)
    obstacle.position = CGPoint(x: size.width/2, y: size.height/2)
    addChild(obstacle)

    let rotateAction = SKAction.rotate(byAngle: CGFloat((3.0 * CGFloat(Double.pi / 2)) - 90), duration: 0.5)
    //obstacle.run(SKAction.repeatForever(rotateAction))
    obstacle.run(SKAction.repeat(rotateAction, count: 10))


}

func obstacleByDuplicatingPath(_ path: UIBezierPath, clockwise: Bool) -> SKNode {
    let container = SKNode()

    var rotationFactor = CGFloat(Double.pi / 2)
    if !clockwise {
        rotationFactor *= -1
    }

    for i in 0...3 {
        let section = SKShapeNode(path: path.cgPath)
        section.fillColor = colors[i]
        section.strokeColor = colors[i]
        section.zRotation = rotationFactor * CGFloat(i);

        let origin = CGPoint(x: 0.0, y: 0.0)
        switch i {
        case 0:
            section.position = CGPoint(x: (origin.x + 10), y: (origin.y - 10))
        case 1:
            section.position = CGPoint(x: (origin.x + 10), y: (origin.y + 10))
        case 2:
            section.position = CGPoint(x: (origin.x - 10), y: (origin.y + 10))
        case 3:
            section.position = CGPoint(x: (origin.x - 10), y: (origin.y - 10))
        default:
            print("bolbol")

        }

        container.addChild(section)
    }
    return container
}

}

edit: I was thinking about it and I tried to do it via SKAction, I set another action but this time I set their duration to a long one. first it run a action of duration 0.5, then of 2 and at end of 4. I looks pretty good but still not smooth as I want it to be. here is my code:

        let rotateAction = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 0.5)
    let rotateAction2 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 2)
    let rotateAction3 = SKAction.rotate(byAngle: CGFloat(2.0 * CGFloat(M_PI)), duration: 4)
    let wait = SKAction.wait(forDuration: 5)
    let g1 = SKAction.repeat(rotateAction, count: 10)
    let group = SKAction.group([wait, g1, rotateAction2, rotateAction3])

what do you think? there is any way to do it better??

edit 2: Continued to @Ali Beadle answer, I tried to do it via physics body, the problem now is the when I drag finger on the screen the SKShapeNode (shape) in continue to rotate and never stops. can you detect what is wrong?

class GameScene: SKScene {
var start: CGPoint?
var end:CGPoint?
var startTime: TimeInterval?
let shape = SKShapeNode.init(rectOf: CGSize(width: 150, height: 150))

override func didMove(to view: SKView) {

    self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
    let sceneBody = SKPhysicsBody.init(edgeLoopFrom: self.frame)
    sceneBody.friction = 0
    self.physicsBody = sceneBody


    shape.fillColor = SKColor.red
    shape.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
    shape.physicsBody = SKPhysicsBody.init(rectangleOf: CGSize(width: 50, height: 50))
    shape.physicsBody?.affectedByGravity = false
    shape.physicsBody?.isDynamic = true
    addChild(shape)
}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {return}
    self.start = touch.location(in: self)
    self.startTime = touch.timestamp
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    guard let touch = touches.first else {return}
    self.end = touch.location(in: self)
    var dx = ((self.end?.x)! - (self.start?.x)!)
    var dy = ((self.end?.y)! - (self.start?.y)!)

    let magnitude:CGFloat = sqrt(dx*dx+dy*dy)
    if(magnitude >= 25){
        let dt:CGFloat = CGFloat(touch.timestamp - self.startTime!)
        if dt > 0.1 {
            let speed = magnitude / dt
            dx = dx / magnitude
            dy = dy / magnitude
            print("dx: \(dx), dy: \(dy), speed: \(speed) ")

        }
    }
    let touchPosition = touch.location(in: self)
    if touchPosition.x < (self.frame.width / 2) {
        self.shape.physicsBody?.angularVelocity = 10
        self.shape.physicsBody?.applyAngularImpulse(-180)
    } else {
        self.shape.physicsBody?.angularVelocity = 10
        self.shape.physicsBody?.applyAngularImpulse(180)
    }
}}
2

2 Answers

3
votes

I have created an open source prize spinning wheel in Spritekit that uses physics for realistic movement and flapper control. It also allows the user to drag the wheel to spin or generates a random spin by pushing the center of the wheel.

enter image description here

https://github.com/hsilived/SpinWheel

2
votes

You can add realistic movement like this by using the built-in Physics simulation of SpriteKit. This will allow you to give your wheel a mass and friction and then use forces to rotate it. It will then slow down realistically.

In outline see Simulating Physics in the Apple Documentation:

To use physics in your game, you need to:

  • Attach physics bodies to nodes in the node tree and configure their physical properties. See SKPhysicsBody.
  • Define global characteristics of the scene’s physics simulation, such as gravity. See SKPhysicsWorld.
  • Where necessary to support your gameplay, set the velocity of physics bodies in the scene or apply forces or impulses to them. ...

The most appropriate method for your wheel is probably to make the wheel pinned to the scene and then rotate it with applyAngularImpulse.