zoukankan      html  css  js  c++  java
  • Unity3D初学之2D动画制

    作者:Alex Rose

    Unity最近宣布推出额外的2D游戏支持,添加了Box 2D物理和一个精灵管理器。

    但这里还是有些技巧需要牢记在心。逐帧更改图像只是动画制作的冰山一角,若要让你的游戏出色运行,你还得使用转换和旋转等功能。

    现在让我们先从基本技巧开始。

    更改帧

    如果你已经准备好了制作动画的纹理,你可能会使用SpriteManager脚本的付费版本,或者Unity的新版本。假设你使用的是2D位面和纹理。这就是一个低效率的方法,但如果你是在制作一个game jam的项目,你可能会想塞入一些可行而好看,但却不一定有效的元素。这也是一种覆盖了所有步骤的全面方法,如果是在精灵管理器中则可能被删除某些步骤。

    首先,你将需要一个公开的Texture[] 阵列,所以你可以将纹理拖入到Unity编辑器中的对象,以及一个在Start()中初始化到0的整数currentTexturep。下一步你需要一个像这样运行的NextTexture() 函数:

    NextTexture(){
    currentTexture++;
    if(currentTexture>=textureArray.Length) currentTexture=0;
    AnimatedPlane.renderer.material.mainTexture = textureArray[currentTexture];}

    有两种简便的方法可以调用这种函数:协同程序递归和固定间隔。

    使用固定间隔是最快的方法(但较不精确)。你需要一个整数计数器,在你的Start()函数中初始化到0,以及一个FixedUpdate() 函数(游戏邦注:每次都会更新,你可以在Unity时间管理器中自己调整)。

    在FixedUpdate()中放置你的条件句(例如if(walking)),并在其中用conter++增加你的计时器,之后设置如下声明:

    if(counter>=animationDelay){
    counter=0;
    NextTexture();
    }

    这里的animationDelay可以是你自己选择的任意值。这将以持续速度(取决于你在Unity时间管理器中设置的速度)推进帧。

    第二个方法是使用递归。但这一方法的劣势在于不易处理条件句,但你还是能够获得所需要的准确延时。如果你想让特定帧延长或缩短,这一方法就尤其管用。你需要一个IEnumerator TextureChanger() 以及to StartCoroutine(TextureChanger()) in Start().

    IEnumerator TextureChanger(){
    yield return new WaitForSeconds(timeInterval);
    if([conditions]) NextTexture();
    }

    这里timeInterval也是你自己选择的任意值。有了这些函数,你就可以将任意数量的纹理拖到GameObject,这样只要你提供正确的条件,它就会正确运行动画。

    现在让我们做一些更有趣的操作。

    平滑移动到一个点

    以下公式是制作Unity 2D动画的一个诀窍:

    where 0 < slidespeed < 1. I recommend 0.1f as a good slidespeed value.

    这个公式允许你把对象完美移动到一个点。在滑动GUI、角色控制、关卡生成、摄像跟随、褪色/移位等操作中尤其管用。

    descension(from gamasutra)

    descension(from gamasutra)

    这是我即将发布的新游戏《Rotation Station》中的一个高级版本,是从一个较低点移到一个较高点,最后变成一个小泡。每个贴图都会根据该公司向下移动,但每个贴图都有随机的延时,随机的初始化旋转(也使用这一公式旋转至它所期望的方向)。

    关于角色控制的例子可以参考我最近推出的《Rude Bear Radio》,在这个项目中,该公式运用于制作流畅的鼠标控制方法。

    Bearo-Wing(from gamasutra)

    Bearo-Wing(from gamasutra)

    那么,让我们看看如何将其运用于上述例子。

    首先,我们需要知道鼠标位于2D区域。为了找到它,我们要先将这个代码放置于滑动GameObject的FixedUpdate()函数中:

    Vector3 MousePosition = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
    Input.mousePosition.y, transform.position.z-Camera.main.transform.position.z));

    这里使用了鼠标的X和Y轴位置,以及从摄像机到滑动GameObject的距离来确定鼠标的2D位置和3D坐标。现在我们要从这个环节的开端来调整该公式。所以,要记住Unity中的2D步骤。

    transform.position += new Vector3((MousePosition.x-transform.position.x),
    (MousePosition.y-transform.position.y),0)*slidespeed;

    你就只要这两行代码就搞定了!至于GUI这类东西,你可以在之后编写一个声明:

    if(Mathf.Abs(finalvalue-currentvalue)

    让我们再看另一个例子,《Rude Bear Radio》中的困难模式Mario台阶。

    Super-Bario-Bros(from gamasutra)

    Super-Bario-Bros(from gamasutra)

    其中的背景使用以下公式由黑变白:

    background.renderer.material.color =
    (1-factor)*background.renderer.material.color+factor*desiredcolor;

    你从中可以看到它遵从的基本形式,简写就是Next = current+(final-current)*factor。代码会检查R值是否处于一定的色彩范围,如果是,它就会更改factor,令其更为迅速地褪色。如果R值非常接近于1,它就会将其所需颜色设置为黑。你还可以检查下R、G和B,并以一个阵列推进颜色。你可以在我的第二个案例项目《Rude Bear Rising》的背景中看到这种例子。

    目前来看,这些都很简单,你可以照搬公式做。而下一步操作则需要考虑更多因素。

    三角法和数学的重要性

    三角法对动画制作来说非常重要。就算有了优秀的帧,也不一定能够令它们看起来生动美丽,有时候你根本不需要帧就能做事。

    例如,我首次进入Ludum Dare工作室时,我的室友就给我画了几张图。我至今仍然记得其中的每个角色,用什么方法呢?像木偶一样将角色绑在棍子上操作。

    RudeBear(from gamasutra)

    RudeBear(from gamasutra)

    这种移动方式非常简单,转换时用正弦(sin),旋转时用余弦(cos)。

    为了创造这种动画,你要让波纹停止并在你松手和输入时继续,否则这种动作就会极端分散。

    所以你需要一个首要变量(即我所谓的walkbob),只要对象还在移动,它就会在FixedUpdate中增加Time.deltaTime。之后就制作你的函数:

    translation = maxHeight*Mathf.Sin(speed*walkbob);
    rotation =  maxRoll*Mathf.Cos(speed*walkbob/2);

    然后将位置和旋转设为这些值(例如transform.position = new Vector3(transform.position.x,translation,transform.position.y))。

    这可以处理类似那种动作,但是还有一种动画需要考虑更多因素,这就是我所谓的三角舞,它用于制作可爱角色随着音乐摇摆起舞,例如下图:

    Rude-Bear-Radio(from gamasutra)

    Rude-Bear-Radio(from gamasutra)

    首先,你在打算移动游戏对象时,就要选取一个浮动的initialtime = Time.time,这样你的对象才能以正确的位置和方向开始,并且不会突然跳入动作。

    下一步,我们就要想想三角函数的概念。

    我们使用简谐运动,其形式如下:

    Y是指当前值,A是振幅,f是频率,t是运行时间,phi是指阶段。首先,我们很容易确定振幅。它是我们希望对象所具有的最大化高度或旋转。

    下一个就是运行时间和阶段。我们将用(Time.time-initialtime)轻松取代t而一次性处搞定这两者。这会将φ降为0,所以最后我们只需要得到频率。我强烈推荐令此频率与你的音乐频率吻合(如果你是自己作曲,这一点很容易办到)。

    如果你还不知道自己音乐的BPM,那就去摸索每个节拍,直到弄懂为止。如果你已经有节奏感,这就很好办了。如果你没有,那也不用担心,我们会利用中心极限定理。持续点触你的整首歌,每欠点触都会减少平均值中的错误。

    现在你就知道它每分钟如何打节拍了。你将以60来划分这个值,找到每秒多少拍。如果你只想在半小节或一个完整的小节中使用一次动作,那就可以按2或4数值来划分。这个数值就是频率,你可以从 Mathf.PI获得pi。所以现在你要将对象的位置设为该数值。此时你只是在调整高度:

    transform.position = new Vector3(transform.position.x,maxheight*
    Mathf.Sin(2*Mathf.PI*frequency*(Time.time-initialtime)),transform.position.z);

    但这还不够好。首先,我们要让对象合拍,这样它就得从其最大振幅开始。我们此时要使用余弦。但更重要的是,要让它从一端跳跃到另一端,这样它就不会像波纹一样滑上滑下。这时要用cos^2,这样它才会突然停在0标记,并再次走向正数。因此:

    transform.position = new Vector3(transform.position.x,maxheight*Mathf.Pow(Mathf.Cos(
    2*Mathf.PI*frequency*(Time.time-initialtime)),2),transform.position.z)

    这里要注意舞动的高度。最终旋转要使用正弦,这样其旋转和转化就是异相的。因此:

    transform.rotation = Quaternion.EulerAngles(0, maxRotation*
    Mathf.Cos(2*Mathf.PI*frequency*Mathf.Sin(Time.time-initialtime)), 0);

    这里要记住两件事:如果你使用一个位面,并希望它面对摄像机,这些值就不能是0和0,而必须是pi/2和–pi/2。这里我使用的是EulerAngles而不是Euler,因为Euler使用的是度数,而EulerAngles使用的是弧度。我们要做一些数学运算,我们现在要运用弧度,所以得使用EulerAngles!否则你之扣就得输入一个换算因数。

    在此你可以看到我新游戏的一种类似动画,你可以用同种方法更改比例而不是位置。

    RoStlogo(from gamasutra)

    RoStlogo(from gamasutra)

    现在我们要讨论最后一种动画类型:

    纹理补偿

    你可以用自己所学到的一切来操作2D纹理补偿,以制作美妙的动画背景。你可以在《Rude Bear Radio》及其主界面中看到我对《VVVVVV》的拙劣模仿。抓取一个位面,在其上粘附一个重复纹理,编写一个FixedUpdate() 函数,并根据下属性进行调整:

    renderer.material.mainTextureOffset
    renderer.material.color

    这将导致墙体四处滑动并改变颜色。最后,如果你想让它们看起来更有趣,还可以运用renderer.material.mainTextureScale。这可以制作一个真正有趣的视觉效果,但它很有干扰性,不要让它影响你的主要游戏玩法。

    最后,你还得看一下其中的插值。这可以让动画制作更轻松,但我个人认为在第二部分中最好使用公式

    http://express.ruanko.com/ruanko-express_56/tech-overnight5.html

    作为一款制作3D的跨平台的游戏引擎,相对与其他2D游戏的开发工具(如cocos2d、cocos2d-x等),Unity有许多优点,如可视化、所见即所得;如跨平台;如开放的插件平台等等。作为一个初学者,简单分享一下Unity3D制作2D动画的经验。

    Unity3D制作2D动画方法也有多种:

    1、动画帧序列

    作为常见的方法,相信很多开发者都会用。与cocos2d一样,Unity中也可以使用动画帧使你的角色动起来。示例代码:

    	
    using UnityEngine;
    using System.Collections;
    
    public class AnimateDemo : MonoBehaviour {
    	
    	public Texture[] frames;
    	public float framesPerSecond=10;
    	
    	private int index;
    	private float dTime=0;
    
    	// Use this for initialization
    	void Start () {
    		
    	}
    	
    	// Update is called once per frame
    	void Update () {
    		AnimaOne();
    		AnimaAlways();
    	}
    	
    	void AnimaAlways(){
    		int nowIndex=(int)(Time.time *framesPerSecond) % frames.Length;
    		if(index!=nowIndex){
    			index  = nowIndex;
    			renderer.material.mainTexture = frames[index];
    		}
    	}
    		
    	void AnimaOne(){
    		dTime+=Time.deltaTime;
    		if(dTime>1/framesPerSecond){
    			dTime-=1/framesPerSecond;
    			renderer.material.mainTexture = frames[++index];
    			if(index+1>=frames.Length){
    				
    			}
    		}
    	}
    }
    

    这里代码很详细,没有执行过,到时动画帧按照这种思路可以轻松实现。AnimaOne方法可以在动画最后一帧的时候实现其他控制;AnimaAlways方法则一直循环播放动画。

    2、动画渲染

    有些情况动画帧太多或者太浪费资源,我们就把动画做在一张图片上,控制相机或者材质渲染图片的位置。个人认为这种方法比较适合数据,Tag的动态显示。

    例如:

    	
    public Texture imgTexture; //申明一张图片
    Vector2 scaleVec2=new Vector2(0.1f,1);// 控制好渲染的图片的区域
    renderer.material.mainTexture= imgTexture;// 动画的渲染实现
    renderer.sharedMaterial.SetTextureScale("_MainTex", scaleVec2);
    

    3、上面介绍了简单的动画效果,其实在游戏中,我们经常要使游戏里的角色们动起来,位移,旋转,等等。

    在Unity中,没有刚体的Object的位移和旋转都是通过Transform属性来控制的。transform下有position和rotation属性,且Unity提供了普通旋转和位移的方法。最常用的就是通过坐标增量来控制Object的动作。这里比较难缠的一点就是相对位移和绝对位移。所以在做object移动的时候要格外小心,是相对世界坐标还是其parent的坐标。

    其次iTween是做2D动画比较常用的插件。其官网和其他博客对方法的使用有比较详细的介绍。这里叙述一点,就是关于iTween.Hash().iTween提供了hash表,使得简单的动画更加自然和多样。hash的参数根据iTween不同的方法而稍有差异。

    比较常用的:

    • x,y,z,表示目标Vectore3的量;
    • time,时间;
    • easeType,过渡方式,平滑,直线,或者其他;
    • incomplete,当前iTween动画结束调用的动作或这方法;等等。
  • 相关阅读:
    带你了解什么是Push消息推送
    小白学习如何打日志
    最近学到的前后端分离知识
    记一次愚蠢的操作--线程安全问题
    在工作中常用到的SQL
    在工作中常用的Linux命令
    记一次愚蠢的经历--String不可变性
    在公司做的项目和自己在学校做的有什么区别?
    程序员的快乐就是这么朴素无华且枯燥
    《大型网站系统与Java中间件》读书笔记 (中)
  • 原文地址:https://www.cnblogs.com/lihonglin2016/p/4343159.html
Copyright © 2011-2022 走看看