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的主要内容,如果未能解决你的问题,请参考以下文章