SpriteKit 设备方向改变导致 SKLabelNode 变形 Swift

Posted

技术标签:

【中文标题】SpriteKit 设备方向改变导致 SKLabelNode 变形 Swift【英文标题】:SpriteKit device orientation change causes SKLabelNode distortion Swift 【发布时间】:2017-02-28 00:29:01 【问题描述】:

我正在使用 SpriteKit 在 Swift 中为 ios 编写游戏。我对 SpriteKit 还是很陌生。

我想同时支持 iPhone 和 iPad 的两种方向,并发现了这一点: multiple orientations in SpriteKit

这在模拟器和设备中按预期工作,但是在设备上我注意到一些 SKSpriteNode 在设备旋转动画之前稍微扭曲到它们的新大小。

这非常明显,尤其是对于 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...

提前致谢。

里昂

【问题讨论】:

听起来你在使用 scaleMode 。填充,检查你的视图控制器。顺便说一句,每个节点都已经知道它的场景,没有理由做你正在做的游戏场景。 @Knight0fDragon 我已经用我正在使用的 ViewController 更新了代码,并且看到了将 scaleMode 设置为 resizeFill 的结果。好像调整大小是在设备旋转之前发生的,因此文本中存在轻微失真。 使用 resize fill 你不应该看到任何大小变化,因为它是在点级别,所以没有完成缩放。唯一应该发生的是你的中心点会改变,事情会发生变化。我只需将场景锚点设置为 0.5,0.5,这样位置 0,0 就是场景的中心,那么您就不需要进行除法了。顺便说一句,您的 MenuSceneNode 和您的 ScoresSceneNode 不用作场景,所以我会将它们制作为 SKNodes 并重命名以避免其他开发人员混淆。 @Knight0fDragon 我将进行更改以减少代码混乱。这是问题中链接的代码对 Swift 3 的直接升级,因此我没有进行任何其他更改。感谢您提供有关锚点等的一些提示。只是菜单节点和得分节点大小/帧似乎在动画之前突然发生了变化,使得旋转不像在 UIKit 中看到的那样具有约束等的平滑过渡。我发现将缩放模式设置为 .fill 时,旋转动画会更加流畅,因此我将尝试使用这种缩放模式来获得相同的结果 .fill 只会让一切变胖,这就是为什么我建议在 0 处使用锚点。这就是正在发生的事情,iphone 5 上的 Portait 模式的宽度为 320,因此您定位在 160。然后旋转。现在你的宽度是 568,但你的位置仍然是 160,所以你不是中心。然后下一帧你重新计算你的中心到 276,一切都被重置了 【参考方案1】:

进入您的 Storyboard 文件,选择您的 ViewController 的视图,然后在右侧栏上查找滑块图像,它应该是左起第四个。这称为属性检查器。将内容模式更改为中心。这会给你黑条,但停止挤压。

【讨论】:

是的,我只是在故事板主视图上添加了一个 1000x1000 的视图,关闭了自动布局和自动调整大小,将内容模式设置为居中,并且能够让场景旋转而没有任何黑色边框 呃,无论如何,在你的场景中保持设计和开发分开大声笑只要if let view = self.view view.contentMode = .center @Ian,您的 contentMode 始终处于使用 SKScene 的重绘模式 除非您的意思是您希望尺寸“变形”为横向,在这种情况下,即使 IOS 也不会这样做。在旋转时,它设置新方向的大小,然后执行旋转动画 如果你的视图是一个正方形,你的场景是一个正方形,那么你的方向就变成了一个视口(基本上是一个相机),所以旋转不会导致任何扭曲或拉伸【参考方案2】:

这是根据@Knight0fDragon 的建议创建的代码,生成的 SKLabelNode 在使用 scaleMode .resizeFill 旋转时不会挤压或拉伸。请注意,此处的视图大小适用于分辨率为 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)


【讨论】:

以上是关于SpriteKit 设备方向改变导致 SKLabelNode 变形 Swift的主要内容,如果未能解决你的问题,请参考以下文章

从 UINavigationController 弹出视图会改变设备方向

如何向滑动 SpriteKit 的方向投掷精灵

touchesEnded 在设备改变方向后没有被调用

iOS SpriteKit:以正确的方向移动节点

(Swift SpriteKit) 沿触摸方向旋转精灵

显示到 CGPoint SpriteKit 的方向