【问题标题】:Character falling through ground人物从地上掉下来
【发布时间】:2014-03-11 02:08:55
【问题描述】:

我正在使用精灵套件作为基本游戏的一部分。游戏是有一个人,根据你点击的位置,移动到那个位置。目前,玩家不受重力影响(为了可玩性),但我正在尝试实现重力。但是,当我为玩家打开重力时,我的角色从地面掉下来时遇到了麻烦。我已经为地面创建了一个节点并禁用了地面物理(ground.physicsBody.dynamic = NO),但角色仍然可以直接飞过。我正在使用内置的 sprite-kit 重力功能,如果物理功能被禁用,那么理论上角色应该停在地面上。我不知道为什么问题仍然存在,希望能在这件事上提供一些帮助。

MyScene.m 文件中的代码:

//
//  MyScene.m
//  DodgeMan
//
//  Created by Cormac Chester on 3/8/14.
//  Copyright (c) 2014 Testman Industries. All rights reserved.
//

#import "MyScene.h"
#import "EndGameScene.h"

static const uint32_t redBallCategory =  0x1 << 0;
static const uint32_t playerCategory =  0x1 << 1;

@implementation MyScene

-(id)initWithSize:(CGSize)size
{
    if (self = [super initWithSize:size])
    {
        //Sets up data storage
        self.storeData = [NSUserDefaults standardUserDefaults];

        //Sets gravity
        self.physicsWorld.gravity = CGVectorMake(0,-2);
        self.physicsWorld.contactDelegate = self;

        //Set Background
        self.backgroundColor = [SKColor colorWithRed:0.53 green:0.81 blue:0.92 alpha:1.0];

        //Set Ground
        SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:@"ground"];
        ground.position = CGPointMake(CGRectGetMidX(self.frame), 34);
        ground.xScale = 0.5;
        ground.yScale = 0.5;
        ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
        ground.physicsBody.dynamic = NO;
        ground.physicsBody.collisionBitMask = 1;
        ground.physicsBody.usesPreciseCollisionDetection = YES;

        //Player
        self.posX = 50;
        self.posY = 88;
        self.playerSprite = [SKSpriteNode spriteNodeWithImageNamed:@"character"];
        self.playerSprite.position = CGPointMake(self.posX, self.posY);

        //Set Player Physics
        self.playerSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.playerSprite.size];
        self.playerSprite.physicsBody.dynamic = NO;
        self.playerSprite.physicsBody.categoryBitMask = playerCategory;
        self.playerSprite.physicsBody.contactTestBitMask = redBallCategory;
        self.playerSprite.physicsBody.collisionBitMask = 0;
        self.playerSprite.physicsBody.usesPreciseCollisionDetection = YES;

        //Score Label
        self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:@"Arial-BoldMT"];
        self.scoreLabel.text = @"0";
        self.scoreLabel.fontSize = 40;
        self.scoreLabel.fontColor = [SKColor blackColor];
        self.scoreLabel.position = CGPointMake(50, 260);

        //Pause Button
        self.pauseButton = [SKSpriteNode spriteNodeWithImageNamed:@"pauseButton"];
        self.pauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height - 40);
        self.pauseButton.name = @"pauseButton";

        //Pause Label
        NSString *pauseMessage;
        pauseMessage = @"Game Paused";
        self.pauseLabel = [SKLabelNode labelNodeWithFontNamed:@"Arial-BoldMT"];
        self.pauseLabel.text = pauseMessage;
        self.pauseLabel.fontSize = 40;
        self.pauseLabel.fontColor = [SKColor blackColor];
        self.pauseLabel.position = CGPointMake(self.size.width/2, self.size.height/2);

        //Set score
        self.score = 0;

        //Add nodes
        [self addChild:ground];
        [self addChild:self.playerSprite];
        [self addChild:self.scoreLabel];
        [self addChild:self.pauseButton];

    }
    return self;
}

//Add Red Ball
-(void)addBall
{
    SKSpriteNode *redBall = [SKSpriteNode spriteNodeWithImageNamed:@"locationIndicator"];
    int minY = redBall.size.height / 2;
    int maxY = self.frame.size.height - redBall.size.height / 2;
    int rangeY = maxY - minY;
    int actualY = (arc4random() % rangeY) + minY;

    //Initiates red ball offscreen
    if (actualY >= 75)
    {
        //Prevents balls from spawning in the ground
        redBall.position = CGPointMake(self.frame.size.width + redBall.size.width/2, actualY);
        [self addChild:redBall];
    }
    redBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redBall.size.width/2];
    redBall.physicsBody.dynamic = YES;
    redBall.physicsBody.categoryBitMask = redBallCategory;
    redBall.physicsBody.contactTestBitMask = playerCategory;
    redBall.physicsBody.collisionBitMask = 0;
    redBall.physicsBody.affectedByGravity = NO;
    redBall.physicsBody.usesPreciseCollisionDetection = YES;

    //Determine speed of red ball
    int minDuration = 3.0;
    int maxDuration = 5.0;
    int rangeDuration = maxDuration - minDuration;
    int actualDuration = (arc4random() % rangeDuration) + minDuration;

    // Create the actions
    SKAction *actionMove = [SKAction moveTo:CGPointMake(-redBall.size.width/2, actualY) duration:actualDuration];
    SKAction *actionMoveDone = [SKAction removeFromParent];
    SKAction *ballCross = [SKAction runBlock:^{
        self.score++;
        self.scoreString = [NSString stringWithFormat:@"%i", self.score];
        self.scoreLabel.text = self.scoreString;
        NSLog(@"Score was incremented. Score is now %d", self.score);
    }];
    [redBall runAction:[SKAction sequence:@[actionMove, ballCross, actionMoveDone]]];
}

- (void)pauseScene
{
    self.paused = YES;
    [self addChild:self.pauseLabel];
}

- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast
{
    self.lastSpawnTimeInterval += timeSinceLast;
    if (self.lastSpawnTimeInterval > 0.35) {
        self.lastSpawnTimeInterval = 0;
        [self addBall];
    }
}

//Update Loop
-(void)update:(CFTimeInterval)currentTime
{
    /* Called before each frame is rendered */
    // Handle time delta.
    CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
    self.lastUpdateTimeInterval = currentTime;
    if (timeSinceLast > 1) { //More than a second since last update
        timeSinceLast = 1.0 / 120.0;
        self.lastUpdateTimeInterval = currentTime;
    }

    [self updateWithTimeSinceLastUpdate:timeSinceLast];
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    /* Called when a touch begins */
    [super touchesBegan:touches withEvent:event];

    //Starts Timer
    startTime = [NSDate date];
}

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    /* Called when a touch ends */
    [super touchesEnded:touches withEvent:event];

    NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
    NSString *elapsedTimeString = [NSString stringWithFormat:@"Elapsed time: %f", elapsedTime];
    NSLog(@"%@", elapsedTimeString);


    for (UITouch *touch in touches)
    {
        //Gets location of touch
        CGPoint location = [touch locationInNode:self];
        NSLog(@"Touch Location X: %f \n Touch Location Y: %f", location.x, location.y);
        SKNode *node = [self nodeAtPoint:location];

        //Runs when pause button is pressed
        if ([node.name isEqualToString:@"pauseButton"])
        {
            NSLog(@"Pause button pressed");
            if (!self.paused)
            {
                [self pauseScene];
            }
            else if (self.paused)
            {
                self.paused = NO;
                [self.pauseLabel removeFromParent];
            }
        }
        else
        {
            //Prevents destination from being in the ground
            if (location.y < 88)
            {
                location.y = 88;
            }

            //Moves and animates player
            int velocity = 1000.0/1.0;
            NSLog(@"Velocity: %i", velocity);
            float realMoveDuration = self.size.width / velocity;
            SKAction *actionMove = [SKAction moveTo:location duration:realMoveDuration];
            [self.playerSprite runAction:[SKAction sequence:@[actionMove]]];
        }
    }

    NSLog(@"Touch ended");
}

//Collision between ball and player - player dies
- (void)redBall:(SKSpriteNode *)redBall didCollideWithPlayer:(SKSpriteNode *)playerSprite
{

    NSLog(@"Player died");
    [redBall removeFromParent];
    [playerSprite removeFromParent];

    //Stores Final score
    [self.storeData setObject:self.scoreString forKey:@"scoreStringKey"];

    SKTransition *reveal = [SKTransition fadeWithDuration:0.5];
    SKScene *endGameScene = [[EndGameScene alloc] initWithSize:self.size];
    [self.view presentScene:endGameScene transition: reveal];
}

- (void)didBeginContact:(SKPhysicsContact *)contact
{
    SKPhysicsBody *firstBody, *secondBody;

    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
    {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }
    else
    {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }

    //Red ball collides with the player
    if ((firstBody.categoryBitMask & redBallCategory) != 0 && (secondBody.categoryBitMask & playerCategory) != 0)
    {
        [self redBall:(SKSpriteNode *) firstBody.node didCollideWithPlayer:(SKSpriteNode *) secondBody.node];
        NSLog(@"Player and ball collided");
    }
}

@end

【问题讨论】:

标签: ios objective-c sprite-kit


【解决方案1】:

我会说你的问题是由于这条线

self.playerSprite.physicsBody.collisionBitMask = 0;

而不是将其设置为发生冲突的类别,例如

self.playerSprite.physicsBody.collisionBitMask = redBallCategory;

【讨论】:

  • 把你的代码改成这个,如果它解决了你的问题,请告诉我
  • 如果此代码对您有帮助,请投票并标记为正确答案。谢谢。
【解决方案2】:

您需要定义与角色相关的视图边界。类似于以下内容,其中声明了视图边界,然后定义了字符并将其限制在边界内。以下是该示例代码:

- (void)confineToBounds {

    CGPoint correctedMonkeyPos = _monkey.position;
    CGPoint lowerLeft = CGPointZero;
    CGPoint upperRight = CGPointMake(self.size.width, self.size.height);

    if(correctedMonkeyPos.x <= lowerLeft.x) {correctedMonkeyPos.x = lowerLeft.x;}
    if(correctedMonkeyPos.x >= upperRight.x) {correctedMonkeyPos.x = upperRight.x;}
    if(correctedMonkeyPos.y <= lowerLeft.y) {correctedMonkeyPos.y = lowerLeft.y;}
    if(correctedMonkeyPos.y >= upperRight.y) {correctedMonkeyPos.y = upperRight.y;}

    _monkey.position = correctedMonkeyPos;

}

【讨论】:

  • 我到底什么时候运行 confineToBounds?应该放在update方法还是initialize方法中?
  • 声明 [self confineToBounds]; in - (void)didEndContact:(SKPhysicsContact *)contact 然后将字符限制在边界内的方法将起作用。至少那是我在那种情况下所做的。
  • 这不起作用,但如果我声明 [self confineToBounds];在更新方法中,玩家可以在重力和方法相互对抗时悬停。有没有办法在角色落地时关闭重力,然后在玩家离开地面后重新开启重力?
  • @Comrod33,要关闭重力,请将您的方法中的值 -2 更改为 0 self.physicsWorld.gravity = CGVectorMake(0, 0);但是要根据角色联系人打开和关闭它,您必须在 - (void)didEndContact:(SKPhysicsContact *)contact 中编写该场景。我相信一个 if 语句,如果字符与某某向量接触,则重力关闭,反之亦然。并且使用的方法是 sprite.physicsBody.affectedByGravity = NO;和 sprite.physicsBody.affectedByGravity = YES;
猜你喜欢
  • 1970-01-01
  • 2017-12-10
  • 1970-01-01
  • 2021-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
相关资源
最近更新 更多