坦克大战初步框架
1. java绘图坐标体系
1.1 坐标体系介绍
下图说明了java坐标系。坐标原点位于左上角,以像素为单位。在java坐标系中,第一个是x坐标,便是当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。
1.2 坐标体系–像素
- 绘图还必须要搞清一个非常重要的概念–像素一个像素等于多少厘米?
- 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器的分辨率是800x600,表示计算机屏幕上的每一行由800个点组成,共有600行整个计算机屏幕共有480 000个像素。
- 所以像素是一个密度单位,而厘米是长度单位,两者无法比较。
1.3 绘图快速入门
- 在面板上画一个小圆
- 代码如下
//如何在面板上画圆形
//JFrame对应窗口 可以理解成一个画框
public class DrawCircle extends JFrame {
//定义一个面板
private MyPanel mp = null;
public static void main(String[] args) {
new DrawCircle();
}
public DrawCircle() {
//初始化面板
mp = new MyPanel();
//把面板放入到窗口
this.add(mp);
//设置窗口大小
this.setSize(400, 300);
//当点击窗口的 × , 程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//可以显示
this.setVisible(true);
}
}
//1. 先定义一个MyPanel,继承JPanel类,画图形就在面板上画
//panle 面板
class MyPanel extends JPanel {
//1. MyPanel 对象是一个画板
//2. Graphics g 把 g 理解成一支画笔
//3. Graphics 提供了很多画图的方法
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);//调用父类的方法完成初始化
//调式 paint 方法被调用的情况
System.out.println("paint方法被调用");
//设置圆左上角的坐标(10,10),
//圆的宽度为100 ,圆的高度为 100
g.drawOval(10, 10, 100, 100);
}
}
- 运行后生成界面如下
1.4 绘图原理
- Component类提供了两个和绘图相关最重要的方法:
- paint(Graphicsg)绘制组件的外观
- repaint()刷新组件的外观。
- 当组件第一次在屏幕显示的时候,程序会自动的调用paint()方法来绘制组件
- 在以下情况paint()将会被调用:
- 窗口最小化,再最大化
- 窗口的大小发生变化
- repaint方法被调用
1.5 Graphics类
- Graphics类可以理解为画笔,为我们提供了各种绘制图形的方法:[参考jdk帮助文档]
- 画直线 drawLine(int x1,int y1,int x2,int y2)
- 画矩形边框 drawRect(int xint yint widthint height)
- 画椭圆边框 drawOval(int xint yint widthint height)
- 填充矩形 fillRect(intxint yint width,int height)
- 填充椭圆 fillOval(int xint y int width, int height)
- 画图片drawlmage(lmage img, int xint y.)
- 画字符串 drawString(String str,int xint y)
- 设置画笔的字体 setFont(Font font)
- 设置画笔的颜色 setColor(Color c)
- 下面简单的进行代码演示
//如何在面板上画圆形
//JFrame对应窗口 可以理解成一个画框
public class DrawCircle extends JFrame {
//定义一个面板
private MyPanel mp = null;
public static void main(String[] args) {
new DrawCircle();
}
public DrawCircle() {
//初始化面板
mp = new MyPanel();
//把面板放入到窗口
this.add(mp);
//设置窗口大小
this.setSize(800, 600);
//当点击窗口的 × , 程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//可以显示
this.setVisible(true);
}
}
//1. 先定义一个MyPanel,继承JPanel类,画图形就在面板上画
//panle 面板
class MyPanel extends JPanel {
//1. MyPanel 对象是一个画板
//2. Graphics g 把 g 理解成一支画笔
//3. Graphics 提供了很多画图的方法
@Override
public void paint(Graphics g) {//绘图方法
super.paint(g);//调用父类的方法完成初始化
//调式 paint 方法被调用的情况
System.out.println("paint方法被调用");
//设置圆左上角的坐标(10,10),
//圆的宽度为100 ,圆的高度为 100
g.drawOval(10, 10, 100, 100);
//演示各种绘制图形的方法
//1. 画直线 drawLine(int x1,int y1,int x2,int y2)
g.drawLine(10, 10, 100, 100);
//2. 画矩形边框 drawRect(int xint yint widthint height)
g.drawRect(10, 10, 100, 100);
//3. 画椭圆边框 drawOval(int xint yint widthint height)
//4. 填充矩形 fillRect(intxint yint width,int height)
// 先设置填充的颜色
g.setColor(Color.yellow);//黄色
g.fillRect(10, 10, 100, 100);
//5. 填充椭圆 fillOval(int xint y int width, int height)
// 先设置填充的颜色
g.setColor(Color.red);//红色
g.fillOval(10, 10, 100, 100);
//6. 画图片drawlmage(lmage img, int xint y.)
//第一步:获取图片资源 /mht.png 表示在该项目的根目录去获取 mht.png 图片资源
Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/mht.png"));
//第二步:进行图片位置,大小设置
g.drawImage(image, 100, 100, 362, 330, this);
//7. 画字符串 drawString(String str,int xint y) //就是写字
//第一步:先设置颜色和字体
g.setColor(Color.black);
g.setFont(new Font("宋体", Font.BOLD, 50));
//第二步:进行写字
//位置(100,100)为字符串的左下角
g.drawString("java", 100, 100);
//8. 设置画笔的字体 setFont(Font font)
//9. 设置画笔的颜色 setColor(Color c)
}
}
1.6 坦克初步绘图
- 先创建一个父类坦克;
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;
}
}
- 然后创建自己的坦克类
public class MyTank extends Tank {
public MyTank(int x, int y) {
super(x, y);
}
}
- 游戏面板设置 MyTankGame01类
public class MyTankGame01 extends JFrame {
//定义 MyPanel
MyPanel mp = null;
public static void main(String[] args) {
MyTankGame01 myTankGame01 = new MyTankGame01();
}
public MyTankGame01() {
//初始化
mp = new MyPanel();
//面板(游戏的绘图区域)
this.add(mp);
//面板大小
this.setSize(1000, 750);
//当点击窗口的 × , 程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置显示
this.setVisible(true);
}
}
- 进行坦克的初步绘图
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(getX() + 100, getY() + 100, g, 0, 0);
//敌人的坦克:黄色
drawTank(getX() + 50, getY() + 200, g, 0, 1);
drawTank(getX() + 150, getY() + 200, g, 0, 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;
}
//根据坦克的方向,来绘制坦克
switch (direction) {
case 0://默认方向向上
// 先画第一个矩形 大小 10*60
// 定点(x,y) -> (10,10)
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
// 定点(x+10,y+10) -> (20,20)
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
// 定点(x+30,y) -> (40,10)
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
// 定点(x+10,y+20) -> (20,30)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
// 定点1 (x+20,y) -> (30,10)
// 定点2 (x+20,y+30) -> (30,40)
g.drawLine(x + 20, y, x + 20, y + 30);
break;
default:
System.out.println("暂时不作处理");
}
}
}
- 初步生成的图形:
2. java事件处理机制
2.1 怎么让一个小球通过键盘控制,能上下左右移动;
public class BallMove extends JFrame {
//演示小球通过键盘控制移动 -> 讲解Java的事件控制
MyPanel mp = null;
public static void main(String[] args) {
BallMove ballMove = new BallMove();
}
public BallMove() {
mp = new MyPanel();
this.add(mp);
this.setSize(400, 300);
//窗口 Jframe 对象可以监听键盘事件
this.addKeyListener(mp);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
//面板,可以画出小球
//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(x, y, 20, 20);
}
//有字符输出时,该方法就会触发
@Override
public void keyTyped(KeyEvent e) {
}
//当某个键按下,该方法会触发
@Override
public void keyPressed(KeyEvent e) {
//System.out.println((char) e.getKeyCode()+"被按下...");
//根据用户按下的不同键,来处理小球的移动
if (e.getKeyCode() == KeyEvent.VK_DOWN) {//向下
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++;
}
//让面板重绘
this.repaint();
}
//当某个键释放(松开),该方法会触发
@Override
public void keyReleased(KeyEvent e) {
}
}
2.2 java事件处理机制基本说明
- java事件处理是采取"委派事件模型"。
- 当事件发生时,产生事件的对象,会把此"信息"传递给"事件的监听者"处理;
- 这里所说的"信息"实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为"事件的对象"。
- 示意图如下:
2.3 事件处理机制深入理解
- 前面我们提到几个重要的概念:事件源、事件、事件监听器;下面来全面的介绍它们:
- 事件源:事件源是一个产生事件的对象,比如按钮,窗口等。
- 事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象含有被按下键的Code值。
- java.awt.event包 和javax.swing.event包中定义了各种事件类型;
- 事件类型:查阅jdk文档
- 事件监听器接口:
- 当事件源产生一个事件,可以传送给事件监听者处理
- 事件监听者实际上就是一个类,该类实现了某个事件监听器接口
- 比如前面我们案例中的MyPanel就是一个类,它实现了KeyListener接口,
- 它就可以作为一个事件监听者,对接受到的事件进行处理
- 事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件,一个类可以实现多个监听接口
- 这些接口在java.awt.event包和javax.swing.event包中定义。
- 还有很多常用的事件监听器接口,可以查阅查看jdk 文档;
3. 坦克大战游戏(1.0版)
3.1 让坦克动起来
现在我们学习java事件处理机制和java绘图技术,试试看怎么让坦克通过按键控制上下左右动起来(wsad)。
- 初始框架
public class MyTankGame02 extends JFrame {
//定义 MyPanel
MyPanel mp = null;
public static void main(String[] args) {
MyTankGame02 myTankGame01 = new MyTankGame02();
}
public MyTankGame02() {
//初始化
mp = new MyPanel();
//面板(游戏的绘图区域)
this.add(mp);
//面板大小
this.setSize(1000, 750);
//添加键盘监听
this.addKeyListener(mp);
//当点击窗口的 × , 程序完全退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置显示
this.setVisible(true);
}
}
- 父类坦克
public class Tank {
private int x;//坦克的横坐标
private int y;//坦克的纵坐标
//坦克的方向 0向上 1向右 2向下 3向左
private int direction;
//坦克的速度
private int speed = 1;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//添加上下左右移动方法
//向上
public void moveUp(){
y-=speed;
}
//向下
public void moveDown(){
y+=speed;
}
//向左
public void moveLeft(){
x-=speed;
}
//向右
public void moveRight(){
x+=speed;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
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;
}
}
- 自己的坦克
public class MyTank extends Tank {
public MyTank(int x, int y) {
super(x, y);
}
}
- 敌人的坦克
public class EnemyTank extends Tank{
public EnemyTank(int x, int y) {
super(x, y);
}
}
- 坦克绘图区域
public class MyPanel extends JPanel implements KeyListener {
//定义我的坦克
MyTank myTank = null;
//定义敌人坦克,放入到 Vector
Vector<EnemyTank> enemyTanks = new Vector<>();
int enemyTanksize = 3;
public MyPanel() {
//初始化自己的坦克
myTank = new MyTank(100, 100);
//初始化敌人的坦克
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);
myTank.setSpeed(5);
//画出坦克-封装方法
//自己的坦克
drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);
//敌人的坦克
for (int i = 0; i < enemyTanksize; i++) {
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.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;
}
//根据坦克的方向,来绘制坦克
switch (direction) {//0向上 1向右 2向下 3向左
case 0://默认方向向上
// 先画第一个矩形 大小 10*60
//坦克左边轮子
// 定点(x,y)
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
// 定点(x+10,y+10)
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
// 定点(x+30,y)
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
// 定点(x+10,y+20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
// 定点1 (x+20,y)
// 定点2 (x+20,y+30)
g.drawLine(x + 20, y, x + 20, y + 30);
break;
case 1://默认方向向右
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x + 60, y + 20, x + 30, y + 20);
break;
case 2://默认方向向下
// 先画第一个矩形 大小 10*60
//坦克左边轮子
g.fill3DRect(x, y, 10, 60, false);
// 第二个矩形 大小 20*40
//坦克身体
g.fill3DRect(x + 10, y + 10, 20, 40, false);
// 第三个矩形 大小 10*60
//坦克右边轮子
g.fill3DRect(x + 30, y, 10, 60, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 10, y + 20, 20, 20);
// 最后的炮管
g.drawLine(x + 20, y + 60, x + 20, y + 30);
break;
case 3://默认方向向左
// 先画第一个矩形 大小 60*10
//坦克上边轮子
g.fill3DRect(x, y, 60, 10, false);
// 第二个矩形 大小 40*20
//坦克身体
g.fill3DRect(x + 10, y + 10, 40, 20, false);
// 第三个矩形 大小 10*60
//坦克下边轮子
g.fill3DRect(x, y + 30, 60, 10, false);
// 上面的圆盖子 大小 (20,20)
g.fillOval(x + 20, y + 10, 20, 20);
// 最后的炮管
g.drawLine(x, y + 20, x + 30, y + 20);
break;
default:
System.out.println("暂时不作处理");
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//处理 wsad 按下的情况
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
//改变坦克的方向
myTank.setDirection(0);
myTank.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_S) {
myTank.setDirection(2);
myTank.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {
myTank.setDirection(3);
myTank.moveLeft();
} else if (e.getKeyCode() == KeyEvent.VK_D) {
myTank.setDirection(1);
myTank.moveRight();
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
}
- 运行后生成的界面如下