魍魎校園(Java版AVG遊戲開發入門)源碼分析

0.前言
本例子取自cping1982早期公開的一個AVG源碼,loon-simple-20090212,裏面帶了6個遊戲。這次我們要分析的是AVGSimple這個遊戲。截圖如下:
[img]http://dl2.iteye.com/upload/attachment/0104/7489/a587c7de-da0d-3840-a961-9cc69a143aeb.png[/img]

下載地址:http://code.google.com/p/loon-simple/downloads/list

聲明一下,這個程序不是我寫的,是cping1982寫的。本人在這裏斗膽分析一下高手5年前寫的代碼,一來是提高自己,二來也是給衆多小白以信心和勇氣,分析完源碼你會發現用java寫一個AVG遊戲還是不難的。
如果你無法訪問google code,也可在本文文末下載我已經加上了註釋的版本。

原作者的博客上有3篇文章,可謂說相當好。說的東西都是點到爲止,需要讀者自己去結合源碼細細品味。
[url=http://blog.csdn.net/cping1982/article/details/3868211]Java版AVG遊戲開發入門[0]——遊戲模式轉換中的事件交互[/url]
[url=http://blog.csdn.net/cping1982/article/details/3875027]Java版AVG遊戲開發入門[1] —— CG的繪製[/url]
[url=http://blog.csdn.net/cping1982/article/details/4173442]Java版AVG遊戲開發入門示例[3]——腳本引擎的製作及應用[/url]

我這裏稍微再詳細的分析一點源碼。

1. 遊戲模式轉換

IControl的抽象化是關鍵,可以避免像STG雷霆行動那樣冗長的if else分支判斷現在處於哪種模式,使得擴展更簡單。其實我們看一下這張序列圖可以發現,一切的東西都是繪製到畫布上的(AVGCanvas.paint方法),只是“如何畫”這個邏輯給分散到了不同的IControl子類裏。而AVG.setControl可以達到方便的切換模式的作用。
[img]http://dl2.iteye.com/upload/attachment/0104/7480/4e5d9cb6-29df-3de7-a70e-9c388299c69e.png[/img]

IControl有這些子類
Title 標題模式
Script 遊戲中模式(採用了腳本系統)
AqueductGame 水渠貫通小遊戲模式

[img]http://dl2.iteye.com/upload/attachment/0104/7491/d3b9227d-d099-3853-8c46-3c0c909457c1.png[/img]

IControl有如下這些方法可供子類實現
invoke 轉換控制器接口
draw 畫圖
mouseMoved 鼠標處理
mousePressed 鼠標處理
keyPressed 鍵盤處理

這樣添加新的模式就很簡單了,各個模式之間相對做到解耦,而且功能也都有了。

2. CG
就是實現IControl各個子類的draw方法。如何畫?主要步驟就是先畫背景圖,再畫前景。

下面舉1例
2.1 Title的繪製
[img]http://dl2.iteye.com/upload/attachment/0104/7486/1a4d1300-9dbf-3b67-9896-a97b2f671af6.png[/img]


public void draw(Graphics g) {
//1.先繪製背景圖
graphics.drawImage(title, 0, 0, null);
//2.畫窗體藍色背景
Message.setWindowMessage(graphics, MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
MESSAGE_LINE_X2, MESSAGE_LINE_Y2);
//3.畫窗體邊框
Message.setWindowFrame(graphics, MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
MESSAGE_LINE_X2, MESSAGE_LINE_Y2);
//4.畫菜單選項
for (int i = 0; i < messages.length; i++) {
// 選中
if (i == selectFlag) {
// 變更字體顏色。
graphics.setColor(Color.white);
// 設定浮標。
Message.setWindowBuoyageMessage(selectFlag, 160, 34, graphics,
MESSAGE_LINE_X1, MESSAGE_LINE_Y1,
MESSAGE_LINE_X2 + fontSize + 2, MESSAGE_LINE_Y2
+ fontSize + 40);
// 未選中
} else {
graphics.setColor(Color.gray);
}
//一個字一個字的畫出來
for (int k = 0; k < messages[i].length(); k++) {
Utility.drawDefaultString(messages[i].substring(k, k + 1).toString(), graphics, font * k + left, i
* (font + fontSize) + font + top, 1, font);

}

}

g.drawImage(screen, 0, 0, null);
g.dispose();
}


用的是RPG Maker XP格式的窗體圖。
[img]http://dl2.iteye.com/upload/attachment/0104/7482/6c98a610-9f1c-3b7d-bd38-062ece5789f5.png[/img]
[img]http://dl2.iteye.com/upload/attachment/0104/7484/b6ca7ae8-88fb-380c-9b3a-2acfe1cd677b.png[/img]

第一步簡單,把背景圖畫出來即可。
第二步如圖,A區繪製窗體藍色背景,會有一個比例擴大。
第三步如圖,需要將B區的通過比例換算分8步畫出來(上,下,左,右,還有4個角)
第四步畫菜單選項。需要根據鼠標的移動高亮選中的菜單項。

同時,mouseMoved方法處理鼠標的移動,賦值selectFlag,以待下一次繪圖時高亮菜單選項。

public void mouseMoved(MouseEvent e) {
super.mouseMoved(e);
int k = (Control.mouse.y - top) / (font + fontSize);
//如果鼠標太上面
if (k < 0) {
return;
}
//如果鼠標太下面
if (k >= messages.length) {
return;
}
//賦值selectFlag,以待下一次繪圖時高亮菜單選項
for (int l = 0; l < messages.length; l++) {
if (l == k) {
selectFlag = k;
continue;
}
}
//如果鼠標x軸在框內,則標記“選中”
if ((double) Control.mouse.x > this.MESSAGE_LINE_X1
&& (double) Control.mouse.x < this.MESSAGE_LINE_X1
+ (double) this.MESSAGE_LINE_X2
&& (double) Control.mouse.y > this.MESSAGE_LINE_Y1
&& (double) Control.mouse.y < this.MESSAGE_LINE_Y1
+ (double) this.MESSAGE_LINE_Y2) {
select = true;
} else {
select = false;
}
}


3. 腳本系統

腳本系統的運用一方面可以使得開發效率提高,bug更少,另一方面可以使得後期維護簡單。腳本系統一旦成熟,不懂java的人也可以維護甚至開發一個新的遊戲。

比如下面腳本顯示背景,再顯示了2個人物(xiaoyanyan和ranmin)。

cg del
gb image/ghost.png
cg chara/xiaoyanyan.png 0
cg chara/ranmin.png 200
cgwait


原理和之前Title畫面類似,主要就是nextScript方法執行腳本,賦給變量(修改model層),而draw方法根據變量不同來畫圖(view層)。兩個方法搭配,比較好的實現了MVC模式。
修改model層

private synchronized int nextScript(final int index, final String s) {
//......
for (j = index; j < scriptContent.length; j++) {
//顯示信息
if (messageFlag.equalsIgnoreCase("mes")) {
isMessage = true;
break;
}
//背景
if (messageFlag.equalsIgnoreCase("gb")) {
if (objectFlag == null) {
return index;
}
if (objectFlag.equalsIgnoreCase("none")) {
cg.setBackgroundCG(null);
} else {
cg.setBackgroundCG(Utility.loadImage(objectFlag));
}
continue;
}
//人物
if (messageFlag.equalsIgnoreCase("cg")) {
if (objectFlag == null) {
return index;
}
if (objectFlag.equalsIgnoreCase("del")) {
//刪除原有人物
if (orderFlag != null) {
cg.removeImage(orderFlag);
} else {
cg.clear();
}
} else {
//添加人物
int x = 0, y = 0;
if (orderFlag != null) {
x = Integer.parseInt(orderFlag);
}
if (gotoFlag != null) {
y = Integer.parseInt(gotoFlag);
}
cg.addImage(objectFlag, x, y);
}
continue;
}
}
}


view層

public void draw(final Graphics g) {
//1.畫背景,帶晃動
if (cg.getBackgroundCG() != null) {
if (shakeNumber > 0) {
//通過隨機數,達到圖片在小範圍內晃動效果
graphics.drawImage(cg.getBackgroundCG(),
shakeNumber / 2 - Control.rand.nextInt(shakeNumber),
shakeNumber / 2 - Control.rand.nextInt(shakeNumber), null);
} else {
graphics.drawImage(cg.getBackgroundCG(), 0, 0, null);
}
}
//2.畫人物
for (int i = 0; i < cg.getCharas().size(); i++) {
Chara chara = (Chara) cg.getCharas().get(i);
graphics.drawImage(chara.getCharacterCG(), chara.getX(), chara
.getY(), null);
}
}


附件有比較詳細的註釋,可以參考。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章