2007-6-13
Kevin Lynx
这里稍微提一下最近对于J2ME以及MFC的一些心得感想。
事实上我对J2ME上的游戏开发并不是很感兴趣,因为自己没有实际设备,对于JAVA语言也不是很有好感。而对于MFC呢,一直以来我都认为它不是一个很好的库,不值得我去深入使用(学习)。因为以前我好几次都弄过MFC,所以还算是有发言权。GUI库方面,C++有Qt,有WxWindows,JAVA还有Swing,这些库的设计都比MFC好很多。MFC与这些库比起来最大的特点就是一种common sense 。例如Qt库,只要我掌握了其核心技术signal and slot,我就可以查阅其文档来使用它了。因为其各个控件的设计思想都一样,接口都差不多,使用起来很容易。停止一段时间再去用的话,也很快就上手了。但是MFC就不一样了,其核心机制太多,一堆宏让这个库看起来无比神秘,其各个控件的设计思想更是不一样。甚至于,在使用某个控件时还需要注意其他一些旁支末节。我曾经有几次回过头去用MFC,结果每次都需要重新看很多资料才能上手。后来我发现,很多公司,即使是游戏公司,基本上还在用MFC这个老旧的库。
5月分我忙得太累了,6月分就很想休息休息。休息的时候,我就决定拿MFC和J2ME玩玩。
对于J2ME,我觉得还是很简单的。基本上一天就可以上手了。在进行J2ME编程之前,需要了解很多神秘的概念,诸如CLDC,CDC,MIDP等等之类的。但是,这些概念并不会妨碍你进行J2ME编程。在我了解了他们,并开始真正J2ME编程后,我就基本上忘掉了这些带有一些历史意义的概念。
J2ME程序不同于J2SE,J2ME看上去就象把程序入口点封装了一样。对于客户端程序员而言,面对的就象一个高层的游戏框架。掌握一个基本的J2ME框架是进入J2ME编程的第一步。该框架类似于:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
class Game extends GameCanvas implements Runnable
{
private Graphics g;
public Game()
{
super( true );
g = getGraphics();
}
public void start()
{
Thread t = new Thread( this );
t.start();
}
public void run()
{
update();
render();
}
private void update()
{
}
private void render( long startTime )
{
g.setColor( 128, 255, 0 );
g.fillRect( 20, 20, 10, 10 );
g.setColor( 0, 0, 0 );
g.drawString( "FPS=" + timeElapsed, 0, 0, Graphics.LEFT | Graphics.TOP );
this.flushGraphics();
}
}
/**
* Main Class
*
*/
public class GameDemo extends MIDlet
{
Game game;
public GameDemo()
{
game = new Game();
game.start();
}
public void startApp()
{
Display.getDisplay(this).setCurrent( game );
}
public void pauseApp()
{
}
public void destroyApp( boolean unconditional )
{
}
}
以上程序是基于MIDP2.0的。
一个基本的J2ME程序框架包含两个类,一个类从MIDlet类继承而来,负责应用程序管理部分;一个类从GameCanvas类继承而来,相当于游戏主界面类,游戏大部分渲染活动渲染于此对象之上,然后应用程序类设置该对象为当前显然对象(setCurrent( game ) )即表现了一个J2ME程序。
继承MIDlet类需要实现startApp, pauseApp, destroyApp方法。
对于一个GameCanvas派生类而言,要让游戏逻辑运行于单独的线程中。所以这里就需要创建一个单独的线程。这里要让GameCanvas派生类对象被放在单独的线程类里,就让其实现Runnable接口,然后实现run接口。在创建线程时,需要指定new Thread( this );。
要绘图,就使用getGraphics获得Graphics对象。注意,每次调用该方法都会新创建一个Graphics对象,所以通常时一次创建,然后保存起来被多次使用。
程序需要限制帧率,貌似只能使用传统的限制时间方法。例如:
public void run()
{
long lastTime;
while( true )
{
lastTime = System.currentTimeMillis();
update();
render();
/// delay
long dTime = System.currentTimeMillis() - lastTime;
if( dTime < GAME_DELAY )
{
try
{
Thread.sleep( GAME_DELAY - dTime );
}
catch( Exception e )
{
e.printStackTrace();
}
}/// end if
}
开发J2ME的贪食蛇基本没遇到什么难度,从上午10点开发编码,到晚上就搞定了。通过这个练习,倒可以熟练熟练JAVA这门语言。J2ME应用倒是次要的,毕竟不难。(开发环境使用的NETBEANS)。
关于MFC :
此前在HGE群里和几个大大闲聊MFC。微妙说学MFC得用心去学。初看上去很普通的一句话,但是给了我很多想象的空间。后面几天我就在思考MFC这个东西。然后看了下《深入浅出MFC》对于MFC六大关键技术的讨论。还看别人写的MFC代码。然后慢慢的慢慢的,我就感觉到一些东西。这个“用心学”,真的说得不错。学习的方法就在于,用心去理解MFC的关键东西。毕竟,MFC的旁支末节太多了。正如学习Qt, Swing之类的GUI库一样,我们需要去掌握一个库的核心思想。掌握了其核心思想后,用的时候也就是查查文档而已。要论核心技术的话,MFC的核心技术比之其他库就多了。消息映射,串行化,整体封装架构,document-view等等之类。
相对于用Class Wizard来自动生成消息处理代码,我们这种手写的方式,虽然不懂这些宏会具体地被替换为哪些定义,但是已经深入不少。这真的是一种剔除旁支末节的好方法。用MFC的话,我很想全部手写代码,而不用VC的一些向导啊之类的来自动生成代码,因为我觉得那样会把我的代码弄的乱糟糟的。恩,我想我果然是一个典型的古怪程序员。
将这些核心技术深入理解并铭记于心后,剩下的是什么?剩下的就是控件的使用经验。
在开发J2ME贪食蛇的编辑器时,我用MFC手工写了近千行代码。对于做游戏工具而言,大部分时候都是在和MFC封装的GDI打交道,而对于更多的应用程序相关部分,本身需要关注的信息就比较少。基本上,我没遇到什么大问题。从窗口客户区的图象绘制,到最后的菜单处理,基本完成得很顺利。
可以说,MFC是一种工具,比起其他库而言,它的使用者很多。使用者一多,这个使用经验就丰富。反正最近不是很忙,所以打算继续用MFC开发一些游戏中的小工具。
剔除旁支末节,然后再剔除自己觉得目前没必要接触的东西。最后剩下的东西就不多了。试着去理解消息映射吧。我们知道,一个直接使用 Windows API 写的窗口程序,也需要处理各类窗口消息。每一个消息可以对应一段处理代码,把代码塞进函数,那就是对应一个个的函数。 MFC 的消息映射也需要消息处理函数。这是它的设计目标。而消息处理宏呢,则是完成这种映射的一种方式而已。不用象《深入浅出 MFC 》中那样去掏这些宏具体是什么。我们只需要知道,这些宏会被替换为一些函数定义,函数声明之类即可。