此篇文章是对上一篇文章(http://www.ifiero.com/index.p...)的进一步补充,主要说明如何适配Apple的最新三款手机iPhoneXs、iPhoneXs Max及iPhoneXr !!!php
回顾:为什么要把场景中的全部图片, 都按照屏幕大小为 2048 1536 来绘制。 也就是说, 咱们的背景图的大小是 2048 1536, 其余图片也是依照这个比例来绘制。git
为何这样作呢?github
咱们知道 2048 1536 是iPad Retina 的分辨率。也是咱们须要适配的设备里面分辨率最高的。 因此咱们在游戏中都选择了这个大小,让它来兼容分辨率低的设备。 2048 1536 在iPad Retina上是完美显示的。 那在其余设备上呢? 先用 AspectFill来进行缩放,并应用相应的屏幕辨率高宽比值Ratio, 来适配各个不一样的iPhone尺寸。AspectFill缩放的代码以下:api
if let scene = GameScene(fileNamed: "GameScene") { scene.size = CGSize(width: 2048, height: 1536) scene.scaleMode = .aspectFill /// 缩放 view.presentScene(scene) }
了解了用 AspectFill来进行缩放,那么咱们如今就来看看iPhoneX的屏幕尺寸分辨率
iPhoneX的屏幕尺寸分辨率(上图中 高度812 = 2436缩小1/3):安全
设备 | 屏幕尺寸分辨率 | 图片存放对应的位置 |
---|---|---|
iPhoneX (1倍 @1x) | 375x812 | @1x |
iPhoneX (2倍 @2x) | 750x1624 | @2x |
iPhoneX (3倍 @3x) | 1125x2436 | @3x |
iPhoneX/iPhoneXs/iPhoneXs Max/iPhoneXr的屏幕尺寸分辨率
iPhoneX系列的屏幕分辨率:oop
设备 | 屏幕分辨率 | 图片存放的位置 |
---|---|---|
iPhoneX (3倍 @3x) | 1125x2436 | @3x |
iPhoneXs (3倍 @3x) | 1125x2436 | @3x |
iPhoneXs Max (3倍 @3x) | 1242x2688 | @3x |
iPhoneXr (2倍 @2x) | 828x1792 | @2x |
根据以上iPhoneX系列的屏幕分辨率,得出高宽比Ratio都为2.16
橙色总体区域表示咱们场景的真实大小, 用 AspectFill来进行缩放后,scene.scaleMode = .aspectFill,黑色线框内的区域表示场景展现在设备上的真实大小(即屏幕可视范围)。spa
iPad Retina:橙色区域和黑色线框内的区域是完美吻合的,也就是说在设备上能完整显示。设计
iPhone6/7/8/Plus:黑色线框内的区域是2048 * 1152,这边要注意的是,超出黑色框的内容看不见,设计游戏时,尽可能不要把精灵的Position位置放在位于不可见的区域。code
iPhoneX:黑色线框内的区域是2048 * 948(兰色为安全区域),其中948高度=2048 / 2.16(高宽比)。blog
不一样尺寸的iPhone的屏幕尺寸比例及屏幕高宽比值
| 设备 | 屏幕比例 | 屏幕高宽比值 |
| - | :-: | -: |
| iPad Retina | 4 / 3 | 1.33 |
| iPhone 6/7/8 | 16 / 9 | 1.77 |
| iPhone 6/7/8 Plus | 16 / 9 | 1.77 |
| iPhone X/Xs/Xr/Xs Max | -- | 2.16 |
再者咱们主要是适配SpriteKit游戏开发,所以不须要计算Navigation导航栏的高度,也不须要TabBar状态栏的高度,只须要计算可视区域(屏幕可视范围),还有,注意要把须要交互的元素放在安全区域SafeArea,而不要放在危险区域 Danger Area就好了。
可视区域(屏幕可视范围)= 安全区域 Safe Area + 危险区域 Danger Area
了解了原理后,咱们就开始来编写代码吧。
1.extension拓展UIDevice,判断设备是iPhone或者iPhoneX系列或iPad
import UIKit import SpriteKit // iPhone X 375*812(H) @1x // 竖屏 public let AREA_INSET_HEIGHT_TOP :CGFloat = (UIScreen.main.bounds.height == 812 || UIScreen.main.bounds.height == 896) ? 44.0 : 0 public let AREA_INSET_HEIGHT_BOTTOM:CGFloat = (UIScreen.main.bounds.height == 812 || UIScreen.main.bounds.height == 896) ? 34.0 : 0 // 横屏(安全区域) public let AREA_INSET_WIDTH_LEFT :CGFloat = (UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896) ? 44.0 : 0 public let AREA_INSET_WIDTH_RIGHT :CGFloat = (UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896) ? 34.0 : 0 extension UIDevice { /// 是否是iPhoneX ,若是是竖屏则 UIScreen.main.bounds.height == 812 public func isPhoneX() -> Bool { if UIScreen.main.bounds.width == 812 || UIScreen.main.bounds.width == 896 { /// 横屏 return true } return false } /// 是否是iPad public func isPad() -> Bool { return (UIDevice.current.userInterfaceIdiom == .pad) ? true : false; } }
2.检测是哪一种设备
// MARK: - 检测是哪一种设备 func initCheckDevice(){ if UIDevice.current.isPhoneX() { maxAspectRatio = 2.16 /// iPhoneX || iPhoneXs || iPhoneXs Max || iPhoneXr 2.16 高/宽比 ratio }else { maxAspectRatio = UIDevice.current.isPad() ? (4.0 / 3.0) : (16.0 / 9.0) /// iPhone 16:9,iPad 4:3 } /// 画出可视区域 drawPlayableArea(size: self.size,ratio: maxAspectRatio) /// 画出安全区域 drawSafeArea(size: self.size,ratio: maxAspectRatio) }
3.画出安全区域
// MARK: - 安全区域即用户交互的区域,非可视区域 (iPhoneX的安全区域 < 可视区域) func drawSafeArea(size:CGSize,ratio:CGFloat){ playableHeight = size.width / ratio playableMargin = (size.height - playableHeight ) / 2.0 /// P70 let safeInsetLeft = AREA_INSET_WIDTH_LEFT * ratio let safeInsetRight = size.width - safeInsetLeft - AREA_INSET_WIDTH_RIGHT * ratio playableRect = CGRect(x: safeInsetLeft, y: playableMargin, width:safeInsetRight, height: playableHeight) /// 注意 scene的anchorPoint(0,0)原点的位置; let shapeFrame = SKShapeNode(rect: playableRect) shapeFrame.zPosition = 2 shapeFrame.strokeColor = SKColor.green shapeFrame.lineWidth = 6.0 addChild(shapeFrame) }
4.画出可视区域并赋于可视区域的边届物理特性
// MARK: - 画出可视区域 func drawPlayableArea(size:CGSize,ratio:CGFloat){ playableHeight = size.width / ratio playableMargin = (size.height - playableHeight ) / 2.0 /// P70 playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight) /// 注意 scene的anchorPoint(0,0)原点的位置; print("playable Margin",playableMargin) let shapeFrame = SKShapeNode(rect: playableRect) shapeFrame.zPosition = 1 shapeFrame.strokeColor = SKColor.red shapeFrame.lineWidth = 5.0 addChild(shapeFrame) /// 可视区域的物理状态 let playableBody = SKPhysicsBody(edgeLoopFrom: playableRect) playableBody.friction = 0 self.physicsBody = playableBody playableBody.categoryBitMask = PhysicsCategory.Frame playableBody.contactTestBitMask = PhysicsCategory.Ball playableBody.collisionBitMask = PhysicsCategory.Ball /// 地板 setupFloor() }
这样子Ball球就只在可视区域内(屏幕可视范围)运动了。
重要的一点就是要了解屏幕尺寸和安全区域的不一样,通俗点讲就是,屏幕可视范围能够听任何元素,但全部的用户交互行为都要放在安全区域内(兰色框内)。
便可视区域(屏幕可视范围)= 安全区域 Safe Area + 危险区域 Danger Area
源码传送门: https://github.com/apiapia/Br...
更多游戏教程: http://www.iFIERO.com -- 为游戏开发深感自豪