SceneKit 第3弾は力です。
物体に力を加えて遊んじゃいましょう!
※Xcode7 でうまく動作しないことがわかり修正しました
遊んだシステム構成
Xcode : ver.7.0.1
Language : Objective-C
Device : iPhone5s / iOS 9.0.2
プロジェクトファイルの作成
はい、いつもの様にプロジェクトファイルの作成
Create New Xcode projectを選ぶ
Gameを選んで「Next」をクリック
Product Name:PlayXcodeSceneKit003 ※自由に決めて問題ありません
Language:Objective-C ※世の中の流れに抵抗してみます(笑)
Game Technology:SceneKit
「Next」を押して、保存先を決めて「Create」
プロジェクトファイル完成です。
ソースコードの断捨離
いつもの様にソースコードの断捨離を行いますが、流用しますのでコメントアウトに
しておきたいと思います。
GameViewController.mを選択します。
– (void)viewDidLoadを2箇所コメントアウト
コメントアウトのショートカットは、command + /
ソースコードの追加・修正
– (void)viewDidLoadの最終行に追加・修正していきます
シーンのクラス作成
カメラの追加
物体を遠くまで表示させたいので、zFarを300に設定
全体を照らす光源の配置
影を付ける為のスポットライトの配置
床の配置・床の当たり判定
シーンをViewに反映
背景の色変更
背景が真っ黒ではカッコ悪いのでちょっと赤色の下線部のように変更します。
動作確認 その1
こんな感じになりました。
どうです、うまく動作していますか?
次は、戦闘機を配置します。
オブジェクトの追加
力を加えるオブジェクトである戦闘機を追加
フロアをタップしたら現れるようにします。
戦闘機の配置
GameViewController.mの最終行にを追加します。
赤い下線の @”shipMesh”を間違えると後動かなくなりますので注意してください。
フロアをタップすると戦闘機を配置する
画面をタップすると呼ばれるメソッド
– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeを修正します
ソースコードの削除
赤い部分をすべて削除します
この様にします。
この消した所にフロアをタップしたかどうかの判定をいれていきます。
最初にタップしたオブジェクトの選出
フロアタップの判定
フロアがタップされたら、戦闘機を表示するメソッドに行く様にします。
動作確認 その2
ちょっと、沢山タップしすぎました・・・・。
ここまで、動作に問題ありませんか?
戦闘機表示は1台にする
沢山戦闘機があっても・・・。
戦闘機は1台限定にします。
戦闘機を削除する
– (void)removeStarFighterNodeを追加します。
“shipMesh”に間違いがあると動きませんので注意。
– (void)removeStarFighterNodeは、戦闘機を配置する前に呼ぶようにします。
フロアタップ判定部分に追記
これで戦闘機は1台しか表示されなくなりました。
次は、この戦闘機に力を加えます。
戦闘機に力を加える
前準備が長かったですね、ここからが本題です。
タップした戦闘機がテイクオフする様に力を加えます。
戦闘機に力を加える
赤い下線部 “shipMesh”間違うと動きませんので注意です。
戦闘機のタップ判定
– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeに追加します
赤い部分が追加したソースコードです。
動作確認 その3
飛びました〜!!
でも、なんか・・・。
よし!壁を作りましょう!
壁の配置
空をタップしたら複数のブロックでできた壁が現れる様にします。
ブロックの配置と削除
– (void)createManyBoxNodeで複数のブロックが作られ
– (void)removeBoxNodeで複数のブロックを削除します。
空をタップしたら壁を表示する
– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeの赤く囲った部分を追記
動作確認 その4
いけぇ〜!!!
う〜ん、もっと派手に行ってほしいなぁ〜!
細かい調整は、皆さんにお任せします。
タップ!タップ!して遊ぶ!
何気に、楽しいかも〜!
タップ!タップ!して遊んじゃいましょう!
そして、改造して遊んじゃいましょう!
完成ソースコード
GameViewController.mのソースコード
@implementation GameViewController - (void)viewDidLoad { [super viewDidLoad]; // // create a new scene // SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/ship.dae"]; // // // create and add a camera to the scene // SCNNode *cameraNode = [SCNNode node]; // cameraNode.camera = [SCNCamera camera]; // [scene.rootNode addChildNode:cameraNode]; // // // place the camera // cameraNode.position = SCNVector3Make(0, 0, 15); // // // create and add a light to the scene // SCNNode *lightNode = [SCNNode node]; // lightNode.light = [SCNLight light]; // lightNode.light.type = SCNLightTypeOmni; // lightNode.position = SCNVector3Make(0, 10, 10); // [scene.rootNode addChildNode:lightNode]; // // // create and add an ambient light to the scene // SCNNode *ambientLightNode = [SCNNode node]; // ambientLightNode.light = [SCNLight light]; // ambientLightNode.light.type = SCNLightTypeAmbient; // ambientLightNode.light.color = [UIColor darkGrayColor]; // [scene.rootNode addChildNode:ambientLightNode]; // // // retrieve the ship node // SCNNode *ship = [scene.rootNode childNodeWithName:@"ship" recursively:YES]; // // // animate the 3d object // [ship runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:2 z:0 duration:1]]]; // retrieve the SCNView SCNView *scnView = (SCNView *)self.view; // // set the scene to the view // scnView.scene = scene; // allows the user to manipulate the camera scnView.allowsCameraControl = YES; // show statistics such as fps and timing information scnView.showsStatistics = YES; // configure the view scnView.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:1.0 alpha:1.0]; // add a tap gesture recognizer UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; NSMutableArray *gestureRecognizers = [NSMutableArray array]; [gestureRecognizers addObject:tapGesture]; [gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers]; scnView.gestureRecognizers = gestureRecognizers; // create a new scene SCNScene *scene = [SCNScene new]; // カメラの設置 SCNNode *cameraNode = [SCNNode node]; cameraNode.camera = [SCNCamera camera]; cameraNode.position = SCNVector3Make(0, 18, 20); cameraNode.rotation = SCNVector4Make(1, 0, 0, - 0.4); cameraNode.camera.zFar = 300.f; [scene.rootNode addChildNode:cameraNode]; // 全体を照らす光源の配置 SCNNode *ambientLightNode = [SCNNode node]; ambientLightNode.light = [SCNLight light]; ambientLightNode.light.type = SCNLightTypeAmbient; ambientLightNode.light.color = [UIColor lightGrayColor]; [scene.rootNode addChildNode:ambientLightNode]; // 影を付ける為のスポットライトの配置 SCNNode *lightNode = [SCNNode node]; lightNode.light = [SCNLight light]; lightNode.light.type = SCNLightTypeSpot; lightNode.light.color = [UIColor whiteColor]; lightNode.light.spotOuterAngle = 180; lightNode.light.castsShadow = YES; lightNode.position = SCNVector3Make(0, 50, 0); lightNode.rotation = SCNVector4Make(1, 0, 0, -3.14 / 2.0); lightNode.name = @"spotLight"; [scene.rootNode addChildNode:lightNode]; // 床の配置 SCNFloor *floorGround = [SCNFloor floor]; floorGround.firstMaterial.diffuse.contents = [UIColor orangeColor]; SCNNode *floorNode = [SCNNode node]; floorNode.geometry = floorGround; floorNode.position = SCNVector3Make(0, 0, 0); floorNode.name = @"groundFloor"; // 床への物体の当たり判定の設定 floorNode.physicsBody = [SCNPhysicsBody staticBody]; [scene.rootNode addChildNode:floorNode]; // set the scene to the view scnView.scene = scene; } - (void) handleTap:(UIGestureRecognizer*)gestureRecognize { // retrieve the SCNView SCNView *scnView = (SCNView *)self.view; // check what nodes are tapped CGPoint p = [gestureRecognize locationInView:scnView]; NSArray *hitResults = [scnView hitTest:p options:nil]; // check that we clicked on at least one object if([hitResults count] > 0){ // retrieved the first clicked object SCNHitTestResult *hitResult = [hitResults objectAtIndex:0]; SCNNode *hitResultNode = hitResult.node; // floor touch if ([hitResultNode.name isEqualToString:@"groundFloor"] ) { [self removeStarFighterNode]; [self createStarFighterNode]; } // star fighter touch if ([hitResultNode.name isEqualToString:@"shipMesh"]) { [self tekeOffStarFighterNode]; } } else { [self removeBoxNode]; [self createManyBoxNode]; } } - (BOOL)shouldAutorotate { return YES; } - (BOOL)prefersStatusBarHidden { return YES; } - (UIInterfaceOrientationMask )supportedInterfaceOrientations { if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { return UIInterfaceOrientationMaskAllButUpsideDown; } else { return UIInterfaceOrientationMaskAll; } } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } #pragma mark - 戦闘機の配置 - (void)createStarFighterNode { // ファイルからモデルを読み込む SCNScene *scene = [SCNScene sceneNamed:@"art.scnassets/ship.dae"]; SCNNode *starFighterNode = [scene.rootNode childNodeWithName:@"ship" recursively:YES]; // モデルの初期設定 starFighterNode.position = SCNVector3Make(0.0, 10.0, 0.0); starFighterNode.rotation = SCNVector4Make(0, 1, 0, 3.14); starFighterNode.name = @"shipMesh"; // モデルに重力等を追加 starFighterNode.physicsBody = [SCNPhysicsBody dynamicBody]; // Sceneに追加 SCNView *scnView = (SCNView *)self.view; [scnView.scene.rootNode addChildNode:starFighterNode]; } - (void)removeStarFighterNode { // Star Fighter node 取得 SCNView *scnView = (SCNView *)self.view; SCNNode *starFighterNode = [scnView.scene.rootNode childNodeWithName:@"shipMesh" recursively:YES]; // Sceneに存在していたら削除 if (starFighterNode != nil) { [starFighterNode removeFromParentNode]; } } - (void)tekeOffStarFighterNode { // Star Fighter node 取得 SCNView *scnView = (SCNView *)self.view; SCNNode *starFighterNode = [scnView.scene.rootNode childNodeWithName:@"shipMesh" recursively:YES]; // 力を与える [starFighterNode.physicsBody applyForce:SCNVector3Make(0, 15, -80) impulse:YES]; } #pragma mark - 壁の配置 - (void)createManyBoxNode { [self removeBoxNode]; double startPx = -22.5; double startPy = 0.0; double stepPx = 2.5; double stepPy = 1.5; for (double px = 0; px < 20; px ++) { for (double py = 0 ; py < 10; py ++) { double boxPx = startPx + stepPx * px; double boxPy = startPy + stepPy * py; [self createBoxNode:SCNVector3Make(boxPx, boxPy + py * 1.5, -50)]; } } } - (void)createBoxNode:(SCNVector3 )vecter3 { SCNGeometry *boxGeometory = [SCNBox boxWithWidth:2.5 height:1.5 length:4.5 chamferRadius:0.0]; boxGeometory.firstMaterial.diffuse.contents = [UIColor colorWithRed:[self randomColorNumber] green:[self randomColorNumber] blue:[self randomColorNumber] alpha:1.0]; SCNNode *boxGeometoryNode = [SCNNode nodeWithGeometry:boxGeometory]; boxGeometoryNode.position = vecter3; boxGeometoryNode.name = @"boxGeometory"; boxGeometoryNode.physicsBody = [SCNPhysicsBody dynamicBody]; boxGeometoryNode.physicsBody.mass = 0.01f; boxGeometoryNode.physicsBody.restitution = 0.2f; SCNView *scnView = (SCNView *)self.view; [scnView.scene.rootNode addChildNode:boxGeometoryNode]; } - (CGFloat)randomColorNumber { double colorNumber = arc4random_uniform(100); return colorNumber / 200.0 + 0.5; } - (void)removeBoxNode { // Star Fighter node 取得 SCNView *scnView = (SCNView *)self.view; while ([scnView.scene.rootNode childNodeWithName:@"boxGeometory" recursively:YES]) { SCNNode *boxGeometoryNode =[scnView.scene.rootNode childNodeWithName:@"boxGeometory" recursively:YES]; [boxGeometoryNode removeFromParentNode]; } } @end
コメント