SceneKit_入门13_骨骼动画

作者: 酷走天涯 | 来源:发表于2016-09-12 17:07 被阅读3055次

    SceneKit_入门01_旋转人物
    SceneKit_入门02_如何创建工程
    SceneKit_入门03_节点
    SceneKit_入门04_灯光
    SceneKit_入门05_照相机
    SceneKit_入门06_行为动画
    SceneKit_入门07_几何体
    SceneKit_入门08_材质
    SceneKit_入门09_物理身体
    SceneKit_入门10_物理世界
    SceneKit_入门11_粒子系统
    SceneKit_入门12_物理行为
    SceneKit_入门13_骨骼动画
    SceneKit_中级01_模型之间的过渡动画
    SceneKit_中级02_SCNView 详细讲解
    SceneKit_中级03_切换照相机视角
    SceneKit_中级04_约束的使用
    SceneKit_中级05_力的使用
    SceneKit_中级06_场景的切换
    SceneKit_中级07_动态修改属性
    SceneKit_中级08_阴影详解
    SceneKit_中级09_碰撞检测
    SceneKit_中级10_滤镜效果制作
    SceneKit_中级11_动画事件
    SceneKit_高级01_GLSL
    SceneKit_高级02_粒子系统深入研究
    SceneKit_高级03_自定义力
    SceneKit_高级04_自定义场景过渡效果
    SceneKit_高级05 检测手势点击到节点
    SceneKit_高级06_加载顶点、纹理、法线坐标
    SceneKit_高级07_SCNProgram用法探究
    SceneKit_高级08_天空盒子制作
    SceneKit_高级09_雾效果
    SceneKit_大神01_掉落的文字
    SceneKit_大神02_弹幕来袭
    SceneKit_大神03_navigationbar上的3D文字

    让学习成为一种习惯

    学习目标

    1.理解骨骼动画的含义
    2.学习骨骼动画相关的类(SCNKinner)

    开始吧

    百度百科

    当前有两种模型动画的方式:顶点动画和骨骼动画。顶点动画中,每帧动画其实就是模型特定姿态的一个“快照”。通过在帧之间插值的方法,引擎可以得到平滑的动画效果,在骨骼动画中,模型具有互相连接的“骨骼”组成的骨架结构,通过改变骨骼的朝向和位置来为模型生成动画。
    骨骼动画比顶点动画要求更高的处理器性能,但同时它也具有更多的优点,骨骼动画可以更容易、更快捷地创建。不同的骨骼动画可以被结合到一起——比如,模型可以转动头部、射击并且同时也在走路。一些引擎可以实时操纵单个骨骼,这样就可以和环境更加准确地进行交互——模型可以俯身并向某个方向观察或射击,或者从地上的某个地方捡起一个东西。多数引擎支持顶点动画,但不是所有的引擎都支持骨骼动画。

    苹果官方

    骨骼动画是一种简化复杂几何形状的动画的技术,比如游戏中人的特征,动画骨架是一个简单的控制节点的层次结构,本身没有可见的几何对象,将骨头和几何对象进行结合,当你移动这个骨头控制的节点时允许SceneKit 去自动使几何对象变形。

    给张图理解一下

    让学习成为一种习惯

    相信你已经基本了解骨骼动画的含义了。

    接下来学习一个类的使用

    • SCNKinner 能干什么?

    提供一些方法可以将节点的骨骼动画进行分离,你可以使用这个对象管理从Scene文件导入的骨骼动画与节点和几何对象之间动态关系。

    • 怎么使用骨骼动画?

    1.一般情况下,游戏设计师使用3D 工具创建一个皮肤模型,包含了骨骼的动画,保存在一个场景文件中,你从场景文件中导入这个骨骼模型,然后让他们运动起来,
    2.另外你也可以直接从场景文件中导入动画对象直接操作骨头节点
    3.您还可以单独创建一个自定义的几何和骨架数据的皮肤模型

    • 我们先找一个带骨骼的模型文件,分析一下它的结构
    让学习成为一种习惯 让学习成为一种习惯 让学习成为一种习惯

    我们先看一下完整的动画效果

    让学习成为一种习惯

    接下来我们做一个练习

    如何将一段完整的动画,分阶段执行,我们刚才看见了这段动画的时间为0~24秒左右。

    首先先介绍一个类(SCNSceneSource)

    主要用于管理场景文件的读取任务,也可以读取NSData对象哦!你懂了吧,如果这个模型,我们从网络传输的话,可能就需要使用这个类了。

    这个类不详细讲解,今天主要用到它的两个方法
    N0.1

    - (NSArray<NSString *> *)identifiersOfEntriesWithClass:(Class)entryClass;
    

    作用:

    获取场景中包含的某一类对象的标识(数组),可以获取的类型有 SCNMaterial, SCNScene, SCNGeometry, SCNNode, CAAnimation, SCNLight, SCNCamera, SCNSkinner, SCNMorpher, NSImage

    NO.2

    - (nullable id)entryWithIdentifier:(NSString *)uid withClass:(Class)entryClass;
    

    作用:

    根据对象的ID 和对象的类型,获取对象本身

    NO.3

    + (nullable instancetype)sceneSourceWithURL:(NSURL *)url options:(nullable NSDictionary<NSString *, id> *)options;
    

    作用:

    初始化方法

    NO.4

    - (nullable SCNScene *)sceneWithOptions:(nullable NSDictionary<NSString *, id> *)options error:(NSError **)error;
    

    作用:

    创建场景

    走进代码的世界

    1.创建工程(略)
    2.加载场景文件(略)
    3.添加框架SceneKit/Scenekit.h
    4.创建场景资源对象

     SCNSceneSource *sceneSource = [SCNSceneSource sceneSourceWithURL:[[NSBundle mainBundle] URLForResource:@"skinning" withExtension:@"dae"] options:nil];
    

    5.创建场景

    scnView.scene  = [sceneSource sceneWithOptions:nil error:nil];
    

    6.获取场景中的某种对象的标识数组

    // 我们获取动画类的数组
    NSArray *animationIDs =  [sceneSource identifiersOfEntriesWithClass:[CAAnimation class]];
    

    7.把每个动画帧放到一个大数组中

    NSUInteger animationCount = [animationIDs count];
    NSMutableArray *longAnimations = [[NSMutableArray alloc] initWithCapacity:animationCount];
    CFTimeInterval maxDuration = 0;
    for (NSInteger index = 0; index < animationCount; index++) {
        CAAnimation *animation = [sceneSource entryWithIdentifier:animationIDs[index] withClass:[CAAnimation class]];
        if (animation) {
            maxDuration = MAX(maxDuration, animation.duration);
            [longAnimations addObject:animation];
        }
    }
    

    8.创建一个动画组

     CAAnimationGroup *longAnimationsGroup = [[CAAnimationGroup alloc] init];
     longAnimationsGroup.animations = longAnimations;
     longAnimationsGroup.duration = maxDuration;
    

    9.截取我们要的动画阶段比如(20~24秒)

    // 截取20秒之后的动画组
    CAAnimationGroup *idleAnimationGroup = [longAnimationsGroup copy];
    idleAnimationGroup.timeOffset = 20 ;
    // 创建一个重复执行这个动画的动画组
    CAAnimationGroup *lastAnimationGroup;
    lastAnimationGroup = [CAAnimationGroup animation];
    lastAnimationGroup.animations = @[idleAnimationGroup];
    lastAnimationGroup.duration = 24.71 -20;
    lastAnimationGroup.repeatCount = 10000;
    lastAnimationGroup.autoreverses = YES;
    

    10.然后将这个动画组添加到模型节点去就可以了

    SCNNode *cNode = [scnView.scene.rootNode childNodeWithName:@"avatar_attach" recursively:YES];
    [cNode addAnimation:lastAnimationGroup forKey:@"animation"];
    

    运行一下:

    帅爆了,有没有

    提示:

    模型中的骨头只是一个位置,没有大小和形状的,如果你想要查看骨头在什么位置怎么办呢?

      // 查找骨头节点
        _skeletonNode = [_characterNode childNodeWithName:@"skeleton" recursively:YES];
    //  调用下面的方法给骨头添加一个小四方块
     - (void)visualizeBones:(BOOL)show ofNode:(SCNNode *)node inheritedScale:(CGFloat)scale
    {
     scale *= node.scale.x;
    if (show) {
        if (node.geometry == nil)
            node.geometry = [SCNBox boxWithWidth:6.0 / scale height:6.0 / scale length:6.0 / scale chamferRadius:0.5];
    }
    else {
        if ([node.geometry isKindOfClass:[SCNBox class]])
            node.geometry = nil;
    }
    
    for (SCNNode *child in node.childNodes)
        [self visualizeBones:show ofNode:child inheritedScale:scale];
    }
    

    效果如下:

    让学习成为一种习惯

    SWIFT 版本

    • 第一步 获取资源
     let source = SCNSceneSource(url: file!, options: nil)
    
    • 第二步 获取动画标志数组
      let animationIDs = source?.identifiersOfEntries(withClass: CAAnimation.self)
        var animationArray:[CAAnimation] = []
        for id in animationIDs!{
            let animation = source?.entryWithIdentifier(id, withClass: CAAnimation.self)
            animationArray.append(animation!)
        }
    
    • 第三步 创建动画数组
    let animationGroup = CAAnimationGroup()
    animationGroup.animations = animationArray
     animationGroup.duration = 21.33
     animationGroup.repeatCount = 1000
     animationGroup.beginTime = 0
    
    • 第四步 添加
       rightHetun.addAnimation(animationGroup, forKey: "an")
    
    • 什么玩意,难道我们做个游戏,光添加动画就这么复杂?

    其实不是的,实际开发过程中,我们不需要这么做的,处分你要对文件中的骨骼动画,进行时间上的调整,我们才会使用这种方法

    下面叫大家一种更为简单的方式,添加骨骼动画,找一个带骨骼动画的文件

        let file = Bundle.main.url(forResource: "hetun", withExtension: "dae")
        let source = SCNSceneSource(url: file!, options: nil)
        let  hetun = try! source?.scene(options: nil).rootNode
        // 调整位置
        hetun?.scale = SCNVector3Make(0.01, 0.01, 0.01)
        hetun?.rotation = SCNVector4Make(0, 1, 0, -Float.pi/2)
        hetun?.position = SCNVector3Make(0, 0, 0)
    

    解释一下

    我们只要获取到文件的根节点,根节点中包含文件所有的元素,然后将rootnode添加到指定的位置即可,这样我们就完成了,是不是很简单呢?

    总结

    本节内容简单的介绍了骨骼动画的概念,骨骼动画更高级的用法,我们后面再继续讲解。


    下载方式一

    qq扫码下载

    下载方式三

    请千万appstore 搜索 SceneKit 进行下载

    相关文章

      网友评论

      • 努力才幸运:这个模型自带动画,我怎么去掉自动播放动画来自己控制呢
      • 紫夜流年:楼主:第十六章 过渡动画 中
        //3.索引到模型中的几何对象
        NSURL *url1 = [[NSBundle mainBundle] URLForResource:@"boss" withExtension:@"dae"];
        NSURL *url2 = [[NSBundle mainBundle] URLForResource:@"boss_attack" withExtension:@"dae"];

        SCNScene *scene1 = [SCNScene sceneWithURL:url1 options:nil error:nil];
        SCNScene *scene2 = [SCNScene sceneWithURL:url2 options:nil error:nil];

        SCNNode *node1 = [scene1.rootNode childNodeWithName:@"plane" recursively:true];
        SCNNode *node2 = [scene2.rootNode childNodeWithName:@"plane" recursively:true];
        SCNGeometry *gry1 = node1.geometry;
        SCNGeometry *gry2 = node2.geometry;
        这样写 gry1和gry2 都为空,所以在创建一个过渡期,添加我们要过渡的模型时catNode.morpher.targets = @[gry2]; 这里会报错,我需要怎么修改呢?另是不是 少了第五步?
      • 红叶飘飘Naruto:亲, 我下载了你的SceneKit, 里面关于滤镜的介绍,发现只是针对SCNNode的, 有没有能适应整个相机场景的可能性?
        红叶飘飘Naruto:@酷走天涯 CIFilter * filter = [CIFilter filterWithName:@"CIEdgeWork"];
        self.sceneView.scene.rootNode.filters = @[filter];
        我试了根节点,看不出效果, 反而让原本的自定义3D模型看不见了
        酷走天涯:@红叶飘飘Naruto 根节点滤镜
      • 黄瓜CBS:谢谢分享。想知道模型的骨架除了Blender以外还有什么免费软件可以制作呢?Blender在导出dae的时候一直会出现模型位置变化非常大,骨架位置也很莫名其妙的情况。因为用的公司电脑开发不能随便下载软件。所以希望能有一个免费的软件。
        如果用SCNSkinner直接拿代码写,又感觉不知道怎么写,感觉看这个类的说明的时候没有提到在制作骨架的时候两个相邻骨头之间的父子关系,我觉得是哪里理解的有问题。不知道能不能讲一讲用代码直接建立一个实体然后再给实体添加骨架,然后改变骨架参数实现运动的这种示例。
      • X1aoHey:你好,想咨询一下,我现在需要绘制室内的3D地图(大型商场之类)…需要用iOS原生开发。请问SceneKit是一个好的选择吗?或者还有其他更适合的方式方法吗?我有iOS应用开发经验,但3D模型绘制没有接触过。谢谢博主。
        酷走天涯:@X1aoHey scenekit是可以开发此类项目,
      • 遮住眼睛的草纸:看了挺多你的文章,软件也下了,就是想问问,这东西和unity3D是不是差不多,如果这个上手了,去学unity3d 轻松点吗?有ios编程经验,之前做app开发的
        遮住眼睛的草纸:@酷走天涯 好的,谢谢你的回答
        酷走天涯:@东海岸鸽子王 如果想要学u3d直接,去学u3d,学习这个对学习u3d帮助不大,这个是个轻量级引擎,没有u3d强大,如果你的应用不需要和原生嵌套,就不要用这个引擎了
      • 十一岁的加重:我想问问这个能开发类似,火焰纹章的游戏吗
        酷走天涯:@十一岁的加重 可以
        十一岁的加重:@酷走天涯 火焰纹章就是站棋类游戏,
        酷走天涯:@十一岁的加重 没玩过 极速8 是用这个开发的
      • 崔杰1982:请问这个小人是用什么工具制作呀?MAC上有这个工具吗?我从某宝上买来的模型都是FBX 转换后 不好用
        酷走天涯:@Developer小虎牙 appstore 搜scenekit
        _小鱼:伟大的楼主,您写的关于SceneKit相关文章证明都看不了呀?
        酷走天涯:@崔杰1982
        随便找的
      • 645f61f52a1c:想学习一下,请问为什么不能加群了?
        酷走天涯:@bigGIfts 超载了
      • c382c56c6d23:您好,请教一下自带的动画怎么去掉?我想自己控制骨骼进行动画
        酷走天涯:@c382c56c6d23 下个3d软件自己处理一下
      • 0b31d3a77f0b:现在你讲的sceneKit为啥不推荐,国内现在sceneKit资料很少,比如说如何向opengl一样根据顶点坐标和坐标索引构建自定义的模型
        酷走天涯:@易风萧萧 就是由于资料少,没有经过市场的大量验证,冒然使用会有风险的,处分你已经对它了如执掌了,出了bug你能够找到解决方案
      • 0b31d3a77f0b:楼主,如何在app上加载3d建筑模型,哪个框架比较好些。本人刚接触3d,不是很了解,望指点一下。
        酷走天涯:@易风萧萧 建议u3d比较成熟
      • 懒闲生:应该说是骨骼动画 加上 Ar显示!!
        酷走天涯:@瞌睡农 当个摄像头就好了
      • 懒闲生:楼主 做过3d模型➕AR吗?
      • 若非长得丑怎会做逗比:我们的APP 最先就是用这个库加载骨骼动画资源,但是各种问题,最后换成cocos2dx了:grin:
        酷走天涯:@若非长得丑怎会做逗比 好的
        若非长得丑怎会做逗比:我们的资源是用那个 叫sperin 啥的做的,APP 叫 Vae+ 你可以看看:smile:
        酷走天涯:@若非长得丑怎会做逗比 有什么问题,说来听听,我去填坑
      • 酷走天涯:明天发给你,记得加群
        酷走天涯:@_洪小瑶 578734141
        俊瑶先森:@酷走天涯 群号是?
      • 叶舞清风:谢谢分享知识
        酷走天涯:@叶舞清风 喜欢就好!
        叶舞清风:@酷走天涯 学习下,张张见识
        酷走天涯:@叶舞清风 感谢你的支持!
      • 俊瑶先森::+1:这个模型能提供玩玩?
        酷走天涯:@_洪小瑶 可以的,到群里

      本文标题:SceneKit_入门13_骨骼动画

      本文链接:https://www.haomeiwen.com/subject/oxirettx.html