赞
踩
本次项目模仿了小时候玩的坦克大战小游戏,其中的功能基本实现,里面最重要的思想就是对象池,类似于前面所学的线程池,让对象重复的使用,减少资源的浪费。做的时候逻辑性很强,很不容易,不过总算是有所实现。
用到的知识点:
主要是:Frame窗体,Graphics绘图,
其次是:集合 多线程。
/**
* 主界面的绘制
*/
public class GameFrame extends Frame implements Runnable{
// 1.定义一张和屏幕大小一致的图片(双缓存)
private BufferedImage bufImg=new BufferedImage(FRAME_WIDTH,FRAME_HEIGTH,BufferedImage.TYPE_4BYTE_ABGR);
// 游戏的状态
public static int gameStart;
// 菜单指向
private int menuIndex;
// 创建坦克对象
private Tank myTank;
// 敌人坦克对象
private List<Tank> enemies=new ArrayList<>();
// 标题栏的高度
public static int menuTitle;
// 绘制游戏结束的方法,(用到的时候在加载)
private static Image overImage =null;
//定义地图相关的内容
private GameMap gameMap;
/**
* 对窗口进行初始化
*/
public GameFrame(){
initFrame();
iniEventListener();
new Thread(this).start();
}
/**
* 对窗口初始化
*/
private void initFrame(){
gameStart=START_MENU;
setTitle(NAME_TANK);
setSize(FRAME_WIDTH,FRAME_HEIGTH);
setLocationRelativeTo(null);
// 设置不可修改
setResizable(false);
menuTitle=getInsets().top;
setVisible(true);
}
/**
* 该方法负责了所有的绘制内容,该方法不能主动调用
* repaint去回调该方法
*/
public void update(Graphics g2) {
// 定义图片画笔
Graphics g=bufImg.getGraphics();
switch (gameStart){
case START_MENU:
drawMenus(g);
break;
case START_HELP:
drawHelp(g);
break;
case START_ABOUT:
drawAbout(g);
break;
case START_RUN:
drawRun(g);
break;
case START_OVER:
drawOver(g);
break;
}
// 使用系统画笔将内容绘制在窗口上
g2.drawImage(bufImg,0,0,FRAME_WIDTH, FRAME_HEIGTH,this);
}
// 主菜单的方法
private void drawMenus(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setFont(FONT_SIZE);
// 绘制黑色背景颜色
g2.setColor(Color.GRAY);
g2.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGTH);
// 把文字绘制成白色
// 该方法默认设置字体
g2.setColor(Color.black);
final int STR_WIDTH = 50;
final int x = FRAME_WIDTH / 2-50;
final int y = FRAME_HEIGTH / 3;
for (int i = 0; i < MENUS.length; i++) {
// 设置菜单的触发颜色
if (i == menuIndex) {
g2.setColor(Color.pink);
} else {
g2.setColor(Color.white);
}
g2.drawString(MENUS[i], x, y + STR_WIDTH * i);
}
}
/**
* 负责存放所以监听事件
*/
private void iniEventListener(){
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
// 获得事件相关联的整数
int keyCode=e.getKeyCode();
//不同的游戏状态,给出不同的处理方法
switch (gameStart){
case START_MENU:
keyEventMenu(keyCode);
break;
case START_HELP:
keyEventHELP(keyCode);
break;
case START_ABOUT:
keyEventABOUT(keyCode);
break;
case START_RUN:
keyEventRUN(keyCode);
break;
case START_OVER:
keyEventOVER(keyCode);
break;
}
}
@Override
public void keyReleased(KeyEvent e) {
// 获得事件相关联的整数
int keyCode=e.getKeyCode();
//不同的游戏状态,给出不同的处理方法
if(gameStart==START_RUN) {
keyReleaseEventMenu(keyCode);
}
}
});
}
// 按键松开的时候游戏处理的方法
private void keyReleaseEventMenu(int keyCode) {
switch (keyCode){
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
case KeyEvent.VK_A:
case KeyEvent.VK_LEFT:
case KeyEvent.VK_D:
case KeyEvent.VK_RIGHT:
myTank.setState(Tank.STATE_STAND);
}
}
// 菜单状态下的按键处理
private void keyEventMenu(int keyCode) {
switch (keyCode){
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
if(--menuIndex<0){
menuIndex=MENUS.length-1;
}
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
if(++menuIndex>MENUS.length-1){
menuIndex=0;
}
break;
case KeyEvent.VK_ENTER:
newGame();
break;
}
}
// 游戏中行走时的监听
private void keyEventRUN(int keyCode) {
switch (keyCode){
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
myTank.setDir(Tank.UP);
myTank.setState(Tank.STATE_MOVE);
break;
case KeyEvent.VK_DOWN:
case KeyEvent.VK_S:
myTank.setDir(Tank.DOWN);
myTank.setState(Tank.STATE_MOVE);
break;
case KeyEvent.VK_A:
case KeyEvent.VK_LEFT:
myTank.setDir(Tank.LEFT);
myTank.setState(Tank.STATE_MOVE);
break;
case KeyEvent.VK_D:
case KeyEvent.VK_RIGHT:
myTank.setDir(Tank.RIGHT);
myTank.setState(Tank.STATE_MOVE);
break;
case KeyEvent.VK_SPACE:
myTank.fire();
break;
}
}
private void keyEventHELP(int keyCode) {
}
private void keyEventABOUT(int keyCode) {
}
// 游戏结束后的执行
private void keyEventOVER(int keyCode) {
// 结束游戏
if(keyCode==KeyEvent.VK_ESCAPE){
System.exit(0);
}else if(keyCode==KeyEvent.VK_ENTER){
setGameStart(START_MENU);
resetGame();
}
}
/**
* 游戏运行状态的内容绘制
* @param g
*/
private void drawRun(Graphics g) {
g.setColor(Color.gray);
g.fillRect(0,0,FRAME_WIDTH,FRAME_HEIGTH);
// 绘制地图
gameMap.drawBK(g);
drawEnemies(g);
myTank.draw(g);
// 绘制地图遮挡
gameMap.drawCover(g);
// 子弹和坦克碰撞
drawExplodes(g);
bulletCollideTank();
bulletAndTankCollideMapTile();
}
/**
* 绘制敌人坦克
* @param g
*/
private void drawEnemies(Graphics g){
for (int i = 0; i < enemies.size(); i++) {
Tank enemy=enemies.get(i);
if(enemy.isDie()){
enemies.remove(i);
i--;
continue;
}
enemy.draw(g);
}
}
private void drawAbout(Graphics g) {
}
/**
* 游戏结束按键处理
* @param g
*/
private void drawOver(Graphics g) {
// 保证只加载一次
if(overImage ==null){
overImage =MyUtil.creatImage(gameOver);
}
// 得到图片的宽和高
int imgW=overImage.getWidth(null);
int imgH=overImage.getHeight(null);
g.drawImage(overImage,FRAME_WIDTH-imgW>>1,FRAME_HEIGTH-imgH>>1,null);
// 添加按键提示信息
g.setColor(Color.red);
g.drawString(OVER_STA,10,FRAME_HEIGTH-40);
g.drawString(OVER_STA1,FRAME_WIDTH-200,FRAME_HEIGTH-30);
}
private void drawHelp(Graphics g) {
}
@Override
public void run() {
while (true) {
repaint();
try {
Thread.sleep(REPAIN_TIME);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 重置游戏状态
*/
private void resetGame(){
menuIndex=0;
// 先让自己坦克的子弹还回对象池
myTank.bulletsReturn();
// 销毁自己坦克
myTank=null;
for (Tank enemy : enemies) {
enemy.bulletsReturn();
}
// 清空敌人
enemies.clear();
gameMap=null;
}
/**
* 开始游戏的方法
*/
private void newGame(){
gameStart=START_RUN;
// 创建坦克对象
myTank=new MyTank(FRAME_WIDTH/3,FRAME_HEIGTH-Tank.BAN_JIN ,Tank.UP);
gameMap=new GameMap();
// 使用一个单独的线程用于控制敌人的坦克
new Thread(){
@Override
public void run() {
while (true){
if(enemies.size()<MAX_ENEMY_COUNT){
Tank enemy= EnemyTank.creatEnemy();
enemies.add(enemy);
}
// 产生敌人坦克间隔
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 如果游戏不再运行中,我们就停止创建敌人坦克
if(gameStart!=START_RUN){
break;
}
}
}
}.start();
}
// 敌所有人坦克的子弹和我坦克的碰撞
// 我的坦克子弹敌人的坦克碰撞
private void bulletCollideTank(){
// 我的坦克子弹碰撞所有敌人
for (Tank enemy : enemies) {
enemy.collideBullets((myTank.getBullets()));
}
// 所有敌人坦克子弹碰撞我坦克
for (Tank enemy : enemies) {
myTank.collideBullets((enemy.getBullets()));
}
}
// 所有的子弹和地图块的碰撞
private void bulletAndTankCollideMapTile(){
// 自己子弹和砖块碰撞
myTank.bulletCollideMapTile(gameMap.getTiles());
// 敌人子弹和砖块碰撞
for (Tank enemy : enemies) {
enemy.bulletCollideMapTile(gameMap.getTiles());
}
// 坦克和地图的碰撞
if( myTank.isCollideTile(gameMap.getTiles())){
myTank.backLast();
}
// 敌人的坦克和地图的碰撞
for (Tank enemy : enemies) {
if(enemy.isCollideTile(gameMap.getTiles())){
enemy.backLast();
}
}
// 清理被销毁的地图块
gameMap.clearDestoryTile();
}
// 所有坦克的爆炸效果
private void drawExplodes(Graphics g){
for (Tank enemy : enemies) {
enemy.drawExplodes(g);
}
myTank.drawExplodes(g);
}
public static int getGameStart() {
return gameStart;
}
public static void setGameStart(int gameStart) {
GameFrame.gameStart = gameStart;
}
}
package com.hg.tank;
import com.hg.MainJFrame.GameFrame;
import com.hg.TankBattle.Bullet;
import com.hg.TankBattle.Explode;
import com.hg.Util.*;
import com.hg.map.MapTile;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import static com.hg.Util.ConsDemo.*;
/**
* 坦克共有的属性
*/
public abstract class Tank {
public static final int UP=0;
public static final int DOWN=1;
public static final int LEFT=2;
public static final int RIGHT=3;
// 坦克状态
public static final int STATE_STAND=0;
public static final int STATE_MOVE=1;
// 死亡
private static final int DIE=2;
// 坦克初始生命
public static final int DEFAULT_HP=1000;
// 坐标
private int x;
private int y;
// 半径
public static final int BAN_JIN=23;
// 坦克的默认状态
private int state;
// 默认速度,每一帧30ms 4速度
public static final int DEFAULT_SPEED=6;
// 血量
private int hp=DEFAULT_HP;
// 攻击力
private int atk;
public int ATK_MAX=500;
public int ATK_MIN=90;
// 颜色
private Color color;
// 方向
private int dir;
// 速度
private int speed=DEFAULT_SPEED;
// 是否是敌人
private boolean isEnemy =false;
// TODO炮弹
private List<Bullet> bullets=new ArrayList();
// 使用容器来保持当前坦克上的所有爆炸效果
private List<Explode> explodes=new ArrayList<>();
// 名字
private String name;
// 血条对象
private BloodBar bar=new BloodBar();
// 坦克的初始状态
public Tank(int x,int y,int dir){
this.x=x;
this.y=y;
this.dir=dir;
initTank();
}
public Tank() {
// 初始化坦克
initTank();
}
public void initTank(){
color= MyUtil.getRamdomColor();
name=MyUtil.getRandomName();
atk=MyUtil.getRamdomNumber(ATK_MIN,ATK_MAX);
}
/**
* 坦克的逻辑处理
*/
public void tankLogic(){
switch (state){
case STATE_MOVE:
move();
break;
case STATE_STAND:
break;
}
}
/**
* 坦克的移动
*/
private int oldX=-1,oidY=-1;
public void move(){
oldX=x;
oidY=y;
switch (dir){
case UP:
y-=speed;
if(y<(3*BAN_JIN))
y=3*BAN_JIN;
break;
case DOWN:
y+=speed;
if(y>(ConsDemo.FRAME_HEIGTH-BAN_JIN))
y=(ConsDemo.FRAME_HEIGTH-BAN_JIN);
break;
case LEFT:
x-=speed;
if(x<BAN_JIN)
x=BAN_JIN;
break;
case RIGHT:
x+=speed;
if(x>(ConsDemo.FRAME_WIDTH-BAN_JIN))
x =(ConsDemo.FRAME_WIDTH-BAN_JIN);
break;
}
}
/**
* @param g 绘制坦克图片,使用图片
*/
public abstract void drawTankImg(Graphics g);
public void draw(Graphics g){
tankLogic();
drawBullet(g);
drawTankImg(g);
drawName(g);
bar.drawBlood(g);
}
/**
* 绘制坦克的名字
* @param g
*/
private void drawName(Graphics g){
g.setColor(color);
g.drawString(name,x-(2*BAN_JIN),y-40);
g.setFont(GAME_FONT);
}
/**
* 对坦克进行绘画->使用系统的方式绘制
* @param g2
*/
public void drawTank(Graphics g2){
Graphics2D g=(Graphics2D)g2;
g.setColor(color);
int endX=x;
int endY=y;
// 设置画笔的宽度
Stroke stroke=new BasicStroke(4.0f);
g.setStroke(stroke);
// 绘制坦克的圆,是以矩形的一个端点开始
g.fillOval(x-BAN_JIN,y-BAN_JIN,BAN_JIN<<1,BAN_JIN<<1);
// 判断坦克走向
switch (dir){
case UP:
endY=y-2*BAN_JIN;
break;
case DOWN:
endY=y+2*BAN_JIN;
break;
case LEFT:
endX=x-2*BAN_JIN;
break;
case RIGHT:
endX=x+2*BAN_JIN;
break;
}
g.drawLine(x,y,endX,endY);
}
/**
* 坦克开火的功能
*/
// 上一次开火的时间
private long millis;
// 发射最小间隔
public static final int FIRE_INTEN=350;
public void fire(){
if(System.currentTimeMillis()-millis>FIRE_INTEN){
int bulletX=x;
int bulletY=y;
switch (dir){
case UP:
bulletY-=2*BAN_JIN;
break;
case DOWN:
bulletY+=2*BAN_JIN;
break;
case LEFT:
bulletX-=2*BAN_JIN;
break;
case RIGHT:
bulletX+=2*BAN_JIN;
break;
}
// 从池塘中拿取
Bullet bullet= BulletsPool.get();
bullet.setX(bulletX);
bullet.setY(bulletY);
bullet.setDir(dir);
bullet.setAtk(atk);
bullet.setColor(color);
bullet.setVisible(true);
//Bullet bullet = new Bullet(bulletX,bulletY,dir,atk,color);
// 将子弹添加到坦克管理的容器中。
bullets.add(bullet);
// 发射子弹后,记录本次发射子弹的时间
millis=System.currentTimeMillis();
}
}
// 发射的所有子弹绘制出来
public void drawBullet(Graphics g){
for (Bullet bullet : bullets) {
bullet.draw(g);
}
// 遍历所有的子弹,把不可见的子弹移除还原对象池
for (int i = 0; i < bullets.size(); i++) {
Bullet bullet=bullets.get(i);
if(!bullet.isVisible()){
Bullet re=bullets.remove(i);
i--;
BulletsPool.back(re);
}
}
}
/**
* 坦克销毁时处理坦克所有的子弹
*/
public void bulletsReturn(){
for (Bullet bullet : bullets) {
// 把每个子弹还回去
BulletsPool.back(bullet);
}
bullets.clear();
}
/**
* 坦克和敌人子弹碰撞的方法。
* @param bullets
*/
public void collideBullets(List<Bullet> bullets){
for (Bullet bullet : bullets) {
int bulletX=bullet.getX();
int bulletY=bullet.getY();
// 子弹和坦克碰撞
if(MyUtil.isCollide(x,y,BAN_JIN,bulletX,bulletY)){
// 子弹消失,坦克减血
bullet.setVisible(false);
hurt(bullet);
// 爆炸效果,就是坦克边沿的位置
addExplode(x,y+BAN_JIN);
}
}
}
/**
* 坦克受到伤害
* @param bullet
*/
private void hurt(Bullet bullet){
// 坦克受到伤害
int atk= bullet.getAtk();
hp-=atk;
if (hp < 0){
hp=0;
die();
}
}
/**
* 坦克死亡的内容
*/
private void die(){
if(isEnemy()){
// 坦克死亡归回对象池
EnemyTanksPool.back(this);
}else{
// 自己坦克死亡,游戏结束或者回到主菜单
delaySecondsToOver();
}
}
/**
* 判断当前坦克是否死亡了
* @return
*/
public boolean isDie(){
return hp<=0;
}
/**
* 绘制当前坦克上的所有爆炸效果
* @param g
*/
public void drawExplodes(Graphics g){
for (Explode explode : explodes) {
explode.draw(g);
}
// 把不可见的爆炸效果删除,返回对象池
for (int i = 0; i < explodes.size(); i++) {
Explode explode=explodes.get(i);
explodes.remove(i);
ExplodePool.back(explode);
i--;
}
}
/**
* 坦克回退的方法
*/
public void backLast() {
x=oldX;
y=oidY;
}
class BloodBar{
// 血条的宽和高
public static final int BAY_LENGTH=50;
public static final int BAY_HEIGHT=5;
public void drawBlood(Graphics g){
// 血条颜色填充
g.setColor(Color.yellow);
g.fillRect(x-(BAN_JIN+10),y-BAN_JIN-BAY_HEIGHT*2,BAY_LENGTH,BAY_HEIGHT);
// 当前血条颜色
g.setColor(Color.red);
g.fillRect(x-(BAN_JIN+10),y-BAN_JIN-BAY_HEIGHT*2,hp*BAY_LENGTH/DEFAULT_HP,BAY_HEIGHT);
// 血条边框
g.setColor(Color.white);
g.drawRect(x-(BAN_JIN+10) ,y-BAN_JIN-BAY_HEIGHT*2,BAY_LENGTH,BAY_HEIGHT);
}
}
// 所有的子弹和地图块的碰撞
public void bulletCollideMapTile(List<MapTile> tiles){
// 自己子弹和砖块碰撞
for (MapTile tile : tiles) {
if(tile.isCollideBullet(bullets)){
// 添加爆炸效果
addExplode(tile.getX(),tile.getY());
// 银块不被击毁
if(tile.getType()==MapTile.TYPE_HARD)
continue;
// 设置地图块销毁
tile.setVisible(false);
// 归还对象池
MapTitlePool.back(tile);
// 老巢被击毁,1s后切换到游戏结束界面
if(tile.isHouse()){
delaySecondsToOver();
}
}
}
}
/**
* 延迟若干毫秒切换到游戏结束
*/
public void delaySecondsToOver(){
new Thread(){
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
GameFrame.setGameStart(START_OVER);
}
}.start();
}
private void addExplode(int x,int y){
// 爆炸效果,就是坦克边沿的位置
Explode explode=ExplodePool.get();
explode.setX(x);
explode.setY(y);
explode.setVisible(true);
explode.setIndex(0);
explodes.add(explode);
}
// 一个地图和当前坦克碰撞的方法
/**
* 从tile`中提取八个点来判断坦克是否和地图碰撞,按照顺时针
* @param
* @return
*/
public boolean isCollideTile(List<MapTile> tiles){
for (MapTile tile : tiles) {
if(!tile.isVisible() || tile.getType()==MapTile.TYPE_COVER)
continue;
// 1左上角的点
int tileX=tile.getX();
int tileY=tile.getY();
boolean collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
// 碰上直接返回否则判断下一个点
if(collide){
return true;
}
// 点二,中上
tileX+=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 3 右上角
tileX+=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 4 右中脚
tileY+=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 5 右下角
tileY+=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 6 中下
tileX-=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 7 左下
tileX-=MapTile.radius;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
// 8 左中
tileY-=MapTile.radius;;
collide = MyUtil.isCollide(x, y, BAN_JIN, tileX, tileY);
if(collide){
return true;
}
}
return false;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
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 int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public int getDir() {
return dir;
}
public void setDir(int dir) {
this.dir = dir;
}
public boolean isEnemy() {
return isEnemy;
}
public void setEnemy(boolean enemy) {
isEnemy = enemy;
}
public List<Bullet> getBullets() {
return bullets;
}
public void setBullets(List<Bullet> bullets) {
this.bullets = bullets;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.hg.tank;
import com.hg.Util.MyUtil;
import com.hg.tank.Tank;
import java.awt.*;
import static com.hg.Util.ConsDemo.*;
import static com.hg.Util.ConsDemo.enemyUrl4;
/**
* 自己的坦克
*/
public class MyTank extends Tank {
// 自己坦克图片数组
private static Image[] tankImg;
// 静态代码块中初始化
static {
tankImg = new Image[4];
tankImg[0]= MyUtil.creatImage(url);
tankImg[1]=MyUtil.creatImage(url1);
tankImg[2]=MyUtil.creatImage(url2);
tankImg[3]=MyUtil.creatImage(url3);
}
public MyTank(int x, int y, int dir) {
super(x, y, dir);
}
public MyTank(){
}
@Override
public void drawTankImg(Graphics g) {
g.drawImage(tankImg[getDir()],getX()-BAN_JIN,getY()-BAN_JIN,null);
}
}
1.敌人坦克类
package com.hg.tank;
import com.hg.Util.EnemyTanksPool;
import com.hg.Util.MyUtil;
import java.awt.*;
import static com.hg.Util.ConsDemo.*;
import static com.hg.Util.ConsDemo.enemyUrl4;
/**
* 敌人的坦克
*/
public class EnemyTank extends Tank {
public static final int TYPE_1=0;
public static final int TYPE_2=1;
// 敌人图片数组
private static Image[] enemyImg;
private static Image[] enemyImg2;
private int type=TYPE_1;
// 记录坦克开始的时间
private long aiTime;
// 静态代码块中初始化
static {
enemyImg = new Image[4];
enemyImg[0]=MyUtil.creatImage(enemyUrl1);
enemyImg[1]=MyUtil.creatImage(enemyUrl2);
enemyImg[2]=MyUtil.creatImage(enemyUrl3);
enemyImg[3]=MyUtil.creatImage(enemyUrl4);
enemyImg2 = new Image[4];
enemyImg2[0]=MyUtil.creatImage(enemy2Url1);
enemyImg2[1]=MyUtil.creatImage(enemy2Url2);
enemyImg2[2]=MyUtil.creatImage(enemy2Url3);
enemyImg2[3]=MyUtil.creatImage(enemy2Url4);
}
public EnemyTank() {
aiTime=System.currentTimeMillis();
type=MyUtil.getRamdomNumber(0,2);
}
/**
*
* @return 用于创建敌人的坦克
*/
public static Tank creatEnemy(){
int x= MyUtil.getRamdomNumber(0,2)==0?BAN_JIN: (FRAME_WIDTH- BAN_JIN);
int y=2*BAN_JIN;
int dir=DOWN;
Tank enemy= EnemyTanksPool.get();
enemy.setX(x);
enemy.setY(y);
enemy.setDir(dir);
enemy.setEnemy(true);
enemy.setState(STATE_MOVE);
enemy.setHp(Tank.DEFAULT_HP);
return enemy;
}
public EnemyTank(int x, int y, int dir) {
super(x, y, dir);
// 敌人创建就开始计时
aiTime=System.currentTimeMillis();
type=MyUtil.getRamdomNumber(0,2);
}
@Override
public void drawTankImg(Graphics g) {
AI();
g.drawImage(type==TYPE_1?enemyImg[getDir()]:enemyImg2[getDir()],getX()-BAN_JIN,getY()-BAN_JIN,null);
}
private void AI(){
if(System.currentTimeMillis()-aiTime> ENEMY_AI){
// 间隔五秒随机切换状态
setDir(MyUtil.getRamdomNumber(UP,4));
setState(MyUtil.getRamdomNumber(0,2)==0?STATE_STAND:STATE_MOVE);
aiTime=System.currentTimeMillis();
// 开火概率
if(Math.random()< ENEMY_FIRE)
fire();
}
}
}
2.敌人坦克对象池
package com.hg.Util;
import com.hg.tank.EnemyTank;
import com.hg.tank.Tank;
import java.util.ArrayList;
import java.util.List;
/**
* 敌人坦克对象池
*/
public class EnemyTanksPool {
// 总的坦克数量
public static final int ALL_BULLET=15;
// 用于保存所有子弹的容器
private static List<Tank> pool =new ArrayList<>();
// 在类加载的时候就创建出200个子弹添加到容器中
static {
for (int i = 0; i < ALL_BULLET; i++) {
pool.add(new EnemyTank());
}
}
/**
*
* @return 从池塘中拿出坦克
*/
public static Tank get(){
Tank tank;
// 如果池塘被掏空
if(pool.size()==0){
tank=new EnemyTank();
}else{
tank= pool.remove(0);
}
return tank;
}
/**
* 子弹别销毁的时候,归还到池塘中
* @return
*/
public static void back(Tank tank){
// 池塘中的子弹已经达到最大
if(pool.size()==5){
return;
}
pool.add(tank);
}
}
package com.hg.TankBattle;
import com.hg.Util.ConsDemo;
import com.hg.tank.Tank;
import java.awt.*;
/**
* 子弹类
*/
public class Bullet {
// 子弹默认速度为坦克两倍<<1
public static final int DEFAULT_SPEED=14;
// 炮弹的半径
public static final int BAN_JIN =4;
private int x;
private int y;
private int speed=DEFAULT_SPEED;
// 方向
private int dir;
// 攻击力
private int atk;
private Color color;
// 子弹默认可见
private boolean visible=true;
public Bullet(int x, int y, int dir, int atk ,Color color) {
this.x = x;
this.y = y;
this.dir = dir;
this.atk = atk;
this.color=color;
}
// 给对象池使用的无参构造器
public Bullet(){
}
/**
* 炮弹的绘制
* @param g
*/
public void draw(Graphics g){
// 不可见就停止绘画
if(!visible)return;
g.setColor(color);
g.fillOval(x-BAN_JIN,y-BAN_JIN,BAN_JIN<<1,BAN_JIN<<1);
logicBullet();
}
/**
* 子弹的逻辑
*/
public void logicBullet(){
move();
}
/**
* 子弹的移动
*/
public void move() {
switch (dir) {
case Tank.UP:
y -= speed;
if(y<=0)
visible=false;
break;
case Tank.DOWN:
y += speed;
if(y>ConsDemo.FRAME_HEIGTH)
visible=false;
break;
case Tank.LEFT:
x -= speed;
if(x<=0)
visible=false;
break;
case Tank.RIGHT:
x += speed;
if(x>ConsDemo.FRAME_WIDTH)
visible=false;
break;
}
}
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 int getDir() {
return dir;
}
public void setDir(int dir) {
this.dir = dir;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
}
package com.hg.Util;
import com.hg.TankBattle.Bullet;
import java.util.ArrayList;
import java.util.List;
/**
* 子弹的对象池
*/
public class BulletsPool {
// 初始子弹数量
public static final int ALL_BULLET=200;
// 用于保存所有子弹的容器
private static List<Bullet> pool =new ArrayList<>();
// 在类加载的时候就创建出200个子弹添加到容器中
static {
for (int i = 0; i < ALL_BULLET; i++) {
pool.add(new Bullet());
}
}
/**
*
* @return 从池塘中拿出子弹
*/
public static Bullet get(){
Bullet bullet=null;
// 如果池塘被掏空
if(pool.size()==0){
bullet=new Bullet();
}else{
bullet= pool.remove(0);
}
return bullet;
}
/**
* 子弹别销毁的时候,归还到池塘中
* @return
*/
public static void back(Bullet bullet){
// 池塘中的子弹已经达到最大,那就不再归还
if(pool.size()==300){
return;
}
pool.add(bullet);
}
}
1.爆炸类
package com.hg.TankBattle;
import com.hg.Util.ConsDemo;
import com.hg.Util.MyUtil;
import java.awt.*;
/**
* 控制爆炸效果
*/
public class Explode {
public static final int EXPLODE_FRAME=8;
private static Image[] img;
static {
img=new Image[EXPLODE_FRAME];
for (int i = 0; i < img.length; i++) {
img[i]= MyUtil.creatImage(ConsDemo.BOOM);
}
}
// 爆炸效果的属性
private int x,y;
// 当前播放帧的下表
private int index=0;
// 设置是否可见
private boolean visible=true;
// 对爆炸效果图片的宽高
private static int imgHeight;
private static int imgWidth;
public Explode(){
}
public Explode(int x, int y) {
this.x = x;
this.y = y;
}
/**
* 绘制爆炸效果
* @param g
*/
public void draw(Graphics g){
if(imgHeight<=0){
imgHeight =img[0].getHeight(null);
imgWidth =img[0].getWidth(null)>>2;
}
if(!visible)return;
g.drawImage(img[index],x,y,null);
index++;
// 播放完最后一帧设置为不可见
if(index>=EXPLODE_FRAME){
visible=false;
}
}
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 int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
public int getImgHeight() {
return imgHeight;
}
public void setImgHeight(int imgHeight) {
this.imgHeight = imgHeight;
}
public int getImgWidth() {
return imgWidth;
}
public void setImgWidth(int imgWidth) {
this.imgWidth = imgWidth;
}
}
2.爆炸效果对象池
package com.hg.Util;
import com.hg.TankBattle.Bullet;
import java.util.ArrayList;
import java.util.List;
/**
* 爆炸效果的对象池
*/
public class BulletsPool {
// 初始爆炸效果数量
public static final int ALL_BULLET=50;
// 用于保存所有子弹的容器
private static List<Bullet> pool =new ArrayList<>();
// 在类加载的时候就创建出200个子弹添加到容器中
static {
for (int i = 0; i < ALL_BULLET; i++) {
pool.add(new Bullet());
}
}
/**
*
* @return 从池塘中拿出爆炸效果
*/
public static Bullet get(){
Bullet bullet=null;
// 如果池塘被掏空
if(pool.size()==0){
bullet=new Bullet();
}else{
bullet= pool.remove(0);
}
return bullet;
}
/**
* 销毁的时候,归还到池塘中
* @return
*/
public static void back(Bullet bullet){
// 池塘中的子弹已经达到最大,那就不再归还
if(pool.size()==ALL_BULLET){
return;
}
pool.add(bullet);
}
}
package com.hg.map;
import com.hg.TankBattle.Bullet;
import com.hg.Util.BulletsPool;
import com.hg.Util.ConsDemo;
import com.hg.Util.MyUtil;
import java.util.List;
import java.awt.*;
/**
* 地图砖块
*/
public class MapTile {
public static final int TYPE_NORMAL=0;
public static final int TYPE_HOUSE=1;
public static final int TYPE_COVER=2;
public static final int TYPE_HARD=3;
public static int tileW=20;
public static int radius=(tileW>>1);
private int type=TYPE_NORMAL;
private static Image[] titleImg;
static {
titleImg=new Image[4];
titleImg[TYPE_NORMAL]= MyUtil.creatImage(ConsDemo.BRICK);
titleImg[TYPE_COVER]=MyUtil.creatImage(ConsDemo.GRASS);
titleImg[TYPE_HOUSE]=MyUtil.creatImage(ConsDemo.BOSS);
titleImg[TYPE_HARD]=MyUtil.creatImage(ConsDemo.IRON);
if(tileW<=0){
tileW=titleImg[TYPE_HOUSE].getWidth(null);
}
}
// 图片资源的左上角坐标
private int x,y;
private boolean visible=true;
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
/**
* 地图块和子弹是否有碰撞
* @param
* @return
*/
public boolean isCollideBullet(List<Bullet> bullets){
if(!visible || type==TYPE_COVER)
return false;
for (Bullet bullet : bullets) {
int bulletX=bullet.getX();
int bulletY= bullet.getY();
boolean collide = MyUtil.isCollide((x + radius), (y + radius), radius, bulletX, bulletY);
if(collide){
// 子弹销毁
bullet.setVisible(false);
BulletsPool.back(bullet);
return true;
}
}
return false;
}
public MapTile(int x, int y){
this.x=x;
this.y=y;
if(tileW<=0){
tileW=titleImg[TYPE_HOUSE].getWidth(null);
}
}
public MapTile() {
}
// 判断当前地图块是否是老巢
public boolean isHouse(){
return type==TYPE_HOUSE;
}
public void draw(Graphics g){
if(!visible)
return;
if(tileW<=0){
tileW=titleImg[TYPE_HOUSE].getWidth(null);
}
g.drawImage(titleImg[type],x,y,null);
}
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 int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
package com.hg.map;
import com.hg.MainJFrame.GameFrame;
import com.hg.Util.ConsDemo;
import com.hg.Util.MapTitlePool;
import com.hg.Util.MyUtil;
import com.hg.tank.Tank;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* 游戏地图类
*/
public class GameMap {
// 地图坐标
public static final int MAP_X = Tank.BAN_JIN*3;
public static final int MAP_Y = Tank.BAN_JIN*3+ GameFrame.menuTitle;
public static final int MAP_WIDTH = ConsDemo.FRAME_WIDTH-Tank.BAN_JIN*6;
public static final int MAP_HEIGHT = (ConsDemo.FRAME_HEIGTH-Tank.BAN_JIN*8-GameFrame.menuTitle);
// 地图大小
private int width,height;
private TankHouse house;
// 地图元素块容器
private List<MapTile> tiles=new ArrayList<>();
public GameMap() {
initMap();
}
/**
* 初始化地图元素块
*/
private void initMap(){
// 可以加上,可以不加
// 随机添加一个地图块元素,添加到容器中
// final int COUNT=30;
// for (int i = 0; i < COUNT; i++) {
// MapTile title= MapTitlePool.get();
// int x= MyUtil.getRamdomNumber(MAP_X,MAP_X+MAP_WIDTH-MapTile.tileW);
// int y= MyUtil.getRamdomNumber(MAP_Y,MAP_Y+MAP_HEIGHT-MapTile.tileW);
// title.setX(x);
// title.setY(y);
// tiles.add(title);
// }
// 三行地图
addRow(MAP_X,MAP_Y,MAP_X+MAP_WIDTH,MapTile.TYPE_NORMAL,0);
addRow(MAP_X,MAP_Y+MapTile.tileW*6,MAP_X+MAP_WIDTH,MapTile.TYPE_COVER,0);
addRow(MAP_X,MAP_Y+MapTile.tileW*20,MAP_X+MAP_WIDTH,MapTile.TYPE_HARD,MapTile.tileW*3);
house=new TankHouse();
addHouse();
}
/**
* 往地图块容器中添加一行指定类型的地图块
* @param startX 地图块起始坐标
* @param startY
* @param endX 新增地图块的结束坐标
* @param type 地图块类型
* @param DIS 地图块的间隔
*/
public void addRow(int startX,int startY,int endX,int type,final int DIS){
int count=0;
count=(endX-startX)/MapTile.tileW;
for (int i = 0; i < count; i++) {
MapTile tile=MapTitlePool.get();
tile.setType(type);
tile.setX(startX+i*(MapTile.tileW+DIS));
tile.setY(startY);
tiles.add(tile);
}
}
/**
* 某一个点是否和 titles 集合中的所有块用重叠部分,有返回true
* @param tiles
* @param x
* @param y
* @return
*/
private boolean isCollide(List<MapTile> tiles,int x,int y){
for (MapTile tile : tiles) {
int tiltX=tile.getX();
int tiltY=tile.getY();
if(Math.abs(tiltX-x)<MapTile.tileW && Math.abs(tiltY-y)<MapTile.tileW);
return true;
}
return false;
}
// 对没有遮挡效果的块绘制
public void drawBK(Graphics g){
for (MapTile tile : tiles) {
if(tile.getType()!=MapTile.TYPE_COVER)
tile.draw(g);
}
// 初始化大本营
//house.draw(g);
}
// 只绘制有遮挡的块
public void drawCover(Graphics g){
for (MapTile tile : tiles) {
if(tile.getType()==MapTile.TYPE_COVER)
tile.draw(g);
}
// 初始化大本营
//house.draw(g);
}
/**
* 把老巢元素块添加到地图的容器中
*/
private void addHouse(){
tiles.addAll(house.getTiles());
}
/**
* 将所有不可见的地图块全部移除
*/
public void clearDestoryTile(){
for (int i = 0; i < tiles.size(); i++) {
MapTile tile=tiles.get(i);
if(!tile.isVisible()){
tiles.remove(i);
}
}
}
public List<MapTile> getTiles() {
return tiles;
}
public void setTiles(List<MapTile> tiles) {
this.tiles = tiles;
}
}
package com.hg.Util;
import com.hg.map.MapTile;
import java.util.ArrayList;
import java.util.List;
public class MapTitlePool {
// 初始数量
public static final int ALL_BULLET=50;
// 最大数量
public static final int POOL_MAX_SIZE=70;
// 用于保存所有砖块
private static List<MapTile> pool =new ArrayList<>();
static {
for (int i = 0; i < ALL_BULLET; i++) {
pool.add(new MapTile());
}
}
/**
*
* @return 从池塘中拿出砖块
*/
public static MapTile get(){
MapTile tile;
// 如果池塘被掏空
if(pool.size()==0){
tile=new MapTile();
}else{
tile= pool.remove(0);
}
return tile;
}
/**
* 销毁的时候,归还到池塘中
* @return
*/
public static void back(MapTile tile){
// 池塘中的砖块已经达到最大
if(pool.size()==POOL_MAX_SIZE){
return;
}
pool.add(tile);
}
}
package com.hg.map;
import com.hg.Util.ConsDemo;
import com.hg.Util.MyUtil;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* Boss所在地方
*/
public class TankHouse {
public static final int HOUSE_X= (ConsDemo.FRAME_WIDTH-4*MapTile.tileW>>1);
public static final int HOUSE_Y= (ConsDemo.FRAME_HEIGTH-3*MapTile.tileW)-5;
public static Image bossImg= MyUtil.creatImage(ConsDemo.BOSS);
// 一共7块老鬼地图
private List<MapTile> tiles=new ArrayList<>();
public TankHouse() {
tiles.add(new MapTile(HOUSE_X,HOUSE_Y));
tiles.add(new MapTile(HOUSE_X,HOUSE_Y+MapTile.tileW));
tiles.add(new MapTile(HOUSE_X,HOUSE_Y+MapTile.tileW*2));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW,HOUSE_Y));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW*2,HOUSE_Y));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW*3,HOUSE_Y));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW*3,HOUSE_Y+MapTile.tileW));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW*3,HOUSE_Y+MapTile.tileW*2));
//tiles.add(new MapTile(HOUSE_X+MapTile.tileW+6,HOUSE_Y+MapTile.tileW+15));
tiles.add(new MapTile(HOUSE_X+MapTile.tileW,HOUSE_Y+MapTile.tileW));
// 设置老巢地图块类型
tiles.get(tiles.size()-1).setType(MapTile.TYPE_HOUSE);
}
public void draw(Graphics g){
for (MapTile tile : tiles) {
tile.draw(g);
}
//g.drawImage(bossImg,HOUSE_X+MapTile.tileW,HOUSE_Y+MapTile.tileW,null);
}
public List<MapTile> getTiles() {
return tiles;
}
public void setTiles(List<MapTile> tiles) {
this.tiles = tiles;
}
}
package com.hg.Util;
import java.awt.*;
import java.net.URL;
/**
* 工具类
*/
public class MyUtil {
private MyUtil(){
}
/**
*
* @param min 区间最小值
* @param max 区间最大值 (左闭右开)
* @return 随机数
*/
public static final int getRamdomNumber(int min,int max){
//return (int)(Math.random()*max-min);
return (int)(Math.random()*(max-min)+min);
}
/**
* @return 获取随机三原色
*/
public static final Color getRamdomColor(){
int red=getRamdomNumber(0,256);
int blue=getRamdomNumber(0,256);
int green=getRamdomNumber(0,256);
return new Color(red,blue,green);
}
/**
* 判断一个点是否在一个正方形中心点的内部
* @param retX 正方形中心点x
* @param retY 正方形中心点y
* @param radius 正方形边长的一半
* @param pointX 圆的x坐标
* @param pointY 圆的y坐标
* @return 如果点在正方形内部返回true
*/
public static final boolean isCollide(int retX,int retY,int radius,int pointX,int pointY){
// 正方形中心点的 x和 y的距离
int disX=Math.abs(retX-pointX);
int disY=Math.abs(retY-pointY);
if(disX<radius && disY< radius)
return true;
return false;
}
/**
*
* @param path 图片路径
* @return
*/
public static final Image creatImage(URL path){
return Toolkit.getDefaultToolkit().createImage(path);
}
public static final String[] AI_Name={
"行人","乐园", "花草","人才","左手","目的","课文","优点","年代","灰尘","沙子","小说","儿女","难题","明星","本子",
"彩色", "水珠","路灯","把握","房屋","心愿","左边","新闻","早点", "市场","雨点", "细雨","书房","毛巾","画家",
"元旦","绿豆","本领" , "起点","青菜","土豆","总结","礼貌","右边"
};
public static final String[] AI_NAME={
"可爱","傻傻","萌萌","羞羞","笨笨","呆呆","美丽","聪明" ,"伶俐","狡猾","胖乎乎","粉嫩嫩","白胖胖","漂亮"
,"可爱","聪明","懂事","乖巧","淘气","淘气","顽劣","调皮","顽皮", "天真","可爱","无邪","单纯","纯洁","无暇","纯真"
};
/**
* 随机获取一个名字
*/
public static final String getRandomName(){
return AI_NAME[getRamdomNumber(0,AI_NAME.length)]+"的"+AI_Name[getRamdomNumber(0,AI_Name.length)];
}
}
package com.hg.Util;
import com.hg.tank.Tank;
import java.awt.*;
import java.net.URL;
/**
* 游戏中的常量都在该类中,方便后期管理
*/
public class ConsDemo {
public static final String NAME_TANK="坦克大战";
public static final int FRAME_WIDTH=1200;
public static final int FRAME_HEIGTH=800;
/**
* 游戏菜单相关常量
*/
public static final int START_MENU=0;
public static final int START_HELP=1;
public static final int START_ABOUT=2;
public static final int START_RUN=3;
public static final int START_OVER =4;
public static final String[] MENUS={
"开始游戏","继续游戏","游戏帮助","游戏关于","退出游戏"
};
public static final String OVER_STA="ESC退出游戏";
public static final String OVER_STA1="Enter回到主菜单";
// 坦克名字
public static final Font GAME_FONT=new Font("楷体",Font.BOLD,14);
public static final Font FONT_SIZE=new Font("楷体",Font.PLAIN,25);
// 重绘时间
public static final int REPAIN_TIME=30;
// 坦克图片的地址
public static final URL url= Tank.class.getResource("/tank/player1_up.png");
public static final URL url1=Tank.class.getResource("/tank/player1_down.png");
public static final URL url2=Tank.class.getResource("/tank/player1_left.png");
public static final URL url3=Tank.class.getResource("/tank/player1_right.png");
// 敌人坦克图片地址
public static final URL enemyUrl1= Tank.class.getResource("/tank/bot_up.png");
public static final URL enemyUrl2=Tank.class.getResource("/tank/bot_down.png");
public static final URL enemyUrl3=Tank.class.getResource("/tank/bot_left.png");
public static final URL enemyUrl4=Tank.class.getResource("/tank/bot_right.png");
// 敌人坦克图片地址
public static final URL enemy2Url1= Tank.class.getResource("/tank/player2_up.png");
public static final URL enemy2Url2=Tank.class.getResource("/tank/player2_down.png");
public static final URL enemy2Url3=Tank.class.getResource("/tank/player2_left.png");
public static final URL enemy2Url4=Tank.class.getResource("/tank/player2_right.png");
// 最大敌人数量
public static final int MAX_ENEMY_COUNT=10;
// 敌人随机切换状态的时间
public static final int ENEMY_AI=1000;
// 敌人开火的几率
public static final double ENEMY_FIRE=0.6;
// 爆炸的路径
public static final URL BOOM= Tank.class.getResource("/img/boom/boom.png");
// 结束游戏图片路径
public static final URL gameOver= Tank.class.getResource("/tank/gameover.jpg");
// 砖块路径
public static final URL BRICK= Tank.class.getResource("/img/wall/brick.png");
public static final URL GRASS= Tank.class.getResource("/img/wall/grass.png");
public static final URL IRON= Tank.class.getResource("/img/wall/iron.png");
// BOSS
public static final URL BOSS= Tank.class.getResource("/img/wall/base.png");
}
package com.hg.TankBattle;
import com.hg.MainJFrame.GameFrame;
public class TextMain {
public static void main(String[] args) {
new GameFrame();
}
}
大家有图片的可以自行设置路径
第一次做还有很多不足之处,欢迎大家指出,大家有什么问题也可以留言咨询!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。