一. 程序基本結構
基本結構
一. 程序基本結構
基本結構
二. 實現的功能
1、單人模式
2、雙人模式
3、通過廣度優先算法實現坦克尋路功能
三. 運行效果
主界面
一. 程序基本結構
基本結構
二. 實現的功能
1、單人模式
2、雙人模式
3、通過廣度優先算法實現坦克尋路功能
三. 運行效果
主界面
運行
一. 程序基本結構
基本結構
二. 實現的功能
1、單人模式
2、雙人模式
3、通過廣度優先算法實現坦克尋路功能
三. 運行效果
主界面
運行
四. 實現思路
數據存儲表示: 在JPanel繪製圖像,統一規定各個方塊的大小為同一大小(如牆壁,坦克之類,子彈除外),從而方便使用二維數組存儲地圖的各個元素。
關於檢測物體碰撞,這裡使用了一個MyImage的父類,將坦克,牆壁
定義為繼承這個父類的一個類。
class MyImage {
int width = Game.width;
int height = Game.height;
//二維地圖的座標
Coord coord;
//屏幕上的像素座標
int x;
int y;
MyImage(Coord coord) {
x = coord.x * width;
y = coord.y * height;
this.coord = coord;
}
private Rectangle getRect() {
return new Rectangle(x, y, width, height);
}
//碰撞檢測
boolean isIntersects(MyImage other) {
return other.getRect().intersects(getRect());
}
}
圖像打印則藉助遍歷兩個ConcurrentHashMap分別儲存坦克和其他類型的方塊。將這些方塊使用Map而不是使用數組是因為管理起來比較方便,而二維數組則是為了尋路算法而準備的,防止了頻繁使用上面的兩個Map而導致線程鎖的問題。
五. 遇到的問題
java的按鍵監聽在響應按鍵長按時會有1-2秒的延遲,導致操作手感極差
解決方法:在坦克類中設置一個布爾屬性move以及整形變量key儲存鍵入的按鍵值,創建一個線程來響應按鍵。
實現代碼:
//按鍵響應的線程類
class MyTankMove implements Runnable{
public void run(){
while(flag){
GetKey(key);
while(move){//決定是否移動
try {
e.printStackTrace();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(10);//防止無按鍵時陷入死循環導致線程堵塞
} catch (InterruptedException e) {
}
}
}
按鍵監聽事件,即給key和move賦值
//按鍵監聽接口
private class KeyBoradListener extends KeyAdapter{
public void keyPressed(KeyEvent e){
super.keyPressed(e);
int key = e.getKeyCode();
if(key<65){//為了實現雙人對戰而設置的。。。
if(key!=KeyEvent.VK_SHIFT&&!MyTank.isEmpty()){
MyTank.getFirst().key=key;
MyTank.getFirst().move=true;
}
}
else{
if(key!=KeyEvent.VK_G&&!MyTank.isEmpty()){
switch (key){
case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;
case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;
case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;
case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;
}
MyTank.getLast().key=key;
MyTank.getLast().move=true;
}
}
}
public void keyReleased(KeyEvent e){
super.keyReleased(e);
int key = e.getKeyCode();
if(key<65){
if(!MyTank.isEmpty()){
if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getFirst().key){//避免了同時按兩個以上按鍵後會卡住
MyTank.getFirst().move=false;
}
else{
MyTank.getFirst().GetKey(key);
}
}
}
else{
switch (key){
case KeyEvent.VK_W:key = KeyEvent.VK_UP;break;
case KeyEvent.VK_A:key = KeyEvent.VK_LEFT;break;
case KeyEvent.VK_S:key = KeyEvent.VK_DOWN;break;
case KeyEvent.VK_D:key = KeyEvent.VK_RIGHT;break;
case KeyEvent.VK_G:key = KeyEvent.VK_SHIFT;break;
}
if(!MyTank.isEmpty()){
if(key!=KeyEvent.VK_SHIFT&&key==MyTank.getLast().key){
MyTank.getLast().move=false;
}
else{
MyTank.getLast().GetKey(key);
}
}
}
}
}
項目內的圖片資源如何在項目導出後也能使用
解決方法:假設在項目的scr文件夾中建立img文件夾,在項目的.classpath中加一句<classpathentry kind="src" path="src/img"/>(Eclipse),也可以通過設置項目的Modules,將圖像文件夾設置為resources(IDEA),並使用當前類的名.class.getResource("/(文件名)")).getImage()獲得圖像對象。
尋路的實現
實現思路:通過使用一個存儲了地圖內各個元素的二維數組Game.map,使用廣度優先算法遍歷出一條路線,將結果存放於棧之中。
實現代碼:
/**
* 使用廣度遍歷算法,使用隊列存儲遍歷的節點
*
* @return 移動的路徑
*/
private Stack<Coord> GetPath() {
Coord target = Game.tanks.get(Game.P1_TAG).coord;
Queue<Coord> d_q = new LinkedBlockingQueue<>();
ArrayList<Coord> IsMove = new ArrayList<>();
d_q.offer(coord);
IsMove.add(coord);
Coord last = null;
boolean flag;
while (!d_q.isEmpty()) {
Coord t = d_q.poll();
int tx = t.x;
int ty = t.y;
int i;
//遍歷所有的方向
for (i = 0; i < 4; ++i) {
switch (i) {
case Game.UP:
ty -= 1;
break;
case Game.LEFT:
tx -= 1;
break;
case Game.RIGHT:
tx += 1;
break;
case Game.DOWN:
ty += 1;
break;
}
//判斷該點是否可行
flag = true;
Coord z = new Coord(tx, ty);
//檢查是否為目標終點
if (z.equals(target)) {
z.per = t;
last = z;
break;
}
//檢查該座標是否已經遍歷了
for (Coord c : IsMove) {
if (c.equals(z)) {
flag = false;
break;
}
}
if (flag) {
//檢查下一格是否可以抵達
flag = !(Game.map[ty][tx] == Game.BLANK || Game.map[ty][tx] == Game.WALLS);
}
//該點可以用
if (flag) {
//將座標納入已經遍歷的隊列中
d_q.offer(z);
IsMove.add(z);
z.per = t;
last = z;
}
IsMove.add(z);
//重新選擇方向遍歷
tx = t.x;
ty = t.y;
}
//如果沒有四個方向都遍歷完就跳出,說明已經找到了終點
if (i != 4) {
break;
}
}
Stack<Coord> coords = new Stack<>();
while (null != last && last.per != null) {
coords.push(last);
last = last.per;
}
return coords;
}
關注小編,回覆坦克大戰即可獲得源碼;
本文來源:CSDN-madongyu-的博客