上一次藉着實現一個隨屏幕旋轉的小玩意,瞭解了iPhone內置的加速計。今天我們繼續搞點好玩的東東。按照計劃此次要看看陀螺儀了。git
最終我們會完成一個小球撞壁的小遊戲,一個超級賤的利用陀螺儀的APP。小球能夠感覺到重力,從而可以隨着手機的運動來一塊兒運動。爲了增長一點點趣味性,對小球的運動範圍作了限制。當小球碰到屏幕的邊緣的時候,會進行反彈,相反方向運動。我們一塊兒來看看實現後的實況錄像: github
今天的代碼比起上次的加速計稍微多了一點點,因此就提供了源碼供你們批評。同時因爲這部分Swfit和Objective-C略微有不太同樣的地方,因此源碼提供了兩版。swift
其實不論是加速計仍是今天的陀螺儀,都是用到了上次說的iOS當中的那個核心運動框架CoreMotion
。bash
陀螺儀主要是用來測量沿着某個特定的座標軸旋轉速度的。在使用中,陀螺儀始終指向一個固定的方向,當運動物體的運動方向偏離預約方向時,陀螺儀就能夠感覺出來。多線程
在手機上,僅用加速度計沒辦法測量或重構出完整的3D動做,測不到轉動的動做的,加速計只能檢測軸向的線性動做。但陀螺儀則能夠對轉動、偏轉的動做作很好的測量,這樣就能夠精確分析判斷出使用者的實際動做。然後根據動做,能夠對手機作相應的操做。框架
各位童鞋相比都玩過Wii,那個體感手柄確定就用到了陀螺儀。玩家經過揮動運動手柄,來控制遊戲。例如乒乓球、網球、賽車等等。有一些酷炫的APP會經過小幅度的傾斜,偏轉手機,實現彩蛋功能,例如放大縮小之類的。或者把手機屏幕翻轉,就能夠拒接電話或者靜音啥的。 拍照類的APP也會經過陀螺儀把拍照時候手的抖動反饋交給圖像處理器,以便抓到更清晰穩定的圖片。異步
還有一些是最近剛剛看到的好賤好賤的APP。例如Send Me To Heaven
,遊戲的玩法超級簡單,只需向天空拋擲手機,扔得越高,分數也就越高。async
Throw Me App 另一個賤不拉幾的APP。這是一個相機APP,使用時打開APP並將手機拋向空中,當手機在空中時,使用陀螺儀和加速計探測手機是否達到了最高點,且攝像頭是否向下。隨後,該應用將激活攝像頭快門進行拍照。ui
iPhone、iPad、iWatch都有內置的陀螺儀,也均可以讓開發者進行調用。一樣,用一張圖展示一下:spa
陀螺儀一樣也是經過CoreMotion
這個框架來管理的,因此和加速計同樣,四個標準步驟:
CoreMotion中有2種獲取數據方式,一種叫作PUSH的方式,一種叫作PULL的方式。顧名思義,PUSH就是被動的獲取。設定完了以後,線程定時把獲取到的數據推送回來。可想而知,對於資源的消耗是會稍微大一點的。 PULL,就是要去索取。拉一下才會獲取到數據。不要不給。上一次加速計我們給出的代碼是OC的,今天我們就用Swift的。
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)")
}
複製代碼
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("陀螺儀不可用")
}
}
複製代碼
// 對球在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
}
複製代碼
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)
}
複製代碼
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()
}
複製代碼
其實寫到這裏的時候才忽然想起來,我們歷來沒有說過Swift怎麼重寫Set/Get方法。並且貌似也沒有分享過iOS開發中多線程的東東。下個系列非典型技術宅就能夠寫寫多線程相關的玩意兒吧,若是多線程這部分不太明白的話,對不住對不住對不住,立刻補上。
在swift中其實重寫set不太常見,但這都是OC留下來的臭毛病,就非要從新咋辦?請自行搜索,就不提供連接到*書
了。
這個不是重點,我們在寫小球的時候用到的是didSet這個方法。這是啥吶?這是swift當中的觀察者,用來監視屬性除了初始化以外的屬性變化。
didSet
:在屬性值改變後觸發,didSet能夠帶一個oldName的參數,表示舊的屬性,不帶的話默認命名爲oldValue。willSet
:在屬性值改變前觸發,能夠帶一個newName的參數,沒有的話,該參數默認命名爲newValue。源代碼下載地址:OC+Swift兩版。下載地址