第16章:坦克大战1.0
总体内容
项目说明
坦克大战的演示就省略了,和小时候玩的坦克大战差不多
java绘图坐标体系
坐标体系 - 介绍
坐标体系 - 像素
java绘图技术
绘图快速入门案例
案例要求:在面板上画一个小圆
import javax.swing.*;
import java.awt.*;
//JFrame 相当于一个窗口
// 本例中窗口(Excise继承了JFrame)内有一画板(MyPanel),用画笔(Graphics)来绘图
public class Excise extends JFrame {//JFrame对应一个窗口
//定义一个面板
private MyPanel mp = null;
//主方法
public static void main(String[] args) {
new Excise();
}
//构造器
public Excise() {
//初始化面板
mp = new MyPanel();
//把面板放到窗口中
this.add(mp);
//设置窗口大小
this.setSize(400, 300);
//设置窗口可以显示
this.setVisible(true);
//设置 当点击窗口的X时,程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
//定义一个MyPanel类(面板类),继承JPanel
// 画图形,就在面板上画
class MyPanel extends JPanel {
//说明:
//1. MyPanel对象 相当于一个画板
//2. Graphics g 这里g相当于画笔
//3. Graphics中提供了很多绘图的方法
@Override
public void paint(Graphics g) {//paint()是绘图方法
super.paint(g);//调用父类的方法完成初始化(不可省略)
//画出一个圆,调用Graphics的drawOval()方法
g.drawOval(10, 10, 100, 100);
}
}
输出结果:
绘图中的paint()
绘图常用方法
项目:绘制游戏区域
游戏中有很多坦克,所以我们将坦克先抽象成一个父类,定义一些方法让子类去继承
Tank.java
package com.wpz.tankgame;
/**
* @author 王胖子
* @version 1.0
* 因为该游戏会有很多坦克
* 所以先抽象成一个父类
*/
public class Tank {
private int x;//坦克横坐标
private int y;//坦克纵坐标
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
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;
}
}
编写我的坦克->继承Tank
MyTank.java
package com.wpz.tankgame;
/**
* @author 王胖子
* @version 1.0
* 我的坦克
*/
public class MyTank extends Tank {
public MyTank(int x, int y) {
super(x, y);
}
}
编写面板类->继承Jpanel(在面板上绘制游戏区域)
MyPanel.java
package com.wpz.tankgame;
import javax.swing.*;
import java.awt.*;
/**
* @author 王胖子
* @version 1.0
* 坦克大战的绘图区域
*/
public class MyPanel extends JPanel {
MyTank myTank = null;//在绘图区域中先定义一个自己的坦克
public MyPanel() {
myTank = new MyTank(100, 100);//初始化自己的坦克
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 1000, 750);//绘图区域:填充矩形,默认是黑色
}
}
编写窗口类->继承JFrame
TankGame.java
package com.wpz.tankgame;
import javax.swing.*;
/**
* @author 王胖子
* @version 1.0
* 窗口类
*/
public class TankGame extends JFrame {
private MyPanel mp = null;//定义面板
public static void main(String[] args) {
new TankGame();
}
public TankGame() {
this.mp = new MyPanel();//初始化面板
this.add(mp);//把面板加到窗口中
this.setSize(1000, 750);//设置窗口大小
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置关闭方式:点击X则退出程序
this.setVisible(true);//设置是否可见
}
}
输出效果
项目:绘制坦克
package com.wpz.tankgame;
import javax.swing.*;
import java.awt.*;
/**
* @author 王胖子
* @version 1.0
* 坦克大战的绘图区域
*/
public class MyPanel extends JPanel {
MyTank myTank = null;//先定义一个自己的坦克
public MyPanel() {
myTank = new MyTank(100, 100);//初始化自己的坦克
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 1000, 750);//绘图区域:填充矩形,默认是黑色
//画出坦克->封装到画坦克的方法中
drawTank(myTank.getX(), myTank.getY(), g, 0, 0);//画出我的坦克
}
/**
* 该项目有两种类型坦克:①我方②敌方 -> 不同类型的坦克,颜色不同
* 该项目有四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
*
* @param x 坦克左上角x坐标
* @param y 坦克左上角y坐标
* @param g 画笔
* @param direction 坦克的移动方向(上下左右)
* @param type 坦克的类型(敌方/我方)
*/
public void drawTank(int x, int y, Graphics g, int direction, int type) {
//两种类型的坦克①我方②敌方 -> 不同类型的坦克,颜色不同
switch (type) {
case 0://我方
g.setColor(Color.CYAN);
break;
case 1://敌方
g.setColor(Color.YELLOW);
break;
}
//四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
switch (direction) {
case 0://向上
g.fill3DRect(x, y, 10, 60, false);//画坦克左边轱辘(fillRect()画填充的矩形)
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, x + 20, y + 30);//画坦克的炮筒
break;
default:
System.out.println("暂时没有处理");
}
}
}
输出效果:
java事件处理机制
案例:小球移动
解决:怎样让小球受到键盘的控制,上下左右移动
package com.wpz.event;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author 王胖子
* @version 1.0
* 让小球收到键盘的控制,上下左右移动 -->来讲解java的事件控制
*/
//窗口类
public class BallMove extends JFrame {
MyPanel mp = null;
public static void main(String[] args) {
BallMove ballMove = new BallMove();
}
public BallMove() {
mp = new MyPanel();
this.add(mp);
this.setSize(250, 200);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
//JFrame窗口对象 可以监听键盘事件:即监听面板上发生的键盘事件
this.addKeyListener(mp);
}
}
//面板类
//KeyListener 是监听器,可以监听键盘事件
//面板类实现键盘监听器,所以在面板类中可以写需要监听的键盘事件
class MyPanel extends JPanel implements KeyListener {
//为了让小球可以移动,把他左上角的坐标(x,y)设置成变量
int x = 10;
int y = 10;
@Override
public void paint(Graphics g) {
super.paint(g);
// g.fillOval(10,10,20,20);
g.fillOval(x, y, 20, 20);//填充的小球,默认填充黑色
}
//当某个键按下,该方法被触发
@Override
public void keyPressed(KeyEvent e) {
// System.out.println((char) e.getKeyCode()+"键 被按下");
//根据用户按下不同键,来处理小球移动
//在java中会给每一个键,分配一个值(int)
if (e.getKeyCode() == KeyEvent.VK_DOWN) {//KeyEvent.VK_DOWN就是向下箭头对应的code
y++;
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
y--;
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
x--;
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
x++;
}
//让面板重绘(当坐标x/y发生变化时,需要主动调用paint()方法进行重绘,这样才能显示小球移动的效果)
this.repaint();
}
//当某个键释放(松开),该方法被触发
@Override
public void keyReleased(KeyEvent e) {
}
//有字符输出时,该方法被触发
@Override
public void keyTyped(KeyEvent e) {
}
}
静态效果图,按↑↓←→可以控制小球移动
事件处理机制
基本说明
概念说明
项目:控制坦克上下左右移动(代码改进过程)
面板类MyPanel
package com.wpz.tankgame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* @author 王胖子
* @version 1.0
* 坦克大战的绘图区域
*/
public class MyPanel extends JPanel implements KeyListener {
MyTank myTank = null;//先定义一个自己的坦克
public MyPanel() {
myTank = new MyTank(100, 100);//初始化自己的坦克
myTank.setSpeed(5);//设置坦克移动的速度
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 1000, 750);//绘图区域:填充矩形,默认是黑色
//画出坦克->封装到画坦克的方法中
//对direction做了修改,把它放到了tank父类中(使用get()方法访问)
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);//画出我的坦克
}
/**
* 该项目有两种类型坦克:①我方②敌方 -> 不同类型的坦克,颜色不同
* 该项目有四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
*
* @param x 坦克左上角x坐标
* @param y 坦克左上角y坐标
* @param g 画笔
* @param direction 坦克的移动方向(上下左右)
* @param type 坦克的类型(敌方/我方)
*/
public void drawTank(int x, int y, Graphics g, int direction, int type) {
//两种类型的坦克①我方②敌方 -> 不同类型的坦克,颜色不同
switch (type) {
case 0://我方
g.setColor(Color.CYAN);
break;
case 1://敌方
g.setColor(Color.YELLOW);
break;
}
//四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
//direction:0:向上,1:向右,2:向下,3:向左
switch (direction) {
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, x + 20, y + 30);//画坦克的炮筒
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;
}
}
//事件处理方法(对按键进行监听)
@Override
public void keyPressed(KeyEvent e) {
//判断事件(当按下WDSA键时进行处理)
if (e.getKeyCode() == KeyEvent.VK_W) {//上
myTank.moveUp();//改变坦克的坐标(将改变坐标封装到父类的moveUp()方法中)
myTank.setDirection(0);//改变坦克的方向(将direction作为所有坦克的属性放到父类中)
} else if (e.getKeyCode() == KeyEvent.VK_D) {//右
myTank.moveRight();
myTank.setDirection(1);
} else if (e.getKeyCode() == KeyEvent.VK_S) {//下
myTank.moveDown();
myTank.setDirection(2);
} else if (e.getKeyCode() == KeyEvent.VK_A) {//左
myTank.moveLeft();
myTank.setDirection(3);
}
//重绘
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
实体类Tank
package com.wpz.tankgame;
/**
* @author 王胖子
* @version 1.0
* 因为该游戏会有很多坦克
* 所以先抽象成一个父类
*/
public class Tank {
private int x;//坦克横坐标
private int y;//坦克纵坐标
//将坦克的方向写到父类中 这样任意坦克在不同方法中 都可以设置移动方向
private int direction;//坦克的方向(0:上,1:右,2:下,3:左)
private int speed = 2;//移动速度
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
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 getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//将坦克移动时的坐标改变封装到 父类的方法中
//直接改变坐标的x/y值(取代myTank.setY(myTank.getY()-2);)
//先死后活:开始时坐标是加减固定值-->可以扩展成变化的--引入变量-->speed(可以在初始化坦克对象时赋值)
public void moveUp() {
y -= speed;
}
public void moveRight() {
x += speed;
}
public void moveDown() {
y += speed;
}
public void moveLeft() {
x -= speed;
}
}
窗口类
package com.wpz.tankgame;
import javax.swing.*;
/**
* @author 王胖子
* @version 1.0
* 窗口类
*/
public class TankGame extends JFrame {
private MyPanel mp = null;//定义面板
public static void main(String[] args) {
new TankGame();
}
public TankGame() {
this.mp = new MyPanel();
this.add(mp);
this.setSize(1000, 750);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
this.addKeyListener(mp);//为面板添加键盘监听器
}
}
本章作业
分析:
敌人坦克类
package com.wpz.tankgame;
/**
* @author 王胖子
* @version 1.0
* 敌人的坦克
*/
public class EnemyTank extends Tank {
public EnemyTank(int x, int y) {
super(x, y);
}
}
面板类
package com.wpz.tankgame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* @author 王胖子
* @version 1.0
* 坦克大战的绘图区域
*/
public class MyPanel extends JPanel implements KeyListener {
MyTank myTank = null;//定义一个自己的坦克
Vector<EnemyTank> enemyTanks = new Vector<>();//定义敌人的坦克,放到Vector中
int enemyTankSize = 3;//敌人坦克的数量
public MyPanel() {
myTank = new MyTank(100, 100);//初始化自己的坦克
myTank.setSpeed(5);//设置坦克移动的速度
//初始化敌人的坦克(注意:使用循环来添加。因为敌人坦克数量多,不要一个一个add)
for (int i = 0; i < enemyTankSize; i++) {
//创建一个敌人的坦克
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
//设置方向
enemyTank.setDirection(2);
//加入敌人坦克集合
enemyTanks.add(enemyTank);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 1000, 750);//绘图区域:填充矩形,默认是黑色
//画出自己的坦克->封装到画坦克的方法中
//对direction做了修改,把它放到了tank父类中(使用get()方法访问)
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);//画出我的坦克
//画敌人的坦克->遍历集合
for (int i = 0; i < enemyTanks.size(); i++) {
//取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
}
}
/**
* 该项目有两种类型坦克:①我方②敌方 -> 不同类型的坦克,颜色不同
* 该项目有四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
*
* @param x 坦克左上角x坐标
* @param y 坦克左上角y坐标
* @param g 画笔
* @param direction 坦克的移动方向(上下左右)
* @param type 坦克的类型(敌方/我方)
*/
public void drawTank(int x, int y, Graphics g, int direction, int type) {
//两种类型的坦克①我方②敌方 -> 不同类型的坦克,颜色不同
switch (type) {
case 0://我方
g.setColor(Color.CYAN);
break;
case 1://敌方
g.setColor(Color.YELLOW);
break;
}
//四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
//direction:0:向上,1:向右,2:向下,3:向左
switch (direction) {
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, x + 20, y + 30);//画坦克的炮筒
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;
}
}
//事件处理方法(对按键进行监听)
@Override
public void keyPressed(KeyEvent e) {
//判断事件(当按下WDSA键时进行处理)
if (e.getKeyCode() == KeyEvent.VK_W) {//上
myTank.moveUp();//改变坦克的坐标(将改变坐标封装到父类的moveUp()方法中)
myTank.setDirection(0);//改变坦克的方向(将direction作为所有坦克的属性放到父类中)
} else if (e.getKeyCode() == KeyEvent.VK_D) {//右
myTank.moveRight();
myTank.setDirection(1);
} else if (e.getKeyCode() == KeyEvent.VK_S) {//下
myTank.moveDown();
myTank.setDirection(2);
} else if (e.getKeyCode() == KeyEvent.VK_A) {//左
myTank.moveLeft();
myTank.setDirection(3);
}
//重绘
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
输出效果