ARKit中控制.dae動畫的播放

lijingpei2016發表於2018-05-17

思路

1.把模型匯入 2.從dea檔案中取出動畫 裝在字典裡 3.點選3D模型的時候就在rootNode新增動畫或刪除動畫 4.用時間控制動畫--CAAnimation 裡的 timeOffset 控制開始時間 duration控制播放時間

程式碼如下

//
//  ViewController.m
//  控制動畫
//
//  Created by LJP on 15/12/17.
//  Copyright © 2017年 poco. All rights reserved.
//

#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)

#define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)

#import "ViewController.h"

@interface ViewController () <ARSCNViewDelegate>

@property (nonatomic, strong) IBOutlet ARSCNView *sceneView;

@property (nonatomic, strong) NSMutableDictionary * animations;

@property (nonatomic, strong) SCNSceneSource * sceneSource;

@property (nonatomic, assign) BOOL idle;

@end

    
@implementation ViewController

#pragma mark ======================== 生命週期 ========================

- (void)viewDidLoad {
    
    [super viewDidLoad];

    self.sceneView.delegate = self;
    
    self.sceneView.showsStatistics = YES;
    
    SCNScene *scene = [[SCNScene alloc]init];
    
    self.sceneView.scene = scene;
    
    self.animations = [[NSMutableDictionary alloc]init];
    
    self.idle = YES;
    
    [self loadAnimations];
    
    [self initBtn];
}

//按鈕控制動畫從何時開始 持續多久
- (void)initBtn {
    
    UIButton * b1 = [UIButton buttonWithType:0];
    UIButton * b2 = [UIButton buttonWithType:0];
    UIButton * b3 = [UIButton buttonWithType:0];
    
    b1.frame = CGRectMake(SCREEN_WIDTH/5-30, SCREEN_HEIGHT*0.8, 60, 30);
    [b1 setTitle:@"0-2s" forState:0];
    [b1 addTarget:self action:@selector(click1) forControlEvents:UIControlEventTouchUpInside];
    
    b2.frame = CGRectMake(SCREEN_WIDTH/5*2, SCREEN_HEIGHT*0.8, 60, 30);
    [b2 setTitle:@"2-5s" forState:0];
    [b2 addTarget:self action:@selector(click2) forControlEvents:UIControlEventTouchUpInside];
    
    b3.frame = CGRectMake(SCREEN_WIDTH/5*3+20, SCREEN_HEIGHT*0.8, 60, 30);
    [b3 setTitle:@"5-~" forState:0];
    [b3 addTarget:self action:@selector(click3) forControlEvents:UIControlEventTouchUpInside];
    
    
    [self.view addSubview:b1];
    [self.view addSubview:b2];
    [self.view addSubview:b3];
    
}

- (void)click1 {
    
    [self.sceneView.scene.rootNode removeAllAnimations];
    
    //在場景源中返回指定的類
    CAAnimation * animation1 = [self.sceneSource  entryWithIdentifier:@"twist_danceFixed-1" withClass:[CAAnimation class]];
    
    if (animation1) {
        animation1.repeatCount = 1; //動畫次數
        animation1.fadeInDuration  = 1.0; //讓動畫開始得沒那麼突兀
        animation1.fadeOutDuration = 0.5;
        
        animation1.timeOffset = 0;
        animation1.duration = 2;  //播放時間
    }
    
    [self.sceneView.scene.rootNode addAnimation:animation1 forKey:@"dancing"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self.sceneView.scene.rootNode removeAnimationForKey:@"dancing" blendOutDuration:0.5];
    });
    
    
}

- (void)click2 {
    
    [self.sceneView.scene.rootNode removeAllAnimations];
    
    //在場景源中返回指定的類
    CAAnimation * animation2 = [self.sceneSource entryWithIdentifier:@"twist_danceFixed-1" withClass:[CAAnimation class]];
    
    if (animation2) {
        animation2.repeatCount = 1; //動畫次數
        animation2.fadeInDuration  = 1.0; //讓動畫開始得沒那麼突兀
        animation2.fadeOutDuration = 0.5;
        
        animation2.timeOffset = 2;

    }
    
    [self.sceneView.scene.rootNode addAnimation:animation2 forKey:@"dancing"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self.sceneView.scene.rootNode removeAnimationForKey:@"dancing" blendOutDuration:0.5];
    });
    
}

- (void)click3 {
    
    [self.sceneView.scene.rootNode removeAllAnimations];
    
    //在場景源中返回指定的類
    CAAnimation * animation3 = [self.sceneSource entryWithIdentifier:@"twist_danceFixed-1" withClass:[CAAnimation class]];
    
    if (animation3) {
        animation3.repeatCount = 1; //動畫次數
        animation3.fadeInDuration  = 1.0; //讓動畫開始得沒那麼突兀
        animation3.fadeOutDuration = 0.5;
        
        animation3.timeOffset = 4;
    }
    
    [self.sceneView.scene.rootNode addAnimation:animation3 forKey:@"dancing"];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        [self.sceneView.scene.rootNode removeAnimationForKey:@"dancing" blendOutDuration:0.5];
    });
    
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfiguration new];

    [self.sceneView.session runWithConfiguration:configuration];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    [self.sceneView.session pause];
}


#pragma mark ======================== 私有方法 ========================

- (void)loadAnimations {
    
    SCNScene * idleScene = [SCNScene sceneNamed:@"art.scnassets/idleFixed.dae"]; //獲取.dae檔案都是用SCNScene來接收
    
    SCNNode * node = [[SCNNode alloc]init];  //一個節點可以有很多子節點
    
    for (SCNNode * child in idleScene.rootNode.childNodes) {
        [node addChildNode:child];
    }
    
    SCNVector3 extractedExpr = SCNVector3Make(0, -1, -2);
    node.position = extractedExpr;
    node.scale    = SCNVector3Make(0.2, 0.2, 0.2); //模型的大小
    
    [self.sceneView.scene.rootNode addChildNode:node];
    
    [self loadAnimationWithKey:@"dancing" sceneName:@"art.scnassets/twist_danceFixed" animationIdentifier:@"twist_danceFixed-1"];
    
}


// 把動畫準備好 裝在字典裡
- (void)loadAnimationWithKey:(NSString *)key sceneName:(NSString *)sceneName animationIdentifier:(NSString *)animationIdentifier {
    
    NSString * sceneURL = [[NSBundle mainBundle] pathForResource:sceneName ofType:@"dae"];
    
    NSURL * url = [NSURL fileURLWithPath:sceneURL];
    
    SCNSceneSource * sceneSource = [SCNSceneSource sceneSourceWithURL:url options:nil];
    self.sceneSource = sceneSource;
    
    //在場景源中返回指定的類
    CAAnimation * animationObject = [sceneSource entryWithIdentifier:animationIdentifier withClass:[CAAnimation class]];
    
    if (animationObject) {
        NSLog(@"獲取到了這個物件");
        animationObject.repeatCount = 1; //動畫次數
        animationObject.fadeInDuration  = 1.0; //讓動畫開始得沒那麼突兀
        animationObject.fadeOutDuration = 0.5;
//        animationObject.speed = 3;  //3倍速度播放
        
        [self.animations setObject:animationObject forKey:key];;
        
    }
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    NSLog(@"點選了螢幕");
    
    // 獲取到手勢的物件
    UITouch *touch = [touches allObjects].firstObject;
    
    // 手勢在SCNView中的位置
    CGPoint touchPoint = [touch locationInView:self.sceneView];
    
    //該方法會返回一個SCNHitTestResult陣列,這個陣列中每個元素的node都包含了指定的點(CGPoint)
    NSArray *hitResults = [self.sceneView hitTest:touchPoint options:nil];
    
    if (hitResults.count > 0) {
        if (self.idle) {
            [self playAnimation:@"dancing"];
        }else{
            [self stopAnimation:@"dancing"];
        }
        self.idle = !self.idle;
        return;
    }
    
}

- (void)playAnimation:(NSString *)key {
    CAAnimation * animationObject = self.animations[key];
    if (animationObject) {
        [self.sceneView.scene.rootNode addAnimation:animationObject forKey:key];
    }
}

- (void)stopAnimation:(NSString *)key {
    
    self.idle = NO;
    
    [self.sceneView.scene.rootNode removeAnimationForKey:key blendOutDuration:0.5];
    
}

@end
複製程式碼

SCNSceneSource

 管理與從檔案或資料載入場景內容相關的資料讀取任務。
 您還可以使用場景源來檢查場景檔案的內容,或選擇性地提取場景的某些元素,而不保留整個場景及其包含的所有資源。
 SceneKit可以從支援格式的檔案中讀取場景內容,也可以從儲存這種檔案內容的NSData物件中讀取場景內容。 支援的格式包括以下內容:

 ![image.png](http://upload-images.jianshu.io/upload_images/3463764-cd47d57833c2faea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

當您在Xcode專案中以DAE或Alembic格式包含場景檔案時,Xcode會自動將檔案轉換為SceneKit的壓縮場景格式,以便在構建的應用程式中使用。 壓縮檔案保留其原始的.dae或.abc副檔名。
SCNSceneSource類還可以載入在Xcode場景編輯器中建立的SceneKit存檔檔案,或者通過使用NSKeyedArchiver類來序列化SCNScene物件及其包含的場景圖形,以程式設計方式載入。
注意
為獲得最佳效果,請將放在應用程式包中的場景檔案放置在.scnassets副檔名的資料夾中,並將這些場景中引用的影象檔案放置在資產目錄中。 然後,Xcode將優化場景和紋理資源,以便在每個目標裝置上獲得最佳效能,並準備您的紋理資源以實現諸如應用細化和按需資源等交付功能。
複製程式碼

CAAnimation 的屬性

fadeInDuration

對於附加到SceneKit物件的動畫,在開始時轉換為動畫效果的持續時間。
使用此屬性在多個動畫的效果之間建立平滑過渡。 這些轉換對於使用外部3D創作工具建立的幾何動畫特別有用。
例如,從遊戲角色的場景檔案載入的幾何可能具有用於諸如步行和跳躍的玩家動作的關聯動畫。 當玩家跳躍時,如果衰退持續時間為零,則SceneKit突然從步行動畫的當前幀切換到跳躍動畫的第一幀。 如果淡入淡出持續時間大於零,則SceneKit會在該持續時間內同時播放兩個動畫,並從一個動畫到另一個動畫插入頂點位置,從而建立平滑過渡。
複製程式碼

fadeOutDuration

這個是結束 上面的是開始 效果一樣
複製程式碼

程式碼

相關文章