You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift SpriteKit开发JRPG游戏:Tile Map无法加载问题求助

问题

我正在用Swift开发一款类JRPG游戏,在基于SpriteKit实现Tile Map时遇到了问题:自定义的地图无法加载到游戏中。我尝试过修改代码和图集中的瓦片名称,但推测问题根源可能是项目整体结构搭建不当。

相关代码
import SpriteKit
import GameplayKit

class GameScene: SKScene, SKPhysicsContactDelegate {
    var singleWall: SKTileMapNode?
    var singleFloor: SKTileMapNode?
    var houses: SKTileMapNode?
    var heroEntity: Mileina?
    
    // Collision Mask
    let doorCategory: UInt32 = 0x1 << 4
    let heroCategory: UInt32 = 0x1 << 0
    let wallCollisionMask: UInt32 = 0x1 << 1
    let playerCollisionMask: UInt32 = 0x1 << 0
    
    override func didMove(to view: SKView) {
        // Set delegate for collisions
        physicsWorld.contactDelegate = self
        
        // TileMap configuration
        setupTileMaps()
        
        // Set up collisions
        if let wallMap = singleWall {
            setupCollisions(for: wallMap)
        }
        
        // Hero set
        setupHero()
        
        // Camera set
        setupCamera()
    }
    
    func setupTileMaps() {
        // Look up for parent nodes
        if let wallNode = self.childNode(withName: "Wall") {
            singleWall = wallNode.childNode(withName: "SingleWall") as? SKTileMapNode
        }
        
        if let floorNode = self.childNode(withName: "Floor") {
            singleFloor = floorNode.childNode(withName: "SingleFloor") as? SKTileMapNode
        }
        
        if let houseNode = self.childNode(withName: "HousePH") {
            houses = houseNode.childNode(withName: "Tile") as? SKTileMapNode
        }
        
        // Debug
        print("Wall map: \(String(describing: singleWall))")
        print("Floor map: \(String(describing: singleFloor))")
        print("Houses map: \(String(describing: houses))")
    }
    
    // Set collisions based on TileMap
    func setupCollisions(for tileMap: SKTileMapNode) {
        let tileSize = tileMap.tileSize
        let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
        let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height

        for col in 0..<tileMap.numberOfColumns {
            for row in 0..<tileMap.numberOfRows {
                guard let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row) else { continue }
                
                if let isEdgeTile = tileDefinition.userData?["edgeTile"] as? Bool, isEdgeTile {
                    let x = CGFloat(col) * tileSize.width - halfWidth
                    let y = CGFloat(row) * tileSize.height - halfHeight

                    let physicsBody = SKPhysicsBody(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
                    physicsBody.isDynamic = false
                    physicsBody.categoryBitMask = wallCollisionMask
                    physicsBody.collisionBitMask = playerCollisionMask

                    let tileNode = SKNode()
                    tileNode.position = CGPoint(x: x, y: y)
                    tileNode.physicsBody = physicsBody
                    
                    addChild(tileNode)
                }
            }
        }
    }
    
    // Sets hero in the scene
    func setupHero() {
        let spawnPosition = CGPoint(x: 100, y: 100) // Cambia con la posizione di spawn desiderata
        heroEntity = Mileina(position: spawnPosition)

        if let heroSprite = heroEntity?.component(ofType: SpriteComponent.self)?.spriteNode {
            addChild(heroSprite)
            configureHeroPhysics(heroSprite)
        }
    }

    // Set hero physically
    func configureHeroPhysics(_ heroSprite: SKSpriteNode) {
        heroSprite.physicsBody = SKPhysicsBody(circleOfRadius: heroSprite.size.width / 2)
        heroSprite.physicsBody?.isDynamic = true
        heroSprite.physicsBody?.affectedByGravity = false
        heroSprite.physicsBody?.categoryBitMask = heroCategory
        heroSprite.physicsBody?.contactTestBitMask = doorCategory | wallCollisionMask
        heroSprite.physicsBody?.collisionBitMask = wallCollisionMask
    }

    // Set camera
    func setupCamera() {
        let cameraNode = SKCameraNode()
        self.camera = cameraNode
        addChild(cameraNode)

        if let heroSprite = heroEntity?.component(ofType: SpriteComponent.self)?.spriteNode {
            cameraNode.position = heroSprite.position
        }
    }
    
    // Input key manager
    override func keyDown(with event: NSEvent) {
        guard let heroEntity = heroEntity else { return }

        let direction: String
        switch event.charactersIgnoringModifiers {
        case "w": direction = "Up"
        case "a": direction = "Left"
        case "s": direction = "Down"
        case "d": direction = "Right"
        default: return
        }

        heroEntity.move(direction: direction)
    }

    // Input key stops
    override func keyUp(with event: NSEvent) {
        heroEntity?.stopMoving()
    }

    // Updates position in the map
    override func update(_ currentTime: TimeInterval) {
        if let heroSprite = heroEntity?.component(ofType: SpriteComponent.self)?.spriteNode {
            camera?.position = heroSprite.position
        }
    }
}
排查与修复方案
  • 节点名称精确匹配:setupTileMaps里通过childNode(withName:)查找节点,要确保代码里的名称("Wall"、"SingleWall"、"Floor"等)和SpriteKit编辑器中节点的名称完全一致,包括大小写、空格,SpriteKit对节点名称区分大小写。
  • 检查.sks文件结构:确认GameScene对应的.sks文件中,确实存在这些父节点和TileMap子节点,没有被误删除或设置为隐藏(isHidden = true)。
  • 渲染层级调整:如果TileMap加载成功但看不到,检查节点的层级顺序——把Floor放在最底层,Wall和House节点放在上层,避免被其他节点遮挡。
  • 分析调试输出:查看setupTileMaps中的print结果,如果输出是nil,说明节点未找到,优先排查名称和.sks结构;如果不是nil但看不到地图,检查TileMap的位置、缩放比例,或者是否超出了相机的可视范围。
  • Tile Set关联验证:确认TileMap节点已经正确关联了对应的Tile Set,图集中的瓦片没有缺失,且TileMap的瓦片尺寸与图集瓦片尺寸一致。
  • TileMap属性检查:查看TileMap节点的alpha值是否为1,isHidden是否为false,避免因属性设置导致地图不可见。

内容的提问来源于stack exchange,提问作者ミレイナ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 13:34:50