上一次借著實(shí)現(xiàn)一個(gè)隨屏幕旋轉(zhuǎn)的小玩意甸怕,了解了iPhone內(nèi)置的加速計(jì)挠羔。今天咱們繼續(xù)搞點(diǎn)好玩的東東盖奈。按照計(jì)劃這次要看看陀螺儀了。
最終咱們會(huì)完成一個(gè)小球撞壁的小游戲,一個(gè)超級(jí)賤的利用陀螺儀的APP磁奖。小球可以感受到重力囊拜,從而能夠隨著手機(jī)的運(yùn)動(dòng)來(lái)一起運(yùn)動(dòng)。為了增加一點(diǎn)點(diǎn)趣味性比搭,對(duì)小球的運(yùn)動(dòng)范圍做了限制冠跷。當(dāng)小球碰到屏幕的邊緣的時(shí)候,會(huì)進(jìn)行反彈身诺,相反方向運(yùn)動(dòng)蜜托。咱們一起來(lái)看看實(shí)現(xiàn)后的實(shí)況錄像:
今天的代碼比起上次的加速計(jì)稍微多了一點(diǎn)點(diǎn),所以就提供了源碼供大家批評(píng)霉赡。同時(shí)由于這部分Swfit和Objective-C略微有不太一樣的地方橄务,所以源碼提供了兩版。
其實(shí)不管是加速計(jì)還是今天的陀螺儀穴亏,都是用到了上次說(shuō)的iOS當(dāng)中的那個(gè)核心運(yùn)動(dòng)框架CoreMotion蜂挪。
1. 陀螺儀介紹
陀螺儀主要是用來(lái)測(cè)量沿著某個(gè)特定的坐標(biāo)軸旋轉(zhuǎn)速度的。在使用中嗓化,陀螺儀始終指向一個(gè)固定的方向棠涮,當(dāng)運(yùn)動(dòng)物體的運(yùn)動(dòng)方向偏離預(yù)定方向時(shí),陀螺儀就可以感受出來(lái)刺覆。
在手機(jī)上严肪,僅用加速度計(jì)沒(méi)辦法測(cè)量或重構(gòu)出完整的3D動(dòng)作,測(cè)不到轉(zhuǎn)動(dòng)的動(dòng)作的谦屑,加速計(jì)只能檢測(cè)軸向的線性動(dòng)作驳糯。但陀螺儀則可以對(duì)轉(zhuǎn)動(dòng)、偏轉(zhuǎn)的動(dòng)作做很好的測(cè)量伦仍,這樣就可以精確分析判斷出使用者的實(shí)際動(dòng)作结窘。而后根據(jù)動(dòng)作,可以對(duì)手機(jī)做相應(yīng)的操作充蓝。
1.1 陀螺儀的應(yīng)用場(chǎng)景
各位童鞋相比都玩過(guò)Wii,那個(gè)體感手柄肯定就用到了陀螺儀。玩家通過(guò)揮動(dòng)運(yùn)動(dòng)手柄谓苟,來(lái)控制游戲官脓。例如乒乓球、網(wǎng)球涝焙、賽車(chē)等等卑笨。有一些酷炫的APP會(huì)通過(guò)小幅度的傾斜,偏轉(zhuǎn)手機(jī)仑撞,實(shí)現(xiàn)彩蛋功能赤兴,例如放大縮小之類(lèi)的∷硐或者把手機(jī)屏幕翻轉(zhuǎn)桶良,就可以拒接電話或者靜音啥的。 拍照類(lèi)的APP也會(huì)通過(guò)陀螺儀把拍照時(shí)候手的抖動(dòng)反饋交給圖像處理器沮翔,以便抓到更清晰穩(wěn)定的圖片陨帆。
還有一些是最近剛剛看到的好賤好賤的APP。例如Send Me To Heaven采蚀,游戲的玩法超級(jí)簡(jiǎn)單疲牵,只需向天空拋擲手機(jī),扔得越高榆鼠,分?jǐn)?shù)也就越高纲爸。
Throw Me App 另外一個(gè)賤不拉幾的APP。這是一個(gè)相機(jī)APP妆够,使用時(shí)打開(kāi)APP并將手機(jī)拋向空中识啦,當(dāng)手機(jī)在空中時(shí),使用陀螺儀和加速計(jì)探測(cè)手機(jī)是否達(dá)到了最高點(diǎn)责静,且攝像頭是否向下袁滥。隨后,該應(yīng)用將激活攝像頭快門(mén)進(jìn)行拍照灾螃。
1.2 陀螺儀在iOS中的使用
iPhone题翻、iPad、iWatch都有內(nèi)置的陀螺儀腰鬼,也都可以讓開(kāi)發(fā)者進(jìn)行調(diào)用嵌赠。同樣,用一張圖展現(xiàn)一下:
2. 陀螺儀的使用
2.1 使用步驟
陀螺儀同樣也是通過(guò)CoreMotion這個(gè)框架來(lái)管理的熄赡,所以和加速計(jì)一樣姜挺,四個(gè)標(biāo)準(zhǔn)步驟:
初始化CMMotionManager管理對(duì)象;
調(diào)用管理對(duì)象的對(duì)象方法獲取數(shù)據(jù)彼硫;
處理數(shù)據(jù)炊豪;
當(dāng)不需要使用的時(shí)候凌箕,停止獲取數(shù)據(jù)。
2.2 陀螺儀數(shù)據(jù)獲取的兩種方法
CoreMotion中有2種獲取數(shù)據(jù)方式词渤,一種叫做PUSH的方式牵舱,一種叫做PULL的方式。顧名思義缺虐,PUSH就是被動(dòng)的獲取芜壁。設(shè)定完了之后,線程定時(shí)把獲取到的數(shù)據(jù)推送回來(lái)高氮』弁可想而知,對(duì)于資源的消耗是會(huì)稍微大一點(diǎn)的剪芍。 PULL塞淹,就是要去索取。拉一下才會(huì)獲取到數(shù)據(jù)紊浩。不要不給窖铡。上一次加速計(jì)咱們給出的代碼是OC的,今天咱們就用Swift的坊谁。
2.2.1 PULL的方式
privatefunc?useGyroPull()?{
//判斷陀螺儀可不可用
ifmanager.isGyroAvailable?{
//設(shè)置陀螺儀多久采樣一次
manager.gyroUpdateInterval?=0.1
//開(kāi)始更新费彼,后臺(tái)線程開(kāi)始運(yùn)行。這是Pull方式口芍。
manager.startGyroUpdates()
}
//獲取并處理陀螺儀數(shù)據(jù)箍铲。這里我們就只是簡(jiǎn)單的做了打印。
print("X?=?\(manager.gyroData?.rotationRate.x????0)","Y?=?\(manager.gyroData?.rotationRate.y????0)","Z?=?\(manager.gyroData?.rotationRate.z????0)")
}
2.2.2 PUSH的方式
privatefunc?useGyroPush()?{
//判斷陀螺儀可不可用
ifmanager.isGyroAvailable?{
//設(shè)置陀螺儀多久采樣一次
manager.gyroUpdateInterval?=0.1
//Push方式獲取和處理數(shù)據(jù)鬓椭,這里我們一樣只是做了簡(jiǎn)單的打印颠猴。把采樣的工作放在了主線程中。?????????????????????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. 開(kāi)始我們的小游戲
3.1 思維導(dǎo)圖
3.2 實(shí)現(xiàn)
3.2.1 以X軸邊界值處理及碰壁后速度處理為例
//????????????對(duì)球在X軸碰壁進(jìn)行處理
ifcurrentPoint.x?=?bounds.size.width?-?imageWidth?/2{
currentPoint.x?=?bounds.size.width?-?imageWidth?/2
ballXVelocity?=?-ballXVelocity?*0.8
}
3.2.2 開(kāi)啟陀螺儀并更新
manager.deviceMotionUpdateInterval?=1/60
//注意一下小染,在Swift沒(méi)有了NSOperation翘瓮。被OperationQueue取代了。
manager.startDeviceMotionUpdates(to:?OperationQueue.main)?{?(motion,?error)in
self.ballView!.accelleration?=?(motion?.gravity)!
//開(kāi)啟主隊(duì)列異步線程裤翩,更新球的位置资盅。
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()
}
3.3 關(guān)于Swift中重寫(xiě)set/get
其實(shí)寫(xiě)到這里的時(shí)候才突然想起來(lái),咱們從來(lái)沒(méi)有說(shuō)過(guò)Swift怎么重寫(xiě)Set/Get方法踊赠。而且貌似也沒(méi)有分享過(guò)iOS開(kāi)發(fā)中多線程的東東呵扛。下個(gè)系列非典型技術(shù)宅就可以寫(xiě)寫(xiě)多線程相關(guān)的玩意兒吧,如果多線程這部分不太明白的話筐带,對(duì)不住對(duì)不住對(duì)不住今穿,馬上補(bǔ)上。
在swift中其實(shí)重寫(xiě)set不太常見(jiàn)伦籍,但這都是OC留下來(lái)的臭毛病蓝晒,就非要重新咋辦腮出?請(qǐng)自行搜索,就不提供鏈接到*書(shū)了拔创。
這個(gè)不是重點(diǎn)利诺,咱們?cè)趯?xiě)小球的時(shí)候用到的是didSet這個(gè)方法富蓄。這是啥吶剩燥?這是swift當(dāng)中的觀察者,用來(lái)監(jiān)視屬性除了初始化之外的屬性變化立倍。
didSet:在屬性值改變后觸發(fā)灭红,didSet可以帶一個(gè)oldName的參數(shù),表示舊的屬性口注,不帶的話默認(rèn)命名為oldValue变擒。
willSet:在屬性值改變前觸發(fā),可以帶一個(gè)newName的參數(shù)寝志,沒(méi)有的話娇斑,該參數(shù)默認(rèn)命名為newValue。