咱们会完成一个小球撞壁的小游戏。小球可以感受到重力,从而能够随着手机的运动来一起运动。为了增加一点点趣味性,对小球的运动范围做了限制。当小球碰到屏幕的边缘的时候,会进行反弹,相反方向运动。其实不管是加速计还是今天的陀螺仪,都是用到了上次说的iOS当中的那个核心运动框架CoreMotion
。
1. 陀螺仪介绍
陀螺仪主要是用来测量沿着某个特定的坐标轴旋转速度的。在使用中,陀螺仪始终指向一个固定的方向,当运动物体的运动方向偏离预定方向时,陀螺仪就可以感受出来。
在手机上,仅用加速度计没办法测量或重构出完整的3D动作,测不到转动的动作的,加速计只能检测轴向的线性动作。但陀螺仪则可以对转动、偏转的动作做很好的测量,这样就可以精确分析判断出使用者的实际动作。而后根据动作,可以对手机做相应的操作。
1.1 陀螺仪的应用场景
各位童鞋相比都玩过Wii,那个体感手柄肯定就用到了陀螺仪。玩家通过挥动运动手柄,来控制游戏。例如乒乓球、网球、赛车等等。有一些酷炫的APP会通过小幅度的倾斜,偏转手机,实现彩蛋功能,例如放大缩小之类的。或者把手机屏幕翻转,就可以拒接电话或者静音啥的。
拍照类的APP也会通过陀螺仪把拍照时候手的抖动反馈交给图像处理器,以便抓到更清晰稳定的图片。
1.2 陀螺仪在iOS中的使用
iPhone、iPad、iWatch都有内置的陀螺仪,也都可以让开发者进行调用。同样,用一张图展现一下:
2. 陀螺仪的使用
2.1 使用步骤
陀螺仪同样也是通过CoreMotion
这个框架来管理的,所以和加速计一样,四个标准步骤:
- 初始化CMMotionManager管理对象;
- 调用管理对象的对象方法获取数据;
- 处理数据;
- 当不需要使用的时候,停止获取数据。
2.2 陀螺仪数据获取的两种方法
CoreMotion中有2种获取数据方式,一种叫做PUSH的方式,一种叫做PULL的方式。顾名思义,PUSH就是被动的获取。设定完了之后,线程定时把获取到的数据推送回来。可想而知,对于资源的消耗是会稍微大一点的。
PULL,就是要去索取。拉一下才会获取到数据。不要不给。上一次加速计咱们给出的代码是OC的,今天咱们就用Swift的。
2.2.1 PULL的方式
private func useGyroPull() { //判断陀螺仪可不可用 if manager.isGyroAvailable { //设置陀螺仪多久采样一次 manager.gyroUpdateInterval = 0.1 //开始更新,后台线程开始运行。这是Pull方式。 manager.startGyroUpdates() } //获取并处理陀螺仪数据。这里我们就只是简单的做了打印。 print("X = \(manager.gyroData?.rotationRate.x ?? 0)","Y = \(manager.gyroData?.rotationRate.y ?? 0)","Z = \(manager.gyroData?.rotationRate.z ?? 0)") }
2.2.2 PUSH的方式
private func useGyroPush() { //判断陀螺仪可不可用 if manager.isGyroAvailable { //设置陀螺仪多久采样一次 manager.gyroUpdateInterval = 0.1 //Push方式获取和处理数据,这里我们一样只是做了简单的打印。把采样的工作放在了主线程中。 manager.startGyroUpdates(to: OperationQueue.main, withHandler: { (gyroData, error) in print("X = \(self.manager.gyroData?.rotationRate.x ?? 0)","Y = \(self.manager.gyroData?.rotationRate.y ?? 0)","Z = \(self.manager.gyroData?.rotationRate.z ?? 0)") }) } else { print("陀螺仪不可用") } }
3. 开始我们的小游戏
3.1 思维导图
3.2 实现
3.2.1 以X轴边界值处理及碰壁后速度处理为例
// 对球在X轴碰壁进行处理 if currentPoint.x <= imageWidth / 2 { currentPoint.x = imageWidth / 2 ballXVelocity = -ballXVelocity * 0.8 } if currentPoint.x >= bounds.size.width - imageWidth / 2 { currentPoint.x = bounds.size.width - imageWidth / 2 ballXVelocity = -ballXVelocity * 0.8 }
3.2.2 开启陀螺仪并更新
manager.deviceMotionUpdateInterval = 1 / 60 //注意一下,在Swift没有了NSOperation。被OperationQueue取代了。 manager.startDeviceMotionUpdates(to: OperationQueue.main) { (motion, error) in self.ballView!.accelleration = (motion?.gravity)! //开启主队列异步线程,更新球的位置。 DispatchQueue.main.async { self.ballView!.updateLocation(multiplier: 5000) }
3.2.3 更新小球的位置
func updateLocation(multiplier : Double) { if (lastUpdateTime != nil) { let updatePeriod : Double = Date.init().timeIntervalSince(lastUpdateTime!) ballXVelocity = ballXVelocity + accelleration.x * updatePeriod ballYVelocity = ballYVelocity + accelleration.y * updatePeriod let coefficient = updatePeriod * multiplier currentPoint = CGPoint(x: currentPoint.x + (CGFloat)(ballXVelocity * coefficient), y: currentPoint.y - (CGFloat)(ballYVelocity * coefficient)) } lastUpdateTime = Date() }