Sprite-kit:物理体在运动过程中被取代

时间:2017-02-14 21:02:03

标签: ios sprite-kit physics

精灵有一个物理体。当精灵向任何方向移动时,物理体在该方向上移位(参见附图)。这对接触和碰撞很不利。

物理体只在运动过程中被移位。当精灵静止不动时,物理体不会移位。

我的问题: 为什么物理体在精灵工具包中移动了?有什么办法可以阻止吗?

非常感谢

编辑:

好的..我写了一个小测试来证明这个问题。我只有一个物理体的节点不受重力的影响。物理世界也没有引力。通过触摸屏幕,英雄向上移动。如果您需要测试代码,请设置SKView showsPhysics property = YES。通过触摸,观察物理身体如何从正确的位置移动。然后它一直在移动方向上移动。

感谢您提出任何建议。

// header file

#import <SpriteKit/SpriteKit.h>

@interface TestScene : SKScene <SKPhysicsContactDelegate>

@end


// implementation file


#import "TestScene.h"

@implementation TestScene
{
    SKNode *_world;
    SKSpriteNode *_hero;

    BOOL _play;
    BOOL _touch;
    CGFloat _startY;
}

- (instancetype)initWithSize:(CGSize)size
{
    if(self = [super initWithSize:size])
    {
        self.backgroundColor = [SKColor whiteColor];
        self.physicsWorld.contactDelegate = self;
        // no gravity
        //self.physicsWorld.gravity = CGVectorMake(0.0f, -9.8f);

        _play = NO;
        _touch = NO;
        _startY = 50;

        [self addWorld];
        [self addHero];
    }

    return self;
}

- (void)addWorld
{
    _world = [SKNode node];
    _world.position = CGPointMake(0, 0);
    [self addChild:_world];
}

- (void)addHero
{
    _hero = [SKSpriteNode spriteNodeWithImageNamed:@"hero"];
    _hero.size = CGSizeMake(50, 50);
    _hero.position = CGPointMake(CGRectGetMidX(self.frame), _startY);
    [_world addChild:_hero];

    _hero.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_hero.size.width/2.0f];
    _hero.physicsBody.affectedByGravity = NO;
    _hero.physicsBody.dynamic = YES;
    _hero.physicsBody.allowsRotation = NO;
}

- (void)applyHeroUpwardForce
{
    [_hero.physicsBody applyForce:(CGVectorMake(0, 100))];

    // limit velocity
    CGVector vel = _hero.physicsBody.velocity;
    vel.dy = vel.dy > 1000 ? 1000 : vel.dy;
    _hero.physicsBody.velocity = vel;

    // control the hero movement. it really moves upwards even we don’t see that, since the _world moves downwards
    NSLog(@"_hero.position.y: %f", _hero.position.y);
}

- (void)updateWorld
{
    _world.position = CGPointMake(_world.position.x, -(_hero.position.y - _startY));
}

- (void)didSimulatePhysics
{
    if(!_play)
        return;

    if(_touch)
    {
        [self applyHeroUpwardForce];
        [self updateWorld];
    }

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if(!_play)
        _play = YES;

    _touch = YES;
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    _touch = NO;
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    _touch = NO;
}

@end

enter image description here

编辑2:

The same issue in the game 'Pierre Penguin Escapes The Antarctic'

enter image description here

3 个答案:

答案 0 :(得分:1)

使用SpriteKit,我发现在你更新物理联系处理程序委托方法中精灵位置的情况下会发生这种情况。

永远不要在回调委托方法中更新基础节点的位置/旋转/比例。相反,有一种机制可以识别和跟踪在物理回调中进行的所需更新,并且具有一个从场景的更新方法调用的单独方法,该方法应用对节点进行的更改。

答案 1 :(得分:0)

如果你正确地初始化物理体,它应该从父节点中移出。 是的,物理身体会移动,但它会移动完全相同的路径移动节点。 检查你的物理身体形状。

答案 2 :(得分:0)

经过多次测试后,这个问题的最佳解决方案(在apple docu中描述):

  

在didBeginContact:或didEndContact:中设置一个标志并进行更改   响应更新中的那个标志:forScene:a中的方法   SKSceneDelegate。

@implementation TestScene
{
    // . . . 

    BOOL _didBeginContact;
    SKPhysicsContact *_contact;
}


- (instancetype)initWithSize:(CGSize)size
{
    if(self = [super initWithSize:size])
    {
        // . . . 

        _didBeginContact = NO;
        _contact = nil;
    }

    return self;
}

- (void)didBeginContact:(SKPhysicsContact *)contact
{
    // to be on the safe side: avoid multiple contacts
    if(_didBeginContact || _contact)
        return;

    // set your variables which you maybe need in your contact method 

    _contact = contact;
    _didBeginContact = YES;
}

- (void)didContact
{
    // to be on the safe side
    if(_contact == nil)
        return;

    // what ever you need during/after contact. 
    // use _contact set in didBeginContact: 

    if(_contact.bodyA.categoryBitMask // what ever
    if(_contact.bodyB.categoryBitMask // what ever
    // etc.

    // . . . 

    // important
    _contact = nil;
}

- (void)update:(NSTimeInterval)currentTime
{
    // if contact happens, so call your contact: method
    if(_didBeginContact)
    {
        _didBeginContact = NO; // important
        [self didContact];
    }
}

就是这样。谢谢大家。