gpt4 book ai didi

ios - 模拟零重力风格的球员运动

转载 作者:搜寻专家 更新时间:2023-11-01 07:22:49 27 4
gpt4 key购买 nike

我在建模以正确方式移动播放器节点时遇到了一些麻烦我想要的。

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

我添加玩家移动的方式是跟踪开始触摸位置和将其存储在场景类范围变量(称为 beginningTouchPosition)中,并存储当前触摸位置(称为 currentTouchPosition)。我还跟踪玩家 Sprite 节点位置 (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)
}

}

最佳答案

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

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

我在代码末尾提供了注释,上面写着//IMPORTANT CODE,旁边有一个#。

这里详细说明了为什么要使用每个“重要代码”

  1. 我们需要物理学来完成您所描述的,因此首先要确保玩家类有一个物理体。 body 将是动态的并受重力影响(零重力),但是为了游戏玩法,您可能想要稍微调整一下重力。

    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)
}
  1. 如果你想让玩家减速,我们必须添加一个函数来将玩家的速度设置为 0。我已经做到了,所以这会在函数最初被调用后 0.5 秒发生。否则“通过重力漂浮”效果并没有真正引起注意,因为移动将以 touchesEnded() 结束。

在下面的暂停 Action 之前,您可以尝试其他方法来减速,例如最初使用的负力。

还有许多其他方法可以使它更像真正的减速……比如第二个序列,在我们将速度硬编码为 0 之前,在设定的时间间隔从速度中减去 -1 直到它达到 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() .. 但是,在没有这个的情况下尝试它可以看到它没有那种“减速”。

    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)

}

}

关于ios - 模拟零重力风格的球员运动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37807227/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com