Faire tourner l'image-object au toucher avec une vitesse de rotation limitée

J'essaye de faire tourner mon spriteNode sur le doigt.

Jusqu'à présent, je peux le faire, mais ce que je veux, c'est que mon noeud ait une "vitesse de rotation". Donc, je calcule la longueur de l'angle puis définissez un timing différent pour tourner avec lui (si l'arc est long, cela prendra du time …).

Voici mon code:

override func touchesMoved(touches: NSSet, withEvent event: UIEvent) { _isTouched = true for touch in touches { let location:CGVector = touch.locationInNode(self) - miner.position miner.weaponRotation = location.angle() - CGFloat(M_PI_2) } } var wantedRotation: CGFloat { get { return _wantedRotation } set(rotation) { if rotation > CGFloat(M_PI) || rotation < -CGFloat(M_PI) { _wantedRotation = CGFloat(M_PI) + rotation % CGFloat(M_PI) } else { _wantedRotation = rotation } removeActionForKey("rotation") let arc: CGFloat = CGFloat(abs(_wantedRotation - zRotation)) let shortestArc: CGFloat = min(arc, CGFloat(M_PI * 2.0) - arc) runAction(SKAction.rotateToAngle(_wantedRotation, duration: NSTimeInterval(shortestArc / CGFloat(M_PI) * rotationSpeed), shortestUnitArc: true), withKey: "rotation") } } 

Le principal problème est que l'ajout de plusieurs SKAction au nœud bloque le mouvement.

Je voudrais savoir quelle pourrait être la solution? Si possible en utilisant SKAction, puisque je voudrais éviter de faire une mise à jour sur chaque image pour calculer le mouvement (mais si c'est la seule solution …)

NOTE APRÈS RÉPONSE

Comme j'ai reçu des réponses, j'ai relu la documentation de SpriteKit et j'ai trouvé cette note claire :

Quand vous ne devriez pas utiliser d'actions

Bien que les actions soient efficaces, leur création et leur exécution ont un coût. Si vous modifiez les propriétés d'un noeud dans chaque image d'animation et que ces modifications doivent être recalculées dans chaque image, il est préférable de modifier directement le noeud et de ne pas utiliser d'actions pour le faire. Pour plus d'informations sur l'endroit où vous pourriez faire cela dans votre jeu, voir Advanced Scene Processing.

J'ai une tourelle qui … devrait cibler la position des doigts, mais pas immédiatement, en prenant le time de tourner.

Vous ne pourrez pas vous en sortir avec SKActions pour quelque chose comme ça. Vous pouvez essayer, mais ce sera vraiment désordonné et inefficace. Vous avez besoin d' un contrôle de mouvement en time réel pour quelque chose comme ça parce que la vitesse angular de votre tourelle doit changer constamment en fonction de la position tactile.

Je vous ai donc écrit un exemple rapide montrant comment calculer la vitesse angular. Le projet gère également tous les cas spéciaux, comme empêcher l'angle de sauter par-dessus votre rotation cible.

 import SpriteKit class GameScene: SKScene { let turret = SKSpriteNode(imageNamed: "Spaceship") let rotationSpeed: CGFloat = CGFloat(M_PI) //Speed turret rotates. let rotationOffset: CGFloat = -CGFloat(M_PI/2.0) //Controls which side of the sprite faces the touch point. I offset the angle by -90 degrees so that the top of my image faces the touch point. private var touchPosition: CGFloat = 0 private var targetZRotation: CGFloat = 0 override func didMoveToView(view: SKView) { turret.physicsBody = SKPhysicsBody(rectangleOfSize: turret.size) turret.physicsBody!.affectedByGravity = false turret.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0) self.addChild(turret) } override func touchesMoved(touches: NSSet, withEvent event: UIEvent) { calculateAngleToTouch(touches.anyObject() as UITouch) } override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { calculateAngleToTouch(touches.anyObject() as UITouch) } func calculateAngleToTouch(touch: UITouch) { let position = touch.locationInNode(self) let angle = atan2(position.y-turret.position.y, position.x-turret.position.x) targetZRotation = angle + rotationOffset } override func update(currentTime: NSTimeInterval) { var angularDisplacement = targetZRotation - turret.zRotation if angularDisplacement > CGFloat(M_PI) { angularDisplacement = (angularDisplacement - CGFloat(M_PI)*2) } else if angularDisplacement < -CGFloat(M_PI) { angularDisplacement = (angularDisplacement + CGFloat(M_PI)*2) } if abs(angularDisplacement) > rotationSpeed*(1.0/60.0) { let angularVelocity = angularDisplacement < 0 ? -rotationSpeed : rotationSpeed turret.physicsBody!.angularVelocity = angularVelocity } else { turret.physicsBody!.angularVelocity = 0 turret.zPosition = targetZRotation } } } 

entrez la description de l'image ici

Vous n'avez pas besoin de faire cela: removeActionForKey ("rotation") – c'est la raison pour laquelle vous avez un blocage de mouvement. Jetez un oeil à ces methods

  • speedBy: durée:
  • speedTo: durée:

et propriété d'animation de la documentation:

speed – Le facteur de vitesse ajuste la vitesse d'exécution de l'animation d'une action. Par exemple, un facteur de vitesse de 2,0 signifie que l'animation est deux fois plus rapide.

C'est difficile à dire dans votre cas, mais la solution consisterait à créer des actions séquentielles , de sorte qu'elles fonctionneront une par une. J'espère que cela t'aides

Je vais essayer. Mais je ne peux pas le tester maintenant. ceci est un code (pseudo) Objective-C.

 - (void)rotateSprite:(SKSpriteNode *)sprite toAngle:(float)angle inDuration:(NSTimeInterval)duration { SKAction *rotation = [SKAction rotateToAngle:angle duration:duration]; [sprite runAction:rotation completion:^{ [sprite removeAllActions]; }]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { . . . // your sprite [node removeAllActions]; float angle = [self calcAngle]; // your angle NSTimeInterval duration = [self calcDuration]; // your duration [self rotateSprite:(SKSpriteNode *)node toAngle:angle inDuration:duration]; } 

J'ai créé du code pour transformer le sprite node0 vers le sharepoint contact à une vitesse constante quel que soit l'angle. Vous avez indiqué dans votre question que vous préférez utiliser SKAction au lieu d'avoir du code dans votre méthode de mise à jour.

Voici l'exemple de code de projet pour le file GameScene.m:

 #import "GameScene.h" @implementation GameScene { SKAction *objectAnimation; SKSpriteNode *node0; } -(void)didMoveToView:(SKView *)view { self.backgroundColor = [SKColor blackColor]; node0 = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(100, 10)]; node0.position = CGPointMake(300, 300); node0.zRotation = 0.0; [self addChild:node0]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) { CGPoint touchLocation = [touch locationInNode:self]; // calculate angle between object and touch float deltaY = touchLocation.y - node0.position.y; float deltaX = touchLocation.x - node0.position.x; float moveBydegrees = atan2(deltaY, deltaX) * 180 / 3.14159265359; // convert degrees to radians float moveByRadians = moveBydegrees * M_PI / 180; float myFloat0 = 0; if(moveByRadians < 0) { myFloat0 = fabs(moveByRadians); } else { myFloat0 = moveByRadians; } NSLog(@"myFloat0 = %f",myFloat0); float myFloat1 = 0; if(node0.zRotation < 0) { myFloat1 = fabs(node0.zRotation); } else { myFloat1 = node0.zRotation; } NSLog(@"myFloat1 = %f",myFloat1); float durationTime = fabs(myFloat0 - myFloat1); NSLog(@"durationTime = %f",durationTime); objectAnimation = [SKAction rotateToAngle:moveByRadians duration:durationTime shortestUnitArc:YES]; [self startObjectAnimation]; } } -(void)startObjectAnimation { [node0 removeActionForKey:@"animation"]; if (![node0 actionForKey:@"animation"]) { if(objectAnimation != nil) { [node0 runAction:objectAnimation withKey:@"animation"]; NSLog(@"runnign animation"); } else { NSLog(@"enemy node animation is nil"); } } } 

Personnellement, je pense qu'il serait préférable d'utiliser la méthode de mise à jour pour accomplir cette tâche. Cela vous donnerait un meilleur contrôle dans le mouvement pas à pas de la zRotation de l'object plutôt que d'utiliser un SKAction.