#swift #xcode #sprite-kit
#swift #xcode #набор спрайтов
Вопрос:
Я построил лабиринт из файлов формата png размером 32×32, и к ним было применено SKPhysicsBody с целью блокирования перемещения игрока по ним (игрок здесь не показан)
По какой-то странной причине некоторые квадраты в лабиринте движутся с очень маленькой скоростью (координаты меняются, и они медленно перемещаются на экране), но их SKPhysicsBody.velocity равен 0 по обеим осям
Пытаясь разобраться в этом, я настраиваю некоторый минимальный код, который воспроизводит среду, и этот код выделяет движущиеся квадраты синим цветом. Код находится внизу этого поста
Особенности кода
- позволяет использовать клавиши со стрелками на клавиатуре для перемещения всего лабиринта на экране… это приводит к изменению выделенных квадратов, и при этом возникают некоторые любопытные закономерности
- позволяет щелкнуть любой квадрат, чтобы его положение и скорость были введены в консоль… синие квадраты имеют постоянно меняющееся положение, но скорость равна 0
Факты
- Закомментирование объявления SKPhysicsBody в BrokenTile устраняет проблему, но поскольку я хочу иметь обнаружение столкновений, для меня это неподходящее решение
- При перемещении лабиринта с помощью клавиш со стрелками меняется, какие группы квадратов окрашены в синий цвет, но на них появляются узоры. Синей всегда является целая строка или столбец, а иногда и то, и другое.
- Даже белые квадраты при первоначальном создании лабиринта не находятся в своих идеальных координатах и немного смещаются перед установлением (что было бы кратно 34 из-за настройки размера в BrokenWorld.load)
У кого-нибудь есть какие-либо идеи, что заставляет синие квадраты перемещаться с небольшой скоростью, даже если скорость равна 0?
редактировать: Я попытался добавить расширение к SKSpriteNode, которое предупреждает меня при изменении spritenode. Я подтвердил, что изменение x внутри position вызывает срабатывание willSet, но, похоже, срабатывает только тогда, когда мой код изменяет значение напрямую. Есть ли какая-то причина, по которой он не срабатывает, когда позиция изменяется каким-либо неизвестным процессом, заставляющим его перемещаться? Я надеялся поставить здесь точку останова, чтобы получить стек, который точно сообщает мне, откуда происходит изменение
extension SKSpriteNode {
open override var position: CGPoint {
willSet {
print("Position of (self) will change to: (newValue)")
}
}
}
import SpriteKit
import GameplayKit
import Carbon.HIToolbox.Events // kVK
class IssuePhysics: SKScene, SKPhysicsContactDelegate {
static var instance: IssuePhysics?
var world = BrokenWorld()
var keys = [Int: Bool]()
override func didMove(to view: SKView) {
IssuePhysics.instance = self
let O = "e"
let X = "b"
// Load Map
let map = [
[O, X, X, X, X, X, X, X, X, X, X, X, X, X],
[X, O, X, O, O, O, O, X, O, O, O, O, X, X],
[X, O, X, O, X, X, X, X, X, X, X, O, X, X],
[X, O, X, O, O, O, O, X, O, O, O, O, X, X],
[X, O, X, X, X, X, O, X, O, X, X, O, X, X],
[X, O, O, O, O, O, O, X, O, O, X, O, X, X],
[X, X, X, X, X, O, X, X, X, O, X, O, X, X],
[X, O, O, O, X, O, X, O, X, O, X, O, X, X],
[X, O, X, O, X, O, X, O, X, O, X, O, X, X],
[X, O, X, O, X, O, X, O, X, O, X, O, X, X],
[X, O, X, O, O, O, O, O, O, O, X, O, X, X],
[X, X, X, X, X, X, X, X, X, X, X, X, X, X]
]
world.load(map: map)
addChild(world)
world.position = CGPoint(x: 50, y: 50)
}
override func mouseDown(with event: NSEvent) {
// Get mouse position in scene coordinates
let location = event.location(in: self)
// Get node at mouse position
let node = self.atPoint(location)
print((node.name ?? "empty") ": " node.position.debugDescription)
print((node.physicsBody?.velocity.debugDescription) as Any)
}
override func keyDown(with event: NSEvent) {
keys[Int(event.keyCode)] = true
}
override func keyUp(with event: NSEvent) {
keys[Int(event.keyCode)] = false
}
override func update(_ currentTime: TimeInterval) {
world.update(keys: keys)
}
}
class BrokenWorld : SKNode {
static var instance: BrokenWorld?
var coords = [[BrokenTile]]()
static let squareSize = 32
var tile_templates = [String: BrokenTile]()
override init(){
super.init()
BrokenWorld.instance = self
self.name = "world"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func load(map: [[String]]){
var tile = BrokenTile()
tile_templates["e"] = tile // register empty tile
tile = BrokenTile(imageNamed: "Blocker")
tile.initPhysics()
tile_templates["b"] = tile // register standard white tile
let size = 34 // deliberately higher than 32 so that each square in the maze is clearly visible
self.coords = Array(repeating: Array(repeating: BrokenTile(), count: map.count), count: map[0].count)
for y in 0...map.count-1 {
for x in 0...map[y].count-1 {
let tile_tmp = tile_templates[map[y][x]]!.clone()
tile_tmp.position = CGPoint(x: x*size, y: y*size)
self.coords[x][y] = tile_tmp
self.addChild(self.coords[x][y])
}
}
}
func update(keys: [Int: Bool]){
for row in self.coords {
for tile in row {
tile.update(keys: keys)
}
}
if keys[kVK_LeftArrow] ?? false {
self.position.x -= 10
} else if keys[kVK_RightArrow] ?? false {
self.position.x = 10
} else if keys[kVK_UpArrow] ?? false {
self.position.y = 10
} else if keys[kVK_DownArrow] ?? false {
self.position.y -= 10
}
}
}
class BrokenTile : SKSpriteNode {
var imageName: String = "empty"
var lastPosition: CGPoint = CGPoint(x: 0, y: 0)
var changeTimer = 0
init(imageNamed: String){
let texture = SKTexture(imageNamed: imageNamed)
self.imageName = imageNamed
super.init(texture: texture, color: SKColor.clear, size: texture.size())
self.colorBlendFactor = 1.0
self.name = "tile"
}
init(){
super.init(texture: nil, color: SKColor.clear, size: CGSize(width: 32, height: 32))
self.name = "tile"
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func initPhysics(){
// commenting out next two lines resolves the problem, but then collission cannot be enabled, so that is not a satisfactory solution
self.physicsBody = SKPhysicsBody(rectangleOf: self.size)
self.physicsBody!.isDynamic = false
}
func update(keys: [Int: Bool]){
if self.lastPosition.x != self.position.x || self.lastPosition.y != self.position.y {
self.lastPosition.x = self.position.x
self.lastPosition.y = self.position.y
changeTimer = 1
}
if self.imageName == "Blocker" {
if changeTimer > 0 {
self.color = SKColor.blue
changeTimer -= 1
} else {
self.color = SKColor.white
}
}
}
func clone() -> BrokenTile {
let clone = self.copy() as! BrokenTile
clone.imageName = self.imageName
return clone
}
}
Комментарии:
1. Вам необходимо предоставить минимально воспроизводимый пример. в противном случае вы просите помочь вам в отладке, а все, что вы показали, это 3 строки кода.
2. Можете ли вы сократить свой код, просто поместив узел на экран, а затем ничего не делая, кроме печати их координат (возможно, раз в секунду) в update ()? Они все еще движутся?
3. Я закомментировал методы update (…) и didBegin(…) в GameScene.swift (ничто другое не выполняет эти методы в других классах). Как и ожидалось, я потерял всякую возможность перемещать плеер или камеру, но движущиеся квадраты все еще движутся