2016-06-14 32 views
1

我有點麻煩建模移動我的球員節點的方式 我想要的正確方法。模擬零重力風格的球員運動

這是我首次涉足Spritekit和我有基礎和運行(我有一個靜態的背景,加入播放器節點,並與邊界檢查可玩的邊界矩形)

我已經加入我的球員的方式運動是跟蹤開始的觸摸位置,並將其存儲在場景類scoped變量(稱爲beginningTouchPosition)中,並存儲當前的觸摸位置(稱爲currentTouchPosition)。 我還跟蹤玩家精靈節點位置(currentPlayerPosition)

我要做的就是onTouchesBegan我更新「beginningTouchPosition」,然後內onTouchesMoved我更新「currentTouchPosition」,這樣我可以知道用戶想要他的船移動的方向通過在他/她移動手指時獲得相對於'beginningTouchPosition'的方向。 「currentTouchPosition」距'beginningTouchPosition'的距離決定了船舶移動的速度。

我通過使用上述點創建CGVector並使用SKAction.MoveBy調用來移動更新中的播放器。

我這樣做是因爲我希望用戶能夠觸摸屏幕上的任何位置來控制移動。

我想讓玩家移動。我寧願讓船在特定的方向上以設定的加速度施加一定的設定速度。因此,當手指移動時,玩家將在1/2秒的空間內從零加速到1,並沿該方向繼續,直到手指再次移動或擡起。

如果手指擡起,則船舶應該繼續沿最後方向移動,但開始減速直到速度回到零。

我基本上是試圖模擬一個物體如何在零重力下移動,具有明顯的減速非現實功能。

我發現了一些教程,展示瞭如何將對象移動到手指觸摸,但是這並不是我想要的,因爲我正在嘗試製作一款側滾動空間射擊遊戲,玩家可以在可玩的任何地方區域,而不是簡單地上下。類似於舊的復古遊戲「復仇女神」,見下圖:

Nemesis

我們已經附上我的播放器類的代碼和場景代碼的更好的可視化如何我目前做這一切。

任何指針文學如何在特定的方向加速申請速度會有所幫助:)

場景文件 - Level_1.swift

import SpriteKit 

// Global 

/* 
Level_1 set up and control 
*/ 
class Level_1: SKScene { 
    // Instance variables 
    var lastUpdateTime:NSTimeInterval = 0 
    var dt:NSTimeInterval = 0 
    var player = Player() // Sub classed SKSpriteNode for all player related stuff 

    var currentTouchPosition: CGPoint! 
    var beginningTouchPosition:CGPoint! 
    var currentPlayerPosition: CGPoint! 

    let playableRectArea:CGRect 

    override init(size: CGSize) { 
     // Constant - Max aspect ratio supported 
     let maxAspectRatio:CGFloat = 16.0/9.0 

     // Calculate playable height 
     let playableHeight = size.width/maxAspectRatio 

     // Determine margin on top and bottom by subtracting playable height 
     // from scene height and then divide by 2 
     let playableMargin = (size.height-playableHeight)/2.0 

     // Calculate the actual playable area rectangle 
     playableRectArea = CGRect(x: 0, y: playableMargin, 
           width: size.width, 
           height: playableHeight) 
     super.init(size: size) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    override func didMoveToView(view: SKView) { 
     /* Setup your scene here */ 

     currentTouchPosition = CGPointZero 
     beginningTouchPosition = CGPointZero 

     let background = SKSpriteNode(imageNamed: "background1") 
     background.position = CGPoint(x: size.width/2, y: size.height/2) 
     background.zPosition = -1 

     self.addChild(background) 

     currentPlayerPosition = CGPoint(x: 100, y: size.height/2) 

     player.position = currentPlayerPosition 

     self.addChild(player) 

    } 

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { 
     for touch: AnyObject in touches { 
      currentTouchPosition = touch.locationInNode(self) 
     } 

     let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x) 
     let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y) 

     player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt) 
    } 

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { 
     player.removeAllActions() 
    } 

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

     for touch: AnyObject in touches { 
      beginningTouchPosition = touch.locationInNode(self) 
      currentTouchPosition = beginningTouchPosition 
     } 

    } 

    override func update(currentTime: CFTimeInterval) { 
     /* Called before each frame is rendered */ 
     currentPlayerPosition = player.position 

     if lastUpdateTime > 0 { 
      dt = currentTime - lastUpdateTime 
     }else{ 
      dt = 0 
     } 
     lastUpdateTime = currentTime 

     player.boundsCheckPlayer(playableRectArea) 
    } 
} 

球員節點 - Player.swift

import Foundation 
import SpriteKit 

struct PhysicsCategory { 
    static let None   : UInt32 = 0 
    static let All   : UInt32 = UInt32.max 
    static let Player  : UInt32 = 0b1  // 1 
    static let Enemy  : UInt32 = 0b10  // 2 
} 

class Player: SKSpriteNode{ 

    init(){ 

     // Initialize the player object 
     let texture = SKTexture(imageNamed: "ship1") 

     super.init(texture: texture, color: UIColor.clearColor(), size: texture.size()) 

     self.xScale = 2 
     self.yScale = 2 
     self.anchorPoint = CGPoint(x: 0.5, y: 0.5) 
     self.zPosition = 1 

     // Player physics 
     self.physicsBody?.allowsRotation = false 
     self.physicsBody?.dynamic = false 
     self.physicsBody?.categoryBitMask = PhysicsCategory.Player 
    } 

    required init(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 

    // Check if the player sprite is within the playable area bounds 
    func boundsCheckPlayer(playableArea: CGRect){ 
     let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea)) 
     let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea)) 

     if(self.position.x <= bottomLeft.x){ 
      self.position.x = bottomLeft.x 
      // velocity.x = -velocity.x 
     } 

     if(self.position.x >= topRight.x){ 
      self.position.x = topRight.x 
      // velocity.x = -velocity.x 
     } 

     if(self.position.y <= bottomLeft.y){ 
      self.position.y = bottomLeft.y 
      // velocity.y = -velocity.y 
     } 

     if(self.position.y >= topRight.y){ 
      self.position.y = topRight.y 
      // velocity.y = -velocity.y 
     } 
    } 

    /* 
     Move the player in a certain direction by a specific amount 
    */ 
    func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){ 
     let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue) 
     let movePlayerAction = SKAction.moveBy(moveActionVector, duration: 1/duration) 
     self.runAction(movePlayerAction) 
    } 

} 

回答

1

基本上我們需要一個具有零重力的場景和一個玩家在哪裏觸碰力量類型的物理動作。這不是moveBy類型的數字動作,通過這樣那樣的簡單移動屏幕上的字符。

我繼續前進並測試了代碼,嘗試讓您看到類似於您所描述的內容。我改變了一些你的代碼......以便讓它與我自己的設置一起工作,因爲你沒有提供你的GameViewController代碼,所以詢問你是否有任何問題。

我在最後提供了帶有#旁邊帶有重要代碼的註釋的代碼。

這裏的細節,爲什麼你使用的每一件「重要的代碼

  1. 我們需要物理學來完成你描述所以首先保證玩家全班將有一個物理的身體,身體將是動態的和受影響通過重力(零重力),但您可能想稍微重力遊戲的緣故亂動。

    let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size()) 
    
    self.physicsBody = body 
    self.physicsBody?.allowsRotation = false 
    
    self.physicsBody?.dynamic = true 
    self.physicsBody?.affectedByGravity = true 
    
  2. 既然你想體驗零重力,我們需要改變我們的物理世界的重力在我們的場景

    scene?.physicsWorld.gravity = CGVectorMake(0, 0) 
    
  3. 接下來我們改變你的movePlayerBy()來使用力而不是簡單的數字運動。我們用SKAction.applyForce做到這一點。

這給你一個基於與滑動相關的力的設置。 但是,無論滑動有多困難,您都可能需要恆定速度。你可以做到這一點通過歸一矢量..在這裏看到莫名其妙誰問這個問題,以及它如何可以在這裏申請 (http://www.scriptscoop2.com/t/adc37b4f2ea8/swift-giving-a-physicsbody-a-constant-force.html

 func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){ 

    print("move player") 
    let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue) 
    let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration) 
    self.runAction(movePlayerAction) 
    } 
  • 如果你想讓玩家我們必須添加一個函數來將播放器的速度設置爲0.我已經做了這樣的事情,在函數初始調用0.5秒後發生這種情況。否則,「通過重力漂浮」效果並不真正被注意到,因爲運動會以touchesEnded()結束。
  • 您可以嘗試其他方法,例如在下面的序列中的暫停動作之前,像最初使用的反作用力那樣去加速。

    還有很多其他方法可以使它更加真實的減速......就像第二個序列,它在設定的時間間隔內從速度減去-1,直到它達到0,然後我們將速度硬編碼爲0. 但是,從遊戲角度來看,這取決於你。

    所以這應該足以給你一個想法。

    func stopMoving() { 
          let delayTime: NSTimeInterval = 0.5 // 0.5 second pause 
    
          let stopAction: SKAction = SKAction.runBlock{ 
           self.physicsBody?.velocity = CGVectorMake(0, 0) 
          } 
    
          let pause: SKAction = SKAction.waitForDuration(delayTime) 
    
          let stopSequence: SKAction = SKAction.sequence([pause,stopAction]) 
    
          self.runAction(stopSequence) 
    
        } 
    
  • 我們改變touchesEnded()調用stopMoving()..但是,嘗試沒有這個,看看它沒有這種 「減速」。

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { 
         player.removeAllActions() 
    
         player.stopMoving() 
    } 
    
  • 其它注意事項。

    目前界限只能用我創建的代碼捕捉左右球員......我不確定這是否會在您的設置中發生。但是,由於這是另一個需要弄清楚的問題,我沒有進一步研究。

    這是我使用的代碼...我提供了它,因爲我爲了測試而做了一些其他小改動。除了放置新的重要代碼片段之外,我不擔心其他任何事情。

    GameScene.Swift

    import SpriteKit 
    
    // Global 
    
    /* 
    Level_1 set up and control 
    */ 
    
    
    class GameScene: SKScene { 
    override func didMoveToView(view: SKView) { 
        /* Setup your scene here */ 
    
    } 
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 
        /* Called when a touch begins */ 
    
    
    
    } 
    
    override func update(currentTime: CFTimeInterval) { 
        /* Called before each frame is rendered */ 
    } 
    } 
    
    
    
    class Level_1: GameScene { 
    // Instance variables 
    var lastUpdateTime:NSTimeInterval = 0 
    var dt:NSTimeInterval = 0 
    var player = Player() // Sub classed SKSpriteNode for all player related stuff 
    
    var currentTouchPosition: CGPoint = CGPointZero 
    var beginningTouchPosition:CGPoint = CGPointZero 
    var currentPlayerPosition: CGPoint = CGPointZero 
    
    var playableRectArea:CGRect = CGRectZero 
    
    
    override func didMoveToView(view: SKView) { 
        /* Setup your scene here */ 
        // IMPORTANT CODE 2 // 
    
        scene?.physicsWorld.gravity = CGVectorMake(0, 0) 
    
        // Constant - Max aspect ratio supported 
        let maxAspectRatio:CGFloat = 16.0/9.0 
    
        // Calculate playable height 
        let playableHeight = size.width/maxAspectRatio 
    
        // Determine margin on top and bottom by subtracting playable height 
        // from scene height and then divide by 2 
        let playableMargin = (size.height-playableHeight)/2.0 
    
    
        // Calculate the actual playable area rectangle 
        playableRectArea = CGRect(x: 0, y: playableMargin, 
               width: size.width, 
               height: playableHeight) 
    
        currentTouchPosition = CGPointZero 
        beginningTouchPosition = CGPointZero 
    
        let background = SKSpriteNode(imageNamed: "Level1_Background") 
        background.position = CGPoint(x: size.width/2, y: size.height/2) 
        background.zPosition = -1 
    
        self.addChild(background) 
    
        // CHANGED TO Put my own texture visible on the screen 
    
        currentPlayerPosition = CGPoint(x: size.width/2, y: size.height/2) 
    
        player.position = currentPlayerPosition 
    
        self.addChild(player) 
    
    } 
    
    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { 
        for touch: AnyObject in touches { 
         currentTouchPosition = touch.locationInNode(self) 
        } 
    
        let dxVectorValue = (-1) * (beginningTouchPosition.x - currentTouchPosition.x) 
        let dyVectorValue = (-1) * (beginningTouchPosition.y - currentTouchPosition.y) 
    
    
        player.movePlayerBy(dxVectorValue, dyVectorValue: dyVectorValue, duration: dt) 
    
    } 
    
    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { 
        player.removeAllActions() 
    
        // IMPORTANT CODE 5 // 
        player.stopMoving() 
    } 
    
    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { 
        /* Called when a touch begins */ 
        print("touch") 
        for touch: AnyObject in touches { 
         beginningTouchPosition = touch.locationInNode(self) 
         currentTouchPosition = beginningTouchPosition 
        } 
    
    } 
    
    override func update(currentTime: CFTimeInterval) { 
        /* Called before each frame is rendered */ 
        currentPlayerPosition = player.position 
    
        if lastUpdateTime > 0 { 
         dt = currentTime - lastUpdateTime 
        }else{ 
         dt = 0 
        } 
        lastUpdateTime = currentTime 
    
        player.boundsCheckPlayer(playableRectArea) 
    } 
    } 
    

    GameViewController.swift

    import UIKit 
    import SpriteKit 
    
    class GameViewController: UIViewController { 
    
    override func viewDidLoad() { 
        super.viewDidLoad() 
    
        if let scene = GameScene(fileNamed:"GameScene") { 
         // Configure the view. 
         let skView = self.view as! SKView 
         skView.showsFPS = true 
         skView.showsNodeCount = true 
    
         /* Sprite Kit applies additional optimizations to improve rendering performance */ 
         skView.ignoresSiblingOrder = true 
    
         /* Set the scale mode to scale to fit the window */ 
         scene.scaleMode = .AspectFill 
    
         skView.presentScene(scene) 
        } 
    } 
    
    override func shouldAutorotate() -> Bool { 
        return true 
    } 
    
    override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask { 
        if UIDevice.currentDevice().userInterfaceIdiom == .Phone { 
         return .AllButUpsideDown 
        } else { 
         return .All 
        } 
    } 
    
    override func didReceiveMemoryWarning() { 
        super.didReceiveMemoryWarning() 
        // Release any cached data, images, etc that aren't in use. 
    } 
    
    override func prefersStatusBarHidden() -> Bool { 
        return true 
    } 
    } 
    

    Player.swift

    import Foundation 
    import SpriteKit 
    
    struct PhysicsCategory { 
        static let None   : UInt32 = 0 
        static let All   : UInt32 = UInt32.max 
        static let Player  : UInt32 = 0b1  // 1 
        static let Enemy  : UInt32 = 0b10  // 2 
    } 
    
    class Player: SKSpriteNode{ 
    
    init(){ 
    
        // Initialize the player object 
        let texture = SKTexture(imageNamed: "Player1") 
    
        super.init(texture: texture, color: UIColor.clearColor(), size: texture.size()) 
    
        self.xScale = 2 
        self.yScale = 2 
        self.anchorPoint = CGPoint(x: 0.5, y: 0.5) 
        self.zPosition = 1 
    
        // Player physics 
    
        // IMPORTANT CODE 1 // 
    
        let body:SKPhysicsBody = SKPhysicsBody(texture: texture, alphaThreshold: 0, size: texture.size()) 
    
        self.physicsBody = body 
        self.physicsBody?.allowsRotation = false 
    
    
    
        self.physicsBody?.dynamic = true 
        self.physicsBody?.affectedByGravity = true 
    
        self.physicsBody?.categoryBitMask = PhysicsCategory.Player 
    } 
    
    
    
    required init?(coder aDecoder: NSCoder) { 
    
    
        super.init(coder: aDecoder) 
    } 
    
    
    // Check if the player sprite is within the playable area bounds 
    func boundsCheckPlayer(playableArea: CGRect){ 
        let bottomLeft = CGPoint(x: 0, y: CGRectGetMinY(playableArea)) 
        let topRight = CGPoint(x: playableArea.size.width, y: CGRectGetMaxY(playableArea)) 
    
        if(self.position.x <= bottomLeft.x){ 
         self.position.x = bottomLeft.x 
         // velocity.x = -velocity.x 
        } 
    
        if(self.position.x >= topRight.x){ 
         self.position.x = topRight.x 
         // velocity.x = -velocity.x 
        } 
    
        if(self.position.y <= bottomLeft.y){ 
         self.position.y = bottomLeft.y 
         // velocity.y = -velocity.y 
        } 
    
        if(self.position.y >= topRight.y){ 
         self.position.y = topRight.y 
         // velocity.y = -velocity.y 
        } 
    } 
    
    /* 
    Move the player in a certain direction by a specific amount 
    */ 
    
    
    // IMPORTANT CODE 3 // 
    
    func movePlayerBy(dxVectorValue: CGFloat, dyVectorValue: CGFloat, duration: NSTimeInterval)->(){ 
    
        print("move player") 
        let moveActionVector = CGVectorMake(dxVectorValue, dyVectorValue) 
        let movePlayerAction = SKAction.applyForce(moveActionVector, duration: 1/duration) 
        self.runAction(movePlayerAction) 
    } 
    
    // IMPORTANT CODE 4 // 
    
    func stopMoving() { 
        let delayTime: NSTimeInterval = 0.5 // 0.5 second pause 
    
        let stopAction: SKAction = SKAction.runBlock{ 
         self.physicsBody?.velocity = CGVectorMake(0, 0) 
        } 
    
        let pause: SKAction = SKAction.waitForDuration(delayTime) 
    
        let stopSequence: SKAction = SKAction.sequence([pause,stopAction]) 
    
        self.runAction(stopSequence) 
    
    } 
    
    } 
    
    +0

    科瑞嗨。完全沒有期待你的深入如何,我很欣賞這個immensly,因爲它確實解釋了我很多,並嘗試第一次嘗試。我還沒有實現速度部分,但今天晚些時候將會完成。非常感謝,非常感謝。 –