ARKit中控制.dae动画的播放

思路

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

这个是结束 上面的是开始 效果一样
复制代码

代码

转载于:https://juejin.im/post/5afd55edf265da0ba17cba98

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程介绍 本套课程从技术理念到项目实践,教大家系统掌握ARKit技术开发,随心打造iOS端AR增强现实应用。由一开始的开发环境搭建,到Unity ARKit Plugin、ARFoundation ARKit等不同时期技术的讲解。从平面模型放置、识别图片、手势交互、3D物体识别、面部AR贴纸、光照估计、环境探针、多人AR技术,甚至包含ARKit3.0的动作捕捉技术等。除了以上课程内容,更加入了随着技术更新与时俱进更新的ARKit连载技术教学内容。课程收益 轻松理解ARKit的技术原理,从零到一创建自己的第一个AR项目。学习UnityARKit Plugin经典技术,了解ARKit的常见概念和原理。学会在 ARFoundation 使用ARKit,进行企业级AR项目开发。学会如何在AR项目里播放模型动画,通过触屏手势交互实现模型的旋转和缩放。 了解ARKit的图片识别,掌握AR卡片、AR书籍的实现方法。 学会使用面部增强技术,实现热门短视频应用的AR贴纸效果,实现面部表情追踪。学会使用ARKit物体检测技术,实现3D物体识别,可以广泛应用于工业、建筑、古董、玩具手办、艺展览等不同场景。学习ARKit的经典交互案例,优化AR项目的用户体验。熟练使用环境纹理、光照估计技术,让AR内容随当前现实场景自动变化调节,看起来更加真实。基于WorldMap、CollaborativeSession AR技术,实现AR场景的持久化及多人AR体验效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值