Постоянное движение в SpriteKit - программирование
Подтвердить что ты не робот

Постоянное движение в SpriteKit

У меня есть игра, в которой я бы хотел переместить SKNode влево или вправо в зависимости от того, касается ли пользователь касания левой или правой стороны экрана. Как применить постоянное движение к node, пока пользователь прикасается к экрану?

4b9b3361

Ответ 1

У вас действительно есть три варианта сохранения скорости.

Один из них заключается в том, чтобы просто обновлять скорость до требуемой суммы при каждом обновлении. В этом случае я выбрал 200 200. Это сделает ваше движение полностью статическим.

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

Второй вариант - сохранить скорость, но оставить место для искажений движения с использованием коэффициента скорости. Пример ниже сохранит скорость до 200 200, но не мгновенно. Вместо этого это будет зависеть от скорости. Это сделает ваше движение более реалистичным и немного менее статичным. Например, скажем, что у вас есть персонаж, движущийся с постоянной скоростью, и вы меняете направление, вы увидите небольшое ускорение, когда оно изменится на новое значение постоянной скорости, сделав изменение в движении менее статичным и более динамичным. Я настоятельно рекомендую этот вариант.

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

}

Третий вариант - просто установить скорость или применить импульс ONCE (в отличие от каждого кадра, как мы делали выше), но отключите его гравитацию и сопротивление воздуха. Проблема с этим подходом заключается в том, что он не всегда сохранит вашу скорость. Он будет поддерживать постоянную скорость, пока не будет действовать внешняя сила (т.е. Трение, столкновение с другим телом и т.д.). Этот подход работает в тех случаях, когда вы хотите, чтобы какой-то объект двигался с постоянной скоростью, но все еще оставался полностью динамичным. Я не рекомендую этот вариант вообще, если вы хотите контролировать и/или сохранять скорость ваших объектов. Единственный сценарий, о котором я мог подумать, - это игра с движущимися астероидами, которые плавают вокруг экрана с постоянной скоростью, но по-прежнему страдают от внешних сил (т.е. 2 ​​астероида сталкиваются, что приведет к новой постоянной скорости).

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

Вот пример проекта, который я написал в Swift с использованием опции 2. Я попытался соответствовать вашему описанию. Он просто перемещает node влево или вправо с постоянной скоростью. Обязательно поэкспериментируйте с коэффициентом скорости.

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

Примечание. Старайтесь держаться подальше от SKActions для движения в реальном времени. Используйте их только для анимации.

В режиме реального времени я подразумеваю движение, в котором положение и скорость ваших объектов важны для вас на каждом этапе их моделирования. В то время как в анимации вы действительно озабочены только начальным и конечным точками в течение некоторого времени. Например, игра с движущимися персонажами требует движения в реальном времени. Вы должны иметь возможность отслеживать на каждом этапе симуляции положения и скорости ваших персонажей и, возможно, делать корректировки через определенные промежутки времени. Но для более простых вещей, таких как перемещение элементов пользовательского интерфейса, вам не нужно отслеживать их движение на каждом шаге, поэтому используйте анимацию

Ответ 2

Добавьте ivar:

CGPoint velocity;

Установите движение, например, перемещение вправо:

velocity.x = 5;

Интегрируйте скорость на каждый шаг:

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

PS: действия, как известно, плохо для непрерывных операций, в частности движения, поскольку они основаны на времени, т.е. должны заканчиваться через определенную продолжительность. Но что, если время действия истекает до того, как оно достигнет пункта назначения?

Ответ 3

Я устанавливаю скорость .dx напрямую, однако я не рекомендую этот подход, так как он полностью игнорирует предыдущую скорость.

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

Я хотел бы предоставить вам решение, которое лучше работает с существующим физическим движком spritekit, но у меня его нет, я предложу щедрость для него, так как мне тоже нужно решение для этого.

Для прикосновений в Swift вы можете:

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
}

Ответ 4

Вот как вы это делаете математически...

поэтому в этом примере палец хочет переместить элемент.

Это точно определяет, сколько вы хотите, чтобы он перемещался во время цикла.

введите описание изображения здесь

(Во многих (конечно, не во всех) ситуациях у вас будет масса как нормализующийся "один килограмм". (Если ваша физика работает над "абстрактными" телами - вы нажимаете "значки", "капли", или тому подобное - сделайте это, используйте 1,0 кг. Естественно, если ваша физика - это танки, футбольные мячи или что-то еще, вы должны использовать ценности реального мира.) С массой "один килограмм", как вы это делаете.)

Наслаждайтесь!

Ответ 5

Зависит, если вы хотите использовать физические силы или действия.

Что-то вроде этого

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

    }
}
}

Если вы решите использовать действия, в touchhesEnded удалите все действия из спрайта, который вы перемещаете.

Если вы применяете небольшие силы в touchhesBegan, спрайт должен остановиться.

Ответ 6

Используйте свойство velocity для физического объекта. Переменная right является точкой касания минус объект (в данном случае self) и обеспечивает соответствующую скорость

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

Ответ 7

Если ваш SKNode имеет physicsBody, примените к нему силу:

@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

Если это не так, используйте ответ LearnCocos2D.

Ответ 8

если вам нужно остановить движение в фиксированном положении слева направо.

Это для Swift и objective-c.

вам нужно использовать в физическом приложении:

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

Ответ 9

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