当前位置: 首页 > news >正文

柯桥建设局网站首页长沙网站优化排名推广

柯桥建设局网站首页,长沙网站优化排名推广,wordpress document,河南省建设人才教育信息网官网目录 坦克大战【2】 线程-应用到坦克大战 坦克大战0.3 思路分析: 代码实现: 坦克大战0.4 增加功能 特别说明 思路分析: 代码实现: 坦克大战0.5 增加功能 思路分析: 代码实现: 坦克大战【2】 …

目录

坦克大战【2】

线程-应用到坦克大战

坦克大战0.3

思路分析:

代码实现:

坦克大战0.4

增加功能

特别说明

思路分析:

代码实现:

坦克大战0.5

增加功能

思路分析:

代码实现:


坦克大战【2】

线程-应用到坦克大战

坦克大战0.3

在坦克大战游戏(0.2版)基础上添加如下功能:当玩家按一下j键,就发射一颗子弹

思路分析:

  1. 当发射一颗子弹后,就相当于启动的一个线程
  2. Hero有子弹的对象,当按下J时,就启动一个发射行为(线程),让子弹不停的移动,形成一个射击效果
  3. MyPanel需要不断地重绘,才能出现该效果
  4. 当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)

代码实现:

package com18.tank_game03;/*** @author 甲柒* @version 1.0* @title Shot* @package com18.tank_game03* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变x,y坐标switch (direct) {case 0://上y -= speed;break;case 1://右x += speed;break;case 2://下y += speed;break;case 3://左x -= speed;break;}//测试,输出子弹的坐标System.out.println("子弹 x=" + x + " y=" + y);//当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750)) {isLive = false;break;}}}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title Tank* @package com18.tank_game03* @time 2023/3/29 22:27*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public Tank(int x, int y) {this.x = x;this.y = y;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {this.direct = direct;}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;}
}
package com18.tank_game03;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.3* @title MyPanel* @package com18.tank_game03* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();int enemyTankSize = 3;public MyPanel() {hero = new Hero(100, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//加入enemyTanks.add(enemyTank);}hero.setSpeed(10);}@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);//画出hero射击的子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//取出坦克EnemyTank enemyTank = enemyTanks.get(i);drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);}}//编写方法,画出坦克/*** @param x      坦克的左上角x坐标* @param y      坦克的左上角y坐标* @param g      画笔* @param direct 坦克方向(上下左右)* @param type   坦克类型*/public void drawTank(int x, int y, Graphics g, int direct, int type) {switch (type) {case 0://自己的坦克g.setColor(Color.cyan);break;case 1://敌人的坦克g.setColor(Color.yellow);break;}//根据坦克的方向,来绘制对应形状坦克//direct表示方向(0:向上 1:向右 2:向下 3:向左)switch (direct) {case 0://表示向上g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒break;case 1://表示向右g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒break;case 2://表示向下g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒break;case 3://表示向左g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒break;default:System.out.println("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1hero.moveUp();} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键hero.setDirect(1);hero.moveRight();} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);hero.moveDown();} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);hero.moveLeft();}//如果用户按下J,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}this.repaint();}}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title Hero* @package com18.tank_game03* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game03;/*** @author 甲柒* @version 1.3* @title EnemyTank* @package com18.tank_game03* @time 2023/3/29 12:06* 敌人的坦克*/
public class EnemyTank extends Tank {public EnemyTank(int x, int y) {super(x, y);}
}
package com18.tank_game03;import javax.swing.*;/*** @author 甲柒* @version 1.3* @title TankGame03* @package com18.tank_game03* @time 2023/3/29 22:33*/
public class TankGame03 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame03() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame03 tankGame03 = new TankGame03();}
}

坦克大战0.4

增加功能

  1. 让敌人的坦克也能发射子弹(可以有多颗子弹)
  2. 当我方坦克击中敌人时,敌人的坦克就消失,如果能做出爆炸效果更好
  3. 让敌人的坦克也可以自由随机的上下左右移动
  4. 控制我方坦克和敌方坦克在规定的范围移动

特别说明

  1. 只要能实现就行
  2. 完成上面的任务

思路分析:

让敌人的坦克也能发射子弹(可以有多颗子弹)

  1. 在敌人的坦克类,使用Vector保存多个Shot
  2. 当每创建一个敌人坦克对象,给该敌人坦克对象初始化一个Shot对象,同时启动Shot
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除

让敌人的坦克也可以自由随机的上下左右移动

  1. 需要将敌人坦克当成线程使用
  2. 需要EnemyTank implements Runnable
  3. 在run方法中写上相应的业务代码
  4. 在创建敌人坦克对象时,启动线程

代码实现:

package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Tank* @package com18.tank_game04* @time 2023/3/29 22:27*/
public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public Tank(int x, int y) {this.x = x;this.y = y;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {this.direct = direct;}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;}
}
package com18.tank_game04;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.4* @title MyPanel* @package com18.tank_game04* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();//定义一个Vector 用于存放炸弹//说明 当子弹击中坦克时,加入一个Bomb对象到bombsVector<Bomb> bombs = new Vector<>();int enemyTankSize = 3;//定义三张炸弹图片,用于显示爆炸效果Image image1 = null;Image image2 = null;Image image3 = null;public MyPanel() {hero = new Hero(100, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//启动敌人坦克进程new Thread(enemyTank).start();//给该enemyTank 加入一颗子弹Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());//加入enemyTank的Vector成员enemyTank.shots.add(shot);//启动shot对象new Thread(shot).start();//加入enemyTanks.add(enemyTank);}//初始化图片对象image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));//设置我方坦克速度hero.setSpeed(10);}//编写方法,判断我方的子弹是否击中敌方的坦克public void hiTank(Shot s, EnemyTank enemyTank) {//判断s击中坦克switch (enemyTank.getDirect()) {case 0://坦克向上case 2://坦克向下if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {s.isLive = false;enemyTank.isLive = false;//当我的子弹击中敌人坦克后,将enemyTank 从 Vector 拿掉enemyTanks.remove(enemyTank);//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;case 1://坦克向右case 3://坦克向左if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {s.isLive = false;enemyTank.isLive = false;//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;}}//编写方法,画出坦克@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);//画出hero射击的子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//如果bombs 集合中有对象,就画出for (int i = 0; i < bombs.size(); i++) {//取出炸弹Bomb bomb = bombs.get(i);//根据当前这个bomb对象的life值去画出对应的的图片if (bomb.life > 6) {g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);} else if (bomb.life > 3) {g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);} else {g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);}//让这个炸弹的生命值减少bomb.lifeDown();//如果bomb life 为0,就从bombs的集合中删除if (bomb.life == 0) {bombs.remove(bomb);}}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//从 Vector 取出坦克EnemyTank enemyTank = enemyTanks.get(i);if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);//画出 enemyTank 所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//绘制if (shot.isLive) {//isLive == trueg.draw3DRect(shot.x, shot.y, 1, 1, false);} else {//从Vector中移除enemyTank.shots.remove(shot);}}}}}/*** @param x      坦克的左上角x坐标* @param y      坦克的左上角y坐标* @param g      画笔* @param direct 坦克方向(上下左右)* @param type   坦克类型*/public void drawTank(int x, int y, Graphics g, int direct, int type) {switch (type) {case 0://自己的坦克g.setColor(Color.cyan);break;case 1://敌人的坦克g.setColor(Color.yellow);break;}//根据坦克的方向,来绘制对应形状坦克//direct表示方向(0:向上 1:向右 2:向下 3:向左)switch (direct) {case 0://表示向上g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒break;case 1://表示向右g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒break;case 2://表示向下g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒break;case 3://表示向左g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒break;default:System.out.println("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1if (hero.getY() > 0) {hero.moveUp();}} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键hero.setDirect(1);if (hero.getX() + 60 < 1000) {hero.moveRight();}} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);if (hero.getY() + 60 < 750) {hero.moveDown();}} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);if (hero.getX() > 0) {hero.moveLeft();}}//如果用户按下J,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//判断是否击中了敌人的坦克if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活//遍历敌人的所有坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank enemyTank = enemyTanks.get(i);hiTank(hero.shot, enemyTank);}}this.repaint();}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Hero* @package com18.tank_game04* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Shot* @package com18.tank_game04* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变x,y坐标switch (direct) {case 0://上y -= speed;break;case 1://右x += speed;break;case 2://下y += speed;break;case 3://左x -= speed;break;}//测试,输出子弹的坐标System.out.println("子弹 x=" + x + " y=" + y);//当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)//当子弹碰到敌人坦克时,也应该结束线程if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)) {isLive = false;break;}}}
}
package com18.tank_game04;import java.util.Vector;/*** @author 甲柒* @version 1.4* @title EnemyTank* @package com18.tank_game04* @time 2023/3/29 12:06* 敌人的坦克*/
public class EnemyTank extends Tank implements Runnable {//在敌人的坦克类,使用Vector保存多个ShotVector<Shot> shots = new Vector<>();boolean isLive = true;public EnemyTank(int x, int y) {super(x, y);}@Overridepublic void run() {while (true) {//根据坦克的方向来继续移动switch (getDirect()) {case 0://向上for (int i = 0; i < 100; i++) {if (getY() > 0) {moveUp();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 1://向右for (int i = 0; i < 100; i++) {if (getX() + 60 < 1000) {moveRight();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 2://向下for (int i = 0; i < 100; i++) {if (getY() + 60 < 750) {moveDown();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 3://向左for (int i = 0; i < 100; i++) {if (getX() > 0) {moveLeft();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;}//休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//然后随机地改变坦克方向 0-3setDirect((int) (Math.random() * 4));//注意 写并发程序 一定要考虑清楚 该线程什么时候结束if (!isLive) {break;//退出程序}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.0* @title Bomb* @package com18.tank_game04* @time 2023/3/29 22:47* 炸弹*/
public class Bomb {int x, y;//炸弹的坐标int life = 9;//炸弹的生命周期boolean isLive = true;//是否存活public Bomb(int x, int y) {this.x = x;this.y = y;}//减少生命值public void lifeDown() {//配合图片爆炸的效果if (life > 0) {life--;} else {isLive = false;}}
}
package com18.tank_game04;import javax.swing.*;/*** @author 甲柒* @version 1.4* @title TankGame04* @package com18.tank_game04* @time 2023/3/29 22:33*/
public class TankGame04 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame04() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1000, 750);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame04 tankGame04 = new TankGame04();}
}

坦克大战0.5

增加功能

  1. 我方坦克在发射的子弹消亡后,才能发射新的子弹 =》拓展(发射多颗子弹,控制5颗子弹)
  2. 让敌方坦克发射的子弹消亡后,可以再次发射子弹
  3. 当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果

思路分析:

我方坦克在发射的子弹消亡后,才能发射新的子弹 =》拓展(发射多颗子弹,控制5颗子弹)

  1. 在按下J键,判断当前hero对象的子弹,是否已经销毁
  2. 如果没有销毁,就不去触发shotEnemyTank
  3. 如果已经销毁,才去触发shotEnemyTank
  4. 如果要发射多颗子弹,就使用Vector保存
  5. 在绘制我方子弹时,需要遍历该Vector集合

当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果

  1. 编写方法,判断敌方坦克是否击中我方坦克

代码实现:

package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Tank* @package com18.tank_game04* @time 2023/3/29 22:27*/
public class Tank {boolean isLive = true;private int x;//坦克的横坐标private int y;//坦克的纵坐标private int direct;//坦克方向 0 上 1 右 2 下 3 左private int speed = 1;public Tank(int x, int y) {this.x = x;this.y = y;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}//上下移动方法public void moveUp() {y -= speed;}public void moveRight() {x += speed;}public void moveDown() {y += speed;}public void moveLeft() {x -= speed;}public int getDirect() {return direct;}public void setDirect(int direct) {this.direct = direct;}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;}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Hero* @package com18.tank_game04* @time 2023/3/29 22:29*/
public class Hero extends Tank {//定义一个Shot对象,表示一个射击(线程)Shot shot = null;//可以发射多颗子弹
//    Vector<Shot> shots = new Vector<>();public Hero(int x, int y) {super(x, y);}//射击public void shotEnemyTank() {//控制只有5颗子弹
//        if (shots.size() == 5) {
//            return;
//        }//创建Shot对象,根据当前Hero对象的位置和方向来创建Shotswitch (getDirect()) {case 0://向上shot = new Shot(getX() + 20, getY(), 0);break;case 1://向右shot = new Shot(getX() + 60, getY() + 20, 1);break;case 2://向下shot = new Shot(getX() + 20, getY() + 60, 2);break;case 3://向左shot = new Shot(getX(), getY() + 20, 3);break;}//把新创建的shot加入到集合shots中
//        shots.add(shot);//启动Shot线程new Thread(shot).start();}
}
package com18.tank_game04;import java.util.Vector;/*** @author 甲柒* @version 1.4* @title EnemyTank* @package com18.tank_game04* @time 2023/3/29 12:06* 敌人的坦克*/
@SuppressWarnings({"all"})
public class EnemyTank extends Tank implements Runnable {//在敌人的坦克类,使用Vector保存多个ShotVector<Shot> shots = new Vector<>();boolean isLive = true;public EnemyTank(int x, int y) {super(x, y);}@Overridepublic void run() {while (true) {//这里判断如果shots size() == 0//创建一颗子弹 放入到shots 集合 并启动if (isLive && shots.size() < 1) {Shot s = null;//判断坦克的方向,创建对应的子弹switch (getDirect()) {case 0://上s = new Shot(getX() + 20, getY(), 0);break;case 1://右s = new Shot(getX() + 60, getY() + 20, 1);break;case 2://下s = new Shot(getX() + 20, getY() + 60, 2);break;case 3://左s = new Shot(getX(), getY() + 20, 3);break;}shots.add(s);//启动new Thread(s).start();}//根据坦克的方向来继续移动switch (getDirect()) {case 0://向上for (int i = 0; i < 50; i++) {if (getY() > 0) {moveUp();}//让坦克保持一个方向 走50步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 1://向右for (int i = 0; i < 50; i++) {if (getX() + 60 < 1000) {moveRight();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 2://向下for (int i = 0; i < 50; i++) {if (getY() + 60 < 750) {moveDown();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;case 3://向左for (int i = 0; i < 50; i++) {if (getX() > 0) {moveLeft();}//让坦克保持一个方向 走30步try {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}}break;}//休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//然后随机地改变坦克方向 0-3setDirect((int) (Math.random() * 4));//注意 写并发程序 一定要考虑清楚 该线程什么时候结束if (!isLive) {break;//退出程序}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.4* @title Shot* @package com18.tank_game04* @time 2023/3/29 21:11* 射击子弹*/
public class Shot implements Runnable {int x;//子弹x坐标int y;//子弹y坐标int direct = 0;//子弹方向int speed = 5;//子弹速度boolean isLive = true;//子弹是否存活//构造器public Shot(int x, int y, int direct) {this.x = x;this.y = y;this.direct = direct;}@Overridepublic void run() {//射击while (true) {//子弹线程休眠50mstry {Thread.sleep(50);} catch (InterruptedException e) {throw new RuntimeException(e);}//根据方向改变x,y坐标switch (direct) {case 0://上y -= speed;break;case 1://右x += speed;break;case 2://下y += speed;break;case 3://左x -= speed;break;}//测试,输出子弹的坐标System.out.println("子弹 x=" + x + " y=" + y);//当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)//当子弹碰到敌人坦克时,也应该结束线程if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isLive)) {isLive = false;break;}}}
}
package com18.tank_game04;/*** @author 甲柒* @version 1.0* @title Bomb* @package com18.tank_game04* @time 2023/3/29 22:47* 炸弹*/
public class Bomb {int x, y;//炸弹的坐标int life = 9;//炸弹的生命周期boolean isLive = true;//是否存活public Bomb(int x, int y) {this.x = x;this.y = y;}//减少生命值public void lifeDown() {//配合图片爆炸的效果if (life > 0) {life--;} else {isLive = false;}}
}
package com18.tank_game04;import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;/*** @author 甲柒* @version 1.4* @title MyPanel* @package com18.tank_game04* @time 2023/3/29 22:30*/
//为了监听 键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将MyPanel实现Runnable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener, Runnable {//定义我的坦克Hero hero = null;//定义敌人的坦克,放到VectorVector<EnemyTank> enemyTanks = new Vector<>();//定义一个Vector 用于存放炸弹//说明 当子弹击中坦克时,加入一个Bomb对象到bombsVector<Bomb> bombs = new Vector<>();int enemyTankSize = 3;//定义三张炸弹图片,用于显示爆炸效果Image image1 = null;Image image2 = null;Image image3 = null;public MyPanel() {hero = new Hero(600, 100);//初始化自己的坦克//初始化敌人的坦克for (int i = 0; i < enemyTankSize; i++) {//创建一个敌人的坦克EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);//设置方向enemyTank.setDirect(2);//启动敌人坦克进程new Thread(enemyTank).start();//给该enemyTank 加入一颗子弹Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());//加入enemyTank的Vector成员enemyTank.shots.add(shot);//启动shot对象new Thread(shot).start();//加入enemyTanks.add(enemyTank);}//初始化图片对象image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));//设置我方坦克速度hero.setSpeed(10);}//如果我方坦克可以发射多个子弹//在判断我方子弹是否击中敌方坦克时,就需要把我们的子弹集合中//所有的子弹,都取出和敌人的所有坦克,进行判断public void hitEnemyTank() {
//        //遍历我们的子弹
//        for (int j = 0; j < hero.shots.size(); j++) {
//            Shot shot = hero.shots.get(j);
//            //判断我方子弹是否击中了敌人的坦克
//            if (shot != null && shot.isLive) {//当我的子弹还存活
//
//                //遍历敌人的所有坦克
//                for (int i = 0; i < enemyTanks.size(); i++) {
//                    EnemyTank enemyTank = enemyTanks.get(i);
//                    hiTank(shot, enemyTank);
//                }
//            }
//        }//单颗子弹if (hero.shot != null && hero.shot.isLive) {//当我的子弹还存活//遍历敌人的所有坦克for (int i = 0; i < enemyTanks.size(); i++) {EnemyTank enemyTank = enemyTanks.get(i);hiTank(hero.shot, enemyTank);}}}//编写方法,判断敌方坦克是否击中我方坦克public void hitHero() {//遍历所有敌方坦克for (int i = 0; i < enemyTanks.size(); i++) {//取出敌方坦克EnemyTank enemyTank = enemyTanks.get(i);//遍历enemyTank对象的所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//判断shot是否击中我方坦克if (hero.isLive && shot.isLive) {hiTank(shot, hero);}}}}//编写方法,判断我方的子弹是否击中敌方的坦克public void hiTank(Shot s, Tank enemyTank) {//判断s击中坦克switch (enemyTank.getDirect()) {case 0://坦克向上case 2://坦克向下if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) {s.isLive = false;enemyTank.isLive = false;//当我的子弹击中敌人坦克后,将enemyTank 从 Vector 拿掉enemyTanks.remove(enemyTank);//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;case 1://坦克向右case 3://坦克向左if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60&& s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) {s.isLive = false;enemyTank.isLive = false;//创建Bomb对象,加入到bombs集合Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY());bombs.add(bomb);}break;}}//编写方法,画出坦克@Overridepublic void paint(Graphics g) {super.paint(g);g.fillRect(0, 0, 1000, 750);//填充矩形,默认黑色if (hero != null && hero.isLive) {//画出坦克-封装方法drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 0);}//画出hero射击的子弹,单颗子弹if (hero.shot != null && hero.shot.isLive == true) {System.out.println("子弹被绘制~~~~~");g.draw3DRect(hero.shot.x, hero.shot.y, 2, 2, false);}//多颗子弹//将hero的子弹集合shots,遍历取出绘制
//        for (int i = 0; i < hero.shots.size(); i++) {
//            Shot shot = hero.shots.get(i);
//            if (shot != null && shot.isLive) {
//                System.out.println("子弹被绘制~~~~~");
//                g.draw3DRect(shot.x, shot.y, 2, 2, false);
//            } else {//如果该shot对象已经无效,就从shots集合中拿掉
//                hero.shots.remove(shot);
//            }
//        }//如果bombs 集合中有对象,就画出for (int i = 0; i < bombs.size(); i++) {//取出炸弹Bomb bomb = bombs.get(i);//根据当前这个bomb对象的life值去画出对应的的图片if (bomb.life > 6) {g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);} else if (bomb.life > 3) {g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);} else {g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);}//让这个炸弹的生命值减少bomb.lifeDown();//如果bomb life 为0,就从bombs的集合中删除if (bomb.life == 0) {bombs.remove(bomb);}}//画出敌人的坦克,遍历Vectorfor (int i = 0; i < enemyTanks.size(); i++) {//从 Vector 取出坦克EnemyTank enemyTank = enemyTanks.get(i);if (enemyTank.isLive) {//当敌人坦克是存活的,才画出该坦克drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 1);//画出 enemyTank 所有子弹for (int j = 0; j < enemyTank.shots.size(); j++) {//取出子弹Shot shot = enemyTank.shots.get(j);//绘制if (shot.isLive) {//isLive == trueg.draw3DRect(shot.x, shot.y, 1, 1, false);} else {//从Vector中移除enemyTank.shots.remove(shot);}}}}}/*** @param x      坦克的左上角x坐标* @param y      坦克的左上角y坐标* @param g      画笔* @param direct 坦克方向(上下左右)* @param type   坦克类型*/public void drawTank(int x, int y, Graphics g, int direct, int type) {switch (type) {case 0://自己的坦克g.setColor(Color.cyan);break;case 1://敌人的坦克g.setColor(Color.yellow);break;}//根据坦克的方向,来绘制对应形状坦克//direct表示方向(0:向上 1:向右 2:向下 3:向左)switch (direct) {case 0://表示向上g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y);//画出炮筒break;case 1://表示向右g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x + 60, y + 20);//画出炮筒break;case 2://表示向下g.fill3DRect(x, y, 10, 60, false);//画出坦克左边轮子g.fill3DRect(x + 30, y, 10, 60, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 20, 40, false);//画出坦克的盖子g.fillOval(x + 10, y + 20, 20, 20);//画出圆形盖子g.drawLine(x + 20, y + 30, x + 20, y + 60);//画出炮筒break;case 3://表示向左g.fill3DRect(x, y, 60, 10, false);//画出坦克左边轮子g.fill3DRect(x, y + 30, 60, 10, false);//画出坦克右边轮子g.fill3DRect(x + 10, y + 10, 40, 20, false);//画出坦克的盖子g.fillOval(x + 20, y + 10, 20, 20);//画出圆形盖子g.drawLine(x + 30, y + 20, x, y + 20);//画出炮筒break;default:System.out.println("暂时没有处理");}}@Overridepublic void keyTyped(KeyEvent e) {}//处理ADWS键按下的情况@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode() == KeyEvent.VK_W) {//按下W键//改变坦克的方向hero.setDirect(0);//修改坦克坐标 y+=1if (hero.getY() > 0) {hero.moveUp();}} else if (e.getKeyCode() == KeyEvent.VK_D) {//D键hero.setDirect(1);if (hero.getX() + 60 < 1000) {hero.moveRight();}} else if (e.getKeyCode() == KeyEvent.VK_S) {//S键hero.setDirect(2);if (hero.getY() + 60 < 750) {hero.moveDown();}} else if (e.getKeyCode() == KeyEvent.VK_A) {//A键hero.setDirect(3);if (hero.getX() > 0) {hero.moveLeft();}}//如果用户按下J,就发射if (e.getKeyCode() == KeyEvent.VK_J) {System.out.println("用户按下了J开始射击~~~~");//判断hero的子弹是否销毁,发射一颗子弹if (hero.shot == null || !hero.shot.isLive) {hero.shotEnemyTank();}//发射多颗子弹
//            hero.shotEnemyTank();}//重绘this.repaint();}@Overridepublic void keyReleased(KeyEvent e) {}@Overridepublic void run() {//每隔100ms 重绘区域 刷新绘图区域while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//判断敌我方坦克是否击中敌方坦克hitEnemyTank();//判断敌方坦克是否击中了我方坦克hitHero();this.repaint();}}
}
package com18.tank_game04;import javax.swing.*;/*** @author 甲柒* @version 1.4* @title TankGame04* @package com18.tank_game04* @time 2023/3/29 22:33*/
public class TankGame04 extends JFrame {//定义MyPanelMyPanel mp = null;public TankGame04() {mp = new MyPanel();//将mp 放入到Thread,并启动Thread thread = new Thread(mp);thread.start();this.add(mp);//把面板(就是游戏的绘图区域)this.setSize(1200, 950);this.addKeyListener(mp);//让JFrame监听mp的键盘事件this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);this.setVisible(true);}public static void main(String[] args) {TankGame04 tankGame04 = new TankGame04();}
}


文章转载自:
http://achromic.rdbj.cn
http://sick.rdbj.cn
http://smelting.rdbj.cn
http://communise.rdbj.cn
http://restrained.rdbj.cn
http://sharp.rdbj.cn
http://cottonocracy.rdbj.cn
http://noxious.rdbj.cn
http://salomonic.rdbj.cn
http://annalist.rdbj.cn
http://xenon.rdbj.cn
http://alarming.rdbj.cn
http://basque.rdbj.cn
http://warmth.rdbj.cn
http://codein.rdbj.cn
http://armscye.rdbj.cn
http://unmodish.rdbj.cn
http://benediction.rdbj.cn
http://philip.rdbj.cn
http://loculation.rdbj.cn
http://skullfish.rdbj.cn
http://strasbourg.rdbj.cn
http://delenda.rdbj.cn
http://dioicous.rdbj.cn
http://citing.rdbj.cn
http://decartelize.rdbj.cn
http://truebred.rdbj.cn
http://dittany.rdbj.cn
http://modernise.rdbj.cn
http://soothly.rdbj.cn
http://subcabinet.rdbj.cn
http://acetylate.rdbj.cn
http://politeness.rdbj.cn
http://annotation.rdbj.cn
http://laborious.rdbj.cn
http://calycinal.rdbj.cn
http://passant.rdbj.cn
http://fungible.rdbj.cn
http://stinger.rdbj.cn
http://bunchgrass.rdbj.cn
http://uncultured.rdbj.cn
http://reykjavik.rdbj.cn
http://farness.rdbj.cn
http://ramtil.rdbj.cn
http://gamosepalous.rdbj.cn
http://anglophobe.rdbj.cn
http://perchlorinate.rdbj.cn
http://inofficial.rdbj.cn
http://ballute.rdbj.cn
http://farina.rdbj.cn
http://mammalian.rdbj.cn
http://allergic.rdbj.cn
http://disable.rdbj.cn
http://catalog.rdbj.cn
http://anandrous.rdbj.cn
http://jew.rdbj.cn
http://forfeit.rdbj.cn
http://pyemic.rdbj.cn
http://lightheaded.rdbj.cn
http://thermal.rdbj.cn
http://immunization.rdbj.cn
http://demisable.rdbj.cn
http://vitalist.rdbj.cn
http://panier.rdbj.cn
http://enantiotropic.rdbj.cn
http://speck.rdbj.cn
http://psychic.rdbj.cn
http://waterworn.rdbj.cn
http://hypoalonemia.rdbj.cn
http://merrymaker.rdbj.cn
http://cardroom.rdbj.cn
http://lodging.rdbj.cn
http://escallop.rdbj.cn
http://superalloy.rdbj.cn
http://clarion.rdbj.cn
http://animalculum.rdbj.cn
http://revealment.rdbj.cn
http://oxo.rdbj.cn
http://ideaed.rdbj.cn
http://corporally.rdbj.cn
http://eon.rdbj.cn
http://tiger.rdbj.cn
http://loathsome.rdbj.cn
http://spirochaetosis.rdbj.cn
http://fracted.rdbj.cn
http://wallsend.rdbj.cn
http://phelps.rdbj.cn
http://edmond.rdbj.cn
http://vitae.rdbj.cn
http://hymenotomy.rdbj.cn
http://autocatalytically.rdbj.cn
http://normalizer.rdbj.cn
http://rotfl.rdbj.cn
http://institution.rdbj.cn
http://house.rdbj.cn
http://stagewise.rdbj.cn
http://heilungkiang.rdbj.cn
http://biogeocoenose.rdbj.cn
http://peg.rdbj.cn
http://kneel.rdbj.cn
http://www.dt0577.cn/news/114266.html

相关文章:

  • 服务型政府门户网站建设广州seo公司哪个比较好
  • 网站域名解析时间广州专业seo公司
  • 工业设计完整作品集佛山seo外包平台
  • tp做网站签到功能广告公司名字
  • 网站模板与网站开发百度竞价推广投放
  • 上海公司黄页网站如何建立和设计公司网站
  • 免费设计软件网站竞价托管的注意事项
  • 如何建立收费视频网站中央今日头条新闻
  • 网易免费企业邮箱注册网络优化网站
  • 哪些网站的做的好看的图片欧美网站建设
  • 北京icp网站备案经典软文案例标题加内容
  • 女人做一级a网站免费网站运营推广的方法有哪些
  • 广东网站建设哪家好html网站模板免费
  • 网站制作设计收费标准网络营销ppt模板
  • 建设网站具备的知识网络促销的方法有哪些
  • 长沙网站建设开发网站整站优化推广方案
  • 阿里云1m宽带做网站卡吗武汉网络推广广告公司
  • 经营范围 网站建设百度开户
  • 楚雄企业网站建设公司推广普通话心得体会
  • 做明星粉丝网站随州网络推广
  • 浙江网站建设平台南宁整合推广公司
  • 上海网站设计厂家网络app推广是什么工作
  • php做的网站论文推广下载app赚钱
  • 广州网站建设公司排名怎么让客户主动找你
  • 结合七牛云 做视频网站网络营销软件条件
  • 四川省建十一公司官网站长工具seo综合查询访问
  • 南京做网站优化的企业排名互联网宣传推广
  • 日本哪个网站做外贸比较好怎么建立网站?
  • 中小企业怎么优化网站南京seo网站管理
  • 做网站到底要不要营业执照软件编程培训学校排名