Play Xcode : SceneKitで遊ぼう!+力を加える

20150618_002

SceneKit 第3弾はです。

物体に力を加えて遊んじゃいましょう!

※Xcode7 でうまく動作しないことがわかり修正しました

遊んだシステム構成

Xcode : ver.7.0.1
Language : Objective-C
Device : iPhone5s / iOS 9.0.2

プロジェクトファイルの作成

はい、いつもの様にプロジェクトファイルの作成

20150507_001

Create New Xcode projectを選ぶ

20150507_002

Gameを選んで「Next」をクリック

20150618_003

Product Name:PlayXcodeSceneKit003 ※自由に決めて問題ありません

Language:Objective-C ※世の中の流れに抵抗してみます(笑)

Game Technology:SceneKit

「Next」を押して、保存先を決めて「Create」

20150618_004

プロジェクトファイル完成です。

ソースコードの断捨離

いつもの様にソースコードの断捨離を行いますが、流用しますのでコメントアウトに
しておきたいと思います。

GameViewController.mを選択します。

20150618_005

– (void)viewDidLoadを2箇所コメントアウト
コメントアウトのショートカットは、command + /

ソースコードの追加・修正

– (void)viewDidLoadの最終行に追加・修正していきます

シーンのクラス作成

20150618_006

カメラの追加

20150618_007

物体を遠くまで表示させたいので、zFarを300に設定

全体を照らす光源の配置

20150618_008

影を付ける為のスポットライトの配置

20150618_009

床の配置・床の当たり判定

20150618_0101

シーンをViewに反映

20150618_011

背景の色変更

20150618_012

背景が真っ黒ではカッコ悪いのでちょっと赤色の下線部のように変更します。

動作確認 その1

20150618_013

こんな感じになりました。
どうです、うまく動作していますか?

次は、戦闘機を配置します。

オブジェクトの追加

力を加えるオブジェクトである戦闘機を追加
フロアをタップしたら現れるようにします。

戦闘機の配置

GameViewController.mの最終行にを追加します。

20150618_0161

赤い下線の @”shipMesh”を間違えると後動かなくなりますので注意してください。

フロアをタップすると戦闘機を配置する

画面をタップすると呼ばれるメソッド
– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeを修正します

ソースコードの削除

20150618_014

赤い部分をすべて削除します

20150618_017

この様にします。
この消した所にフロアをタップしたかどうかの判定をいれていきます。

最初にタップしたオブジェクトの選出

20150618_018

フロアタップの判定

20150618_019

フロアがタップされたら、戦闘機を表示するメソッドに行く様にします。

動作確認 その2

20150618_020

ちょっと、沢山タップしすぎました・・・・。

ここまで、動作に問題ありませんか?

戦闘機表示は1台にする

沢山戦闘機があっても・・・。

戦闘機は1台限定にします。

戦闘機を削除する

20150618_024

– (void)removeStarFighterNodeを追加します。
“shipMesh”に間違いがあると動きませんので注意。

– (void)removeStarFighterNodeは、戦闘機を配置する前に呼ぶようにします。

フロアタップ判定部分に追記

20150618_026

これで戦闘機は1台しか表示されなくなりました。

次は、この戦闘機に力を加えます。

戦闘機に力を加える

前準備が長かったですね、ここからが本題です。
タップした戦闘機がテイクオフする様に力を加えます。

戦闘機に力を加える

20150618_021

赤い下線部 “shipMesh”間違うと動きませんので注意です。

戦闘機のタップ判定

– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeに追加します

20150618_022

赤い部分が追加したソースコードです。

動作確認 その3

20150618_023

飛びました〜!!

でも、なんか・・・。

よし!壁を作りましょう!

壁の配置

空をタップしたら複数のブロックでできた壁が現れる様にします。

ブロックの配置と削除

20150618_0271

– (void)createManyBoxNodeで複数のブロックが作られ
– (void)removeBoxNodeで複数のブロックを削除します。

空をタップしたら壁を表示する

20150618_028

– (void) handleTap:(UIGestureRecognizer*)gestureRecognizeの赤く囲った部分を追記

動作確認 その4

いけぇ〜!!!

20150618_029

う〜ん、もっと派手に行ってほしいなぁ〜!

細かい調整は、皆さんにお任せします。

タップ!タップ!して遊ぶ!

何気に、楽しいかも〜!

タップ!タップ!して遊んじゃいましょう!

そして、改造して遊んじゃいましょう!

完成ソースコード

@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

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存

フォローする

スポンサーリンク