【问题标题】:Using SKPhysicsBody to create a vehicle使用 SKPhysicsBody 创建车辆
【发布时间】:2015-08-12 03:04:53
【问题描述】:

我正在尝试根据玩家输入使车辆(在本例中为火车)移动。如果我通过 SKAction 移动火车,车轮不会旋转。我可以在其物理体上使用applyForce 方法,但我需要更多控制。我需要让它在一定时间内移动一定距离。这如何实现?

-(void)didMoveToView:(SKView *)view {
    SKTexture *trainBodyTexture = [SKTexture textureWithImageNamed:@"levelselect_trainbody"];
    SKSpriteNode *trainBody = [[SKSpriteNode alloc] initWithTexture:trainBodyTexture];
    trainBody.zPosition = 0;
    trainBody.position = CGPointMake(300, 500);
    trainBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:trainBody.size];
    [self addChild:trainBody];

    SKTexture *trainWheelTexture = [SKTexture textureWithImageNamed:@"levelselect_trainwheel"];
    SKSpriteNode *trainWheel1 = [[SKSpriteNode alloc] initWithTexture:trainWheelTexture];
    trainWheel1.zPosition = 1;
    trainWheel1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:trainWheel1.size.width/2];
    trainWheel1.physicsBody.allowsRotation = YES;
    trainWheel1.position = CGPointMake(220, 400);
    [self addChild:trainWheel1];

    SKSpriteNode *trainWheel2 = [[SKSpriteNode alloc] initWithTexture:trainWheelTexture];
    trainWheel2.zPosition = 1;
    trainWheel2.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:trainWheel2.size.width/2];
    trainWheel2.physicsBody.allowsRotation = YES;
    trainWheel2.position = CGPointMake(380, 400);
    [self addChild:trainWheel2];

    SKShapeNode *dot = [SKShapeNode shapeNodeWithCircleOfRadius:10];
    dot.zPosition = 2;
    dot.fillColor = [NSColor redColor];
    dot.position = CGPointMake(0, -20);
    [trainWheel1 addChild:dot];

    SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:trainBody.physicsBody bodyB:trainWheel1.physicsBody anchor:trainWheel1.position];
    SKPhysicsJointPin *pin2 = [SKPhysicsJointPin jointWithBodyA:trainBody.physicsBody bodyB:trainWheel2.physicsBody anchor:trainWheel2.position];

    [self.scene.physicsWorld addJoint:pin];
    [self.scene.physicsWorld addJoint:pin2];

    //[trainWheel1 runAction:[SKAction moveByX:300 y:0 duration:3]];
    [trainBody.physicsBody applyForce:CGVectorMake(3000, 0)];

}



更新:使用火车类实施(由 Jaffer Sheriff 建议)

Train.h

#import <SpriteKit/SpriteKit.h>

@interface Train : SKSpriteNode

-(void) createPhysics;
-(void) moveLeft;
-(void) moveRight;

@end

Train.m

#import "Train.h"

@interface Train()

@property SKSpriteNode *trainBody, *trainWheelFront, *trainWheelRear;
@property SKPhysicsWorld *physicsWorld;

@end

@implementation Train

-(instancetype) init {
    if (self = [super init]) {
        [self initTrainBody];
        [self initWheels];
    }
    return self;
}

-(void) initTrainBody {
    SKTexture *trainBodyTexture = [SKTexture textureWithImageNamed:@"levelselect_trainbody"];
    _trainBody = [[SKSpriteNode alloc] initWithTexture:trainBodyTexture];
    _trainBody.zPosition = 0;
    _trainBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_trainBody.size];
    [self addChild:_trainBody];
}

-(void) initWheels {
    SKTexture *_trainWheelTexture = [SKTexture textureWithImageNamed:@"levelselect_trainwheel"];
    _trainWheelFront = [[SKSpriteNode alloc] initWithTexture:_trainWheelTexture];
    _trainWheelFront.zPosition = 1;
    _trainWheelFront.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_trainWheelFront.size.width/2];
    _trainWheelFront.physicsBody.allowsRotation = YES;
    _trainWheelFront.position = CGPointMake(-80, -82);
    [self addChild:_trainWheelFront];

    _trainWheelRear = [[SKSpriteNode alloc] initWithTexture:_trainWheelTexture];
    _trainWheelRear.zPosition = 1;
    _trainWheelRear.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_trainWheelRear.size.width/2];
    _trainWheelRear.physicsBody.allowsRotation = YES;
    _trainWheelRear.position = CGPointMake(80, -82);
    [self addChild:_trainWheelRear];

    //dot used to see if wheels are rotating, no other point
    SKShapeNode *dot = [SKShapeNode shapeNodeWithCircleOfRadius:10];
    dot.zPosition = 2;
    dot.fillColor = [NSColor redColor];
    dot.position = CGPointMake(0, -20);
    [_trainWheelFront addChild:dot];
}

//this method is called after the train node is added to the scene in GameScene otherwise will get error adding joints before node is in scene
-(void) createPhysics {
    SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:_trainBody.physicsBody bodyB:_trainWheelFront.physicsBody anchor:_trainWheelFront.position];
    SKPhysicsJointPin *pin2 = [SKPhysicsJointPin jointWithBodyA:_trainBody.physicsBody bodyB:_trainWheelRear.physicsBody anchor:_trainWheelRear.position];

    [self.scene.physicsWorld addJoint:pin];
    [self.scene.physicsWorld addJoint:pin2];
}

-(void) moveLeft {
    SKAction *rotateLeft = [SKAction rotateByAngle:6*M_PI duration:0.2];
    [_trainWheelFront runAction:rotateLeft];
    [_trainWheelRear runAction:rotateLeft];
}

-(void) moveRight {
    SKAction *rotateRight = [SKAction rotateByAngle:-6*M_PI duration:0.2];
    [_trainWheelFront runAction:rotateRight];
    [_trainWheelRear runAction:rotateRight];
}


@end

GameScene.m

#import "GameScene.h"
#import "Train.h"

@interface GameScene()

@property Train *train;

@end

@implementation GameScene

-(void)didMoveToView:(SKView *)view {
    [self initTrain];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil]; //using notifications and custom view class to handle key presses

}

-(void) initTrain {
    _train = [[Train alloc] init];
    _train.position = CGPointMake(500, 300);
    [self addChild:_train];
    [_train createPhysics];
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
}

-(void) keyPressed:(NSNotification*)notification {
    NSNumber *keyCodeObject = notification.userInfo[@"keyCode"];
    NSInteger keyCode = keyCodeObject.integerValue;
    NSLog(@"keycode = %lu", keyCode);
    switch (keyCode) {
        case 123:
            [self leftArrowPressed];
            break;
        case 124:
            [self rightArrowPressed];
            break;
    }
}

-(void) leftArrowPressed {
    SKAction *moveLeft = [SKAction moveByX:-200 y:0 duration:0.2];
    [_train runAction:moveLeft];
    [_train moveLeft];
}

-(void) rightArrowPressed {
    SKAction *moveRight = [SKAction moveByX:200 y:0 duration:0.2];
    [_train runAction:moveRight];
    [_train moveRight];
}

@end

注意:此解决方案会导致整个火车在按下左/右键时翻转并吓坏。看来我的销接头不正确,但对我来说似乎是正确的:/

【问题讨论】:

  • 尝试将车轮动画动作与火车前进动作组合在一起。像这样,SKAction *trainMovent = [SKAction group:@[wheelAnimatingAction,trainForwardMovementAction]]; [self runAction:trainMovent];
  • 是的,我想到了,但是将节点作为子节点难道不应该有一些好处吗?我的意思是,在这种情况下,我肯定只需要对几个节点进行分组,但是当火车上有更多的汽车时,它就会变得很麻烦。如问题所述,我正在寻找一种尽可能依赖物理交互的方法。
  • 尝试以轮子作为子类实现 Train 类,并且还包含所有轮子的数组。当您在火车上运行前移动作时,所有轮子都会向前移动,因为它们是 Train Class 的子级。还可以实现车轮旋转动画,这是您在火车前进时拥有的一系列车轮。
  • 你解决了这个问题吗?
  • 我按照您建议的方式进行了尝试,但是每次火车向左移动 x 个单位,然后向右移动 x 个单位时,它并不总是移动那个数量。我还尝试通过创建一个 Train 类对 SKSpriteNode 进行子类化,但我一直遇到的问题是火车的车身会左右移动,但旋转轮子会导致一团糟。我将每个节点分别添加到 train 对象中。我在self 上运行了左/右移动动作,并在作为self 的子节点的轮子上运行了旋转动作。一定有办法,但它一直在逃避我。

标签: macos sprite-kit skphysicsbody skphysicsjoint


【解决方案1】:

SkSpriteNode 的默认初始化器是- (instancetype)initWithTexture:(SKTexture *)texture color:(SKColor *)color size:(CGSize)size;试试这个,

@interface Train : SKSpriteNode
- (instancetype)initTrainWithColor:(UIColor *) color andSize:(CGSize) size;
-(void) animateWheelsWithTime:(float) time;
@end


@interface Train ()
{
   SKSpriteNode *wheel1;
   SKSpriteNode *wheel2;
   NSMutableArray *wheelsArray;
 }
@end

@implementation Train
- (instancetype)initTrainWithColor:(UIColor *) color andSize:(CGSize) size
 {
 self = [super initWithTexture:nil color:color size:size]; 
 if (self)
 {
     [self addWheels];
 }
return self;
}
  -(void)addWheels
  {
   wheelsArray = [[NSMutableArray alloc]init];
   wheel1 = [SKSpriteNode spriteNodeWithImageNamed:@"wheel"];
   [wheel1 setPosition:CGPointMake(-(self.frame.size.width/2.0f-wheel1.frame.size.width/2.0f), - (self.frame.size.height/2.0f - wheel1.frame.size.height/2.0f))];
 [self addChild:wheel1];

 wheel2 = [SKSpriteNode spriteNodeWithImageNamed:@"wheel"];
 [wheel2 setPosition:CGPointMake((self.frame.size.width/2.0f-wheel2.frame.size.width/2.0f), - (self.frame.size.height/2.0f - wheel2.frame.size.height/2.0f))];
[self addChild:wheel2];

[wheelsArray addObject:wheel1];
[wheelsArray addObject:wheel2];
}

-(void) animateWheelsWithTime:(float) time
{
    for (SKSpriteNode *wheel in wheelsArray)
      {
        SKAction *act = [SKAction rotateByAngle:3*M_PI duration:time];
        [wheel runAction:act];
     }
}
@end

在 GameScene.m 中添加这样的火车,

  -(void) addTrain
   {
      Train *train1 = [[Train alloc]initTrainWithColor:[UIColor yellowColor] andSize:CGSizeMake(150, 100)];
      [train1 setPosition:CGPointMake(self.size.width/2.0f, self.size.height/2.0f)];
     [self addChild:train1];

   SKAction *act = [SKAction moveTo:CGPointMake(self.size.width/2.0f, self.size.height - 50) duration:2];
    [train1 runAction:act];
    [train1 animateWheelsWithTime:2];
 }

我试过了,效果很好。

【讨论】:

  • 我也知道该怎么做。看一下问题标题,我正在寻找一种解决方案,该解决方案涉及使用物理体以获得更逼真的外观。我知道这是可能的,我只是不确定如何解决我遇到的问题。您的解决方案根本没有考虑物理学。例如:spritekit car
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-20
  • 2014-04-12
  • 1970-01-01
相关资源
最近更新 更多