为什么80%的码农都做不了架构师?>>>
我们平时所玩的很多游戏都有着非常绚丽的动画效果,尤其是像ACT、RPG等类型的游戏,必须拥有华丽的魔法和攻击效果才能吸引玩家的眼球。本文将主要介绍如何使用OPhone API以及动画编辑器类的工具来编辑、绘制和处理精灵的动画,整个程序的运行效果如下图所示:
图1 游戏最终效果
动画原理
我们都看过动画片,看过电影,玩过游戏,里面都有各种绚丽的动画,实际上动画的绘制机制概括说来就是以一定的速度连续播放静态的图片,这样人眼就能识别出动画。一般我们人眼能够分辨的动画速度为10——20帧/秒,我们称之为FPS(Frame Per Second),低于10帧就会变得很卡,高于20就会变成快动作,因此,控制好这个数值对游戏整体的流畅度至关重要。
那么总结起来,要想在游戏中绘制动画需要具备以下几个必要元素:
1. 动画的原始图片
2.动画序列数据
3.动画刷新机制(下一帧)
常规的动画播放方法
1.实现第一步(动画的原始图片)
常规的播放2D动画方法的方法实际就是对上述过程的一个分解:在游戏中,精灵的图片经常是被放置到一张图片上,下图就是一个典型的2D游戏精灵图片:
图2 角色原始图片
2.实现第二步(设定动画序列)
有了原始图片之后,我们就可以把这些图片按照一定的尺寸进行分割,切割成的每个块我们称之为“帧”,同时为每一帧进行编号,为每帧分配唯一的索引号,索引号可以从任意数字开始,可连续也可以随机,但要注意不能有重复的索引号,分配好的索引号可参照下图所示:
表1 游戏帧序列
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
有了这个索引,我们就可以为每个动画设定动画序列了,比如,我们可以分别定义精灵的上、下、左、右行走的动画序列,每个动画的序列如下面代码所示:
final static int ACTION_UP[]={0,1,2};
final static int ACTION_DOWN[]={6,7,8};
final static int ACTION_LEFT[]={9,10,11};
final static int ACTION_RIGHT[]={3,4,5};
final static int ACTION_STAND[]={7};
数组中的每个元素值就是上面表格中的数值。
3.实现第三步(切换并播放动画)
由于所有的图片块都被放置到了一个数组中保存起来,因此有个动画数据之后,我们就可以把数据所对应的图片绘制到屏幕上了,然后,通过线程来控制一帧一帧的切换,这就是游戏中动画处理的一般过程。
上面这种播放动画的方式的优点是开发简单,便于切换和控制,但是也有如下的一些缺点:
1.存在重复的图片资源
我们可以从原始图片上看到,如果要实现精灵向下走的动画,需要使用4帧图片,但是这4帧图片中只有精灵脚步的图片存在差异,剩下的都大致相同,这就是一种图片上的浪费。而我们的手机内存目前还比较小,因此这种浪费应该是要尽量避免的。
2.动画数据直接存在于代码中
上面的例子中,我们直接把动画的序列以数组的形式保存到了代码中,也就是说,把对动画数据的处理交给了程序员来完成,而程序员的主要工作应该是处理游戏中的算法逻辑,因此,比较好的解决方法是把动画的处理交给美工和策划来完成。因此,上面这种方法也存在缺点。
3.帧图片要求规整
如果采用上面的方法绘制动画,要求每一帧必须按照指定的长度和宽度进行分割,否则就是出现绘制错误,这在一定程度上也限制了程序的灵活性。
4. 难易实现复杂的动画效果
由于帧的尺寸人为的被限定,同时每一个动作就必须要一帧进行显示,如果要想实现复杂的动画序列就意味着要存在大量的帧序列,而原始图片势必要随之变得很大,进而增加了对内存的占用,而在这种情况下,帧数越多,重复的内容也会随之不断增多,显然这也不适合手机这种资源有限的设备的特点。
上面说了这些缺点,那么该如何解决呢?下面就为你介绍一下目前比较流行的一种动画处理方式,也是本文的重点——使用动画编辑工具。
作者介绍
李建,乐成数字通信学院 高级讲师。层就职于国内数家SP,CP公司,具有丰富的软件、游戏开发经验。并从事多年教学工作,具有丰富的教学经验。目前主要从事OPhone、J2ME开发和教学方面的工作。
动画编辑器的原理
动画编辑器就是一种软件工具,它的作用就是通过软件对原始图片进行切割,然后在软件中把切割好的图片块拼成一帧一帧的图片,再选择相应的图片来组成动画序列,最后将数据导出以文件。
动画编辑器的使用
目前,很多的手机游戏公司都在使用动画编辑器来制作动画,而不同的公司使用的工具也不尽相同,下图为一个功能比较强大且使用很广泛的动画编辑工具,界面如下图所示:
图3 动画编辑器界面
接下来我将一步一步的演示如何使用这个工具来制作动画:
1. 首先要建立一个工程,这个同时点击“”按钮引入一张图片,如下图:
图4 角色原始图片
从图中我们看到,原始图片不再是规则的帧序列,精灵图片被一块块的分解了。
2. 下面我们要对图片进行分割了,选中左边栏的“”,然后在图片上进行切割,把乌龟的各个零部件都用方块圈出来,最后的效果如下图:
图5 分割原始图片
3. 接下来就可以用这些“零件”拼图了。首先我们在右上方的窗口中点击“”按钮,接下来就可以看到右侧窗口变成如下图所示的界面:
图6 frame窗口
4. 接下来双击上图中的“1-Frame”,就会出现如下图所示界面:
图7 拼图界面
5. 接下来你就可以在左边的窗口中按住鼠标左键选择你要的乌龟的零部件图片并将其拖入右边窗口的坐标系中,一般将其放置到第一象限,一个拼好的帧图片如下图所示:
图8 帧的拼图
在拼图中,你可以使用左边的一些按钮来实现各个图块的位置和对齐,如图:
图9 布局排列按钮
可以点击“”来回到帧列表界面,同时可以使用“”按键增加新的帧。按照上述方法可以拼出乌龟的上、下、左、右行走等各个帧序列,拼好的效果如下图:
图10 拼好的各个帧
6. 有了帧图片就可以利用它们来组成动画了,方法如下:
菜单中选择“Action——Edit”,效果如下图:
图11 增加动画
图12 动画编辑界面
7. 接下来在“Action Edit”窗口中通过“”按钮增加一个动画,然后就可以从Frame列表中选择相应的帧来组成相应动画了,如下图所示:
图13 设置动画
这样我们就制作好了一个乌龟向下行走的动画,同时你可以点击“”按钮来观察这个动画,并根据效果对动画进行调整,非常方便。
现在我们已经完成了整个动画的制作过程,下面就要将数据进行导出,过程如下图所示:
图14 导出数据
上面介绍了如何使用动画编辑器来设定动画,相信你已经掌握了这个工具的使用了。但是,用这个工具制作的动画并不能直接显示到手机屏幕上,还需要我们通过代码的方式来对其进行处理。下面来介绍一下如何通过代码来绘制响应的动画。
动画播放类——Animation的实现:
要想实现对动画的播放必须要有一个动画的解析类,也就是需要对“.sprite”数据文件进行解析,这里我们将这个类命名为SpriteX,这个类的作用是解析数据文件并提供动画的绘制方法,因该类内容较多,此处没有贴出源代码,请参考附件。
精灵类——Role的实现
每个精灵类都需要有个SpriteX类的对象用来处理该精灵的动画,因此需要在这个类中定义如下属性:
SpriteX spx;
精灵类的构造方法如下:
public Role(GameView view)
{
spx=new SpriteX("s.sprite",R.drawable.a2,view);
}
这里面的“view”为视图类的对象,稍后会有介绍。
注意:精灵图片需要放置到“res/drawable”文件夹,动画数据要放到“assets”文件夹下面。
精灵类的绘制方法如下:
public void paint(Canvas g)
{
spx.paint(g,100,100);
spx.nextFrame();
}
设计屏幕类
定义好精灵类只是实现了这个项目的第一步,我们还不能在屏幕上看到精灵的动画,我们需要一个视图来把精灵显示出来,接下来我们定义视图类,在这里我们自定义一个类GameView来实现这个功能。作为一个自定义的视图类,这个类需要继承View,关于这个类请参考api文档。这个类用来处理精灵的绘制,按键处理等功能。其构造方法如下:
public GameView(Context view)
{
super(view);
gamePaint = new Paint();
player=new Role(this);
// 建立线程
Thread t = new Thread(this);
t.start();
}
精灵对象创建完毕后,我们就可以将其绘制到屏幕上了,这里需要重写View类中的onDraw()方法,此方法会在该类对象被Activity调用时自动调用,其方法如下:
@Override
protected void onDraw(Canvas g)
{
role.draw(gamePaint);
}
动画的刷新
在GameView类中需要处理动画的刷新,也就是每隔一段时间来播放动画的下一帧,因此我们用线程来实现这个功能,线程的run方法如下所示:
public void run()
{
while (true)
{
// 放慢速度
long bm = System.currentTimeMillis();
// 更新屏幕
postInvalidate();
long cm = System.currentTimeMillis();
if (cm - bm < SPLASH_RATE)
try
{
Thread.sleep(SPLASH_RATE - (cm - bm));
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
在这里要注意给线程适当的休眠时间,用来控制FPS,这样有利于得到更好的游戏体验。
注意:SPLASH_RATE就是每个线程的间隔时间
定义Activity
最后我们定义一个Activity的子类用来运行该游戏,在Activitiy中我们需要创一个GameView对象,然后通过setContentView()方法进行设定,代码如下:
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
instance=this;
view=new GameView(this);
setContentView(view);
}
到此,我们已经完成了所有的功能,你可以运行模拟器并观察其效果了。
总结
使用动画编辑工具的方法可以非常方便的开发复杂的动画效果,尽可能的节约有限的内存资源并且使程序员和策划、美工的工作相分离,是现在手机端2D游戏开发普遍采用的一种高效、方便的方法。