分享2D Unity游戏的动画制作经验
作者: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、角色控制、关卡生成、摄像尾随、褪色/移位等操作中尤其管用。
这是我即将公布的新游戏《Rotation Station》中的一个高级版本号,是从一个较低点移到一个较高点,最后变成一个小泡。
每一个贴图都会依据该公司向下移动,但每一个贴图都有随机的延时。随机的初始化旋转(也使用这一公式旋转至它所期望的方向)。
关于角色控制的样例能够參考我近期推出的《Rude Bear Radio》,在这个项目中,该公式运用于制作流畅的鼠标控制方法。
那么,让我们看看怎样将其运用于上述样例。
首先,我们须要知道鼠标位于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台阶。
当中的背景使用下面公式由黑变白:
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》的背景中看到这样的样例。
眼下来看,这些都非常easy,你能够照搬公式做。而下一步操作则须要考虑很多其它因素。
三角法和数学的重要性
三角法对动画制作来说很重要。就算有了优秀的帧,也不一定可以令它们看起来生动漂亮,有时候你根本不须要帧就能做事。
比如,我首次进入Ludum Dare工作室时,我的室友就给我画了几张图。我至今仍然记得当中的每一个角色,用什么方法呢?像木偶一样将角色绑在棍子上操作。
这样的移动方式很easy。转换时用正弦(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))。
这能够处理类似那种动作。可是另一种动画须要考虑很多其它因素。这就是我所谓的三角舞,它用于制作可爱角色随着音乐摇摆起舞。比例如以下图:
首先。你在打算移动游戏对象时。就要选取一个浮动的initialtime = Time.time,这样你的对象才干以正确的位置和方向開始,而且不会突然跳入动作。
下一步,我们就要想想三角函数的概念。
我们使用简谐运动,其形式例如以下:
Y是指当前值,A是振幅,f是频率,t是执行时间,phi是指阶段。首先,我们非常easy确定振幅。它是我们希望对象所具有的最大化高度或旋转。
下一个就是执行时间和阶段。
我们将用(Time.time-initialtime)轻松代替t而一次性处搞定这两者。
这会将φ降为0,所以最后我们仅仅须要得到频率。我强烈推荐令此频率与你的音乐频率吻合(假设你是自己作曲。这一点非常easy办到)。
假设你还不知道自己音乐的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!否则你之扣就得输入一个换算因数。
在此你能够看到我新游戏的一种类似动画,你能够用同种方法更改比例而不是位置。
如今我们要讨论最后一种动画类型:
纹理补偿
你能够用自己所学到的一切来操作2D纹理补偿。以制作美妙的动画背景。你能够在《Rude Bear Radio》及其主界面中看到我对《VVVVVV》的拙劣模仿。抓取一个位面,在其上粘附一个反复纹理,编写一个FixedUpdate() 函数。并依据下属性进行调整:
renderer.material.mainTextureOffset
renderer.material.color
这将导致墙体四处滑动并改变颜色。
最后。假设你想让它们看起来更有趣,还能够运用renderer.material.mainTextureScale。
这能够制作一个真正有趣的视觉效果,但它非常有干扰性,不要让它影响你的主要游戏玩法。
最后,你还得看一下当中的插值。
这能够让动画制作更轻松,但我个人觉得在第二部分中最好使用公式。