2016-09-10 42 views
3

我在Swift中製作了一個Doodle Jump克隆遊戲,問題在於當玩家跳躍時,它在平臺底部碰到它的頭部並且不通過。我如何讓玩家通過平臺並跳過它們?我在這裏有我的代碼:Sprite Kit中的單向平臺衝突

import SpriteKit 

class GameScene: SKScene, SKPhysicsContactDelegate { 

    var hero = SKSpriteNode(imageNamed: "hero"); 
    var stepSizeTest = SKSpriteNode(imageNamed: "step"); 
    var start = false; 
    var jumpSpeed = CGFloat(0); 
    var gravity = CGFloat(0); 
    var stepPositionDivision:CGFloat = 0 //allows the step to spawn on specific places on y axes 
    var setpPositionHeightIncrease:CGFloat = 0; 

    var positionX:CGFloat = 0; 
    var timeInterval:NSTimeInterval = 0; 
    var CurTime:NSTimeInterval = 0; 
    var TimeWhenThePreviousStepSpawned:NSTimeInterval = 0; 
    var standart:Bool = false; 
    var move:Bool = false; 
    var oneJumpOnly:Bool = false; 
    var cracked:Bool = false; 
    var jumped:Bool = false; 

    struct PhysicsCategory{ 
     static var None: UInt32 = 0; 
     static var step: UInt32 = 0b1; 
     static var hero: UInt32 = 0b10; 
     static var All: UInt32 = UInt32.max; 
    } 

    func didBeginContact(contact: SKPhysicsContact) { 
     var contactBody1: SKPhysicsBody; 
     var contactBody2: SKPhysicsBody; 
     if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask){ 
      contactBody1 = contact.bodyA; 
      contactBody2 = contact.bodyB; 
     } 
     else{ 
      contactBody1 = contact.bodyB; 
      contactBody2 = contact.bodyA; 
     } 

     if (contactBody1.categoryBitMask == PhysicsCategory.step && contactBody2.categoryBitMask == PhysicsCategory.hero){ 
      jumpSpeed = CGFloat(self.frame.size.height*0.01320422535); 

     } 
//  if (contactBody1.categoryBitMask == PhysicsCategory.ground && contactBody2.categoryBitMask == PhysicsCategory.hero){ 
// 
//  } 
    } 

    override func didMoveToView(view: SKView) { 
     /* Setup your scene here */ 
     stepPositionDivision = self.frame.size.height/23; 

     jumpSpeed = CGFloat(self.frame.size.height*0.01320422535); 
     gravity = CGFloat(self.frame.size.height*(-0.0003521126761)); 

     self.physicsWorld.contactDelegate = self; 

     self.physicsWorld.gravity = CGVectorMake(0,0); 

     let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame); 
     sceneBody.friction = 0; 
     self.physicsBody = sceneBody; 


     self.hero.anchorPoint = CGPoint(x: 0.5, y: 0.5); 
     self.hero.position = CGPoint(x:self.frame.size.width/2, y:self.hero.size.height/2); 
     self.hero.physicsBody = SKPhysicsBody(rectangleOfSize: self.hero.size) 
     self.hero.physicsBody?.restitution = 1; 
     self.hero.physicsBody?.affectedByGravity = false; 
     self.hero.physicsBody?.friction = 0; 
     self.hero.physicsBody?.categoryBitMask = PhysicsCategory.hero; 
     self.hero.physicsBody?.collisionBitMask = PhysicsCategory.step; 
     self.hero.physicsBody?.contactTestBitMask = PhysicsCategory.step; 

     self.addChild(self.hero); 
    } 

    func ranPositionX() -> CGFloat{ 
     let stepSise = UInt32(self.stepSizeTest.size.width) 
     let posX = arc4random_uniform(UInt32(self.frame.size.width) - stepSise) + stepSise 
     return CGFloat(posX) 
    } 

    func stepSpawn(){ 
     if (setpPositionHeightIncrease < self.frame.size.height){ 
      let step = SKSpriteNode(imageNamed: "step"); 
      let posX = ranPositionX() 

      step.anchorPoint = CGPoint(x: 0.5, y: 0.5); 
      step.position = CGPoint(x:posX, y:setpPositionHeightIncrease); 
      step.physicsBody = SKPhysicsBody(rectangleOfSize: step.size); 
      step.physicsBody?.affectedByGravity = false; 
      step.physicsBody?.dynamic = true; 
      step.physicsBody?.friction = 0; 
      step.physicsBody?.categoryBitMask = PhysicsCategory.step; 
      step.physicsBody?.collisionBitMask = PhysicsCategory.None; 
      step.physicsBody?.contactTestBitMask = PhysicsCategory.hero; 

      self.addChild(step); 
      setpPositionHeightIncrease += stepPositionDivision 
     } 
    } 

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 
     /* Called when a touch begins */ 
     for touch in touches { 

     } 
    } 

    override func update(currentTime: CFTimeInterval) { 
     /* Called before each frame is rendered */ 

     self.hero.position.y += jumpSpeed 
     jumpSpeed += gravity 

     if (start == false){ 
      //self.hero.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 40)) 
      start = true; 
     } 
     stepSpawn() 
    } 
} 
+0

我認爲你將不得不在所有節點枚舉了現場,如果他們在玩家上方,切換其collisionBitMask以使它們不會相互碰撞,並且如果它們在玩家下面,則切換它以便它們如此。 –

回答

5

一種方式做,這是打開/關閉相應的碰撞位(S),如果英雄正在下降或跳躍。如果dy的速度爲dy屬性小於零,並且跳躍(或彈起),則該英雄正在下降,如果dy大於零。下面是如何做到這一點的例子:

// Add this to the update method 
if let body = hero.physicsBody { 
    let dy = body.velocity.dy 
    if dy > 0 { 
     // Prevent collisions if the hero is jumping 
     body.collisionBitMask &= ~PhysicsCategory.step 
    } 
    else { 
     // Allow collisions if the hero is falling 
     body.collisionBitMask |= PhysicsCategory.step 
    } 
} 
+3

這是一個體面的方法,如果有幾個項目,我已經使用過自己。但有一點可以肯定:如果英雄在跌倒的同時側身移動,他的身體可能會與平臺的碰撞。爲了解決這個問題,我將上面的方法與最小高度的物理體結合起來:只有平臺的頂部邊緣需要一個物體,而不是整個可見的形狀,只有英雄精靈的底部邊緣。 (如果英雄的其他部分需要與敵人或其他任何物體碰撞,請使用不與地面相撞的單獨物體。) – rickster

+0

&=〜PhysicsCategory.step,| =這些字符會讓我困惑。你能詳細說明他們是什麼意思,因爲我5歲? – GeorGios

+2

在高層次上,這些語句設置並取消設置特定位。在這種情況下,他們會在英雄掉落時打開對應於「step」節點的類別位,並在跳躍時關閉。 'x&=〜b010'只是'x = x&〜b010'的簡寫,'x | = b010'是'x = x | b010',其中&,〜和|分別是_and_,_not_和_or_位運算符。請參閱[位運算符](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/AdvancedOperators.html#//apple_ref/doc/uid/TP40014097-CH27-ID29)這些運營商。 – 0x141E

1

去了解這一點,最好的方法是使用2類,姑且稱之爲CollisionPlatform,並NoCollisionPlatform

讓我們把你的SKSpriteNode Hero

集所有平臺NoCollisionPlatform

平臺SKPhysicsBody應該是一條線,而不是一個框。創建這些物體時最好使用邊緣循環,這會減少物理世界的壓力。

Hero應該只有在collisionBitMaskCollisionPlatform,但兩者CollisionPlatformNoCollisionPlatformcontactTestBitMask

您的Hero也應該知道它以前的位置。

在您didBeginContact方法,當你與NoCollisionPlatform碰撞,你檢查Hero底部等於或低於平臺和Hero以前的位置在平臺之上。如果這是真的,那麼你的平臺上,改變平臺的categoryBitMask到CollisionPlatform

在您didEndContact method設置平臺的categoryBitMaskNoCollisionPlatform

現在,小心你怎麼做,因爲如果您將移至位於同一x軸上的下一個平臺,但由於舍入錯誤,您可能會遇到問題。如果你不處理1/2像素位置,我建議四捨五入或鑄造Int

爲了達到強制跳下,只要記住最後平臺的Hero是感人的,並在命令,設置categoryBitMask回到NoCollisionPlatform

在這一點上,我會建議子類別Hero,並保留一個指向他最後接觸到這個子類內的平臺以及他以前的位置。當他不再接觸它時,一定要將它清除爲零。

另請注意,這樣做意味着您不需要將任何物理力施加到您的Hero以外的重力,因此SKAction將與此同時攜手。如果你不施加重力,那麼當你打到平臺時,我不知道會發生什麼,因爲Hero的速度將爲0,誰知道會發生什麼樣的推動反應(如果有的話)。隨着向左和向右(甚至向上),這將發生雖然,所以你可以做的是應用少量的速度到你的精靈,讓系統知道你正在移動的方向,但是足夠弱,它不會移動一個像素。

做所有這些也應該處理側面檢測,因爲您的平臺只有1個像素需要擔心,您的Hero高於上一幀的機率,並且在下一幀以這種方式你期望你的Hero不會落在平臺上是非常渺茫的(他需要在1幀的像素長度上以1度的角度下降來實現這一點,我說讓這個人在這種情況下降落。

如果您想要獲得更高級的功能,請在面具上留出一點空間以解決此問題,並在需要時進行設置,這樣您就可以擁有多個類別,只有1個保留用於衝突測試,而不是必須進行多項輸入,我喜歡保留位30爲了處理這種情況,我保留位31以檢查精靈是活的還是死的。

請注意,如果你想有多個Heroes這隻會有1 Hero

工作,那麼你必須工作的另一種方式。

取而代之,您是在單個HerocollisionBitMask上進行交換。這種方法的問題在於,如果您創建了一個平臺階梯,您最終可能會碰撞到第二個平臺的一側。

你可以做的另一件事,是用我的方法前進,並保留categoryBitMask的某些位爲每個Hero