精华内容
下载资源
问答
  • 用UMG动画实现一个场景切换Loading进度条。

    此文介绍的也是我用UMG动画实现的一个假的Loading进度条。除了0%和100%的中间过程都是假的。
    ##GameInstance设置
    UE4在执行场景切换的时候,GameMode也是会相应的销毁和新建的,所以我们控制Loading进度的变量不能存在GameMode中,在此我选择在GameInstance中来保存Loading进度的控制变量。新建两个float变量:LoadingPerc和LoadingTime,其中LoadingTime初始值为0.8。
    先创建一个widget名为LoadingScreen,两个场景:Battle和Home。然后在GameInstance中写加载LoadingScreen和场景切换的事件。
    这里写图片描述

    ##UMG制作
    现在我们来制作LoadingScreen这个UMG。
    在Designer中加入一张图片覆盖整个屏幕,再加入Text和一个Circular Throbber,最下方是一个ProgressBar。
    这里写图片描述
    接下来在设计窗口最下方添加一个动画名为LoadingAni,添加进度条0进度到0秒的关键帧,0.8进度到1秒的关键帧。
    这里写图片描述
    如何制作UMG动画可以参考官方文档:
    https://docs.unrealengine.com/latest/CHN/Engine/UMG/UserGuide/Animation/index.html

    切换到Graph窗口,我们开始写LoadingScreen的动画播放逻辑:
    这里写图片描述

    此处播放动画LoadingAni的时候,起始点和播放时间就是由GameInstance中定义的两个变量控制的,而且播放之后还会重新set这两个变量的值,上图稍微有点乱=。=

    ##新场景的场景蓝图设置
    最后我们来带Battle场景的场景蓝图,添加如下代码:
    这里写图片描述

    按本例所做,进度条的前80%已经在Openlevel之前播放,此处是用来播放后20%的。至此,假Loading进度条的功能已经全部实现,读者可以在home中通过按键或者点击UMG按钮来触发场景切换,就能看到我们的Loading进度条功能成功实现了。

    ##NOTES

    笔者在使用OpenLevel切换场景的时候遇到了一些问题,因为OpenLevel执行时会清视口,所以前面不加delay的话,我们的loadingScreen就无法显示出来。

    展开全文
  • MG动画设计

    千次阅读 2019-01-02 20:20:52
    MG动画设计一、什么是MG动画二、制作软件PSAE[C4D]...图形创意(1)平面设计法则(2)角色设计(3)场景设计(4)点缀元素4.色彩搭配5.动画制作(1)基本原理-节奏把握、镜头衔接(2)角色动画(3)场景动画(4...

    一、什么是MG动画

    二、制作软件

    PS

    AE

    C4D

    C4D

    PR

    三、制作流程

    1.创意文案策划

    (1)选题

    (2)创意脚本

    2.脚本设计

    (1)分镜头脚本设计

    (2)转场设计

    3.图形创意

    (1)平面设计法则

    (2)角色设计

    (3)场景设计

    (4)点缀元素

    4.色彩搭配

    5.动画制作

    (1)基本原理-节奏把握、镜头衔接

    (2)角色动画

    (3)场景动画

    (4)图形演绎

    6.后期剪辑

    四、商业案例

    展开全文
  • 在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation)。逐帧动画的工作原理很...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/43536355


    在手机上去实现一些动画效果算是件比较炫酷的事情,因此Android系统在一开始的时候就给我们提供了两种实现动画效果的方式,逐帧动画(frame-by-frame animation)和补间动画(tweened animation)。逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似于动画片的工作原理。补间动画则是可以对View进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种。


    然而自Android 3.0版本开始,系统给我们提供了一种全新的动画模式,属性动画(property animation),它的功能非常强大,弥补了之前补间动画的一些缺陷,几乎是可以完全替代掉补间动画了。对于逐帧动画和补间动画的用法,我不想再多讲,它们的技术已经比较老了,而且网上资料也非常多,那么今天我们这篇文章的主题就是对Android属性动画进行一次完全解析。


    为什么要引入属性动画?


    Android之前的补间动画机制其实还算是比较健全的,在android.view.animation包下面有好多的类可以供我们操作,来完成一系列的动画效果,比如说对View进行移动、缩放、旋转和淡入淡出,并且我们还可以借助AnimationSet来将这些动画效果组合起来使用,除此之外还可以通过配置Interpolator来控制动画的播放速度等等等等。那么这里大家可能要产生疑问了,既然之前的动画机制已经这么健全了,为什么还要引入属性动画呢?


    其实上面所谓的健全都是相对的,如果你的需求中只需要对View进行移动、缩放、旋转和淡入淡出操作,那么补间动画确实已经足够健全了。但是很显然,这些功能是不足以覆盖所有的场景的,一旦我们的需求超出了移动、缩放、旋转和淡入淡出这四种对View的操作,那么补间动画就不能再帮我们忙了,也就是说它在功能和可扩展方面都有相当大的局限性,那么下面我们就来看看补间动画所不能胜任的场景。


    注意上面我在介绍补间动画的时候都有使用“对View进行操作”这样的描述,没错,补间动画是只能够作用在View上的。也就是说,我们可以对一个Button、TextView、甚至是LinearLayout、或者其它任何继承自View的组件进行动画操作,但是如果我们想要对一个非View的对象进行动画操作,抱歉,补间动画就帮不上忙了。可能有的朋友会感到不能理解,我怎么会需要对一个非View的对象进行动画操作呢?这里我举一个简单的例子,比如说我们有一个自定义的View,在这个View当中有一个Point对象用于管理坐标,然后在onDraw()方法当中就是根据这个Point对象的坐标值来进行绘制的。也就是说,如果我们可以对Point对象进行动画操作,那么整个自定义View的动画效果就有了。显然,补间动画是不具备这个功能的,这是它的第一个缺陷。


    然后补间动画还有一个缺陷,就是它只能够实现移动、缩放、旋转和淡入淡出这四种动画操作,那如果我们希望可以对View的背景色进行动态地改变呢?很遗憾,我们只能靠自己去实现了。说白了,之前的补间动画机制就是使用硬编码的方式来完成的,功能限定死就是这些,基本上没有任何扩展性可言。


    最后,补间动画还有一个致命的缺陷,就是它只是改变了View的显示效果而已,而不会真正去改变View的属性。什么意思呢?比如说,现在屏幕的左上角有一个按钮,然后我们通过补间动画将它移动到了屏幕的右下角,现在你可以去尝试点击一下这个按钮,点击事件是绝对不会触发的,因为实际上这个按钮还是停留在屏幕的左上角,只不过补间动画将这个按钮绘制到了屏幕的右下角而已。


    也正是因为这些原因,Android开发团队决定在3.0版本当中引入属性动画这个功能,那么属性动画是不是就把上述的问题全部解决掉了?下面我们就来一起看一看。


    新引入的属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。


    既然属性动画的实现机制是通过对目标对象进行赋值并修改其属性来实现的,那么之前所说的按钮显示的问题也就不复存在了,如果我们通过属性动画来移动一个按钮,那么这个按钮就是真正的移动了,而不再是仅仅在另外一个位置绘制了而已。


    好了,介绍了这么多,相信大家已经对属性动画有了一个最基本的认识了,下面我们就来开始学习一下属性动画的用法。


    ValueAnimator


    ValueAnimator是整个属性动画机制当中最核心的一个类,前面我们已经提到了,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。除此之外,ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,确实是一个非常重要的类。


    但是ValueAnimator的用法却一点都不复杂,我们先从最简单的功能看起吧,比如说想要将一个值从0平滑过渡到1,时长300毫秒,就可以这样写:

    ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
    anim.setDuration(300);
    anim.start();

    怎么样?很简单吧,调用ValueAnimator的ofFloat()方法就可以构建出一个ValueAnimator的实例,ofFloat()方法当中允许传入多个float类型的参数,这里传入0和1就表示将值从0平滑过渡到1,然后调用ValueAnimator的setDuration()方法来设置动画运行的时长,最后调用start()方法启动动画。


    用法就是这么简单,现在如果你运行一下上面的代码,动画就会执行了。可是这只是一个将值从0过渡到1的动画,又看不到任何界面效果,我们怎样才能知道这个动画是不是已经真正运行了呢?这就需要借助监听器来实现了,如下所示:

    ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
    anim.setDuration(300);
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            float currentValue = (float) animation.getAnimatedValue();
            Log.d("TAG", "cuurent value is " + currentValue);
        }
    });
    anim.start();

    可以看到,这里我们通过addUpdateListener()方法来添加一个动画的监听器,在动画执行的过程中会不断地进行回调,我们只需要在回调方法当中将当前的值取出并打印出来,就可以知道动画有没有真正运行了。运行上述代码,控制台打印如下所示:




    从打印日志的值我们就可以看出,ValueAnimator确实已经在正常工作了,值在300毫秒的时间内从0平滑过渡到了1,而这个计算工作就是由ValueAnimator帮助我们完成的。另外ofFloat()方法当中是可以传入任意多个参数的,因此我们还可以构建出更加复杂的动画逻辑,比如说将一个值在5秒内从0过渡到5,再过渡到3,再过渡到10,就可以这样写:

    ValueAnimator anim = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);
    anim.setDuration(5000);
    anim.start();
    当然也许你并不需要小数位数的动画过渡,可能你只是希望将一个整数值从0平滑地过渡到100,那么也很简单,只需要调用ValueAnimator的ofInt()方法就可以了,如下所示:
    ValueAnimator anim = ValueAnimator.ofInt(0, 100);

    ValueAnimator当中最常用的应该就是ofFloat()和ofInt()这两个方法了,另外还有一个ofObject()方法,我会在下篇文章进行讲解。


    那么除此之外,我们还可以调用setStartDelay()方法来设置动画延迟播放的时间,调用setRepeatCount()和setRepeatMode()方法来设置动画循环播放的次数以及循环播放的模式,循环模式包括RESTART和REVERSE两种,分别表示重新播放和倒序播放的意思。这些方法都很简单,我就不再进行详细讲解了。


    ObjectAnimator


    相比于ValueAnimator,ObjectAnimator可能才是我们最常接触到的类,因为ValueAnimator只不过是对值进行了一个平滑的动画过渡,但我们实际使用到这种功能的场景好像并不多。而ObjectAnimator则就不同了,它是可以直接对任意对象的任意属性进行动画操作的,比如说View的alpha属性。


    不过虽说ObjectAnimator会更加常用一些,但是它其实是继承自ValueAnimator的,底层的动画实现机制也是基于ValueAnimator来完成的,因此ValueAnimator仍然是整个属性动画当中最核心的一个类。那么既然是继承关系,说明ValueAnimator中可以使用的方法在ObjectAnimator中也是可以正常使用的,它们的用法也非常类似,这里如果我们想要将一个TextView在5秒中内从常规变换成全透明,再从全透明变换成常规,就可以这样写:

    ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
    animator.setDuration(5000);
    animator.start();

    可以看到,我们还是调用了ofFloat()方法来去创建一个ObjectAnimator的实例,只不过ofFloat()方法当中接收的参数有点变化了。这里第一个参数要求传入一个object对象,我们想要对哪个对象进行动画操作就传入什么,这里我传入了一个textview。第二个参数是想要对该对象的哪个属性进行动画操作,由于我们想要改变TextView的不透明度,因此这里传入"alpha"。后面的参数就是不固定长度了,想要完成什么样的动画就传入什么值,这里传入的值就表示将TextView从常规变换成全透明,再从全透明变换成常规。之后调用setDuration()方法来设置动画的时长,然后调用start()方法启动动画,效果如下图所示:




    学会了这一个用法之后,其它的用法我们就可以举一反三了,那比如说我们想要将TextView进行一次360度的旋转,就可以这样写:

    ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
    animator.setDuration(5000);
    animator.start();

    可以看到,这里我们将第二个参数改成了"rotation",然后将动画的初始值和结束值分别设置成0和360,现在运行一下代码,效果如下图所示:




    那么如果想要将TextView先向左移出屏幕,然后再移动回来,就可以这样写:

    float curTranslationX = textview.getTranslationX();
    ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "translationX", curTranslationX, -500f, curTranslationX);
    animator.setDuration(5000);
    animator.start();

    这里我们先是调用了TextView的getTranslationX()方法来获取到当前TextView的translationX的位置,然后ofFloat()方法的第二个参数传入"translationX",紧接着后面三个参数用于告诉系统TextView应该怎么移动,现在运行一下代码,效果如下图所示:




    然后我们还可以TextView进行缩放操作,比如说将TextView在垂直方向上放大3倍再还原,就可以这样写:

    ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleY", 1f, 3f, 1f);
    animator.setDuration(5000);
    animator.start();

    这里将ofFloat()方法的第二个参数改成了"scaleY",表示在垂直方向上进行缩放,现在重新运行一下程序,效果如下图所示:




    到目前为止,ObjectAnimator的用法还算是相当简单吧,但是我相信肯定会有不少朋友现在心里都有同样一个疑问,就是ofFloat()方法的第二个参数到底可以传哪些值呢?目前我们使用过了alpha、rotation、translationX和scaleY这几个值,分别可以完成淡入淡出、旋转、水平移动、垂直缩放这几种动画,那么还有哪些值是可以使用的呢?其实这个问题的答案非常玄乎,就是我们可以传入任意的值到ofFloat()方法的第二个参数当中。任意的值?相信这很出乎大家的意料吧,但事实就是如此。因为ObjectAnimator在设计的时候就没有针对于View来进行设计,而是针对于任意对象的,它所负责的工作就是不断地向某个对象中的某个属性进行赋值,然后对象根据属性值的改变再来决定如何展现出来。


    那么比如说我们调用下面这样一段代码:

    ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f);

    其实这段代码的意思就是ObjectAnimator会帮我们不断地改变textview对象中alpha属性的值,从1f变化到0f。然后textview对象需要根据alpha属性值的改变来不断刷新界面的显示,从而让用户可以看出淡入淡出的动画效果。


    那么textview对象中是不是有alpha属性这个值呢?没有,不仅textview没有这个属性,连它所有的父类也是没有这个属性的!这就奇怪了,textview当中并没有alpha这个属性,ObjectAnimator是如何进行操作的呢?其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的,而是会去寻找这个属性名对应的get和set方法,因此alpha属性所对应的get和set方法应该就是:

    public void setAlpha(float value);
    public float getAlpha();

    那么textview对象中是否有这两个方法呢?确实有,并且这两个方法是由View对象提供的,也就是说不仅TextView可以使用这个属性来进行淡入淡出动画操作,任何继承自View的对象都可以的。


    既然alpha是这个样子,相信大家一定已经明白了,前面我们所用的所有属性都是这个工作原理,那么View当中一定也存在着setRotation()、getRotation()、setTranslationX()、getTranslationX()、setScaleY()、getScaleY()这些方法,不信的话你可以到View当中去找一下。


    组合动画


    独立的动画能够实现的视觉效果毕竟是相当有限的,因此将多个动画组合到一起播放就显得尤为重要。幸运的是,Android团队在设计属性动画的时候也充分考虑到了组合动画的功能,因此提供了一套非常丰富的API来让我们将多个动画组合到一起。


    实现组合动画功能主要需要借助AnimatorSet这个类,这个类提供了一个play()方法,如果我们向这个方法中传入一个Animator对象(ValueAnimator或ObjectAnimator)将会返回一个AnimatorSet.Builder的实例,AnimatorSet.Builder中包括以下四个方法:

    • after(Animator anim)   将现有动画插入到传入的动画之后执行
    • after(long delay)   将现有动画延迟指定毫秒后执行
    • before(Animator anim)   将现有动画插入到传入的动画之前执行
    • with(Animator anim)   将现有动画和传入的动画同时执行

    好的,有了这四个方法,我们就可以完成组合动画的逻辑了,那么比如说我们想要让TextView先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:

    ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
    ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
    ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
    AnimatorSet animSet = new AnimatorSet();
    animSet.play(rotate).with(fadeInOut).after(moveIn);
    animSet.setDuration(5000);
    animSet.start();

    可以看到,这里我们先是把三个动画的对象全部创建出来,然后new出一个AnimatorSet对象之后将这三个动画对象进行播放排序,让旋转和淡入淡出动画同时进行,并把它们插入到了平移动画的后面,最后是设置动画时长以及启动动画。运行一下上述代码,效果如下图所示:




    Animator监听器


    在很多时候,我们希望可以监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理。这个功能是完全可以实现的,Animator类当中提供了一个addListener()方法,这个方法接收一个AnimatorListener,我们只需要去实现这个AnimatorListener就可以监听动画的各种事件了。


    大家已经知道,ObjectAnimator是继承自ValueAnimator的,而ValueAnimator又是继承自Animator的,因此不管是ValueAnimator还是ObjectAnimator都是可以使用addListener()这个方法的。另外AnimatorSet也是继承自Animator的,因此addListener()这个方法算是个通用的方法。


    添加一个监听器的代码如下所示:

    anim.addListener(new AnimatorListener() {
    	@Override
    	public void onAnimationStart(Animator animation) {
    	}
    
    	@Override
    	public void onAnimationRepeat(Animator animation) {
    	}
    
    	@Override
    	public void onAnimationEnd(Animator animation) {
    	}
    
    	@Override
    	public void onAnimationCancel(Animator animation) {
    	}
    });

    可以看到,我们需要实现接口中的四个方法,onAnimationStart()方法会在动画开始的时候调用,onAnimationRepeat()方法会在动画重复执行的时候调用,onAnimationEnd()方法会在动画结束的时候调用,onAnimationCancel()方法会在动画被取消的时候调用。


    但是也许很多时候我们并不想要监听那么多个事件,可能我只想要监听动画结束这一个事件,那么每次都要将四个接口全部实现一遍就显得非常繁琐。没关系,为此Android提供了一个适配器类,叫作AnimatorListenerAdapter,使用这个类就可以解决掉实现接口繁琐的问题了,如下所示:

    anim.addListener(new AnimatorListenerAdapter() {
    });
    这里我们向addListener()方法中传入这个适配器对象,由于AnimatorListenerAdapter中已经将每个接口都实现好了,所以这里不用实现任何一个方法也不会报错。那么如果我想监听动画结束这个事件,就只需要单独重写这一个方法就可以了,如下所示:
    anim.addListener(new AnimatorListenerAdapter() {
    	@Override
    	public void onAnimationEnd(Animator animation) {
    	}
    });


    使用XML编写动画


    我们可以使用代码来编写所有的动画功能,这也是最常用的一种做法。不过,过去的补间动画除了使用代码编写之外也是可以使用XML编写的,因此属性动画也提供了这一功能,即通过XML来完成和代码一样的属性动画功能。


    通过XML来编写动画可能会比通过代码来编写动画要慢一些,但是在重用方面将会变得非常轻松,比如某个将通用的动画编写到XML里面,我们就可以在各个界面当中轻松去重用它。


    如果想要使用XML来编写动画,首先要在res目录下面新建一个animator文件夹,所有属性动画的XML文件都应该存放在这个文件夹当中。然后在XML文件中我们一共可以使用如下三种标签:

    • <animator>  对应代码中的ValueAnimator
    • <objectAnimator>  对应代码中的ObjectAnimator
    • <set>  对应代码中的AnimatorSet

    那么比如说我们想要实现一个从0到100平滑过渡的动画,在XML当中就可以这样写:

    <animator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="0"
        android:valueTo="100"
        android:valueType="intType"/>
    而如果我们想将一个视图的alpha属性从1变成0,就可以这样写:
    <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="1"
        android:valueTo="0"
        android:valueType="floatType"
        android:propertyName="alpha"/>
    其实XML编写动画在可读性方面还是挺高的,上面的内容相信不用我做解释大家也都看得懂吧。


    另外,我们也可以使用XML来完成复杂的组合动画操作,比如将一个视图先从屏幕外移动进屏幕,然后开始旋转360度,旋转的同时进行淡入淡出操作,就可以这样写:

    <set xmlns:android="http://schemas.android.com/apk/res/android"
        android:ordering="sequentially" >
    
        <objectAnimator
            android:duration="2000"
            android:propertyName="translationX"
            android:valueFrom="-500"
            android:valueTo="0"
            android:valueType="floatType" >
        </objectAnimator>
    
        <set android:ordering="together" >
            <objectAnimator
                android:duration="3000"
                android:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType" >
            </objectAnimator>
    
            <set android:ordering="sequentially" >
                <objectAnimator
                    android:duration="1500"
                    android:propertyName="alpha"
                    android:valueFrom="1"
                    android:valueTo="0"
                    android:valueType="floatType" >
                </objectAnimator>
                <objectAnimator
                    android:duration="1500"
                    android:propertyName="alpha"
                    android:valueFrom="0"
                    android:valueTo="1"
                    android:valueType="floatType" >
                </objectAnimator>
            </set>
        </set>
    
    </set>
    这段XML实现的效果和我们刚才通过代码来实现的组合动画的效果是一模一样的,每个参数的含义都非常清楚,相信大家都是一看就懂,我就不再一一解释了。


    最后XML文件是编写好了,那么我们如何在代码中把文件加载进来并将动画启动呢?只需调用如下代码即可:

    Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
    animator.setTarget(view);
    animator.start();
    调用AnimatorInflater的loadAnimator来将XML动画文件加载进来,然后再调用setTarget()方法将这个动画设置到某一个对象上面,最后再调用start()方法启动动画就可以了,就是这么简单。


    好的,通过本篇文章的学习,我相信大家已经对属性动画的基本用法已经有了一个相当不错的认识,并把最常用的一些功能都掌握好了,那么本篇文章的内容就到这里,下篇文章当中会继续介绍属性动画,讲解ValueAnimator和ObjectAnimator的高级用法,感兴趣的朋友请继续阅读 Android属性动画完全解析(中),ValueAnimator和ObjectAnimator的高级用法 。


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • 另外,此场景设计简单,代码简洁,共使用430多行代码便实现了简单的场景设计与基本操作,并且每一句代码都有非常详细的注释,相信就算是一点opengl基础没有也可通过学习本项目轻松入门。 二、运行说明 1、电脑版本:...

    一、项目说明
    这个opengl场景设计是我们的计算机图形学课程期末答辩大作业。此项目实现了一个简单的室内场景设计,此场景由沙发、茶几、桌子、水壶、地板、墙壁等元素构成,其中地板与墙壁采取了纹理贴图,并可使用键盘实现与场景的交互,可以实现视角漫游、物体旋转,控制光照等功能。另外,此场景设计简单,代码简洁,共使用430多行代码便实现了简单的场景设计与基本操作,并且每一句代码都有非常详细的注释,相信就算是一点opengl基础没有也可通过学习本项目轻松入门。
    二、运行说明
    1、电脑版本:win10
    2、运行软件:Visual Studio 2019
    3、运行说明:
    在vs 2019的安装目录中放入opengl函数库(可根据网上教程),然后打开vs2019,创建工程,新建.cpp文件,把源代码粘贴过去,编译运行即可。
    也可下载如下图的计算机图形学场景设计压缩包,解压后直接点击.sln文件,进入vs2019后,即可运行。
    三、部分源代码:

    
    #pragma comment(lib,"opengl32.lib")
    #pragma comment(lib,"glut32.lib")
    #pragma comment(lib,"glu32.lib")
    #pragma comment( lib, "glaux.lib")		// GLaux连接库
    #include <stdlib.h>
    #include <stdio.h>
    #include <math.h>
    #include "glut.h"
    #include <windows.h>
    #include "GLAUX.H"	
    
    #include <iostream>
    using namespace std;
    
    GLuint	texture[4];	//定义纹理
    
    static float ttrans[3] = { 2.64 ,2.64 ,2.64 };//ttrans[3]初始值为观察点的位置
    float  a = 0;//场景绕y旋转角度
    
    
    void makeImage(void);
    
    int myinit(void);
    
    void display(void);
    
    void myReshape(GLsizei w, GLsizei h);
    GLuint g_bitmap[3];//纹理对应的编号 定义纹理的映射个数 
    //定义纹理、控制滤波、创建纹理
    bool  LoadGLTextures(char *filename, GLuint &texture)	//加载位图文件 方法0
    {
    	AUX_RGBImageRec *pImage = NULL;
    	pImage = auxDIBImageLoadA(filename); //装入位图
    	if (pImage == NULL)
    		return false;
    	glGenTextures(1, &texture); //生成贴图
    	glBindTexture(GL_TEXTURE_2D, texture); //贴图生效,绑定此纹理,让之后的任何纹理指令都可以配置当前绑定的纹理
    	//为当前绑定的纹理对象设置环绕、过滤方式
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //缩小时线性滤波
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_REPEAT); //扩大时线性滤波
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //纹理可以在s(X)方向上重复映射 
    	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//纹理可以再t(Y)方向上重复映射
    	gluBuild2DMipmaps(GL_TEXTURE_2D, 4, pImage->sizeX, pImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pImage->data); //生成纹理,将载入的位图文件(*.bmp)转换成纹理贴图。
    	/*
    	GL_TEXTURE_2D  此纹理是一个2D纹理
    	4代表颜色成分是R(红色分量)、G(绿色分量)、B(蓝色分量)、Alpha
    	pImage->sizeX  纹理的宽度
    	pImage->sizeY  纹理的高度
    	GL_RGB, 告诉OpenGL图像数据由红、绿、蓝三色数据组成
    	GL_UNSIGNED_BYTE  组成图像的数据是无符号字节类型
    	pImage->data  告知opengl纹理数据的来源
    	*/
    	
    	free(pImage->data);//释放位图内存
    	free(pImage);
    	return true;
    }
    void  LoadTextMap(void) //加载图
    {
    
    	LoadGLTextures("floor.bmp", g_bitmap[0]);//加载第一个图
    	LoadGLTextures("wall.bmp", g_bitmap[1]);
    	
    
    }
    
    int myinit(void)
    
    {
    	
    	LoadTextMap();//加载图
    	
    	glShadeModel(GL_SMOOTH);//	glShadeModel函数用于控制opengl中绘制指定两点间其他点颜色的过渡模式,	GL_SMOOTH会出现过渡效果,				
    	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);	//使用当前缓冲区清除值,四个分量分别代表红、绿、蓝、Alpha		
    	glClearDepth(1.0f);	//指定清除深度缓存时使用的深度值					
    	glEnable(GL_DEPTH_TEST);	// 用来开启更新深度缓冲区的功能,OpenGL在绘制的时候就会检查,当前像素前面是否有别的像素				
    	glDepthFunc(GL_LEQUAL);	//gl_lequal,如果输入的深度值小于或等于参考值,则通过
    
    	
    	//定义光源的颜色和位置
        glClearColor(0.0, 0.1, 0.1, 0.0);
    	GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 };//环境光
        GLfloat diffuse[] = { 1.0, 1.0, 1.0, 1.0 };//漫射光
        GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 };//镜面光
        GLfloat position[] = { 0.0, 3.0, 2.0, 0.0 };//光源位置
        GLfloat lmodel_ambient[] = { 0.4, 0.4, 0.4, 1.0 };//选择光照模型
    
     
    
        
    //设置光照
        glDepthFunc(GL_LESS);
        glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);//创建环境光
        glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);//创建漫射光
        glLightfv(GL_LIGHT0, GL_POSITION, position);//设置光源位置
        glEnable(GL_LIGHTING);//启动光照
        glEnable(GL_LIGHT0);// Enable Light 0  启用0号光源
    
    
    
     
     
    	return 1;
    
    
    }
    
     void safa()//沙发
     {
    
    	  GLfloat mat_diffuse1[] = { 1,0.5,0.4};//    材料的散射颜色
    	  GLfloat mat_diffuse3[] = {1,0,0};
    	  GLfloat mat_diffuse8[] = {0.82, 0.41,0.12};
    	  GLfloat mat_diffuse5[] = {0.5, 0.25,0.12};
    	 glPushMatrix();//将当前矩阵保存入堆栈顶(保存当前矩阵)。
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse3);//材质属性
    	 glTranslatef(0,-0.75,-0.5);//表示将当前自图形向x轴平移0,向y轴平移-0.75,向z轴平移-0.5
    	 auxSolidBox(15,0.6,6);//长方体绘制函数,参数分别为长、宽、高
    	 glPopMatrix(); //栈顶矩阵被弹出
    	 //沙发左边板子
    
    
    	 glPushMatrix();
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
    	 glTranslatef(0,-2.05,-0.5);
    	 auxSolidBox(16,2,6);
    	 glPopMatrix();//沙发右边边板子
    
    	 glPushMatrix();
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
    	 glTranslatef(0,0.45,2.5);
    	 auxSolidBox(16,7,2);
    	 glPopMatrix();//沙发后板子
    
    
    
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glRotatef(90,-1.0,0.0,0.0);//按照向量[-1.0,0.0,0.0]旋转
    	 glTranslatef(-7.8,3.0,-0.2);
    	 glScalef(1,3,1);
    	 /*
    	 void glScalef(GLfloat x, 
             GLfloat y, 
             GLfloat z);
    
              参数x,y,z分别为模型在x,y,z轴方向的缩放比。
    
    	 */
    	 glutSolidCube(1.2);//绘制立方体函数,1.2为尺寸
    	 auxSolidCylinder(1,5.5);//画圆柱
    	 glPopMatrix();//沙发把手
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glRotatef(90,-1.0,0.0,0.0);
    	 glTranslatef(7.8,3.0,-0.2);
    	 glScalef(1,3,1);
    	 glutSolidCube(1.2);
    	 auxSolidCylinder(1,5.5);
    	 glPopMatrix();//沙发把手
     }
     void desk()//桌子
     {
    	 GLfloat mat_diffuse1[] = { 0.7,0.3,0.24};
    	 GLfloat mat_diffuse5[] = { 0.3, 1,0.7};
    	 GLfloat mat_diffuse8[] = {0.52, 0.21,0.62};
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(4,0,-7);
    	  auxSolidCylinder(0.2,3);//画圆柱
    	 glPopMatrix();//桌子腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(-4,0,-7);
    	  auxSolidCylinder(0.2,3);
    	 glPopMatrix();//桌子腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(-4,0,-12);
    	 auxSolidCylinder(0.2,3);
    	 glPopMatrix();//桌子腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(4,0,-12);
    	 auxSolidCylinder(0.2,3);
    	 glPopMatrix();//桌子腿
    
    	 glPushMatrix();
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
    	 glTranslatef(0,0.85,-9.5);
    	 auxSolidBox(10,0.3,6);//桌面
    	 glPopMatrix();
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse5);
    	 glTranslatef(0,2,-9.5);
    	auxSolidTeapot(1);//茶壶
    	 glutSolidSphere(1,100,100);
    	 glPopMatrix();
     }
    
    
     void chaji()//床
     {
    	 GLfloat mat_diffuse1[] = { 0.2,0.7,0.4};
    	 GLfloat mat_diffuse5[] = { 0.9, 0.9,0.9};
    	 GLfloat mat_diffuse8[] = {0.32, 0.81,0.12};
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(4,0,-7);
    	 auxSolidCylinder(0.4,3);
    	 glPopMatrix();//床腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(-4,0,-7);
    	 auxSolidCylinder(0.4,1.5);
    	 glPopMatrix();//床腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(-4,0,-12);
    	 auxSolidCylinder(0.4,1.5);
    	 glPopMatrix();//床腿
    
    	 glPushMatrix();	
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse8);
    	 glTranslatef(4,0,-12);
    	 auxSolidCylinder(0.4,1.5);
    	 glPopMatrix();//床腿
    
    	 glPushMatrix();
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse1);
    	 glTranslatef(0,0.85,-9.5);
    	 auxSolidBox(10,0.3,6);//床面,绘制长方体
    	 glPopMatrix();
    
    	 glPushMatrix();
    	 glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse5);
    	 glTranslatef(0,1.15,-9.5);
    	 auxSolidBox(10,0.9,6);//床面
    	 glPopMatrix();
    
     }
     //绘制地面
    

    四、运行截图
    1、场景运行截图
    在这里插入图片描图一述2、场景设计源码压缩包目录展示

    在这里插入图片描述
    在这里插入图片描述3、在vs 2019中部分源代码展示
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述想要完整项目代码的,可私聊我获取

    展开全文
  • 本文总结了矢量图形和矢量动画的使用方法。
  • 游戏场景管理设计

    千次阅读 2011-11-22 16:16:37
    这里值得说明的是,这个场景类继承不是绝对的标准,我们根据游戏需求更好的去完善和修改它。 例如, SoundObject 我们可以从 IObject 继承实现,也可以从 IRenderObject 去继承,这需要看你对 Render 的理解了...
  • Android材料设计动画之触摸反馈

    千次阅读 2017-03-10 16:35:37
    前言在Android 5.0版本发布时,所公布的Android材料设计动画更新幅度很大,有各种各样,各种场景下的动画动画对于增强用户体验的感受有着超级重要的贡献,为APP适量添加合适的动画,会让APP“生龙活虎”,焕发...
  • 小议游戏场景管理设计

    千次阅读 2008-06-12 09:32:00
    游戏场景管理设计Author:FreeKnight_DuzhiBlog: http://hi.baidu.com/freedomknightduzhiData: 2008-6-11允许转载,转载请保留头部信息。前言如之前博客中所说,最近一直忙于业余游戏开发项目中。我一直是个比较懒散...
  • 一、序大家好,我是承香墨影!我又来写 Airbnb 开源的 Lottie 动画了,之前写的几篇文章,反响还不错,今天继续说说 Lottie 预览效果的问题,单独的 Lottie-JSON...
  • logo,开始按钮,关于按钮需要通过查看图片像素定制好相应大小,如下199×96。 制作各类根节点 使用父节点来管理子节点,方便以后管理UI动画 UI_ROOT、anchor_center、start_anim_root、loading_door ...
  • 游戏设计情境探秘之动画

    千次阅读 2015-06-30 11:38:51
    动作可以用来塑造角色、宾语和规则。《蜘蛛女皇》中手持盾牌的奴隶比其他角色移动更慢,但只能从背面或侧面被攻击到,玩家需要为之做更多的准备。...4.10 盾牌奴隶的行走动画 玩家很清楚,那些活的运动的物体是重
  •  好了废话不多说了,我们开始正式的内容,这次要说的内容有两个,一个是3D场景的渲染,另一个就是骨骼动画的显示。( PS:代码较长,文章之中只给出了实现思路,如果需要完整的代码,可以留下邮箱或者私信我! ) ...
  • SVG 矢量和矢量动画介绍

    千次阅读 2017-06-03 20:22:04
    根标签,表示一个矢量动画 支持的属性: 1)android:name: 定义矢量图形的名称 2)android:width: 定义Drawable的宽度,支持所有dimension单位,一般使用dp。drawable的宽度不一定是最终绘制...
  • 如今,短视频应用百花齐放,为了吸引更多流量,动画设计师和开发工程师往往为了制作夺人眼球,极具创意的动画特效花费大量心血,但最终动画需求交付上线的效率和质量,仍旧不尽如人意。问题出在哪里?主要还是工具...
  • 图像和动画Graphics and Animation 介绍Xamarin.Android 中的图像和动画An Introduction to Graphics and Animation in Xamarin.Android 概述Overview 考虑到运行的地方,通常,设备电源都是有限的,绝大多数移动...
  • 三维图形及动画软件开发流程

    千次阅读 2007-05-30 13:46:00
     三维图形及动画场景的显示,就是把所建立的三维空间模型,经过计算机的 复杂处理,最终在计算机二维屏幕上显示的过程。一般,设计三维图形软件要经 过以下步骤: A.图元建立三维模型。(以OpenGL(Open Graphics...
  • 以基本的捕鱼游戏为例,功能做得比较简单,因为我是个新人,刚学完C++不久,后面做的也...其实后面鱼类的游动,捕鱼网的动画以及子弹都是在游戏界面里面设计。在这里先不讲,只讲场景吧。 添加背景图片: auto ba
  • Silverlight的基础动画包括...相信看过上一篇《偏移动画(TranslateTransform)》文章的朋友大多数对Silverlight & Blend动画设计已经产生了莫大的兴趣,本篇将继续介绍Silverlight中的基础动画之旋转动画(RotateTrans
  • 本文将详细介绍 Android 动画中 属性动画的原理 & 使用 相关文章阅读: 1. Android 动画系列 Android 动画:手把手教你使用 补间动画 Android 动画:你真的会使用插值器与估值器吗?(含详细实例教学) 2. ...
  • CompositionTarget是一个静态类,表示您的应用程序要在其上进行绘制的显示面。 每次绘制应用程序的场景时,都会引发  Rendering  事件。 呈现帧速率是指每秒绘制场景的次数。”弄了好半天才搞明白,其实说白了...
  • Unity Shader实现简单水体效果 效果展示 ...用贴图和uv动画模拟水...贴图流动的实质就是uv偏移,图片各个部分的偏移程度有区别(可以借助噪声让uv偏移程度具有随机性的区别)就实现了扭曲效果。让偏移程度与...
  • 前言因为工作中有用到,所以我抽出空闲把之前的LinkageAnimation优化了一下,如果有类似的需求(比如场景中有大量的物体,都按照同一频率在运动),那么这个工具可能适合你,当然如果你的环境是2017,TimeLine会是一...
  • Android动画之旅-Android动画基本介绍

    千次阅读 2016-04-13 17:18:22
    在上一篇博客中,我们简单了解了在Android中的动画特效,小伙伴们是不是意犹未尽呀。还没有看的猛戳这里:Android动画之旅一开篇动画简介 本篇博客,将和大家一起来分析Android中的四大动画,准备好了吗?GO~ GO~ GO...
  • 这个教程很简单也好学,希望对初步认识FLASH的动画爱好者有帮助,废话少说,下面我们开始制作 教程吧 教程准备,准备一张带水的风景图片为例,其他没有什么需求 一,首先打开FLASH软件8.0,这个版本比较好用,设置...
  • 动画的多种实现方式与性能对比

    万次阅读 2018-07-11 10:42:47
    概述 前面我分享了《Web动画形式》,各种动画...帧动画一般用来实现稍微复杂一点的动画效果,同时希望动画更细腻,设计师更自由的发挥。他可以定义到每一个时间刻度上的展现内容,我们一般用帧动画来做页面的Loadi...
  • 转载请标明出处:...优点:相对于其他开源的第三方图片加载库,Fresco拥有更好的内存管理和强大的功能,基本上能满足所有的日常使用场景。缺点:整体比较大,不过目前的版
  • 为什么设计师的眼里饱含着泪水,因为对页面效果爱的深沉。本篇就介绍一下transform。(擎天柱:Autobot transform!) transform本质上是一系列变形函数,分别是translate位移,scale缩放,rotate旋转,skew...
  • 动画设计的12条基本原理

    千次阅读 2012-05-12 18:57:20
    动画设计的12条基本原理   1. Squash and Stretch 挤压和拉伸 l 定义: 这个操作产生了对重量和体积的一个假象。同时挤压和拉伸在面部表情和动画的对话框处理也有用到。 l 例子: 1) 一个弹动的球在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,809
精华内容 19,523
关键字:

动画场景设计图