上一次,我主要聊了一下当前手机游戏开发的平台选择和开发环境的安装,也具体的谈了谈开发J2ME程序的简单入门方法。由于当前在手机上做游戏开发的大部分同志们用的都是J2ME,所以朋友们又催我继续深入谈一谈J2ME的开发技巧。本人所学甚浅,许多地方也都只是触及皮毛,因此只能简单的谈谈我在手机游戏的开发中碰到的一些问题和我个人采用的解决方案。另外我把平时在论坛里收集到的部分技巧提供给大家以作参考,这里特别感谢那些无私奉献自己知识的人们。如果在文章中存在什么错误,还请各位老鸟海涵。希望我这篇文章能起到一点抛砖引玉的作用吧。J
开发技巧这个东西是颇不好谈的。还记得我最早学习J2ME的时候,曾经在SUN的网站上看到过一篇文章,题目是《如何提高J2ME程序的效率》,作者口气强烈的要求j2me代码"寸土寸金",仿佛令我回忆起了当年在单片机上的编程,其中有几个观点到现在还记得,比如不到万不得已不要创建新的类,限制使用接口数量,还有要缩短变量或函数名称等等...我刚开始照着这个那个规范编程的时候,反而在许多地方束缚了自己,搞的这也不好做,那也不好办。呵呵,这大概就是尽信书不如无书吧。所以,我在聊技巧的时候不会给大家设置很多条条框框,只讲一下对某个问题通常的解决方法,希望能对大家有一点帮助。再次感谢那些在网上和论坛上给我提供过帮助的朋友们。
游戏贴图
说起来千头万绪,不如就从最常用的贴图技巧开始说起吧。
MIDP手机程序的标准图片格式是PNG(便携式网络图片格式)。这里值得注意的是,不同的手机平台对于图片的要求也满"挑食"的,同样是PNG格式却不一定适用于所有平台。我就碰到过这样的情况,利用WinXP自带画图板生成的PNG格式图片,在WTK的标准模拟器上可以正常显示,到了西门子模拟器上却怎么也显示不出来。经过一番折腾,我在Photoshop中重新生成了新的PNG格式图片后才顺利的显示出。网上的一些朋友也曾问我,为什么在模拟器上运行正常的图片在真实设备上却无法显示。我也只能对他说多换几种生成图片的工具试试看喽。另外,因为图片资源会占用较大空间,所以应该尽量保证其尺寸小,数量少。用不同的编辑工具存储PNG位图时,其文件的大小会有很大的不同,你可以尝试多使用几种工具,选择其中存储最小的来使用。在这里我推荐一个工具:Image Optimizer。它可以在不影响图象品质的前提下将图象减肥,最高可减少50%以上,真的很神奇呦J
要把图片加载到你的应用程序中,需要调用Image.createImage()这个函数,并且需要做相关的异常处理,所以我一般会在MIDlet中定义一个工具函数,LoadImage()
具体代码如下:
//加载图片
public Image LoadImage(String path)
{
try
{
return Image.createImage(path); //成功则返回图片对象
}
catch(Exception e)
{
e.printStackTrace(); //不成功则打印错误信息并返回空值
return null;
}
}
如果有人问我,jar中什么是最占地方的?什么是最让你头疼的?我当然会毫不犹豫地说是图片,是PNG图片了。但是一个好的游戏又怎能少得了那些精美的图片呢?一个经常使用的窍门是将许多的图片文件合并到一个图片文件中来,这样可以在总体上减小将图片占用的空间。最有代表性的例子就是精灵动画了
在文件中载入这类大图像后,可以采用以下的方法来绘出动画的各个帧
g.setClip(x, y, FRAME_WIDTH, FRAME_HEIGHT);
g.drawImage(fiveMenImage, x - FRAME_WIDTH * frameNumber, y, Graphics.TOP | Graphics.LEFT);
其中 x,y 为您绘图的起始坐标,FRAME_WIDTH和FRAME_HEIGHT为大图像的宽度和高度,frameNumber值由0-7的循环。这样你就可以制造出一幅精灵正在行走的动画了。但要注意,如果还有其他的图片需要绘制,请重置你的剪辑窗口。
此外,当然是地图的绘制了
我们的大地图,通常是由许多的小块拼出来的,并会用一个数组来保存地图各个位置的地形和状态,然后统一的在paint方法中刷出整张地图来。
实例代码如下:
HouseVector = new Vector(); //设置一个动态数组存放截下来的图片
Image ImageTemp = null; //存放大图片
Graphics g1 = null;
try
{
ImageTemp = Image.createImage("/res/image/house.png"); //加载整张大图片
}
catch(Exception exception) { }
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
Image image_element = Image.createImage(16, 16);//作为截图的小图片
g1 = image_element.getGraphics(); //获取小图片的图形设备
g1.drawImage(ImageTemp, -16 * j, -16 * i, 20); //开始截图
HouseVector.addElement(image_element); //添加到图片数组中
}
}
ImageTemp = null;
g1 = null;
此后你的HouseVector中就是一块一块在大图片中截取下来的矩形小图片了。以后按照预先设计好的地图数组,直接刷图就是了,你的游戏地图不就pp的秀出来了么?J
绘图技巧
我们通常做游戏是免不了需要使用Canvas类的,他就像一张空白的画布,我可以在上面描绘出我们的游戏主画面。但是游戏是一个连续动画的过程,而许多动画效果的优劣都取决于低级Canvas类的执行速度。当游戏的某一状态改变时,我们都要刷新屏幕。这个时候,我们通常需要用到这个函数
repaint();
serviceRepaints(); //强制重绘,慎用!
但是,这样全屏绘制效率很低,在某些手机上效果很不好。所以如果仅需要改变屏幕中一小部分的话,也可以用以下的方法来请求重绘:
Canvas.repaint(int x, int y, int width, int height)
这样你仅需指定屏幕的特定区域,然后用paint方法重绘,效率会提高很多,还会保存计算结果。不过值得注意的是,如果你发了很多次的重绘请求而快于系统的处理速度的话,他就会合并一些处理的请求,并重绘了全屏幕,所以,请尽量不要在循环或者时序较小的定时器中调用repaint(int x,int y,int width,int height)。
在绘制某一个屏幕区域过于频繁的情况下(譬如动画效果),难免会发生"闪烁"的现象,造成这一问题的原因就在于在绘制屏幕某处之后的瞬间程序又在此处绘制了新的图像,解决这一现象比较有效的方法就是绘制缓冲图像,在内存开辟一块区域作为后台画面,游戏逻辑对它更新,在一次循环结束后再显示,这样就可以在避免出现因一次循环中对画面进行多次更新而产生的闪烁现象,另外请特别注意你在repaint中设定好的矩形范围。
具体的操作方法如下:
public class MyCanvas extends Canvas implements Runnable
{
Graphics bg; //缓冲区图像设备
Image buf; //缓冲区图像
public MyCanvas()
{
......
drawOthers();
int height = getHeight();
int width = getWidth();
buf = Image.createImage(width, height); //按显示屏幕大小建立缓冲对象
//此处也可以设为重复绘制的矩形区域
//将缓冲图像的图形设备赋给bg
bg = buf.getGraphics();
......
}
public void run()
{......
for(i=0;i {
for(j=0;j {
drawBlock(x,y);//在缓冲区内绘制图像
}
}
repaint();//将缓冲区的图像重绘到屏幕上
}
private void drawBlock(int block_x, int block_y)
{
//取得方块的坐标
int x = getLeft(block_x);
int y = getTop(block_y);
//取得方块的颜色
int c= board[block_x][block_y];
bg.drawImage(imgs[c], x, y, Graphics.TOP | Graphics.LEFT); //在缓冲区的图  
[1] [2] [3] 下一页