问题描述
我正在制作我自己的 Mario Bros. 第一级复制品,以学习如何使用我自己的资产在 iOS 上制作游戏。到目前为止,我已经成功地为控件(左、右、上)放置了三个 SKSpriteNodes
,我的玩家节点可以在这三个方向上移动,但是如果我让我的玩家在向任一方向奔跑时跳跃,如当我将手指从“左控制”上移开时,玩家将失去所有动量并直接落在那里(就像撞墙一样)而不是跟随抛物线。
我不知道在这种情况下可能需要什么才能成为 MRE,所以这基本上是可以重现问题的全部内容,以及我为使其工作所做的一些尝试。
基本上我尝试施加脉冲/设置速度/直接改变位置,最后一个效果更好(但它仍然使玩家节点在我从方向控件上移开手指后立即下降).
此处是 a video 演示该问题。
这是GameScene
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var player = SKSpriteNode()
private var bg = SKSpriteNode()
private var leftArrow = SKSpriteNode()
private var rightArrow = SKSpriteNode()
private var upArrow = SKSpriteNode()
private var floor = [SKSpriteNode]()
private var isLeftTouched = false
private var isRightTouched = false
private var selectednodes: [UITouch:SKSpriteNode] = [:]
override func didMove(to view: SKView) {
addBackground()
addFloor()
addplayer(xOffset: 0,yOffset: 0)
addControls()
}
override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) {
//player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 50))
let touch = touches.first! as UITouch
let positionInScene = touch.location(in: self)
let touchednode = self.atPoint(positionInScene)
for touch in touches {
let location = touch.location(in:self)
if let node = self.atPoint(location) as? SKSpriteNode {
if let name = touchednode.name {
selectednodes[touch] = node
if name == "up" {
player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 60))
} else if name == "left" {
isLeftTouched = true
} else if name == "right" {
isRightTouched = true
}
}
}
}
if let name = touchednode.name {
if name == "up" {
player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 60))
} else if name == "left" {
isLeftTouched = true
} else if name == "right" {
isRightTouched = true
}
}
}
override func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?) {
//let direction = ((touches.first?.location(in: self).x)! < (touches.first?.prevIoUsLocation(in: self).x)!) ? Direction.LEFT : Direction.RIGHT
//runIn(direction: direction)
}
override func touchesEnded(_ touches: Set<UITouch>,with event: UIEvent?) {
for touch in touches {
if selectednodes[touch] != nil {
if selectednodes[touch]?.name == "left" {
isLeftTouched = false
} else if selectednodes[touch]?.name == "right" {
isRightTouched = false
}
selectednodes[touch] = nil
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if isLeftTouched {
runIn(direction: Direction.LEFT)
}
if isRightTouched {
runIn(direction: Direction.RIGHT)
}
}
// MARK: INteraCTION METHODS
func runIn(direction: Direction) {
let x = player.position.x + (direction == Direction.RIGHT ? 5 : -5)
let position = CGPoint(x: x,y: player.position.y)
if position.x >= self.frame.maxX || position.x <= self.frame.minX {
return
}
player.position = position
//player.physicsBody?.veLocity = CGVector(dx: direction == Direction.RIGHT ? 50 : -50,dy: 0)
//player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 5 : -5,dy: 0))
}
// MARK: UI METHODS
func addBackground() {
let bgTexture = SKTexture(imageNamed: "bg")
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX,y: self.frame.midY)
bg.size.height = self.frame.height
bg.zPosition = -10
self.addChild(bg)
}
func addplayer(xOffset: CGFloat,yOffset: CGFloat) {
let playerTexture = SKTexture(imageNamed: "player")
player = SKSpriteNode(texture: playerTexture)
//let xPos = calculateXOffset(for: player,from: self.frame.midX,offset: xOffset)
//let yPos = calculateXOffset(for: player,from: self.frame.midY,offset: yOffset)
player.position = CGPoint(x: self.frame.midX,y: self.frame.midY)
player.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: player.frame.width,height: player.frame.height))
player.physicsBody?.isDynamic = true
self.addChild(player)
}
func addFloor() {
let blockTexture = SKTexture(imageNamed: "block")
for i in 0 ... (Int) (self.frame.width / blockTexture.size().width) {
let blockNode = SKSpriteNode(texture: blockTexture)
blockNode.position = CGPoint(x: self.frame.minX + (blockNode.frame.width * CGFloat(i)),y: self.frame.minY + blockNode.frame.height / 2)
blockNode.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: blockNode.frame.width,height: blockNode.frame.height))
blockNode.physicsBody?.isDynamic = false
floor.append(blockNode)
self.addChild(blockNode)
}
}
func addControls() {
addLeftArrow()
addRightArrow()
addUpArrow()
}
func addLeftArrow() {
let leftTexture = SKTexture(imageNamed: "left")
leftArrow = SKSpriteNode(texture: leftTexture)
leftArrow.name = "left"
leftArrow.position = CGPoint(x: calculateXOffset(for: leftArrow,from: self.frame.minX,offset: 50),y: calculateXOffset(for: leftArrow,from: self.frame.minY,offset: 50))
self.addChild(leftArrow)
}
func addRightArrow() {
let rightTexture = SKTexture(imageNamed: "right")
rightArrow = SKSpriteNode(texture: rightTexture)
rightArrow.name = "right"
rightArrow.position = CGPoint(x: calculateXOffset(for: rightArrow,offset: 150),y: calculateXOffset(for: rightArrow,offset: 50))
self.addChild(rightArrow)
}
func addUpArrow() {
let upTexture = SKTexture(imageNamed: "up")
upArrow = SKSpriteNode(texture: upTexture)
upArrow.name = "up"
upArrow.position = CGPoint(x: calculateXOffset(for: upArrow,from: self.frame.maxX,offset: -(125 + upTexture.size().width)),y: calculateXOffset(for: upArrow,offset: 50))
self.addChild(upArrow)
}
// MARK: UTILITY FUNCTIONS
func calculateXOffset(for asset: SKSpriteNode,from coord: CGFloat,offset: CGFloat) -> CGFloat {
let width = asset.frame.width
return coord + offset + width;
}
func calculateYOffset(for asset: SKSpriteNode,offset: CGFloat) -> CGFloat {
let height = asset.frame.height
return coord + offset + height;
}
}
我的 Direction
枚举:
enum Direction {
case LEFT
case RIGHT
case UP
case DOWN
}
我在 GameViewController
中所做的唯一更改是:
scene.scaleMode = .resizefill
我的 GameScene.sks
是 926 x 428,仅支持横向。由于 Xcode 12 中的错误,我还将 LaunchScreen
设置为 Main
:Background is not filling the whole view SpriteKit
这些都是我的资产:
编辑
我尝试在我的 runIn
方法中应用这样的冲动:
player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 2 : -2,dy: 0))
这使得玩家节点在抛物线上移动,但现在它不时被卡住,唯一让它移动的方法是让它跳跃直到它再次发生。
这里的 a video 再次演示了这个问题。
如果我尝试设置速度,那么我将无法在移动时跳跃,并且在跳跃和之后移动时似乎会滑行。
解决方法
我最终在上面的评论中遵循了@JohnL 的建议,以使用冲动来移动我的播放器节点:
player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 2 : -2,dy: 0))
在更改单个资产而不是多个相邻块的楼层时,玩家节点在移动时卡住的问题已被消除。