ios SceneKit實現飛機小遊戲
簡介
本文主要介紹使用xcode自帶的sceneKit框架做的簡單飛機遊戲。效果圖

airPlaneGame.gif
實現的功能:3D場景的建立,飛機運動動畫,飛機尾部粒子噴射,飛機子彈發射動畫,碰撞檢測等。 git專案地址
分析與實現
1.建立一個3D場景
new file->resource->scene catalog

yanshi_1.png
現在建立了一個sceneKit檔案管理,還需要建立scene場景,右鍵點選該資料夾,點選new file,會直接建立一個字尾為.scn的檔案,這就是我們要載入的場景。

yanshi_2.png
SCNScene *scene = [SCNScene sceneNamed:@"SceneKitSrc.scnassets/nor.scn"];
這裡說一下,在這個scene graph裡面可以右鍵喚出輔助選單,

yanshi_3.png
喚出輔助選單後,看目錄,有建立節點等各種功能。建立後,在右邊選單欄可以設定該節點的詳細屬性。才接觸的同學可以通過這種方式快速熟悉節點的各種屬性,比程式碼建立迅速,簡便些。這裡我建立了一個圓球體當做背景。
2.飛機的運動動畫
通過手指在螢幕上滑動,控制飛機的飛行軌跡,這裡想到的就是用touch事件來控制。但是有個問題,觸控點在檢視空間是個二維座標CGPoint,要轉換成場景空間座標SCNVector3才能在該場景使用,方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //NSLog(@"touch began"); //由於觸控點在檢視空間為二維座標,要轉化成三維場景空間座標,需要知道節點的深度,這裡直接使用根節點的z座標 //意思就是,無論你點選哪裡,深度都以根節點所在座標為準 SCNVector3 rootNodeVec = [_scnView projectPoint:_scnView.scene.rootNode.position]; float projectedDepth = rootNodeVec.z; //再進行x,y軸上的座標轉換 CGPoint point = [[touches anyObject] locationInView:_scnView]; SCNVector3 vpWithDepth = SCNVector3Make(point.x, point.y, projectedDepth); SCNVector3 movePoint = [_scnView unprojectPoint:vpWithDepth]; //飛船移動 [self shipMoveWithPoint:movePoint]; //飛船移動時左右偏轉 [self shipDeflectionWithPoint:movePoint]; //飛船發射子彈 [self bulletLaunch]; }
然後根據觸控點進行移動
- (void)shipMoveWithPoint:(SCNVector3)movePoint { //飛船的飛行動畫 [_shipNode runAction:[SCNAction moveTo:movePoint duration:0.4]]; }
3.飛機尾部粒子噴射
這裡我直接在node上新增的particle system,方便我除錯效果

yanshi_4.png
比如,粒子生出率,粒子加速方向,粒子大小,粒子形狀等,改變後馬上可以看到效果。
4.飛機子彈發射動畫
就是子彈在場景空間中做平移運動,知道飛機的飛行運動後,子彈的運動也很好理解。
- (void)bulletLaunch { //建立子彈物件 SCNGeometry *sphere = [SCNSphere sphereWithRadius:0.2]; sphere.firstMaterial.diffuse.contents = kOrangeColor; SCNNode *bullet = [SCNNode nodeWithGeometry:sphere]; bullet.physicsBody = [SCNPhysicsBody dynamicBody]; bullet.physicsBody.affectedByGravity = NO; bullet.position = SCNVector3Make(_shipNode.position.x, _shipNode.position.y, _shipNode.position.z); bullet.physicsBody.categoryBitMask = MaskTypeWithBullet; bullet.physicsBody.collisionBitMask = MaskTypeWithEnemy; bullet.physicsBody.contactTestBitMask = MaskTypeWithEnemy; [_scnView.scene.rootNode addChildNode:bullet]; //子彈移動 [bullet runAction:[SCNAction moveTo:SCNVector3Make(bullet.position.x, bullet.position.y + 30, bullet.position.z) duration:1] completionHandler:^{ [bullet removeFromParentNode]; }]; }
5.碰撞檢測
每個SCNNode都有個預設為空的physicsBody,初始化後,node就會受各種力的影響,並且會和同樣初始化了physicsBody的其他node產生碰撞效果。那麼怎樣確定碰撞的是哪兩個物體,碰撞後又怎麼對這兩個物體進行操作呢?
先來看physicsBody的三個屬性:
1.categoryBitMask,類別掩碼,簡單理解就是該物體的一個身份標識
2.collisionBitMask,碰撞掩碼,設定碰撞物體的身份
3.contactTestBitMask,關聯掩碼,設定關聯物體,用於在關聯代理裡面對碰撞物體做後續操作。
這裡有一篇對掩碼講解比較詳細的文章,想深入理解的可以去看看。傳送門
關聯代理:
<SCNPhysicsContactDelegate> //物理引擎關聯代理 _scnView.scene.physicsWorld.contactDelegate = self;
代理方法實現:
- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact{ //開始碰撞,得到兩個碰撞的node SCNNode *nodeA = contact.nodeA; SCNNode *nodeB = contact.nodeB; //執行碰撞後的操作 if((nodeA.physicsBody.categoryBitMask == MaskTypeWithEnemy && nodeB.physicsBody.categoryBitMask == MaskTypeWithBullet) || (nodeA.physicsBody.categoryBitMask == MaskTypeWithBullet && nodeB.physicsBody.categoryBitMask == MaskTypeWithEnemy)){ [nodeA removeFromParentNode]; [nodeB removeFromParentNode]; } }
完結
還有些細節實現請檢視程式碼,寫得有問題的地方請提出來,互相交流。 git專案地址