web-dev-qa-db-fra.com

Mouvement constant dans SpriteKit

J'ai un jeu dans lequel j'aimerais déplacer un SKNode vers la gauche ou vers la droite, selon que l'utilisateur touche le côté gauche ou le côté droit de l'écran. Comment appliquer un mouvement constant à un nœud lorsque l'utilisateur touche l'écran?

17
user1007895

Vous avez vraiment trois options pour préserver la vélocité. 

La première consiste simplement à continuer à réinitialiser la vélocité au montant souhaité à chaque mise à jour. Dans ce cas, j'ai choisi 200.200. Cela rendra votre mouvement complètement statique.

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

La deuxième option consiste à préserver la vitesse tout en laissant de la place aux distorsions de mouvement en utilisant un facteur de taux. L'exemple ci-dessous conserverait la vitesse à 200 200 mais pas instantanément. Cela dépendrait plutôt du taux. Cela rendra votre motion beaucoup plus réaliste et un peu moins statique. Par exemple, disons que votre personnage se déplace à vitesse constante et que vous changez de direction, vous verrez une accélération minime au fur et à mesure que celle-ci passe à la nouvelle valeur de vitesse constante, rendant le changement de mouvement moins statique et plus dynamique. Je recommande fortement cette option.

-(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 troisième option consiste à définir simplement la vitesse ou à appliquer une impulsionUNE FOIS(contrairement à chaque image, comme précédemment), mais à désactiver sa résistance à la gravité et à l'air. Le problème avec cette approche est qu’elle ne préservera pas toujours votre vitesse. Il ne gardera une vitesse constante que s’il est sollicité par une force externe (friction, collision avec un autre corps, etc.). Cette approche fonctionne dans les cas où vous souhaitez que certains objets se déplacent à une vitesse constante tout en restant complètement dynamiques. Je ne recommande pas cette option du tout si vous cherchez à contrôler et/ou préserver la vélocité de vos objets. Le seul scénario auquel je puisse penser est peut-être un jeu avec des astéroïdes en mouvement qui flottent autour de l’écran à une vitesse constante mais qui sont toujours affectés par des forces externes (c’est-à-dire que 2 astéroïdes entrent en collision, ce qui entraîne une nouvelle vitesse 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

Voici un exemple de projet que j'ai écrit dans Swift en utilisant l'option 2. J'ai essayé de correspondre à votre description. Il déplace simplement un nœud à gauche ou à droite à vitesse constante. Veillez à expérimenter avec le facteur de taux.

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

Remarque: Essayez de rester à l'écart de SKActions pour un mouvement en temps réel. Utilisez-les uniquement pour les animations.

Par mouvement en temps réel, j'entends un mouvement dans lequel la position et la vitesse de vos objets sont importantes pour vous à chaque étape de leur simulation. Tandis qu'en animation, vous ne vous préoccupez que des points de départ et d'arrivée sur une certaine durée. Par exemple, un jeu avec des personnages en mouvement nécessite un mouvement en temps réel. Vous devez être capable de surveiller à chaque étape de la simulation les positions et les vitesses de vos personnages, et éventuellement de faire des ajustements à certains intervalles. Mais pour des choses plus simples comme le déplacement d'éléments d'interface utilisateur, il n'est pas nécessaire de suivre leur mouvement à chaque étape. Utilisez donc l'animation.

35
Epic Byte

Ajouter un ivar:

CGPoint velocity;

Définissez le mouvement, par exemple en vous déplaçant à droite:

velocity.x = 5;

Intégrez la vélocité à chaque étape:

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

PS: les actions sont notoirement mauvaises pour les opérations continues, en particulier les mouvements, car elles sont basées sur le temps, c'est-à-dire qu'elles sont censées se terminer après une certaine durée. Mais que se passe-t-il si le temps imparti à l'action est épuisé avant d'atteindre sa destination?

5
LearnCocos2D

Je règle directement velocity.dx, mais je ne recommande pas cette approche car elle ignore complètement la vélocité précédente.

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

J'aimerais vous proposer une solution qui fonctionne mieux avec le moteur physique spritekit existant, mais je n'en ai pas, je vous en offrirai une prime, car j'ai également besoin d'une solution.

Pour les touches dans Swift, vous pouvez faire:

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
}
1
lisovaccaro

Voici exactement comment vous le faites mathématiquement ...

ainsi, dans cet exemple, un doigt veut déplacer l'élément.

Cela définit exactement combien vous voulez que cela bouge dans le frametime.

 enter image description here

(Dans de nombreuses (certainement pas toutes) situations, vous aurez la masse comme normalisant "un kilogramme". (Si votre physique travaille sur des corps "abstraits" - vous poussez autour "d'icônes", "de gouttes" ou comme si vous utilisiez 1,0 kg. Naturellement, si votre physique est un char, des ballons de football ou autre, vous devez utiliser les valeurs du monde réel.) Avec une masse de "un kilo", c’est comme ça que vous le faites.)

Prendre plaisir!

1
Fattie

Si votre SKNode a physicsBody, appliquez-lui de la force:

@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 ce n'est pas le cas, utilisez la réponse de LearnCocos2D.

0
Andrey Gordeev

Cela dépend si vous souhaitez utiliser des forces ou des actions de la physique.

Quelque chose comme ça

-(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 vous décidez d’utiliser des actions, dans touchesEnded , supprimez toutes les actions du Sprite que vous déplacez.

Si vous appliquez une petite force dans toucheBegan , le Sprite devrait s'arrêter.

0
DogCoffee

Utilisez la propriété velocity de physicsBody. La variable droite est le point du toucher moins l'objet (dans ce cas, soi) et fournit la vitesse associée

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