- 論壇徽章:
- 0
|
第三章. 游戲地圖的繪制
在前一章里面,我介紹了我的主角設(shè)計思想:只讓主角對對應(yīng)的按鍵做出相應(yīng)的動作,但實際上并不發(fā)生位移。那么肯定就有朋友會問了:一個RPG里面主角都不能發(fā)生位移,那能算是個RPG么?今天咱們就來解決看上去主角沒有位移的問題。
在前面我也提到了一個相對移動的概念,所以我在這里不再重復(fù)了。今天咱們就來仔細(xì)看看游戲地圖(背景)的實現(xiàn),通過移動繪制地圖,我們就可以讓主角相對移動起來。
首先,我們先將該死的移動繪制地圖拋開,考慮一下我們的地圖大小吧。在一個RPG里面,主要的組成部分就是龐大的地圖,巨大的文字量。也就是說我們的地圖可能會以一個大怪物的身份登場,這個大怪物很有可能會將移動設(shè)備那可憐的內(nèi)存一口吃掉,甚至讓你的移動設(shè)備罷工。同時由于移動設(shè)備的屏幕大小是完全固定的,用戶能看到的地圖范圍是固定的,因此我就想是不是只繪制用戶所能看到的范圍大小,而不是完全繪制地圖呢?很明顯的,這個思路能夠大量地節(jié)約內(nèi)存,并且能夠達(dá)到盡量高效地輸出圖象楨的目的。
下邊就是我用來繪制可視范圍的方法:
- public void setAllCells(int offset_block_columns, int offset_block_rows) {
- int new_block_column, new_block_row;
- for (int i = 0; i < blocks_rows; i++) {
- for (int j = 0; j < blocks_columns; j++) {
- new_block_column = j + offset_block_columns;
- new_block_row = i + offset_block_rows;
- if (
- new_block_column >= 0 && new_block_column < current_map_data[0].length
- && new_block_row >= 0 && new_block_row < current_map_data.length - 1
- ) tiled_layer.setCell(j, i, parseModel(current_map_data[new_block_row][new_block_column]));
- else tiled_layer.setCell(j, i, 0);
- }
- }
- }
- private int parseModel(String block_content) {
- int p = block_content.indexOf("|");
- if (p != -1) return Integer.parseInt(block_content.substring(0, p).trim());
- else return Integer.parseInt(block_content.trim());
- }
復(fù)制代碼
傳遞的兩個參數(shù)分別是當(dāng)前主角相對啟始點的X位移和Y位移,每次按一次向左方向鍵,這個相對X位移就減1,每次按一次向上方向鍵,相對Y位移就加1。然后在繪制地圖的時候,我首先根據(jù)這兩個參數(shù)找到可視范圍的左上角在實際地圖數(shù)據(jù)中的位置(new_block_column 和 new_block_row),然后因為固定移動設(shè)備的屏幕大小是固定的,因此就可以根據(jù)這些繪制出可視范圍內(nèi)的所有地圖元素了。
接著,讓我們來想想如果我們的地圖不僅僅是一張,比如分為了城市/野外/室內(nèi)幾種,那么我們不可能為每一張地圖都寫一個繪制方法吧?我們要考慮到方法復(fù)用,因此我就生成了一個專用的地圖讀取繪制類。這個地圖讀取繪制類的作用就是首先從地圖配置文件里面讀取數(shù)據(jù),然后將這個數(shù)據(jù)存放在緩沖中供繪制方法使用。
最后看看我的完整的地圖繪制類吧:
- // 地圖繪制工具
- /* *
- * 地圖格式
- *
- * 模型ID|事件描述
- * 地圖最后一行為初始地圖絕對坐標(biāo),如果不傳入需要前往的地圖絕對坐標(biāo),則使用該初始絕對坐標(biāo)
- * */
- /*
- * 事件格式
- * 事件類型|事件是否自動運行|事件發(fā)生地圖ID|事件需求類型|事件需求|允許輸入|事件完成以后當(dāng)前任務(wù)ID
- * 101|0|切換的目標(biāo)地圖ID|在目標(biāo)地圖的絕對塊X坐標(biāo)|在目標(biāo)地圖的絕對塊Y坐標(biāo)|0|0 切換地圖
- * 102|1|NPC ID|0|0|0|0 自動對話。事件需求類型為任務(wù),需求當(dāng)前任務(wù)ID為0,不允許輸入。輸出的是ID為 NPC ID 的NPC對話
- * 事件類型
- * 事件ID<100則不能移動,即不監(jiān)聽上下左右按鍵事件
- * 2 對話
- * 101 切換地圖
- */
- import javax.microedition.lcdui.*;
- import javax.microedition.lcdui.game.*;
- import java.lang.Math;
- public class BackgroundLayer {
- private int screen_width, screen_height; // 屏幕寬、高度
- public static final int cell_width = 16; // 地圖單元格寬度
- public static final int cell_height = 16; // 地圖單元格高度
- private TiledLayer tiled_layer;
- private String[][] current_map_data; // 當(dāng)前地圖的數(shù)據(jù)
- private int current_map_id; // 當(dāng)前地圖ID
- public static int blocks_columns, blocks_rows; // 屏幕最大塊列數(shù)和行數(shù)
- private int start_block_x, start_block_y; // 起始絕對坐標(biāo)
- // 構(gòu)造器
- public BackgroundLayer(int screen_width, int screen_height) {
- this.screen_width = screen_width;
- this.screen_height = screen_height;
- blocks_columns = (int)Math.ceil(screen_width / cell_width); // 單元格列數(shù)
- blocks_rows = (int)Math.ceil(screen_height / cell_height); // 單元格行數(shù)
- }
- // 裝載指定地圖
- public String[][] loadMapData(int map_id) {
- if (current_map_id != map_id) {
- FileTool file_tool = new FileTool();
- current_map_data = file_tool.readFromTxtFile("/res/maps/" + map_id + ".txt");
- file_tool = null;
- current_map_id = map_id;
- this.start_block_x = Integer.parseInt(current_map_data[current_map_data.length - 1][0]);
- this.start_block_y = Integer.parseInt(current_map_data[current_map_data.length - 1][1]);
- tiled_layer = null;
- System.gc();
- try {
- tiled_layer = new TiledLayer(blocks_columns, blocks_rows, Image.createImage("/res/images/background/" + current_map_data[current_map_data.length - 1][2] + ".png"), cell_width, cell_height);
- }
- catch (Exception e) { }
- }
- return current_map_data;
- }
- // 裝載指定地圖
- public String[][] loadMapData(int map_id, int start_block_x, int start_block_y) {
- if (current_map_id != map_id) {
- FileTool file_tool = new FileTool();
- current_map_data = file_tool.readFromTxtFile("/res/maps/" + map_id + ".txt");
- file_tool = null;
- current_map_id = map_id;
- this.start_block_x = start_block_x;
- this.start_block_y = start_block_y;
- tiled_layer = null;
- System.gc();
- try {
- tiled_layer = new TiledLayer(blocks_columns, blocks_rows, Image.createImage("/res/images/background/" + current_map_data[current_map_data.length - 1][2] + ".png"), cell_width, cell_height);
- }
- catch (Exception e) { }
- }
- return current_map_data;
- }
- public void paint(Graphics g) {
- tiled_layer.paint(g);
- }
- // 根據(jù)地圖范圍設(shè)置顯示區(qū)域的模型內(nèi)容
- public void setAllCells(int offset_block_columns, int offset_block_rows) {
- int new_block_column, new_block_row;
- for (int i = 0; i < blocks_rows; i++) {
- for (int j = 0; j < blocks_columns; j++) {
- new_block_column = j + offset_block_columns;
- new_block_row = i + offset_block_rows;
- if (
- new_block_column >= 0 && new_block_column < current_map_data[0].length
- && new_block_row >= 0 && new_block_row < current_map_data.length - 1
- ) tiled_layer.setCell(j, i, parseModel(current_map_data[new_block_row][new_block_column]));
- else tiled_layer.setCell(j, i, 0);
- }
- }
- }
- // 根據(jù)地圖范圍設(shè)置顯示區(qū)域的模型內(nèi)容
- // default_block_id 缺省的模型ID,超出地圖配置文件范圍的內(nèi)容使用該塊繪制
- public void setAllCells(int offset_block_columns, int offset_block_rows, int default_block_id) {
- int new_block_column, new_block_row;
- for (int i = 0; i < blocks_rows; i++) {
- for (int j = 0; j < blocks_columns; j++) {
- new_block_column = j + offset_block_columns;
- new_block_row = i + offset_block_rows;
- if (
- new_block_column >= 0 && new_block_column < current_map_data[0].length
- && new_block_row >= 0 && new_block_row < current_map_data.length - 1
- ) tiled_layer.setCell(j, i, parseModel(current_map_data[new_block_row][new_block_column]));
- else tiled_layer.setCell(j, i, default_block_id);
- }
- }
- }
- // 解析地圖模型內(nèi)容
- private int parseModel(String block_content) {
- int p = block_content.indexOf("|");
- if (p != -1) return Integer.parseInt(block_content.substring(0, p).trim());
- else return Integer.parseInt(block_content.trim());
- }
- // 解析英雄所在塊數(shù)據(jù)內(nèi)容
- // 第一元素為事件類型,小于MainGameCanvas.STATUS_IN_MOVING的事件類型不能移動
- // 其他參考 EventManager中的說明
- public int[] parseBlock(int offset_x, int offset_y) {
- int[] block_info = new int[7];
- int brave_position_x = blocks_columns / 2 + offset_x;
- int brave_position_y = blocks_rows / 2 + offset_y;
- if (
- brave_position_x >= 0 && brave_position_x < current_map_data[0].length
- && brave_position_y >= 0 && brave_position_y < current_map_data.length - 1
- )
- {
- String tmp = current_map_data[brave_position_y][brave_position_x];
- int p = tmp.indexOf("|");
- int i = 0;
- if (p != -1) {
- while ((p = tmp.indexOf("|")) != -1) {
- if (i > 0) block_info[i - 1] = Integer.parseInt(tmp.substring(0, p).trim()); // 跳過地圖模型描述字段
- tmp = tmp.substring(p + 1);
- i++;
- }
- block_info[i - 1] = Integer.parseInt(tmp.trim());
- }
- else block_info[0] = MainGameCanvas.STATUS_IN_MOVING;
- }
- else block_info[0] = MainGameCanvas.STATUS_IN_GAMING;
- return block_info;
- }
- public int getStartOffset(boolean is_x) {
- if (is_x) return start_block_x - blocks_columns / 2;
- else return start_block_y - blocks_rows / 2;
- }
- }
復(fù)制代碼
其中用到了一個我的自定義類 FileTool,這個文件我在下一次再發(fā)上來吧~~ |
|