游戏介绍

本次项目模仿了小时候玩的坦克大战小游戏,其中的功能基本实现,里面最重要的思想就是对象池,类似于前面所学的线程池,让对象重复的使用,减少资源的浪费。做的时候逻辑性很强,很不容易,不过总算是有所实现。
用到的知识点:
主要是: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() {@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});addKeyListener(new KeyAdapter() {@Overridepublic 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;}}@Overridepublic 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) {}@Overridepublic 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(){@Overridepublic 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(){@Overridepublic 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(){}@Overridepublic 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);}@Overridepublic 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);}}

子弹类

  1. 子弹属性
package com.hg.TankBattle;import com.hg.Util.ConsDemo;
import com.hg.tank.Tank;
import java.awt.*;/*** 子弹类*/
public class Bullet {// 子弹默认速度为坦克两倍<<1public 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;}
}
  1. 子弹的对象池
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);}
}

BOSS所在类

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");// BOSSpublic static final URL BOSS= Tank.class.getResource("/img/wall/base.png");}

main方法

package com.hg.TankBattle;import com.hg.MainJFrame.GameFrame;public class TextMain {public static void main(String[] args) {new GameFrame();}
}

图片目录

大家有图片的可以自行设置路径

游戏效果截图:

第一次做还有很多不足之处,欢迎大家指出,大家有什么问题也可以留言咨询!

2022最新java坦克大战+免费源码+坦克大战图片相关推荐

  1. 2022最新英皇CMS影视系统源码+多种模板/自带采集/支持AP

    正文: 2022最新英皇CMS影视系统源码+多种模板/自带采集/支持AP,测试环境:PHP7.0+MYSQL5.6 数据库配置文件路径 /yhcms/config/config.php 拉到最底部修改 ...

  2. 2022最新物联网卡管理平台源码+去授权的

    正文: 2022最新物联网卡管理平台源码+去授权的,最新版本的,已经是去除授权的,有兴趣的自己上传访问安装就行,多的就没什么可介绍的了. 程序: wwurh.lanzoup.com/i7M4Z07ia ...

  3. 2020最新java学习资料,全套源码无加密网盘下载

    点击上方蓝字关注我们! 前言--------2020最新java学习资料完整版,全套源码无加密网盘下载 最近小编整理了三套java的学习资料(视频+资料+源码),无加密: 1.2020java会员版 ...

  4. 2022最新在线工单管理系统源码+PHP内核

    正文: 安装环境要求: 服务器空间需要支持php5.6-7.1 + mysql 1.源码包完整上传至空间,并解压 2.执行http://域名/ install根据提示完成安装 3.安装完成,其它的自己 ...

  5. 2022最新彩虹易支付系统源码原版全开源源码

    需要的工具: 1.浏览器 2.一台虚拟主机 3.下载好的源码 4.要聪明的脑子 5.灵活的小手 安装教程: 第一步:首先我们进入已有的主机/服务器控制面板 第二步:点击文件管理,上传下载好的源码并解压 ...

  6. 2022最新nft数字藏品系统源码,商业运营版

    nft数藏系统源码开源,开发语言php+uniapp,支持h5,小程序,app. 具体功能:空投.盲盒.合成.转赠.转售.上链(文昌链).优先购.白名单.积分商城.三方支付.预约抽奖等等. 高并发优化 ...

  7. 2022最新任务悬赏抢单系统源码/UI非常新颖

    正文: 任务悬赏抢单系统源码. 其它的就没什么好介绍的了,有兴趣的自行去研究吧. 程序: wwegxs.lanzoux.com/igEY90hmxfwf 图片:  

  8. 2022最新超简洁小米步数网页源码

    正文: 小米步数源码超级简洁,使用vue框架饿了么uivite,有兴趣的自行拿去研究体验吧. 程序: wwrhru.lanzoue.com/ikejK0dhmdyf 图片:

  9. 2022最新周易测算H5网站源码+功能超级多

    正文: 没有测试支付,因为后台对接的是Z支付. 附带有视频搭建教程,有兴趣的可以研究研究,看能不能加个彩虹易支付的接口啥的. 程序: wwwfsg.lanzouv.com/iQolM0d0p2hi 图 ...

  10. 最新仿商城发卡网源码,带图片展示

    介绍: 仿商城发卡,带图片展示,可上传商品图片,无后门 仿商城发卡,带图片展示 可上传商品图片,js悬浮动画样式 网盘下载地址: https://zijiewangpan.com/WmBw6LEfV6 ...

最新文章

  1. 「懒人」LeCun想让计算机自己编程?网友:还差10个 GPT-3
  2. pip不是内部 pycharm_PyCharm的简单使用
  3. HttpServletRequest对象方法的用法(转)
  4. Nginx服务的信号控制之USR2
  5. P2375 [NOI2014] 动物园 kmp fail指针/倍增
  6. java中的class 类的作用_Java中Class和单例类的作用与类成员的理解
  7. PAT 乙级 1008. 数组元素循环右移问题 (20) Java版
  8. c语言sinx幂级数展开,求幂级数展开式的方法
  9. 物联网、大数据和云计算的基本关系和应用
  10. Ubantu基础指令大集合
  11. radix-tree算法浅析--从不懂到装懂
  12. 计算机如何更新目录,wps怎么更新目录【具体阐明】
  13. restTemplate实现 authorization basic权限认证(带账号密码)
  14. 学Android必须懂的
  15. 学习笔记——PA的stability问题
  16. 微信小程序钱包支付页面案例
  17. HEX,S19互相转换
  18. WrapPanel在不同页面渲染使用
  19. win版敬业签怎么通过便签快捷键对便签内容执行标记已完成操作?
  20. HTTP协议详解 - 通过C++实现HTTP服务剖析HTTP协议

热门文章

  1. c3300 京瓷km5050_京瓷复印机通用维修代码2
  2. python生成手写文字图片_使用PHP辅助 快速制作一套自己的手写字体实践
  3. 整合Springboot+BlazeDS+Spring+Flex
  4. JMETER使用CURL导入功能
  5. SOJSONV5解密,SOJSON.V5解密方法,SOJSONV5解密分析
  6. android屏幕适配无效_Android 屏幕适配终结者
  7. 硬件加速 | 基于FPGA的深度学习CNN加速器设计(论文复现)
  8. 中标麒麟系统安装步骤
  9. 谷歌免费域名邮箱申请全解
  10. Python爬虫开源项目代码分享,100个