spritekit ios sprite-kit

ios - spritekit - skview



Movimiento constante en SpriteKit (9)

Tengo un juego en el que me gustaría mover un SKNode hacia la izquierda o hacia 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?


Agrega un ivar:

CGPoint velocity;

Establezca el movimiento, por ejemplo, moviéndose 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 notoriamente malas para las operaciones continuas, específicamente el movimiento, ya que se basan en el tiempo, es decir, se supone que terminan después de una cierta duración. Pero, ¿qué pasa si el tiempo de la acción se agota antes de llegar a su destino?


Depende si quieres usar fuerzas físicas o acciones.

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, en touchesEnded elimina todas las acciones del sprite que estás moviendo.

Si aplicas pequeñas fuerzas en touchesBegan, el sprite debería detenerse.


Usa la propiedad de velocidad del physicsBody. La variable derecha es el punto del toque menos el objeto (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 tiene que detener su movimiento en una posición fija de izquierda a derecha.

Esto para Swift y objetivo-c.

tienes que usar en física Cuerpo:

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


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

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

Me gustaría ofrecerle una solución que funcione mejor con el motor físico existente de spritekit, pero no la tengo, ofreceré una recompensa, ya que también necesito una solución.

Para los 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 }


Si su SKNode tiene physicsBody , aplique fuerza a él:

@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 es así, use la respuesta de LearnCocos2D.


-(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); }


Realmente tienes tres opciones para preservar la velocidad.

Una es simplemente mantener el restablecimiento de la velocidad a su cantidad deseada en 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 preservarí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 realista y un poco menos estático. Por ejemplo, supongamos que tienes un personaje moviéndose a velocidad constante y cambias de dirección, verás una pequeña aceleración a medida que cambia al nuevo valor de velocidad constante, haciendo que el cambio de movimiento sea menos estático y más dinámico. Recomiendo 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 tiene es simplemente establecer la velocidad o aplicar un impulso UNA VEZ (a diferencia de cada cuadro como hicimos anteriormente), pero apague su gravedad y la resistencia del aire. El problema con este enfoque es que no siempre conservará su velocidad. Solo mantendrá una velocidad constante hasta que actúe sobre ella una fuerza externa (es decir, fricción, colisión con otro cuerpo, etc.). Este enfoque funciona para los casos en los que desea que un objeto se mueva a velocidad constante, pero que permanezca completamente dinámico. No recomiendo esta opción en absoluto si buscas controlar y / o preservar la velocidad de tus objetos. El único escenario en el que podría pensar es quizás un juego con asteroides en movimiento que flotan alrededor de la pantalla a velocidad constante pero que aún se ven afectados por fuerzas externas (es decir, chocan 2 asteroides que darán 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úrese 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.redColor(), 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: trate de mantenerse alejado de SKActions para el movimiento en tiempo real. Solo úsalos para animaciones.

Por movimiento en tiempo real, me refiero al movimiento en el que la posición y la velocidad de tus objetos son importantes para ti en cada paso de su simulación. Mientras que en la animación, en realidad solo le preocupan los puntos de inicio y finalización durante algún tiempo. Por ejemplo, un juego con personajes en movimiento requiere movimiento en tiempo real. Necesita poder monitorear en cada paso de la simulación las posiciones y velocidades de sus personajes, y posiblemente realizar ajustes en ciertos intervalos. Pero para cosas más simples como mover elementos UI, no necesita seguir su movimiento en cada paso, así que use animación


Aquí está exactamente cómo lo haces matemáticamente ...

Entonces, en este ejemplo, un dedo quiere mover el objeto.

Eso define exactamente cuánto quieres que se mueva en el frametime.

(En muchas (ciertamente no en todas) situaciones, tendrás la masa como un "kilogramo" normalizador. (Si tu física está trabajando en cuerpos "abstractos", estás empujando alrededor de "iconos", "blobs" o similares - haz eso, usa 1.0 kg. Naturalmente, si tu física es tanques, balones de fútbol o lo que sea, debes usar valores del mundo real). Con una masa de "un kilo", así es como lo haces).

¡Disfrutar!