【发布时间】:2021-06-14 10:14:45
【问题描述】:
我正在制作自己的 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, from: self.frame.minX, offset: 150),
y: calculateXOffset(for: rightArrow, from: self.frame.minY, 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, from: self.frame.minY, 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, from coord: CGFloat, 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 再次演示该问题。
如果我尝试设置速度,那么我无法在移动时跳跃,并且在跳跃和移动时似乎会滑行。
【问题讨论】:
-
我相信你是在使用冲动进行跳跃,但左右是通过在位置上加上或减去一个x值来完成的。当您释放左键或右键时,您不再移动(基本上是 player.position.x + 0),没有动力,因为您只是使用硬编码值而不是物理。跳跃是使用冲动移动,而重力则用于使您的角色向后退。如果你使用冲动来左右移动角色,它会更像马里奥。
-
在这种情况下我应该使用冲动吗?还是来自
physicsbody的速度? @JohnL -
this 回答有帮助吗?
-
这有点帮助@JohnL,我编辑了我的问题
-
好的,很高兴你能正常工作。仅供参考,在您的最新视频中,您的播放器会弹跳一点,请使用 player.restitution = 0 停止弹跳。
标签: ios swift sprite-kit game-physics skphysicsbody