模拟零重力式球员移动

时间:2016-06-14 08:51:23

标签: ios swift sprite-kit

我在设置移动播放器节点的正确方法时遇到了一些麻烦 我想要的。

这是我第一次涉足Spritekit,我已经掌握了基础知识(我有一个静态背景,添加了播放器节点并且有一个带边界检查的可播放边界矩形)

我添加播放器移动的方式是跟踪开始触摸位置和 将其存储在场景类范围的变量(称为beginTouchPosition)中,并存储当前触摸位置(称为currentTouchPosition)。 我还跟踪球员精灵节点位置(currentPlayerPosition)

我所做的就是onTouchesBegan我更新了" beginTouchPosition'然后在onTouchesMoved中我更新' currentTouchPosition'这样我就可以通过获取相对于" beginTouchPosition'的方向来了解用户想要移动船只的方向。当他/她移动他们的手指时。还有' currentTouchPosition'的距离来自' beginningTouchPosition'确定船舶移动的速度。

我通过使用以上几点创建CGVector并使用SKAction.MoveBy调用来移动更新中的播放器。

我是这样做的,因为我希望用户能够触摸屏幕上的任何位置以便能够控制移动。

我希望玩家如何移动。我宁愿让船只通过在某个方向上施加设定的加速度来设定一定的速度。因此,当手指移动时,玩家将在1/2秒的时间内从零加速到1,然后继续朝那个方向移动,直到手指再次移动或抬起为止。

如果手指被抬起,那么船应继续沿最后一个方向移动,但要开始减速直到速度回到零。

我基本上试图模拟一个物体在零重力下如何移动,具有明显的非现实减速特征。

我找到了教程,展示如何将物体移向手指触摸,但这不是我想要的,因为我正在尝试制作一个侧面滚动太空射击游戏,玩家可以在其中的任何地方可玩区域,而不是简单的上下。类似于旧的复古游戏' Nemesis',请参见下面的屏幕:

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 个答案:

答案 0 :(得分:2)

基本上我们需要一个零重力场景和一个玩家,其中触摸会导致力型物理动作。这不是moveBy类型的数字动作,而是通过这样的简单移动屏幕上的角色。

我继续测试代码,尝试为您提供类似于您描述的内容。我改变了你的一些代码......让它与我自己的设置一起工作,因为你没有提供你的GameViewController代码,所以问你是否有任何问题。

我在最后提供了一些评论,上面写着//带有#旁边的重要代码。

以下详细说明了为什么要使用每件&#34;重要代码

  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。

  4. 执行此操作

    这会根据与滑动相关的力量进行设置。 但是,无论滑动有多难,您都可能需要恒定的速度。你可以通过规范化矢量来做到这一点..请看这里以某种方式提出这个问题以及它如何适用于此处 (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)
        }
    
    1. 如果你想让玩家减速,我们必须添加一个功能来将玩家的速度设置为0.我已经做到这一点,所以这发生在最初调用该函数0.5秒后..否则&#34;通过重力漂浮&#34;效果并没有被注意到,因为运动会以touchesEnded()结束。
    2. 在下面的序列中的暂停操作之前,您可以尝试使用其他方式来减速,就像最初使用的负面力量一样。

      还有很多其他方法可以让它更像真正的减速......就像第二个序列在设定的时间间隔内从速度减去-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)
      
           }
      
      1. 我们改变touchesEnded()来调用stopMoving()..但是,没有这个就试试看它没有那个&#34;减速&#34;。

        override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
              player.removeAllActions()
        
               player.stopMoving()
         }
        
      2. 其他说明。

        目前,界限只能用我创建的代码左右抓住玩家...我不确定你的设置是否会发生这种情况。但是,作为另一个要弄清楚的问题,我没有进一步研究它。

        这是我使用的代码......我提供了它,因为我为了测试而进行了一些其他的小改动。除了放置新的重要代码之外,我不会担心任何其他事情。

        <强> 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)
        
        }
        
        }