3

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)
    }
}}
Ali Beadle
  • 4,486
  • 3
  • 30
  • 55
omerc
  • 179
  • 1
  • 11

2 Answers2

4

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

Ron Myschuk
  • 6,011
  • 2
  • 20
  • 32
2

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.

Community
  • 1
  • 1
Ali Beadle
  • 4,486
  • 3
  • 30
  • 55
  • can you see my new edit, i used skphysicsbody but i got a new problem... @Ali Beadle – omerc Dec 17 '17 at 19:19
  • @omerc a Don't use angular velocity and angular impulse at the same time - an impulse to give it a quick nudge, or a velocity will make it spin constantly. Also, to keep SO answers useful to others it is best to ask new questions separately, not edit old ones. – Ali Beadle Dec 17 '17 at 19:53
  • I just delete the angular velocity but it didn't change anything. can you explain this? @Ali Beadle – omerc Dec 17 '17 at 19:59
  • Probably you need to adjust the impulse values, mass and friction - it may be at the moment, with zero friction, default mass and a big nudge of 180 impulse that you are just hitting it too hard. – Ali Beadle Dec 17 '17 at 20:03
  • how can I change the impulse value? when I set it to 10 it didn't change anything. @Ali Beadle – omerc Dec 17 '17 at 20:07
  • Keep playing with the values - not just the impulse but the other properties of the physics body. How fast it spins will depend on its mass and angularDamping as well. You may have to go well below 1 on some values to achieve what you need. – Ali Beadle Dec 17 '17 at 20:10
  • I changed the angular damping to 1 and its working! thanks! dow you know how can I set the node will rotate in 45 degrees each second? @Ali Beadle – omerc Dec 17 '17 at 20:34
  • You want it to slow down realistically *and* go at a constant speed? Look, read the documentation that I linked, experiment with the various parameters, learn about SKPhysics and see what works for you. If you get stuck ask a new question. – Ali Beadle Dec 18 '17 at 07:37