SpriteKit设备方向变化导致SKLabelNode失真问题 Swift

3
我正在使用SpriteKit和Swift编写iOS游戏。我对SpriteKit还不是很熟悉。
我希望支持iPhone和iPad的两种方向,并发现了这个链接:multiple orientations in SpriteKit 在模拟器和设备上,它的工作方式如预期一样,但在设备上,我注意到一些SKSpriteNodes在设备旋转动画之前略微失真。
这在SKLabelNodes中非常明显,其中文本会根据方向变化而略微扭曲或拉伸。
我知道为什么会出现这种失真,但确认并解决问题将是非常棒的。
在设备上,代码与链接中描述的相同,但我已经更新为Swift 3。
class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let scene = GameScene(size:self.view.bounds.size)
        scene.scaleMode = .resizeFill
        (self.view as! SKView).presentScene(scene)
    }

    override var shouldAutorotate: Bool {
        return true
    }

    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

    override var prefersStatusBarHidden: Bool {
        return true
    }
}

class GameScene: SKScene {
    var currentNode: CustomNode!

    override func didMove(to view: SKView) {
        self.backgroundColor = SKColor.white
        transitionToScene(sceneType: .Menu)
    }
    override func didChangeSize(_ oldSize: CGSize) {
        currentNode?.layout()
    }
    func transitionToScene(sceneType: SceneTransition) {
        switch sceneType {
        case .Menu:
            currentNode?.dismissWithAnimation(animation: .Right)
            currentNode = MenuNode(gameScene: self)
            currentNode.presentWithAnimation(animation: .Right)

        case .Scores:
            currentNode?.dismissWithAnimation(animation: .Left)
            currentNode = ScoresNode(gameScene: self)
            currentNode.presentWithAnimation(animation: .Left)

        default: fatalError("Unknown scene transition.")
        }
    }
}

class CustomNode: SKNode {
    weak var gameScene: GameScene!

    init(gameScene: GameScene) {
        self.gameScene = gameScene
        super.init()
    }
    func layout() {}
    func presentWithAnimation(animation:Animation) {
        layout()
        let invert: CGFloat = animation == .Left ? 1 : -1
        self.position = CGPoint(x: invert*gameScene.size.width, y: 0)
        gameScene.addChild(self)
        let action = SKAction.move(to: CGPoint(x: 0, y: 0), duration: 0.3)
        action.timingMode = SKActionTimingMode.easeInEaseOut
        self.run(action)
    }
    func dismissWithAnimation(animation:Animation) {
        let invert: CGFloat = animation == .Left ? 1 : -1
        self.position = CGPoint(x: 0, y: 0)
        let action = SKAction.move(to: CGPoint(x: invert*(-gameScene.size.width), y: 0), duration: 0.3)
        action.timingMode = SKActionTimingMode.easeInEaseOut
        self.run(action, completion: {self.removeFromParent()})
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


class MenuNode: CustomNode {
    var label: SKLabelNode
    var container: SKSpriteNode

    override func layout() {
        container.position = CGPoint(x: gameScene.size.width/2.0, y: gameScene.size.height/2.0)
    }
    override init(gameScene: GameScene) {
        label = SKLabelNode(text: "Menu Scene")
        label.horizontalAlignmentMode = .center
        label.verticalAlignmentMode = .center
        container = SKSpriteNode(color: UIColor.black, size: CGSize(width: 200, height: 200))
        container.addChild(label)
        super.init(gameScene: gameScene)
        self.addChild(container)
        self.isUserInteractionEnabled = true
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.gameScene.transitionToScene(sceneType: .Scores)
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class ScoresNode: CustomNode {
    var label: SKLabelNode
    var container: SKSpriteNode

    override func layout() {
        container.position = CGPoint(x: gameScene.size.width/2.0, y: gameScene.size.height/2.0)
    }
    override init(gameScene: GameScene) {
        label = SKLabelNode(text: "Scores Scene")
        label.horizontalAlignmentMode = .center
        label.verticalAlignmentMode = .center
        container = SKSpriteNode(color: UIColor.black, size: CGSize(width: 200, height: 200))
        container.addChild(label)
        super.init(gameScene: gameScene)
        self.addChild(container)
        self.isUserInteractionEnabled = true
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.gameScene.transitionToScene(sceneType: .Menu)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

enum SceneTransition{
    case Menu, Scores
}
enum Animation {
    case Left, Right, None
}

来源: Epic Byte

我也尝试使用了以下方法:

viewWillTransitionToSize...

由于它可以处理设备方向的更改,因此在设备旋转时,我看到didChangeSize...被多次调用,因此我更喜欢使用viewWillTransitionToSize...。谢谢。Leon

听起来你正在使用scaleMode .Fill,请检查你的视图控制器。顺便说一句,每个节点已经知道它所在的场景,所以你做gameScene的那些事情是没有必要的。 - Knight0fDragon
使用“调整大小以填充”模式时,您不应该看到任何大小更改,因为它是在点级别上完成的,所以没有进行缩放。唯一可能发生的事情就是中心点会改变,东西会移动。我会将场景锚点设置为0.5,0.5,这样位置0,0就成为场景的中心,然后您就不需要进行除法了。顺便说一下,您的MenuSceneNode和ScoresSceneNode不被用作场景,所以我会将它们设置为SKNodes并重新命名,以避免其他开发人员的混淆。 - Knight0fDragon
@Knight0fDragon 我会进行更改,使代码更清晰易懂。这段代码是直接升级到Swift 3的,因此我没有做其他任何更改。 感谢您提供关于锚点等方面的一些提示。 问题在于,在动画之前,菜单节点和得分节点的大小/框架似乎会突然改变,导致旋转不像在UIKit中使用约束等那样平稳过渡。 我发现将比例模式设置为.fill后,旋转动画会更加平滑,因此我将尝试使用此比例模式来获得相同的结果。 - Itergator
.fill只是让所有东西变得肥胖,这就是为什么我建议在0处使用anchorpoint。这里发生了什么,iPhone 5上的纵向模式宽度为320,因此您在160处定位。然后你旋转。现在你的宽度是568,但是你的位置仍然在160,所以你不是中心。然后下一帧你重新计算你的中心到276,一切都重置了。 - Knight0fDragon
除非场景在之后才调整大小,否则我将不得不创建一个测试来查看发生了什么。 - Knight0fDragon
显示剩余4条评论
2个回答

3

进入您的Storyboard文件,选择您的ViewController的视图,在右侧栏中查找滑块图像,它应该是从左边数第4个。这称为属性检查器。将内容模式更改为居中。这将给您黑色条,但停止了挤压。


1
是的,我只是在Storyboard主视图上添加了一个1000x1000的视图,关闭了自动布局和自动调整大小,将内容模式设置为中心,就能够使场景旋转而没有任何黑色边框。 - Knight0fDragon
1
尽量将设计和开发分开,哈哈。无论如何,在你的场景中只需执行 if let view = self.view { view.contentMode = .center} 即可。 - Knight0fDragon
1
@Ian,你的contentMode一直是SKScene的重绘模式。 - Knight0fDragon
1
除非您希望尺寸“变形”为横向,否则即使IOS也不会这样做。在旋转时,它设置新方向的大小,然后执行旋转动画。 - Knight0fDragon
1
如果你的视图是一个正方形,且你的场景也是一个正方形,那么你的方向就变成了一个视口(基本上是相机),因此旋转不会造成任何扭曲或拉伸。 - Knight0fDragon
显示剩余13条评论

1
这是从@Knight0fDragon的建议创建的代码,使用.scaleMode .resizeFill旋转时不会压缩或拉伸SKLabelNodes。请注意,此处的视图大小适用于具有1024x768分辨率的iPad,创建了一个大小为1024x1024的正方形skview。在应用程序加载时,根据方向使用宽度或高度中的最大值轻松复制此操作。
class GameViewController: UIViewController {

var scene : GameScene!
var newView: SKView!

override func viewDidLoad() {
    super.viewDidLoad()

    self.newView = SKView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 1024, height: 1024)))
    (self.view as? SKView)?.contentMode = .center
    self.newView.contentMode = .center
    self.newView.autoresizesSubviews = false
    self.newView.autoresizingMask = [.flexibleBottomMargin, .flexibleTopMargin, .flexibleLeftMargin, .flexibleRightMargin]
    (self.view as? SKView)?.addSubview(newView)
    self.newView.center = view.center
    self.scene = GameScene(size: CGSize(width: 1024, height: 1024))
    self.scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    self.scene.scaleMode = .resizeFill
    self.newView.presentScene(scene)
}

override var shouldAutorotate: Bool {
    return true
}

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    if UIDevice.current.userInterfaceIdiom == .phone {
        return .allButUpsideDown
    } else {
        return .all
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Release any cached data, images, etc that aren't in use.
}

override var prefersStatusBarHidden: Bool {
    return true
}
}

class GameScene: SKScene {

var labelNode: SKLabelNode!
var container: SKSpriteNode!

override func didMove(to view: SKView) {
    self.backgroundColor = SKColor.darkGray

    container = SKSpriteNode(color: .black, size: CGSize(width: 300, height: 300))
    self.addChild(container)

    labelNode = SKLabelNode(text: "hello")
    labelNode.fontColor = .white
    container.addChild(labelNode)
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接