Java版戰棋(SLG)遊戲AI及尋徑處理入門

  SLG或者說戰棋遊戲,在大多數英文站點是歸類到Simulation Game的(包括模擬城市之類的純SIM),並沒有進行SRPG(Strategies Role Play Games)、RTS(Real-Time Strategy Game)乃至RSLG(Role play Simulation Game)種種的細分。歸結原因,想必還是因爲近似因素太多,在大多數時候已經難以區分其本來面貌,只能一概而論,所以本文也可以理解爲SRPG或 RSLG開發的入門示例。

 
前言:

 這是一篇孤立的博文,精簡了示例代碼效果及行數,僅保留最基礎功能,與以前寫過的[Java版SLG遊戲開發入門]沒有直接聯繫,但可以互相參看。
 
關於AI:

 AI(Artificial Intelligence),即人工智能,有時也稱作機器智能或人工腦,是指那些由人類製造出來的系統,在面對具體事務時,所表現出的類人反應。通常情況下人工智能多指以人類思維模式爲準繩,通過計算機模擬實現的智能。

 人工智能的定義可以分爲兩部分,即“人工”和“智能”。“人工”比較好理解,爭議性也不大。有時我們會要考慮什麼是人力所能及製造的,或著人自身的智能程度有沒有高到可以創造人工智能的地步,等等。但總的來說,“人工系統”就是通常意義下的人工系統。

 舉凡涉及到什麼是“智能”的話題,就問題多多……人唯一瞭解的智能是人本身的智能,這是普遍認同的觀點。但我們對自身智能的理解也都非常有限,對構成人的智能的必要元素也知之甚少,所以很難真正定義什麼是“人工”製造的“智能”。

 據此,我同樣很怕寫涉及到AI題材的博文,首先AI處理本就是個有待研究的領域,因爲甚至連[智能]究竟是什麼都是個很難解釋的問題,這 其中還涉及到其它諸如意識(consciousness)、自我(self)、心靈(mind)(包括無意識的精神(unconscious_mind) 等等,存在相當大程度的爭議。

 而評判AI的標準也不盡相同,拋開還很遙遠的人工生命(“強人工智能”或“弱人工智能”),似乎要成爲標 準的“機器人三定律”,圖靈測試等等不說,單從遊戲AI引擎的設計角度講,這已經是個很嚴肅的話題,並非一兩行代碼就能構建完成的,負責任的說,如果要嚴 謹的寫出一箇中等規模戰棋遊戲的AI處理代碼,並配合圖文解釋且標註上參考文獻,加上關鍵字摘要等等,差不多就是篇碩士論文|||,總之水很深。考慮到篇 幅及鄙人水平因素,故此文中並沒有深入探究,僅僅給出一個“入門示例”,供看客參考而已。
 
正文:

 事實上我們所以喜愛遊戲,大多是基於“與天鬥,與地鬥,與人鬥,其樂無窮”的因素,相信很少有玩家會喜歡戰場上的敵人永遠一動不動任你蹂躪,更不會有人喜歡僅僅出 現You win again字樣的遊戲。應該說,遊戲中的AI很大程度上講是體現在電腦與玩家的對抗中,一款好的遊戲AI應該能足夠刺激玩家,“蹂躪”玩家,吸引玩家參與 對抗。
 
 不妨這樣講,製作遊戲的目的相較於體現玩家的“聰明”,倒不如說是更希望看見他們的“愚蠢”,看見他們被遊戲玩弄得驚慌失措,叫苦連天,還樂此不疲的“憨佬”形象,這纔是我們作爲遊戲開發者的最大快樂(^^)~

 根據具體事件的不同,遊戲AI可具體體現在以下兩個方面:

 一、單元活動AI(Unit Behavioral AI)

  遊戲AI並不總是標準含義上的AI。而單元(也可理解爲角色或者基本對象,以下同)遊戲AI正是爲設計出具有提供某種挑戰或某種真實體現的生命特徵的一次真正的嘗試。

  譬如在玩家與遊戲的互動中,只站在一處、從不移動的警衛會顯得非常不真實。不過,如果你創建一個例行程序(routine),使他不時的朝四周張望,或變 換他的姿勢,他會看起來更具活力。通過創建一個在預設的路徑上行走的警衛偶然停在站崗的警衛前,並好像與他談話這樣的情景,真實的體現能被極大地提高。單 元活動AI,正是出於這種目的製作的“擬人性”而非“擬人”AI。

  在單元AI中,動作模式可分爲被動式(Passive)與自發式(Spontaneous)兩大類。
 
  1、被動式:現實生活中,如果有人打了你的左臉,要不然就伸出右臉讓他再打,要不然就伸出右手還他一巴掌,總之,你會有相應的“反應”。而被動式AI,正是這種情況的體現。

  在被動模式下,單元(角色)隨時會對自身環境中的變化做出響應。如果一個敵人發現了你,開始向你跑來,並朝你射擊,那麼他們已經做出了看到你的反應。
 
  2、自發式:在自發模式下,單元做出行動時並不依賴於自身環境中的任何變化。一個單元決定從其所站立的崗哨移向基地周圍的某個遊動崗哨,則這個單元已經做出了一次自發性的行動。

   通過在你的遊戲中加入不同的單元活動元素,就能夠製造出單元的“聰明”假象,令玩家產生對手如同真人的錯覺。

 二、單元行動AI(Unit Actions AI)
  

   好比人類的智商是體現在行爲及對世情的準確判斷上,真正讓一個遊戲單元看起來聰明或者愚蠢的,同樣是他們的行動。

   簡單的說,如果遊戲單元依照玩家認爲可行的方式移動,或者在玩家認知範圍合理的情景下做如閃避這樣的動作,那麼單元看起來會很聰明,相反則會給人愚蠢的印 象。但是,實際開發中這種現象都並非真實存在,而是看起來聰明或愚蠢的假象,因爲程序僅僅與玩家面對的基本情景相關聯,而並非遊戲中角色真的聰明抑或愚 蠢。

   如果你處理恰當,且這一應用包含的範圍廣泛,你的玩家就會相信你的單元足夠“聰明”。爲了實現這一目的,你需要把自己放在你所構建單元的位置上思考,如果 把你丟到遊戲中,在他們的情景下你會怎麼做?你將怎樣迴應各種各樣的***或遭遇敵人?如果什麼事都根本沒發生,你又將會做些什麼?

   如果你回答了這些問題,並針對你的單元將遇到的每種情景正確的實施了它們,你擁有“看似聰明”單元的機會就會最大化,這也是創建一個優秀的、穩健的遊戲AI的第一步。
 
談過了單元行爲,我們再來說說單元運行中的事件分類。

 根據處理事件採取的不同技術,遊戲AI又可分爲確定性(deterministic)AI與非確定性(Non-deterministic)AI兩大類別:

  1、確定性AI:

  確定性AI的單元(角色)行爲或者說表現是特定的,可預測,沒有任何不確定因素。其具體實現如同我在博文[Java僞尋徑追蹤實現]中展示的單元追逐演算,一個非玩家單元緊隨玩家單元X,Y座標前進,直至與玩家單元或目標點重疊爲止。


  2、非確定性AI:

  與確定性AI相反,非確定性AI在行爲模式上存在着很大程度上的不可預測性,理論上講甚至能夠令單元(角色)做出很多超出程序員構想的突現行爲。簡單實現 可見隨本博文發佈的程序示例(單元隨機動作),但其複雜實現則需要應用到神經網路、貝斯葉概率模式、乃至基因演算法等相關知識支撐。故此鄙人對嚴謹意義上 的非確定性AI也不敢置喙太多,深入研究有待看客自行探索。

  3、[隱藏類別] AI處理結果欺詐(流氓手段、作弊、賴招,隨便叫(-_-|||)):

  這種方式事實上是程序員心智肚明,卻又諱莫如深的一種編程技巧,我讀書時老師戲稱其爲“流氓手段”。還記得在當時課堂上,老師曾舉過這樣一個例子,讓我們寫出一段能夠得到1-100相累加結果的最簡代碼,同學們發言很踊躍,但是卻沒人有正確答案。而當我們質疑老師的評判標準時,老師卻給出了絕對最簡的答案——直接顯示5050。

  應該說,在處理絕對可知結果時,這種方式確實非常有效,對於頻繁運算來講更能體現其價值所在;而對於遊戲中某些運算複雜,但結果卻單一的事件,確實可以採用“流氓手段”進行編程,即可提高效率,又減少了代碼量,但卻決不能輕易被用戶知道,尤其是在網遊的賭博、精煉等結果中……

 總結:

  就我目前所知的遊戲AI實現中,確定性AI可謂絕對主流,因爲它的結果固定有窮,所以相對於非確定AI佔用程序資源更小,效率更高,實現也更簡單。但有利 必有弊,對一個聰明的玩家而言,找出一個確定性AI的規律是再簡單不過的事情,有限的行動模式,也必然決定遊戲可玩性同樣有限。

 而非確定性AI,則毫無疑問是塊雷區,無論對我這種業餘玩票性質抑或專業遊戲開發者盡都如此,它已經無限延伸入“人工生命”這塊“神之領域”,並非短時間就能夠學習甚至使用的技術。但如果能在程序中成功利用,則無疑會極大增強遊戲可玩性。

 至於我提到的“流氓手段”,則只能意會,不可言傳,大家心照不宣。

 總體來講,遊戲AI無論是確定性或非確定性,單元(角色)都難免如同巴浦洛夫狗流哈喇子中的dog那樣,僅僅會對特定事件做出“條件反射”,依據製作者設定 好的行爲模式而並非角色的自主思維運作,行爲可能性是“有窮”的,並沒有如人類般擁有“無限可能性”,故此可以看作一種“僞智能”,而非嚴格意義上的“人 工智能”。當然,我相信隨着技術的發展,這種“僞智能”技術最終將進化爲真正意義上的“人工智能”。
 
對於具體處理流程,則可作如下分類:
  
 1、有限狀態機(Finite State Machin,FSM):

 最 廉價、同時也是最實用的技術。在遊戲實現(非遊戲實現有出入)中的基本運作方式是採用窮舉方式,羅列出單元所有可能的動作或狀態,再利用switch、 if等方式判定各種事件關係及滿足條件,據此變更單元的動作或狀態,由於我們所能做的僅是編輯從一狀態到另一狀態的轉換,完成這系列一行爲的算法,就可歸 屬於分層有限狀態機。

 2、模糊狀態機(Fuzzy State Machine,FuSM):

 當利用隨機數等方式觸發模糊邏輯(fuzzy logic)時,會令單元的動作較難預計,產生大量新的分支判斷,這時處理多個有限狀態機情況的技術實現,就是模糊狀態機,它以看“不精準”的響應來進行不確定性結果的處理。

 3、分層有限狀態機(Hierarchical Finite State Machines,HFSM)及擴展分層有限狀態機(Extended Hierarchical Finite State Machines,EHFSM):

 這兩項技術可視同有限狀態機與模糊狀態機的融合體,他們嘗試以一種樹狀結構分別處理有限及模糊狀態,是一系列由同一個支點擴展開的行爲模式樹,不同的是擴展分層有限狀態機有更爲嚴密的控制流及數據流,當然代價是對於遊戲系統的資源損耗也更多。

 以上是AI引擎開發中常用的一些基礎概念,如果想深入瞭解相關細節,還需看客自行深入研究。
 
關於單元(角色)尋徑:

  如果單元擁有AI,那麼他理所應當的能夠自主行動。但是,我們都知道遊戲中角色是不存在或者說很難實現真正意義上AI的,所以與AI處理同樣,尋徑同樣是我們這些程序員的一種“欺詐手段”,用以“矇蔽”用戶,讓他們產生單元擁有自主思維的錯覺。

  關於常見的幾種尋徑方式,可見參本人博文[Java中的A*(A star)尋徑實現]以及[Java僞尋徑追蹤實現],不再贅述。

  簡單的說,平面圖是由x、y兩點構成的,而所謂尋徑就是在網格化的地圖上連接出點到點間的路線交集;如果我們以二維數組mapList表示地圖數 據,moveList表示地圖上可移動點的話,那麼複合mapList地圖數據及moveList上可移動點所構成的交集,就是尋徑後得到的單元行走路 線,即尋徑結果。

  相較於AI部分,尋徑可以看作AI實現中的一項分支技術,個人認爲沒有太過深入探究理論的必要,唯一需要關心的,僅在於多對象尋徑時的效率或準確性取捨問題,同樣請參考相關技術文獻,否則本文隨時超出文章最大字數……
 
具體到遊戲實現流程:
 
SLG離不開戰場及角色與各種事件判斷,而具體到其構建過程,大多遵循如下順序:

1、地圖(背景戰場圖)構建

2、獲得對應地圖基本單元的對象集合(2D遊戲中多爲二維數組)

3、獲得可移動點的對象集合

4、創建地圖上角色(針對於地圖基本單元放置)

5、激活鍵盤或者鼠標事件(處理如光標移動等)

6、根據選擇的不同在地圖單元上繪製相光事件觸發物(菜單等),以供事件觸發

7、當選擇事件時,事件處理開始,各單元(角色)根據預先設定響應事件反饋給玩家(比如移動、***等事件)

8、當我方全部行動結束或者選擇結束後,敵軍開始由AI自行處理事件

9、判定是否滿足戰鬥結束條件

10、如果未滿足步驟9,則循環回步驟1,同時回合數+1,遊戲繼續

 在這一過程中,還可以加入如兵種、物品、特殊人物加成等影響性數據,但基本流程不受影響。 
 
具體到演示代碼:
 
  本博文附帶的演示代碼有核心基本類如下,具體請參見代碼註釋:
 
Role.java(角色處理)

package org.loon.simple.slg.ai;

import java.awt.Image;

/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed . an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email][email protected][/email]
* @version 0.1
*/

public class Role {

  //名稱
  String name;
        //分隊(0:我軍 1:敵軍)
  int team;
        //hp
  int hp;
        //角色圖像
  Image image;
        //移動力
  int move;
  //行動狀態(0:未行動 1:已行動)
  int action;
  //x座標
  int x;
  //y座標
  int y;
  //是否已進行***
  boolean isAttack = false;
  /**
    * 設定角色參數
    *
    * @param name
    * @param team
    * @param image
    * @param move
    * @param x
    * @param y
    */

  public Role(String name, int team, Image image, int move, int x, int y) {
    this.name = name;
    this.team = team;
    this.hp = 10;
    this.image = image;
    this.move = move;
    this.x = x;
    this.y = y;
  }

  public int getAction() {
    return action;
  }

  public void setAction(int action) {
    this.action = action;
  }

  public int getHp() {
    return hp;
  }

  public void setHp(int hp) {
    this.hp = hp;
  }

  public Image getImage() {
    return image;
  }

  public void setImage(Image image) {
    this.image = image;
  }

  public int getMove() {
    return move;
  }

  public void setMove(int move) {
    this.move = move;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getTeam() {
    return team;
  }

  public void setTeam(int team) {
    this.team = team;
  }

  public int getX() {
    return x;
  }

  public void setX(int x) {
    this.x = x;
  }

  public int getY() {
    return y;
  }

  public void setY(int y) {
    this.y = y;
  }

  public boolean isAttack() {
    return isAttack;
  }

  public void setAttack(boolean isAttack) {
    this.isAttack = isAttack;
  }
}


Map.java(地圖處理)

package org.loon.simple.slg.ai;

import java.awt.Image;

/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed . an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email][email protected][/email]
* @version 0.1
*/

public class Role {

  //名稱
  String name;
        //分隊(0:我軍 1:敵軍)
  int team;
        //hp
  int hp;
        //角色圖像
  Image image;
        //移動力
  int move;
  //行動狀態(0:未行動 1:已行動)
  int action;
  //x座標
  int x;
  //y座標
  int y;
  //是否已進行***
  boolean isAttack = false;
  /**
    * 設定角色參數
    *
    * @param name
    * @param team
    * @param image
    * @param move
    * @param x
    * @param y
    */

  public Role(String name, int team, Image image, int move, int x, int y) {
    this.name = name;
    this.team = team;
    this.hp = 10;
    this.image = image;
    this.move = move;
    this.x = x;
    this.y = y;
  }

  public int getAction() {
    return action;
  }

  public void setAction(int action) {
    this.action = action;
  }

  public int getHp() {
    return hp;
  }

  public void setHp(int hp) {
    this.hp = hp;
  }

  public Image getImage() {
    return image;
  }

  public void setImage(Image image) {
    this.image = image;
  }

  public int getMove() {
    return move;
  }

  public void setMove(int move) {
    this.move = move;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getTeam() {
    return team;
  }

  public void setTeam(int team) {
    this.team = team;
  }

  public int getX() {
    return x;
  }

  public void setX(int x) {
    this.x = x;
  }

  public int getY() {
    return y;
  }

  public void setY(int y) {
    this.y = y;
  }

  public boolean isAttack() {
    return isAttack;
  }

  public void setAttack(boolean isAttack) {
    this.isAttack = isAttack;
  }
}

 
GameCanvas.java(戰場繪製及各種事件處理)
package org.loon.simple.slg.ai;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* [url]http://www.apache.org/licenses/LICENSE-2.0[/url]
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed . an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email:[email][email protected][/email]
* @version 0.1
*/

public class GameCanvas extends Canvas implements Runnable, KeyListener {

  /**
    *
    */

  private static final long serialVersionUID = 1L;

  // 地圖
  private Map map = null;

  // 菜單
  private Menu menu = null;

  // 背景窗體
  private Image screen;

  // 地圖圖片
  private Image mapImage;

  private Graphics2D graphics;

  private String state;

  private int lastX;

  private int lastY;

  private int curX;

  private int curY;

  private int turn = 1;

  private int actionUnit = -1;

  private int moveCount = 0;

  private int[][] moveList;

  private int[][] movingList;

  private int[][] attackList;

  private int maxX;

  private int maxY;

  private List unitList = Collections.synchronizedList(new ArrayList(10));

  // 戰鬥個體圖
  private Image[] unitImages = Utility.getSplitImages("image/unit.png", tile,
      tile);

  private Image[] iconImages = Utility.getSplitImages("image/icon.png", tile,
      tile);

  private Image[] listImages = Utility.getSplitImages("image/list.png", tile,
      tile);

  private Thread gameLoop;

  private int eventCode = -1;

  final static int tile = 32;

  public GameCanvas() {
    actionUnit = -1;
    state = "戰鬥開始";
    turn = 1;
    this.setBackground(Color.BLACK);
    this.map = new Map("map.txt", tile);
    // 地圖
    this.mapImage = this.map.getMapImage();
    this.maxX = map.getMaxX();
    this.maxY = map.getMaxY();
    this.moveList = new int[maxX][maxY];
    this.movingList = new int[maxX][maxY];
    this.attackList = new int[maxX][maxY];
    int width = maxX * tile;
    int height = maxY * tile;
    // 菜單
    this.menu = new Menu(maxX - 1);
    // 創建角色:name=空罐少女,team=0(我軍),imageindex=3,x=7,y=1,以下雷同
    createRole("空罐少女", 0, 0, 3, 7, 1);
    createRole("貓貓1", 0, 1, 6, 1, 2);
    createRole("貓貓2", 0, 0, 3, 2, 6);
    // 創建角色:name=躲貓兵團1,team=1(敵軍),imageindex=6,x=4,y=5,以下雷同
    createRole("躲貓兵團1", 1, 2, 4, 4, 5);
    createRole("躲貓兵團2", 1, 2, 4, 8, 5);
    createRole("躲貓兵團3", 1, 2, 4, 5, 7);
    createRole("躲貓兵團4", 1, 2, 4, 7, 2);
    this.screen = Utility.createImage(width, height, true);
    this.graphics = (Graphics2D) screen.getGraphics();
    // 初始化
    this.initRange();
    // 繪製圖像
    this.drawBattle();
    this.setPreferredSize(new Dimension(width - 2, height - 2));
    this.setFocusable(true);
    this.addKeyListener(this);
    // 開始構建遊戲
    this.mainLoop();
  }

  public void mainLoop() {
    gameLoop = new Thread(this);
    gameLoop.start();
  }

  public void run() {
    for (;;) {
      long start = System.currentTimeMillis();
      long end = System.currentTimeMillis();
      long time = end - start;
      long sleepTime = 20L - time;
      if (sleepTime < 0L) {
        sleepTime = 0L;
      }
      this.eventClick();
      try {
        Thread.sleep(sleepTime);
      } catch (InterruptedException e) {
      }
    }
  }

  /**
    * 事件觸發
    *
    */

  public synchronized void eventClick() {

    switch (eventCode) {
    // 按下Enter,開始觸發遊戲事件
    case KeyEvent.VK_ENTER:
      int index = 0;
      // 當遊戲狀態爲[狀態顯示]下
      if (state.equalsIgnoreCase("狀態顯示")) {
        // 光標指向我方未行動角色
        index = getRoleIdx(0, curX, curY);
        if ((index > -1) && (getRole(index).action == 0)) {
          state = "角色移動";
          actionUnit = getRoleIdx(0, curX, curY);
          // 繪製移動範圍
          setMoveRange();
          movingList[curX][curY] = moveCount;
          drawBattle();
          // 光標指向敵方未行動角色
        } else if (getRoleIdx(1, curX, curY) > -1) {
          state = "移動範圍";
          actionUnit = getRoleIdx(1, curX, curY);
          setMoveRange();
          drawBattle();
          // 查看角色情報
        } else {
          state = "情報查看";
          openMenu(0);
          drawBattle();
        }
      }
      // 選擇移動
      else if (state.equalsIgnoreCase("角色移動")) {
        // 無法移動的區域
        if (moveList[curX][curY] < 0) {
          return;
        }
        // 監測移動地點
        if ((getRoleIdx(0, curX, curY) == -1)
            || (moveList[curX][curY] == 0)) {
          lastX = getRole(actionUnit).x;
          lastY = getRole(actionUnit).y;
          moveRole();
          state = "行動菜單";
          // 繪製***範圍
          setAttackRange(true);
          // 判定菜單項
          if (isAttackCheck()) {
            openMenu(2);
          } else {
            openMenu(1);
          }
          drawBattle();
        }
      }
      // 當角色移動後
      else if (state.equalsIgnoreCase("行動菜單")) {
        if (menu.getMenuItem(menu.cur).equalsIgnoreCase("***")) {
          state = "進行***";
          closeMenu();
          drawBattle();
        } else if (menu.getMenuItem(menu.cur).equalsIgnoreCase("待機")) {
          state = "狀態顯示";
          closeMenu();
          getRole(actionUnit).action = 1;
          actionUnit = -1;
          initRange();
          drawBattle();
        }
      }
      // ***時
      else if (state.equalsIgnoreCase("進行***")) {
        // 無法***
        if (attackList[curX][curY] < 2) {
          return;
        }
        // 當指定地點敵方存在時
        if ((index = getRoleIdx(1, curX, curY)) > -1) {
          // 刪除List中敵方角色(此處可設定減血規範)
          unitList.remove(index);
          state = "狀態顯示";
          // 改變行動狀態
          getRole(actionUnit).action = 1;
          actionUnit = -1;
          initRange();
          drawBattle();
        }
      }
      // 查看角色移動範圍
      else if (state.equalsIgnoreCase("移動範圍")) {
        state = "狀態顯示";
        Role role = getRole(actionUnit);
        curX = role.x;
        curY = role.y;
        actionUnit = -1;
        initRange();
        drawBattle();
      }
      // 查看角色情報
      else if (state.equalsIgnoreCase("情報查看")) {
        // 本回合戰鬥結束
        if (menu.getMenuItem(menu.cur).equalsIgnoreCase("結束")) {
          closeMenu();
          curX = 0;
          curY = 0;
          setBeforeAction();
          state = "戰鬥結束";
          drawBattle();
        }
      }
      // 我軍開始行動
      else if (state.equalsIgnoreCase("戰鬥開始")) {
        state = "狀態顯示";
        drawBattle();
      }
      // 敵軍開始行動
      else if (state.equalsIgnoreCase("戰鬥結束")) {
        state = "敵方行動";
        enemyAction();
        setBeforeAction();
        turn = turn + 1;
        state = "戰鬥開始";
        drawBattle();
      }
      break;
    // 按下ESC,取消已做選擇
    case KeyEvent.VK_ESCAPE:
      if (state.equalsIgnoreCase("角色移動")) // 移動
      {
        state = "狀態顯示";
        Role role = (Role) unitList.get(actionUnit);
        curX = role.x;
        curY = role.y;
        actionUnit = -1;
        initRange();
        drawBattle();
      } else if (state.equalsIgnoreCase("行動菜單")) // 移動後
      {
        state = "角色移動";
        closeMenu();
        setAttackRange(false); // 不顯示***範圍
        Role role = (Role) unitList.get(actionUnit);
        role.x = lastX;
        role.y = lastY;
        drawBattle();
      } else if (state.equalsIgnoreCase("進行***")) // ***狀態
      {
        state = "行動菜單";
        Role role = (Role) unitList.get(actionUnit);
        curX = role.x;
        curY = role.y;
        openMenu(menu.menuType);
        drawBattle();
      } else if (state.equalsIgnoreCase("移動範圍")) { // 移動範圍

        state = "狀態顯示";
        Role role = (Role) unitList.get(actionUnit);
        curX = role.x;
        curY = role.y;
        actionUnit = -1;
        initRange();
        drawBattle();
      }

      else if (state.equalsIgnoreCase("情報查看")) // 角色情報
      {
        state = "狀態顯示";
        closeMenu();
        drawBattle();
      }

      else if (state.equalsIgnoreCase("戰鬥開始")) // 我軍行動
      {
        state = "狀態顯示";
        drawBattle();
      }

      else if (state.equalsIgnoreCase("戰鬥結束")) // 敵軍行動
      {
        state = "敵方行動";
        enemyAction();
        setBeforeAction();
        turn = turn + 1;
        state = "戰鬥開始";
        drawBattle();
      }
      break;
    }
    if (eventCode > -1) {
      eventCode = -1;
    }

  }

  /**
    * 初始化各項範圍參數
    *
    */

  public synchronized void initRange() {
    for (int y = 0; y <= maxY - 1; y++) {
      for (int x = 0; x <= maxX - 1; x++) {
        moveCount = 0;
        moveList[x][y] = -1;
        movingList[x][y] = -1;
        attackList[x][y] = 0;
      }
    }
  }

  /**
    * 獲得移動到指定地點所需步數
    *
    * @param x
    * @param y
    * @return
    */

  public synchronized int getMoveCount(int x, int y) {
    if ((x < 0) || (x > maxX - 1) || (y < 0) || (y > maxY - 1)) {
      // 無法移動返回-1
      return -1;
    }
    return moveList[x][y];
  }

  /**
    * 設定移動步數
    *
    * @param x
    * @param y
    * @param count
    */

  public synchronized void setMoveCount(int x, int y, int count) {
    Role role = getRole(actionUnit);
    // 當爲我軍時
    if (role.team == 0) {
      if (getRoleIdx(1, x, y) > -1) {
        return;
      }
    } else {
      if (getRoleIdx(0, x, y) > -1) {
        return;
      }
    }
    int cost = map.getMapCost(x, y);
    // 指定位置無法進入
    if (cost < 0) {
      return;
    }
    count = count + cost;
    // 移動步數超過移動能力
    if (count > role.move) {
      return;
    }
    // 獲得移動所需步數
    if ((moveList[x][y] == -1) || (count < moveList[x][y])) {
      moveList[x][y] = count;
    }
  }

  /**
    * 設定***範圍
    *
    * @param isAttack
    */

  public synchronized void setAttackRange(final boolean isAttack) {
    try {
      int x, y, point;
      if (isAttack == true) {
        point = 2;
      } else {
        point = 1;
      }
      Role role = getRole(actionUnit);
      x = role.x;
      y = role.y;
      // 判定***點
      if (x > 0) {
        attackList[x - 1][y] = point;
      }
      if (y > 0) {
        attackList[x][y - 1] = point;
      }
      if (x < maxX - 1) {
        attackList[x + 1][y] = point;
      }
      if (y < maxY - 1) {
        attackList[x][y + 1] = point;
      }
    } catch (Exception e) {
    }
  }

  /**
    * 判斷是否能做出***
    *
    * @return
    */

  public synchronized boolean isAttackCheck() {
    for (int i = 0; i < unitList.size(); i++) {
      Role role = getRole(i);
      if (role.team != 1) {
        continue;
      }
      if (attackList[role.x][role.y] == 2) {
        return true;
      }
    }
    return false;
  }

  /**
    * 設定菜單
    *
    * @param menuType
    */

  public synchronized void openMenu(int menuType) {
    menu.visible = true;
    menu.setMenuType(menuType);
    menu.cur = 0;
  }

  /**
    * 關閉菜單
    *
    */

  public synchronized void closeMenu() {
    menu.visible = false;
  }

  /**
    * 設定所有角色參與行動
    *
    */

  public synchronized void setBeforeAction() {
    for (Iterator it = unitList.iterator(); it.hasNext();) {
      Role role = (Role) it.next();
      role.setAction(0);
    }
  }

  /**
    * 返回指定索引下角色
    *
    * @param index
    * @return
    */

  public synchronized Role getRole(final int index) {
    if (unitList != null && index > -1) {
      return (Role) unitList.get(index);
    }
    return null;
  }

  /**
    * 設定移動路線
    *
    */

  public synchronized void setMoveCourse() {
    if (moveList[curX][curY] == -1) {
      return;
    }
    if (movingList[curX][curY] == moveCount) {
      return;
    }

    // 選擇可行的最短路徑
    if ((movingList[redressX(curX - 1)][curY] != moveCount)
        && (movingList[curX][redressY(curY - 1)] != moveCount)
        && (movingList[redressX(curX + 1)][curY] != moveCount)
        && (movingList[curX][redressY(curY + 1)] != moveCount)
        || (moveCount + map.getMapCost(curX, curY) > getRole(actionUnit).move)) {

      for (int j = 0; j <= maxY - 1; j++) {
        for (int i = 0; i <= maxX - 1; i++) {
          movingList[i][j] = -1;
        }
      }
      int x = curX;
      int y = curY;
      moveCount = moveList[curX][curY];
      movingList[x][y] = moveCount;
      // 獲得移動路徑
      for (int i = moveCount; i > 0; i--) {
        switch (setMoveCouse(x, y)) {
        case 0:
          x = x - 1;
          break;
        case 1:
          y = y - 1;
          break;
        case 2:
          x = x + 1;
          break;
        case 3:
          y = y + 1;
          break;
        case 4:
          break;
        }

      }
      moveCount = moveList[curX][curY];
      movingList[x][y] = 0;
      return;
    }
    // 獲得矯正的移動步數
    moveCount = moveCount + map.getMapCost(curX, curY);

    if (movingList[curX][curY] > -1) {
      moveCount = movingList[curX][curY];
      for (int j = 0; j <= maxY - 1; j++) {
        for (int i = 0; i <= maxX - 1; i++) {
          if (movingList[i][j] > movingList[curX][curY]) {
            movingList[i][j] = -1;
          }
        }
      }
    }
    movingList[curX][curY] = moveCount;
  }

  /**
    * 設定最短移動路徑
    *
    * @param x
    * @param y
    * @return
    */

  public synchronized int setMoveCouse(int x, int y) {
    // 判定左方最短路徑
    if ((x > 0) && (moveList[x - 1][y] > -1)
        && (moveList[x - 1][y] < moveList[x][y])
        && (moveList[x - 1][y] == moveCount - map.getMapCost(x, y))) {

      moveCount = moveCount - map.getMapCost(x, y);
      movingList[x - 1][y] = moveCount;
      return 0;
    }
    // 判定上方最短路徑
    if ((y > 0) && (moveList[x][y - 1] > -1)
        && (moveList[x][y - 1] < moveList[x][y])
        && (moveList[x][y - 1] == moveCount - map.getMapCost(x, y))) {
      moveCount = moveCount - map.getMapCost(x, y);
      movingList[x][y - 1] = moveCount;
      return 1;
    }

    // 判定右方最短路徑
    if ((x < maxX - 1) && (moveList[x + 1][y] > -1)
        && (moveList[x + 1][y] < moveList[x][y])
        && (moveList[x + 1][y] == moveCount - map.getMapCost(x, y))) {
      moveCount = moveCount - map.getMapCost(x, y);
      movingList[x + 1][y] = moveCount;
      return 2;

    }

    // 判定下方最短路徑
    if ((y < maxY - 1) && (moveList[x][y + 1] > -1)
        && (moveList[x][y + 1] < moveList[x][y])
        && (moveList[x][y + 1] == moveCount - map.getMapCost(x, y))) {

      moveCount = moveCount - map.getMapCost(x, y);
      movingList[x][y + 1] = moveCount;
      return 3;
    }
    return 4;
  }

  /**
    * 移動角色
    *
    */

  public synchronized void moveRole() {
    state = "開始移動";
    int x = lastX;
    int y = lastY;
    int direction;
    // 移動方向判定
    for (int i = 0; i <= moveCount; i++) {
      direction = 4;
      if ((x > 0)
          && (moveList[x - 1][y] > -1)
          && (movingList[x - 1][y] - map.getMapCost(x - 1, y) == movingList[x][y]))
        direction = 0; // 左
      if ((y > 0)
          && (moveList[x][y - 1] > -1)
          && (movingList[x][y - 1] - map.getMapCost(x, y - 1) == movingList[x][y]))
        direction = 1; // 上
      if ((x < maxX - 1)
          && (moveList[x + 1][y] > -1)
          && (movingList[x + 1][y] - map.getMapCost(x + 1, y) == movingList[x][y]))
        direction = 2; // 右
      if ((y < maxY - 1)
          && (moveList[x][y + 1] > -1)
          && (movingList[x][y + 1] - map.getMapCost(x, y + 1) == movingList[x][y]))
        direction = 3; // 下
      switch (direction) {
      case 0:
        x = x - 1;
        break;
      case 1:
        y = y - 1;
        break;
      case 2:
        x = x + 1;
        break;
      case 3:
        y = y + 1;
        break;
      case 4:
        break;
      }
      Role role = getRole(actionUnit);
      role.setX(x);
      role.setY(y);
      drawBattle();
      Utility.wait(10);
    }
    getRole(actionUnit).x = curX;
    getRole(actionUnit).y = curY;
    Utility.wait(10);
  }

  /**
    * 設定移動範圍
    */

  public synchronized void setMoveRange() {
    Role role = getRole(actionUnit);
    int x = role.x;
    int y = role.y;
    int area = role.move; // 有效範圍

    moveList[x][y] = 0; // 設定現在爲移動0步

    for (int count = 0; count <= area - 1; count++) {
      for (int j = redressY(y - area); j < redressY(y + area); j++) {
        for (int i = redressX(x - (area - Math.abs(y - j))); i <= redressX(x
            + (area - Math.abs(y - j))); i++) {
          // 如果能夠移動指定步數
          if ((getMoveCount(i - 1, j) == count)
              || (getMoveCount(i, j - 1) == count)
              || (getMoveCount(i + 1, j) == count)
              || (getMoveCount(i, j + 1) == count)) {
            setMoveCount(i, j, count);
          }
        }
      }
    }

    area = area + 1; // 射程
    for (int j = redressY(y - area); j <= redressY(y + area); j++) {
      for (int i = redressX(x - (area - Math.abs(y - j))); i <= redressX(x
          + (area - Math.abs(y - j))); i++) {
        // 遠程***
        if ((getMoveCount(i - 1, j) > -1)
            || (getMoveCount(i, j - 1) > -1)
            || (getMoveCount(i + 1, j) > -1)
            || (getMoveCount(i, j + 1) > -1)) {
          attackList[i][j] = 1;
        }
      }
    }
  }

  /**
    * 獲得指定索引及分組下角色
    *
    * @param team
    * @param x
    * @param y
    * @return
    */

  public synchronized int getRoleIdx(final int team, final int x, final int y) {
    int index = 0;
    for (Iterator it = unitList.iterator(); it.hasNext();) {
      Role role = (Role) it.next();
      if (x == role.x && y == role.y && team == role.team) {
        return index;
      }
      index++;
    }
    return -1;
  }

  /**
    * 創建角色
    *
    * @param name
    * @param team
    * @param imageIndex
    * @param move
    * @param x
    * @param y
    */

  private synchronized void createRole(String name, int team, int imageIndex,
      int move, int x, int y) {
    unitList.add(new Role(name, team, unitImages[imageIndex], move, x, y));
  }

  /**
    * 繪製畫面
    *
    */

  public synchronized void drawBattle() {

    int count = 0;
    // 繪製地圖
    graphics.drawImage(mapImage, 0, 0, null);

    // 移動範圍繪製
    if ((state.equalsIgnoreCase("角色移動"))
        || (state.equalsIgnoreCase("移動範圍"))) {
      for (int j = 0; j <= maxY - 1; j++) {
        for (int i = 0; i <= maxX - 1; i++) {
          if (moveList[i][j] > -1) {
            graphics.drawImage(iconImages[2], i * tile, j * tile,
                null);
          } else if (attackList[i][j] > 0) {
            graphics.drawImage(iconImages[3], i * tile, j * tile,
                null);
          }
        }
      }
    }
    // 角色繪製
    for (int index = 0; index < unitList.size(); index++) {
      Role role = (Role) unitList.get(index);
      if (index == actionUnit) {
        // 當前控制角色處理(此示例未加入特殊處理)
        graphics.drawImage(role.getImage(), role.getX() * tile, role
            .getY()
            * tile, null);
      } else {
        graphics.drawImage(role.getImage(), role.getX() * tile, role
            .getY()
            * tile, null);
      }
      // 已行動完畢
      if (role.action == 1) {
        graphics.drawImage(unitImages[3], role.getX() * tile, role
            .getY()
            * tile, null);
      }
    }
    // ***範圍繪製
    if (state.equalsIgnoreCase("進行***")) {
      for (int j = 0; j <= maxY - 1; j++) {
        for (int i = 0; i <= maxX - 1; i++) {
          int result = attackList[i][j];
          if (result == 2) {
            graphics.drawImage(iconImages[3], i * tile, j * tile,
                null);
          }
          // 標註選中的***對象
          if (result == 2 && getRoleIdx(1, i, j) > -1 && curX == i
              && curY == j) {
            graphics.drawImage(iconImages[4], i * tile, j * tile,
                null);
          }
        }
      }
    }
    // 繪製移動路線
    if (state.equalsIgnoreCase("角色移動")) {
      for (int j = 0; j <= maxY - 1; j++) {
        for (int i = 0; i <= maxX - 1; i++) {
          if (movingList[i][j] == -1) {
            continue;
          }
          count = 0;
          if ((movingList[i][j] == 0)
              || (movingList[i][j] == moveCount)) {
            if ((i > 0)
                && (movingList[i - 1][j] > -1)
                && ((movingList[i - 1][j]
                    - map.getMapCost(i - 1, j) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i - 1][j]))) {
              count = 1;
            }
            if ((j > 0)
                && (movingList[i][j - 1] > -1)
                && ((movingList[i][j - 1]
                    - map.getMapCost(i, j - 1) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i][j - 1]))) {
              count = 2;
            }
            if ((i < maxX-1)
                && (movingList[i + 1][j] > -1)
                && ((movingList[i + 1][j]
                    - map.getMapCost(i + 1, j) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i + 1][j]))) {
              count = 3;
            }
            if ((j < maxY-1)
                && (movingList[i][j + 1] > -1)
                && ((movingList[i][j + 1]
                    - map.getMapCost(i, j + 1) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i][j + 1]))) {
              count = 4;
            }
            if (movingList[i][j] != 0) {
              count = count + 4;
            }
          } else {
            count = 6;
            if ((i > 0)
                && (movingList[i - 1][j] > -1)
                && ((movingList[i - 1][j]
                    - map.getMapCost(i - 1, j) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i - 1][j]))) {
              count = count + 1;
            }
            if ((j > 0)
                && (movingList[i][j - 1] > -1)
                && ((movingList[i][j - 1]
                    - map.getMapCost(i, j - 1) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i][j - 1]))) {
              count = count + 2;
            }
            if ((i < maxX-1)
                && (movingList[i + 1][j] > -1)
                && ((movingList[i + 1][j]
                    - map.getMapCost(i + 1, j) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i + 1][j]))) {
              count = count + 3;
            }
            if ((j < maxY-1)
                && (movingList[i][j + 1] > -1)
                && ((movingList[i][j + 1]
                    - map.getMapCost(i, j + 1) == movingList[i][j]) || (movingList[i][j]
                    - map.getMapCost(i, j) == movingList[i][j + 1]))) {
              count = count + 5;
            }
          }
          if (count > 0) {
            graphics.drawImage(iconImages[count + 4], i * tile, j
                * tile, null);
          }
        }
      }
    }
    // 菜單
    if (menu.visible) {
      Utility.setAlpha(graphics, 0.50f);
      graphics.drawImage(listImages[0], menu.getLeft(curX) * tile, 0,
          null);
      for (int i = 1; i <= menu.width; i++) {
        graphics.drawImage(listImages[1], (menu.getLeft(curX) + i)
            * tile, 0, null);
      }
      graphics.drawImage(listImages[2],
          (menu.getLeft(curX) + menu.width + 1) * tile, 0, null);
      for (int j = 1; j <= menu.height; j++) {
        graphics.drawImage(listImages[3], menu.getLeft(curX) * tile, j
            * tile, null);
        for (int i = 1; i <= menu.width; i++) {
          graphics.drawImage(listImages[4], (menu.getLeft(curX) + i)
              * tile, j * tile, null);
        }
        graphics.drawImage(listImages[5], (menu.getLeft(curX)
            + menu.width + 1)
            * tile, j * tile, null);
      }
      graphics.drawImage(listImages[6], menu.getLeft(curX) * tile,
          (menu.height + 1) * tile, null);
      for (int i = 1; i <= menu.width; i++) {
        graphics.drawImage(listImages[7], (menu.getLeft(curX) + i)
            * tile, (menu.height + 1) * tile, null);
      }
      graphics.drawImage(listImages[8],
          (menu.getLeft(curX) + menu.width + 1) * tile,
          (menu.height + 1) * tile, null);
      Utility.setAlpha(graphics, 1.0f);
      // 寫入文字
      graphics.drawImage(iconImages[1], (menu.getLeft(curX) + 1) * tile,
          (menu.cur + 1) * tile, null);

      for (int j = 1; j <= menu.height; j++) {
        graphics.setColor(Color.white);
        Utility.drawDefaultString(menu.getMenuItem(j - 1), graphics,
            (menu.getLeft(curX) + 2) * tile, ((j * tile)) + 24, 0,
            23);
      }

    }
    // 顯示狀態
    if (state.equalsIgnoreCase("狀態顯示")) {
      int i = getRoleIdx(0, curX, curY);
      if (i == -1) {
        i = getRoleIdx(1, curX, curY);
      }
      if (i > -1) {
        Role role = (Role) unitList.get(i);
        Utility.setAlpha(graphics, 0.75f);
        graphics.drawImage(listImages[0], menu.getLeft(curX) * tile, 0,
            null);
        graphics.drawImage(listImages[1], (menu.getLeft(curX) + 1)
            * tile, 0, null);
        graphics.drawImage(listImages[1], (menu.getLeft(curX) + 2)
            * tile, 0, null);
        graphics.drawImage(listImages[2], (menu.getLeft(curX) + 3)
            * tile, 0, null);

        graphics.drawImage(listImages[3], (menu.getLeft(curX)) * tile,
            tile, null);
        graphics.drawImage(listImages[4], (menu.getLeft(curX) + 1)
            * tile, tile, null);
        graphics.drawImage(listImages[4], (menu.getLeft(curX) + 2)
            * tile, tile, null);
        graphics.drawImage(listImages[5], (menu.getLeft(curX) + 3)
            * tile, tile, null);

        graphics.drawImage(listImages[3], menu.getLeft(curX) * tile,
            64, null);
        graphics.drawImage(listImages[4], (menu.getLeft(curX) + 1)
            * tile, 64, null);
        graphics.drawImage(listImages[4], (menu.getLeft(curX) + 2)
            * tile, 64, null);
        graphics.drawImage(listImages[5], (menu.getLeft(curX) + 3)
            * tile, 64, null);

        graphics.drawImage(listImages[6], (menu.getLeft(curX)) * tile,
            96, null);
        graphics.drawImage(listImages[7], (menu.getLeft(curX) + 1)
            * tile, 96, null);
        graphics.drawImage(listImages[7], (menu.getLeft(curX) + 2)
            * tile, 96, null);
        graphics.drawImage(listImages[8], (menu.getLeft(curX) + 3)
            * tile, 96, null);
        Utility.setAlpha(graphics, 1.0f);
        // 顯示角色數據
        graphics.drawImage(role.getImage(), (menu.getLeft(curX) + 1)
            * tile + 16, tile, null);
        Utility.drawDefaultString("HP:" + role.getHp(), graphics, (menu
            .getLeft(curX) + 1)
            * tile + 12, 75, 1, 12);
        Utility.drawDefaultString("MV:" + role.getMove(), graphics,
            (menu.getLeft(curX) + 1) * tile + 12, 88, 1, 12);
      }
    }
    // 戰鬥回合
    if (state.equalsIgnoreCase("戰鬥開始") || state.equalsIgnoreCase("戰鬥結束")) {
      Utility.setAlpha(graphics, 0.5f);
      graphics.setColor(Color.black);
      graphics.fillRect(0, 90, 320, 140);
      graphics.setColor(Color.white);
      Utility.setAlpha(graphics, 1.0f);
      Utility.drawDefaultString("第" + turn + "回合", graphics, 120, 160, 0,
          25);
    }
    // 我方移動
    else if (state.equalsIgnoreCase("開始移動")) {
      // 未添加處理
    } else if (state.equalsIgnoreCase("敵方行動")) {
      for (int i = unitList.size() - 1; i > -1; i--) {
        Role role = (Role) unitList.get(i);
        // 敵軍,且無法再次移動和***
        if (role.team == 1 && role.action == 1) {
          int x = role.x;
          int y = role.y;
          int index = 0;
          // 當敵軍移動地點附近才能在我方人物時, 直接刪除List中我方角色(實際開發中應加入相應判定)
          if ((index = getRoleIdx(0, x, y + 1)) > -1
              && !role.isAttack()) {
            unitList.remove(index);
          } else if ((index = getRoleIdx(0, x, y - 1)) > -1
              && !role.isAttack()) {
            unitList.remove(index);
          } else if ((index = getRoleIdx(0, x + 1, y)) > -1
              && !role.isAttack()) {
            unitList.remove(index);
          } else if ((index = getRoleIdx(0, x - 1, y)) > -1
              && !role.isAttack()) {
            unitList.remove(index);
          }
          role.setAttack(true);
        }
      }

    } else {
      // 繪製光標
      graphics.drawImage(iconImages[0], curX * tile, curY * tile, null);

    }

    // 刷新畫面
    this.repaint();
  }

  public void paint(Graphics g) {
    g.drawImage(screen, 0, 0, null);
    g.dispose();
  }

  public void update(Graphics g) {
    paint(g);
  }

  /**
    * 矯正x座標
    *
    * @param x
    * @return
    */

  public synchronized int redressX(int x) {
    if (x < 0)
      x = 0;
    if (x > maxX - 1)
      x = maxX - 1;
    return x;
  }

  /**
    * 矯正y座標
    *
    * @param y
    * @return
    */

  public synchronized int redressY(int y) {
    if (y < 0)
      y = 0;
    if (y > maxY - 1)
      y = maxY - 1;
    return y;
  }

  /**
    * 敵軍行動
    *
    */

  public synchronized void enemyAction() {
    for (int index = 0; index < unitList.size(); index++) {
      Role role = (Role) unitList.get(index);
      if (role.team != 1) {
        continue;
      }
      actionUnit = index;
      setMoveRange();
      // 隨機選擇敵方移動地點
      int x = role.move - new Random().nextInt(role.move * 2 + 1);
      int y = (role.move - Math.abs(x))
          - new Random().nextInt((role.move - Math.abs(x)) * 2 + 1);
      x = redressX(role.x + x);
      y = redressY(role.y + y);
      if ((moveList[x][y] > 0) && (getRoleIdx(0, x, y) == -1)
          && (getRoleIdx(1, x, y) == -1)) {
        // 記錄角色最後的移動位置
        lastX = role.x;
        lastY = role.y;
        curX = x;
        curY = y;
        moveCount = moveList[x][y];
        movingList[x][y] = moveCount;
        for (int i = 0; i < moveCount; i++) {
          switch (setMoveCouse(x, y)) {
          case 0:
            x = x - 1;
            break;
          case 1:
            y = y - 1;
            break;
          case 2:
            x = x + 1;
            break;
          case 3:
            y = y + 1;
            break;
          default:
            break;
          }
        }
        moveCount = moveList[curX][curY];
        movingList[x][y] = 0;
        moveRole();
      }
      state = "敵方行動";
      curX = 0;
      curY = 0;
      role.setAction(1);
      role.setAttack(false);
      actionUnit = -1;
      initRange();
      drawBattle();
      Utility.wait(200);
    }
  }

  public void keyReleased(KeyEvent e) {
    if (state.equalsIgnoreCase("戰鬥開始"))
      return;
    if (state.equalsIgnoreCase("戰鬥結束"))
      return;
    if (state.equalsIgnoreCase("敵方行動"))
      return;
    // 菜單可見
    if (menu.visible) {
      switch (e.getKeyCode()) {
      case KeyEvent.VK_UP:
        if (menu.cur > 0) {
          menu.cur = menu.cur - 1;
        }
        break;
      case KeyEvent.VK_DOWN:
        if (menu.cur < menu.height - 1) {
          menu.cur = menu.cur + 1;
        }
        break;
      }

    }
    // 菜單不可見
    else {
      switch (e.getKeyCode()) {
      case KeyEvent.VK_LEFT:
        curX = redressX(curX - 1);
        break;
      case KeyEvent.VK_UP:
        curY = redressY(curY - 1);
        break;
      case KeyEvent.VK_RIGHT:
        curX = redressX(curX + 1);
        break;
      case KeyEvent.VK_DOWN:
        curY = redressY(curY + 1);
        break;
      }
    }
    if (state.equalsIgnoreCase("角色移動")) {
      setMoveCourse();
    }
    drawBattle();
  }

  public void keyPressed(KeyEvent e) {
    int code = e.getKeyCode();
    eventCode = code;
  }

  public void keyTyped(KeyEvent e) {

  }

}


示例程序截圖如下:
 
回合開始:

 
 
角色狀態:

 
 
移動尋徑:

 
 
菜單交互:

 
 
目標選擇:

 
 
活動單元轉移:

 
 
 示例程序在附件中

 

 ————華麗的分割線———
 
 本來上週就說寫的東西,卻由於某個事件的刺激,導致上週某幾天中我回家就跑去各個論壇跟水軍打嘴仗,拖到本週才動手壘碼……對於這種“嘴勤屁股懶”的行徑,在此強烈鄙視自己(-_-|||)……

PS:實際上示例代碼週二晚已完成,計劃中昨晚就該發博文,結果中途忍不住又點了某個論壇,又和水軍對噴半天,所以耗到今天這篇博文才得以面世,寫的不夠周全,這兩天會慢慢補齊,還望各位大人見諒^^。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章