Movimiento constante en SpriteKit

Tengo un juego en el que me gustaría mover un SKNode a la izquierda o a la derecha dependiendo de si el usuario toca el lado izquierdo o derecho de la pantalla. ¿Cómo aplico un movimiento constante a un nodo mientras el usuario está tocando la pantalla?

Realmente tienes tres opciones para preservar la velocidad.

Una es simplemente seguir restableciendo la velocidad de return a la cantidad deseada cada actualización. En este caso, elegí 200.200. Esto hará que tu movimiento sea completamente estático.

-(void)update:(NSTimeInterval)currentTime { node.physicsBody.velocity=CGVectorMake(200, 200); } 

La segunda opción es preservar la velocidad pero dejar espacio para distorsiones de movimiento usando un factor de velocidad. El siguiente ejemplo conservaría la velocidad a 200.200 pero no instantáneamente. En cambio, dependería de la tasa. Esto hará que su movimiento sea mucho más realist y un poco less estático. Por ejemplo, digamos que tiene un personaje que se mueve a velocidad constante y cambia de dirección, verá una pequeña aceleración al cambiar al nuevo valor de velocidad constante, haciendo que el cambio en movimiento sea less estático y más dynamic. Recomiendo encarecidamente esta opción.

 -(void)update:(NSTimeInterval)currentTime { CGFloat rate = .05; CGVector relativeVelocity = CGVectorMake(200-node.physicsBody.velocity.dx, 200-node.physicsBody.velocity.dy); node.physicsBody.velocity=CGVectorMake(node.physicsBody.velocity.dx+relativeVelocity.dx*rate, node.physicsBody.velocity.dy+relativeVelocity.dy*rate); } 

La tercera opción que tienes es simplemente establecer la velocidad o aplicar un impulso UNA VEZ (en oposition a cada cuadro como hicimos arriba), pero apaga la gravedad y la resistencia del air. El problema con este enfoque es que no siempre conservará su velocidad. Solo mantendrá la velocidad constante hasta que actúe una fuerza externa (es decir, fricción, choque con otro cuerpo, etc.). Este enfoque funciona para los casos en que desea que algún object se mueva a velocidad constante, pero que siga siendo completamente dynamic. No recomiendo esta opción si está buscando controlar y / o preservar la velocidad de sus objects. El único escenario en el que podría pensar es quizás un juego con asteroides en movimiento que flotan alnetworkingedor de la pantalla a velocidad constante, pero que aún se ven afectados por fuerzas externas (es decir, 2 asteroides colisionan, lo que dará como resultado una nueva velocidad constante).

 node.physicsBody.velocity=CGVectorMake(200, 200); node.physicsBody.linearDamping = 0; //You may want to remove the air-resistance external force. node.physicsBody.affectedByGravity = false; //You may want to remove the gravity external force 

Aquí hay un proyecto de ejemplo que escribí en Swift usando la opción 2. Intenté hacer coincidir su descripción. Simplemente mueve un nodo hacia la izquierda o hacia la derecha a velocidad constante. Asegúrate de experimentar con el factor de velocidad.

 import SpriteKit class GameScene: SKScene { var sprite: SKSpriteNode! var xVelocity: CGFloat = 0 override func didMoveToView(view: SKView) { sprite = SKSpriteNode(color: UIColor.networkingColor(), size: CGSize(width: 50, height: 50)) sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size) sprite.physicsBody.affectedByGravity = false sprite.position = CGPoint(x: self.size.width/2.0, y: self.size.height/2.0) self.addChild(sprite) } override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { let touch = touches.anyObject() as UITouch let location = touch.locationInNode(self) if (location.x<self.size.width/2.0) {xVelocity = -200} else {xVelocity = 200} } override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) { xVelocity = 0 } override func update(currentTime: CFTimeInterval) { let rate: CGFloat = 0.5; //Controls rate of motion. 1.0 instantaneous, 0.0 none. let relativeVelocity: CGVector = CGVector(dx:xVelocity-sprite.physicsBody.velocity.dx, dy:0); sprite.physicsBody.velocity=CGVector(dx:sprite.physicsBody.velocity.dx+relativeVelocity.dx*rate, dy:0); } } 

Nota: Intente mantenerse alejado de SKActions para get movimiento en time real. Solo los usa para animaciones.

Por movimiento en time real, me refiero al movimiento en el que la position y la velocidad de sus objects son importantes para usted en cada paso de su simulación. Mientras que en la animation, solo te preocupan los puntos de inicio y finalización durante un time. Por ejemplo, un juego con personajes en movimiento requiere movimiento en time real. Debe ser capaz de monitorear en cada paso de la simulación las posiciones y velocidades de sus personajes y, potencialmente, hacer ajustes a ciertos intervalos. Pero para cosas más simples como mover elementos de interfaz de usuario, no necesita rastrear su movimiento en cada paso, así que use la animation

Agregue un ivar:

 CGPoint velocity; 

Establezca el movimiento, por ejemplo, hacia la derecha:

 velocity.x = 5; 

Integre la velocidad en cada paso:

 -(void) update:(NSTimeInterval)currentTime { CGPoint pos = self.position; pos.x += velocity.x; pos.y += velocity.y; self.position = pos; } 

PD: las acciones son notablemente malas para las operaciones continuas, específicamente el movimiento, ya que están basadas en el time, es decir, se supone que deben terminar después de una cierta duración. ¿Pero qué pasa si el time de la acción se agota antes de que llegue a su destino?

Estoy configurando la velocidad.dx directamente, sin embargo, no recomiendo este enfoque ya que ignora por completo la velocidad anterior.

 public func setSpeed(speed: CGFloat) { physicsBody.velocity.dx = speed } 

Me gustaría proporcionarle una solución que funcione mejor con el motor spritekit physic existente, pero no tengo uno, ofreceré una recompensa por ello ya que también necesito una solución para esto.

Para toques en Swift puedes hacer:

 override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { player.setSpeed(100) // You can use touches.anyObject().position to see the touch position and implement left/right movement } 

Depende si desea usar fuerzas o acciones físicas.

Algo como esto

 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { for (UITouch *touch in touches) { UITouch* touch = [touches anyObject]; CGPoint loc = [touch locationInNode:self]; if (loc.x > self.frame.size.width * 0.5) { NSLog(@"move right"); // move the sprite using an action // or set physics body and apply a force } else { NSLog(@"move left"); } } } 

Si decides usar acciones, toca Eliminar todas las acciones del sprite que estás moviendo.

Si está aplicando pequeñas fuerzas en toques, el sprite debe detenerse.

Usa la propiedad de velocidad de physicsBody. La variable derecha es el punto del toque less el object (en este caso self) y proporciona la velocidad asociada

 -(void)segmentedTouchBegan:(NSValue *)p{ CGPoint point = [p CGPointValue]; int right = (point.x - self.screenWidth/2.0f)/ABS((point.x - self.screenWidth/2.0f)); //1 if right -1 if left self.physicsBody.velocity = CGVectorMake(SHIFT*right, 0); } -(void)segmentedTouchMoved:(NSValue *)p{ CGPoint point = [p CGPointValue]; int right = (point.x - self.screenWidth/2.0f)/ABS((point.x - self.screenWidth/2.0f)); //1 if right -1 if left self.physicsBody.velocity = CGVectorMake(SHIFT*right, 0); } -(void)segmentedTouchEnded:(NSValue *)p{ self.physicsBody.velocity = CGVectorMake(0, 0); } 

Si su SKNode tiene physicsBody , aplique fuerza:

 @interface GameScene() @property (nonatomic) CGVector force; @end @implementation GameScene - (void)update:(CFTimeInterval)currentTime { [self.player.physicsBody applyForce:self.force]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { self.sideForce = CGVectorMake(3000, 0); } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { self.sideForce = CGVectorMake(0, 0); } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { self.sideForce = CGVectorMake(0, 0); } @end 

Si no lo ha hecho, use la respuesta de LearnCocos2D.

Si tiene que detener su movimiento en una position fija de izquierda a derecha.

Esto para Swift y el objective c.

Tienes que usar en physicsBody:

node.position = CGPoint (X: ….., Y: …..) node.physicsBody = nil

 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { /* Called when a touch begins */ UITouch *touch = [touches anyObject]; CGPoint touchLocation = [touch locationInNode:self]; if (touchLocation.x > 50) { // Do whatever.. _timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(movePlayer:) userInfo:nil repeats:YES]; NSLog(@"Right"); } else if (touchLocation.x < 50){ ////// use another NSTimer to call other function//// NSLog(@"left"); } } -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ if (_timer != nil) { [_timer invalidate]; _timer = nil; } } -(void)movePlayer:(id)sender { hero.physicsBody.velocity = CGVectorMake(400, 0); }