ARCore 使用 SceneForm 框架 —— 使用 ObjectAnimator 实现旋转、移动动画

对应 Sceneform 框架,官方给出了几个示例代码,其中一个示例工程 solarsystem (太阳系),给出了旋转动画是如何实现的

旋转动画

为了使代码看着简单,对示例代码中实现旋转的部分做了简化,就有了旋转节点 —— RotatingNode

class RotatingNode : Node() {
    var rotationAnimation: ObjectAnimator? = null
    val animationDuration = 5000L

    // 启动动画
    fun startAnimation() {
        if (rotationAnimation != null) {
            return
        }

        rotationAnimation = createAnimator()
        rotationAnimation!!.target = this
        rotationAnimation!!.duration = animationDuration
        rotationAnimation!!.start()
    }

    // 停止动画
    fun stopAnimation() {
        if (rotationAnimation == null) {
            return
        }

        rotationAnimation!!.cancel()
        rotationAnimation = null
    }

    // 返回一个 ObjectAnimator 用来使节点旋转起来
    private fun createAnimator(): ObjectAnimator {
        // 节点的位置和角度信息设置通过Quaternion来设置
        // 创建4个Quaternion 来设置四个关键位置
        val orientation1 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 0f)
        val orientation2 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 120f)
        val orientation3 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 240f)
        val orientation4 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 360f)
        val rotationAnimation = ObjectAnimator()
        rotationAnimation.setObjectValues(orientation1, orientation2, orientation3, orientation4)
        // 设置属性动画修改的属性为 localRotation
        rotationAnimation.setPropertyName("localRotation")
        // 使用Sceneform 框架提供的估值器 QuaternionEvaluator 作为属性动画估值器
        rotationAnimation.setEvaluator(QuaternionEvaluator())
        //  设置动画重复无限次播放。
        rotationAnimation.repeatCount = ObjectAnimator.INFINITE
        rotationAnimation.repeatMode = ObjectAnimator.RESTART
        rotationAnimation.interpolator = LinearInterpolator()
        rotationAnimation.setAutoCancel(true)
        return rotationAnimation
    }

}

移动动画

示例代码虽然给出了旋转动画是怎么实现的,但是没有找到移动动画的示例代码

好吧,只能先看看旋转动画实现依赖的类,旋转动画的核心是构建动画 createAnimator,构建动画过程中关键的两个类 QuaternionQuaternionEvaluator
在这里插入图片描述
去对应的包名下,发现还有 Vector3Vector3Evaluator 两个类
在这里插入图片描述
google 的工程师都是有很多年经验的,命名肯定是有一定规则,很明显这个名字就意味着和移动相关,再看看源码
在这里插入图片描述
这里说的是线性插值的估计,基本可以断定就是实现移动动画的类,接着仿照旋转节点实现移动节点 TranslatingNode

class TranslatingNode : Node() {
    private var instance = this
    var animation : ObjectAnimator? = null
    var animationDuration = 5000L   // 动画运行时长
    var isAutoDisamiss = true       // 是否自动消失的开关

    // 启动动画
    fun startAnimation() {
        if (animation != null) {
            return
        }

        animation = createAnimator()
        animation!!.target = this
        animation!!.duration = animationDuration
        animation!!.start()
    }

    // 停止动画
    fun stopAnimation() {
        if (animation == null) {
            return
        }

        animation!!.cancel()
        animation = null
    }


    // 返回一个 ObjectAnimator 用来使节点旋转起来
    private fun createAnimator(): ObjectAnimator {
        // 动画开始位置
        val start = Vector3(0f, 0f, 0f)
        // 动画结束位置
        val end = Vector3(0f, 2f, 0f)
        var transAnimation = ObjectAnimator()
        transAnimation.setObjectValues(start, end)
        // 设置属性动画修改的属性为 localPosition
        transAnimation.setPropertyName("localPosition")
        transAnimation.setEvaluator(Vector3Evaluator())
        transAnimation.interpolator = LinearInterpolator()
        transAnimation.setAutoCancel(true)
        transAnimation.addListener(object : Animator.AnimatorListener{
            override fun onAnimationStart(p0: Animator?) {}
            override fun onAnimationCancel(p0: Animator?) {}
            override fun onAnimationRepeat(p0: Animator?) {}
            override fun onAnimationEnd(p0: Animator?) {
                // 上升到最顶部的时候,自动消失
                if (isAutoDisamiss) {
                    instance.isEnabled = false
                }
            }
        })
        return transAnimation
    }
}

测试

代码基本都是完成了,需要一段测试代码

        arFragment!!.setOnTapArPlaneListener { hitResult: HitResult, plane: Plane, motionEvent: MotionEvent ->
            ModelRenderable.builder()
                    .setSource(this@MainActivity, R.raw.andy)     // 应该使用 BillBoardDetail 的 rollingModelPath 属性,动态获取旋转模型的资源
                    .build().thenAccept(
                    { renderable ->
                        val anchor = hitResult.createAnchor()
                        val anchorNode = AnchorNode(anchor)
                        anchorNode.setParent(arFragment!!.getArSceneView().scene)

//                        val showNode = TranslatingNode() // 测试移动
                        val showNode = RotatingNode()       // 测试旋转
                        showNode.setParent(anchorNode)

                        val show = TransformableNode(arFragment!!.transformationSystem)
                        show.setParent(showNode)
                        show.renderable = renderable
                        showNode.startAnimation()
                    })
        }

在这里插入图片描述
在这里插入图片描述

总结

旋转和移动的动画效果,也可以不使用 ObjectAnimator 实现,直接控制模型的姿态也可以实现,只是相对简单的姿态是用代码控制会比较繁杂,不如直接使用动画简洁

发布了23 篇原创文章 · 获赞 22 · 访问量 3877

猜你喜欢

转载自blog.csdn.net/qq_19154605/article/details/103854643