事實上我對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》中那樣去掏這些宏具體是什麼。我們只需要知道,這些宏會被替換為一些函數定義,函數聲明之類即可。