SpriteKit游戏开发适配iPad/iPhone6/7/8/Plus及iPhoneX的尺寸及安全区域
![](https://upload-images.jianshu.io/upload_images/3896436-a6dc532b042e5e12.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-c95dc7f38a297a2a.gif?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
一、那么如何适配不同的iPhone、iPhoneX及iPad的屏幕尺寸呢?
我们开发一个App的时候, 通常希望它在 iPhone, iPad, Mac上同时能运行, 尤其是游戏。
这样就需要我们考虑不同设备不同的分辨率,但处理起来比较麻烦,比如说,按照官方的做法,我们需要提供诸如 ifiero@1x,ifiero@2x,ifiero@3x, 这样不同尺寸的图片,那如何简便的适配设备不同的分辨率呢,我们的做法是, 固定一个大小, 向下兼容不同的设备。
即场景中的所有图片, 都按照屏幕大小为 2048 * 1536 来绘制。 也就是说, 我们的背景图的大小是 2048 * 1536, 其他图片也是依照这个比例来绘制。
为什么这样做呢?
我们知道 2048 * 1536 是iPad Retina 的分辨率。也是我们需要适配的设备里面分辨率最高的。 所以我们在游戏中都选择了这个大小,让它来兼容分辨率低的设备。 2048 * 1536 在iPad Retina上是完美显示的。 那在其他设备上呢? 这里就要依靠 AspectFill来进行缩放了,代码如下:
if let scene = GameScene(fileNamed: "GameScene") {
scene.size = CGSize(width: 2048, height: 1536)
scene.scaleMode = .aspectFill /// 缩放
view.presentScene(scene)
}
![](https://upload-images.jianshu.io/upload_images/3896436-29f200f59ea78356.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
橙色整体区域表示我们场景的真实大小, 黑色线框内的区域表示场景展示在设备上的真实大小。
iPad Retina:橙色区域和黑色线框内的区域是完美吻合的,也就是说在设备上能完整显示。
iPhone6/7/8/Plus:黑色线框内的区域是2048 * 1152,这边要注意的是,超出黑色框的内容看不见,设计游戏时,尽量不要把精灵的Position位置放在位于不可见的区域。
不同尺寸的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 | -- | 2.16 |
iPhoneX的Safe Area为触发交互行为的区域
![](https://upload-images.jianshu.io/upload_images/3896436-875fac952830fec2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
了解了原理后,我们就开始来编写代码吧
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) ? 44.0 : 0
public let AREA_INSET_HEIGHT_BOTTOM:CGFloat = (UIScreen.main.bounds.height == 812) ? 34.0 : 0
// 横屏(安全区域)
public let AREA_INSET_WIDTH_TOP :CGFloat = (UIScreen.main.bounds.width == 812) ? 44.0 : 0
public let AREA_INSET_WIDTH_BOTTOM :CGFloat = (UIScreen.main.bounds.width == 812) ? 34.0 : 0
public let iPhoneX_REAL_HEIGHT:CGFloat = 812.0 /// 竖屏
extension UIDevice {
/// 是不是iPhoneX ,如果是竖屏则 UIScreen.main.bounds.height == 812
public func isPhoneX() -> Bool {
if UIScreen.main.bounds.width == 812 { /// 横屏
return true
}
return false
}
/// 是不是iPad
public func isPad() -> Bool {
if UIScreen.main.bounds.height > 812 {
return true
}
return false
}
}
2.GameScene定义可视范围的起点及高度 (因为是横屏,所以定义高度)
private var playableRect:CGRect! /// 可视范围
private var playableHeight:CGFloat = 0.0 /// 可视范围的高度
private var playableMargin:CGFloat = 0.0 /// 可视范围的起点
override func didMove(to view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
initCheckDevice()
setupBall()
}
3.检测是哪种设备
// MARK: - 检测是哪种设备
func initCheckDevice(){
if UIDevice.current.isPhoneX() {
maxAspectRatio = 2.16 /// iPhoneX 2.16 ratio
}else {
maxAspectRatio = UIDevice.current.isPad() ? (4.0 / 3.0) : (16.0 / 9.0) /// iPhone 16:9,iPad 4:3
}
/// 画出可视区域
drawPayableArea(size: self.size,ratio: maxAspectRatio)
}
4.画出可视区域并赋于可视区域的边届物理特性
// MARK: - 画出可视区域
func drawPayableArea(size:CGSize,ratio:CGFloat){
/*
/// 安全区域即用户交互的区域,非可视区域 (iPhoneX的安全区域 < 可视区域)
let safeInsetTop = self.size.height * AREA_INSET_TOP / iPhoneX_REAL_HEIGHT
let safeInsetBottom = self.size.height * AREA_INSET_BOTTOM / iPhoneX_REAL_HEIGHT
let safeHeight = self.size.height - safeInsetTop - safeInsetBottom
*/
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)原点的位置;
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
}
这样子Ball球就只在可视区域内运动了
二、iPhoneX的尺寸及安全区域
![](https://upload-images.jianshu.io/upload_images/3896436-aaf923670fdfa7a2.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-f8890a918ddbf45c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-bc43f281da8e9f2d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-e25240506c0e3b7f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-f3d0fb07fde9c2ca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
![](https://upload-images.jianshu.io/upload_images/3896436-0b5385f4f29b4b30.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
iPhoneX的屏幕尺寸及安全区域:
设备 | 屏幕尺寸 | 图片存放的位置 | 安全区域 |
---|---|---|---|
iPhoneX | 375x812 | @1x | 375x(812 - 34 - 44),交互的起点Position(x:0,y:34) |
iPhoneX | 750x1624 | @2x | 交互的起点Position(x:0,y:2 x 34) |
iPhoneX | 1125x2436 | @3x | 交互的起点Position(x:0,y:3 x 34) |
iPhoneX | 1536x2048 | @1x | y:2048 x 34 / 812 (已知812对应34,求2048对应y的值) |
![](https://upload-images.jianshu.io/upload_images/3896436-7da94eb52110d523.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/700)
安全区域
// iPhone X 375*812(H) @1x
// 竖屏
public let AREA_INSET_HEIGHT_TOP :CGFloat = (UIScreen.main.bounds.height == 812) ? 44.0 : 0
public let AREA_INSET_HEIGHT_BOTTOM:CGFloat = (UIScreen.main.bounds.height == 812) ? 34.0 : 0
// 横屏(安全区域)
public let AREA_INSET_WIDTH_TOP :CGFloat = (UIScreen.main.bounds.width == 812) ? 44.0 : 0
public let AREA_INSET_WIDTH_BOTTOM :CGFloat = (UIScreen.main.bounds.width == 812) ? 34.0 : 0
public let iPhoneX_REAL_HEIGHT:CGFloat = 812.0 /// 竖屏
/// 安全区域即用户交互的区域,非可视区域 (iPhoneX的安全区域 < 可视区域)
let safeInsetTop = self.size.height * AREA_INSET_WIDTH_TOP / iPhoneX_REAL_HEIGHT
let safeInsetBottom = self.size.height * AREA_INSET_WIDTH_BOTTOM / iPhoneX_REAL_HEIGHT
let safeHeight = self.size.height - safeInsetTop - safeInsetBottom // 安全区域的高度
可视区域
playableHeight = size.width / ratio /// ratio为2.16
playableMargin = (size.height - playableHeight ) / 2.0
playableRect = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight) /// 注意 scene的anchorPoint(0,0)原点的位置;
重要的一点就是要了解屏幕尺寸和安全区域的不同,通俗点讲就是,屏幕尺寸可以放任何元素,但不可放交互行为,所有的用户交互行为都要放在安全区域内。
源码传送门:http://www.iFIERO.com/uploads/BreakOutGameVansV.zip
更多游戏教程: http://www.iFIERO.com
SpriteKit游戏开发适配iPad/iPhone6/7/8/Plus及iPhoneX的尺寸及安全区域的更多相关文章
- Swift - 多层无缝循环滚动背景(SpriteKit游戏开发)
在游戏开发中,比如跑酷游戏.我们需要实现背景的无限循环滚动,来营造运动的效果.除了单层的背景滚动,还有视差滚动. 视差滚动是指让多层背景以不同的速度移动,形成立体的效果,从而带来非常出色的视觉体验. ...
- Swift - 跑酷游戏开发(SpriteKit游戏开发)
一,下面演示了如何开发一个跑酷游戏,实现的功能如下: 1,平台工厂会不断地生成平台,并且向左移动.当平台移出游戏场景时就可将其移除. 2,生成的平台宽度随机,高度随机.同时短平台踩踏的时候会下落. 3 ...
- Swift - 给游戏添加背景音乐和音效(SpriteKit游戏开发)
游戏少不了背景音乐和音效.下面我们通过创建一个管理音效的类,来实现背景音乐的播放,同时点击屏幕可以播放相应的音效. 声音管理类 SoundManager.swift 1 2 3 4 5 6 7 8 9 ...
- Swift - 跳跃吃苹果游戏开发(SpriteKit游戏开发)
下面通过一个样例演示如何实现飞行道具的生成,以及道具碰撞拾取. 样例说明: 1,屏幕从右到左不断地生成苹果飞过来(苹果高度随机) 2,点击屏幕可以让熊猫跳跃 3,熊猫碰到苹果,苹果消失 运行效果: 样 ...
- Swift - 使用atlas图集实现动画效果(SpriteKit游戏开发)
我们通常继承SKSpriteNode来实现游戏中的元素,除了可以使用图片作为纹理皮肤外.我们还可以使用动画纹理集来实现动画播放. 动画纹理集的制作也很简单,首先要有一套动画序列图,然后把它们放到一个文 ...
- Swift - 在界面上生成81个随机红,灰色圆点(SpriteKit游戏开发)
下面是生成一个“围住神经猫”游戏的初始场景: 1,界面下方会生成9*9共81个圆点,同时圆点内部添加文本标签显示索引 2,默认圆点为灰色,每行随机取两个点变为红色 3,奇数行和偶数行有一定的错位,错位 ...
- Swift - 获取屏幕点击坐标下所有对象(SpriteKit游戏开发)
对于场景内对象元件的点击响应,我们可以在场景的touchesBegan()方法中内统一处理. SKScene中touchesBegan()是响应屏幕点击的方法,在这里面我们可以先获取点击位置下所有的对 ...
- Swift - 创建并设置背景(SpriteKit游戏开发)
1,先把背景图片bg.jpg,bg@2x.jpg直接拖进Images.xcassets中 2,设置如下代码(背景图直接铺满整个屏幕) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ...
- SpriteKit游戏开发
http://blog.csdn.net/larrysai/article/category/1663301 http://blog.csdn.net/ping_yun_long/article/de ...
随机推荐
- 【JeeSite】角色分配
主要是(roleAssign.jsp , selectUserToRole.jsp )2个jsp页面的JS方法调用比较复杂,主页面要获取弹窗页面的数据 var pre_ids = h.find(&qu ...
- python 解决粘包问题
客户端发送hello,如果服务端 recv(1) ,那只能接收到 h 这一个字符,然后再recv(1) 一下,可以再接收一个 e , 因为客户端发送的结果长,所以只能把其他的先缓存下来,下次recv的 ...
- C# String与StringBuilder (转载)
1.什么时候用String?什么时候用StringBuilder? 字符串一旦创建就不可修改大小,所以对字符串添加或删除操作比较频繁的话.那就不要用String而用StringBuilder. 例如: ...
- sql得到表中的列信息
取列全部用的 sys. 中的表 CTE:WITH name AS() 用法: sql树形查询 ①主键信息 SELECT ic.column_id, ic.index_column_id, ic.o ...
- JS JavaScript闭包和作用域
JavaScript高级程序设计中对闭包的定义:闭包是指有权访问另外一个函数作用域中变量的函数. 从概念上,闭包有两个特点: 1.函数 2.能访问另外一个函数的作用域中的变量 在ES6之前,JavaS ...
- flask笔记(一)
1.第一个flask项目 # 首先你要安装flask这个模块 pip install flask # 安装好了之后,直接新建一个py文件,开始写最简单的flask项目了 from flask impo ...
- 前端关于SEO
提高页面加载速度. 能用css解决的不用背景图片,背景图片也尽量压缩大小,可以几个icons放在一个图片上,使用background-position找到需要的图片位置.可以减少HTTP请求数,提高网 ...
- 【TOJ 4493】Remove Digits(单调栈贪心)
描述 Given an N-digit number, you should remove K digits and make the new integer as large as possible ...
- C/C++使用keybd_event模拟键盘按键
#include <stdio.h> #include <Windows.h> /* 设置键盘大小写状态 big:为TRUE则切换大写状态,否则切换小写状态 */ VOID M ...
- ABAP术语-ABAP Dictionary
ABAP Dictionary 原文链接:http://www.cnblogs.com/qiangsheng/archive/2007/12/07/986204.html Central redund ...