我试图让我的spriteNode在手指触摸时旋转。
到目前为止,我可以做到,但我想要的是我的节点具有"旋转速度"。 所以我计算角度的长度然后设置一个不同的时间与它一起旋转(如果弧很长,则需要时间......)。
这是我的代码:
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")
}
}
主要问题是向节点添加多个SKAction
会阻止移动。
我想知道可能是什么解决方案?如果可能的话,使用SKAction,因为我想避免对每个帧进行更新以计算移动(但如果它是唯一的解决方案......)
回答后的说明
当我收到答案时,我再次阅读了SpriteKit文档并找到了this clear note:
何时不应使用操作
尽管操作有效,但创建和执行操作会有成本。如果要在每个动画帧中更改节点的属性,并且需要在每个帧中重新计算这些更改,则最好直接对节点进行更改,而不是使用操作来执行此操作。有关游戏中可能执行此操作的详细信息,请参阅高级场景处理。
答案 0 :(得分:7)
我有一个炮塔......应该瞄准手指的位置,但不是 马上,花时间转。
你无法通过SKActions逃脱这样的事情。你可以尝试,但它会非常混乱和低效。你需要实时运动控制,因为你的炮塔的角速度需要根据触摸位置不断变化。
所以我写了一个快速示例项目,展示了如何计算角速度。该项目还处理所有特殊情况,例如防止角度跳过目标旋转。
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.zRotation = targetZRotation
}
}
}
答案 1 :(得分:1)
你不需要这样做:removeActionForKey(&#34; rotation&#34;) - 这就是你有动作阻塞的原因。请看一下这些方法
来自文档的动画属性:
速度 - 速度因子可调整动作动画的运行速度。例如,速度因子为2.0意味着动画的运行速度是原来的两倍。
在您的情况下很难说,但解决方案是创建序列操作,因此它们将逐个执行。 希望这有帮助
答案 2 :(得分:1)
我试一试。但我现在无法测试它。这是一个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];
}
答案 3 :(得分:1)
我创建了代码,无论角度如何,都将node0精灵以恒定速度转向触摸点。您在问题中表明您更喜欢使用SKAction,而不是在更新方法中使用代码。
以下是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");
}
}
}
我个人认为使用update方法完成此任务会更好。它可以让你更好地控制对象zRotation的逐步移动,而不是使用SKAction。