共计 7285 个字符,预计需要花费 19 分钟才能阅读完成。
文章和代码曾经归档至【Github 仓库:https://github.com/timerring/java-tutorial】或者公众号【AIShareLab】回复 java 也可获取。
坦克大战 1.0
java 绘图坐标体系
坐标体系 - 介绍
下图阐明了 Java 坐标系。坐标原点位于左上角,以像素为单位。在 Java 坐标系中, 第一个是 x 坐标, 示意以后地位为程度方向,间隔坐标原点 x 个像素; 第二个是 y 坐标,示意以后地位为垂直方向,间隔坐标原点 y 个像素。
坐标体系 - 像素
绘图还必须要搞清一个十分重要的概念 - 像素一个像素等于多少厘米?
计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器的分辨率是 800×600,示意计算机屏幕上的每一行由 800 个点组成,共有 600 行, 整个计算机屏幕共有 480 000 个像素。像素是一个密度单位, 而厘米是长度单位, 两者无奈比拟。
介绍 - 疾速入门
绘图原理
在面板上画一个小圆。
Component 类提供了两个和绘图相干最重要的办法:1. paint(Graphics g)绘制组件的外观
repaint()刷新组件的外观。
当组件第一次在屏幕显示的时候, 程序会主动的调用 paint()办法来绘制组件。在以下状况 paint(将会被调用:
1. 窗口最小化. 再最大化
2. 窗口的大小发生变化
3.repaint 办法被调用
Graphics 类
Graphics 类你能够了解就是画笔, 为咱们提供了各种绘制图形的办法:[参考 jdk 帮忙文档]
1. 画直线 drawLine(int x1,int y1,int x2,int y2)
2. 画矩形边框 drawRect(int x, int y, int width, int height)
3. 画椭圆边框 drawOval(int x, int y, int width, int height)
4. 填充矩形 fillRect(int x, int y, int width, int height)
5. 填充椭圆 fillOval(int x, int y, int width, int height)
6. 画图片 drawlmage(Image img, int x, int y. ..)
7. 画字符串 drawString(String str, int x, int y)
这里是以左下角为参考系的。
8. 设置画笔的字体 setFont(Font font)
9. 设置画笔的色彩 setColor(Color c)
package com.hspedu.draw;
import javax.swing.*;
import java.awt.*;
@SuppressWarnings({"all"})
public class DrawCircle extends JFrame { //JFrame 对应窗口, 能够了解成是一个画框
// 定义一个面板
private MyPanel mp = null;
public static void main(String[] args) {new DrawCircle();
System.out.println("退出程序~");
}
public DrawCircle() {// 结构器
// 初始化面板
mp = new MyPanel();
// 把面板放入到窗口(画框)
this.add(mp);
// 设置窗口的大小
this.setSize(400, 300);
// 当点击窗口的小×,程序齐全退出.
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);// 能够显示
}
}
//1. 先定义一个 MyPanel, 继承 JPanel 类,画图形,就在面板上画
class MyPanel extends JPanel {
// 阐明:
//1. MyPanel 对象就是一个画板
//2. Graphics g 把 g 了解成一支画笔
//3. Graphics 提供了很多绘图的办法
//Graphics g
@Override
public void paint(Graphics g) {// 绘图办法
super.paint(g); // 调用父类的办法实现初始化.
System.out.println("paint 办法被调用了~");
// 画出一个圆形.
//g.drawOval(10, 10, 100, 100);
// 演示绘制不同的图形..
// 画直线 drawLine(int x1,int y1,int x2,int y2)
//g.drawLine(10, 10, 100, 100);
// 画矩形边框 drawRect(int x, int y, int width, int height)
//g.drawRect(10, 10, 100, 100);
// 画椭圆边框 drawOval(int x, int y, int width, int height)
// 填充矩形 fillRect(int x, int y, int width, int height)
// 设置画笔的色彩
// g.setColor(Color.blue);
// g.fillRect(10, 10, 100, 100);
// 填充椭圆 fillOval(int x, int y, int width, int height)
// g.setColor(Color.red);
// g.fillOval(10, 10, 100, 100);
// 画图片 drawImage(Image img, int x, int y, ..)
//1. 获取图片资源, /bg.png 示意在该项目标根目录去获取 bg.png 图片资源
// Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bg.png"));
// g.drawImage(image, 10, 10, 175, 221, this);
// 画字符串 drawString(String str, int x, int y)// 写字
// 给画笔设置色彩和字体
g.setColor(Color.red);
g.setFont(new Font("隶书", Font.BOLD, 50));
// 这里设置的 100,100,是 "北京你好" 左下角
g.drawString("北京你好", 100, 100);
// 设置画笔的字体 setFont(Font font)
// 设置画笔的色彩 setColor(Color c)
}
}
绘出坦克
java 事件处理机制
根本阐明
java 事件处理是采取 ” 委派事件模型 ”。当事件产生时, 产生事件的对象, 会把此 ” 信息”传递给 ” 事件的监听者 ” 解决,这里所说的“信息 ” 实际上就是 java.awt.event 事件类库里某个类所创立的对象,把它称为 ” 事件的对象 ”。
示意图
事件处理机制深刻了解
- 后面咱们提到几个重要的概念事件源,事件,事件监听器咱们上面来全面的介绍它们.
- 事件源: 事件源是一个产生事件的对象,比方按钮,窗口等。
- 事件: 事件就是承载事件源状态扭转时的对象,比方当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保留着以后事件很多信息,比方 KeyEvent 对象有含有被按下键的 Code 值。java.awt.event 包和 javax.swing.event 包中定义了各种事件类型
-
事件类型: 查阅 jdk 文档
- 事件监听器接口:
(1)当事件源产生一个事件, 能够传送给事件监听者解决
(2) 事件监听者实际上就是一个类, 该类实现了某个事件监听器接口比方后面咱们案例中的 MyPanle 就是一个类, 它实现了 KeyListener 接口,它就能够作为一个事件监听者,对承受到的事件进行解决。
(3)事件监听器接口有多种,不同的事件监听器接口能够监听不同的事件, 一个类能够实现多个监听接口
(4) 这些接口在 java.awt.event 包和 javax.swing.event 包中定义。列出罕用的事件监听器接口, 查看 jdk 文档。
坦克动起来
当初咱们学习 java 事件处理机制和 java 绘图技术, 请试试看如何让你的坦克能够通过按键管制上右下左 (wdsa 示意) 的挪动。
package com.hspedu.tankgame2;
/**
* 敌人的坦克
*/
public class EnemyTank extends Tank {public EnemyTank(int x, int y) {super(x, y);
}
}
package com.hspedu.tankgame2;
public class Hero extends Tank {public Hero(int x, int y) {super(x, y);
}
}
package com.hspedu.tankgame2;
import javax.swing.*;
import java.awt.event.KeyListener;
public class HspTankGame02 extends JFrame {
// 定义 MyPanel
MyPanel mp = null;
public static void main(String[] args) {HspTankGame02 hspTankGame01 = new HspTankGame02();
}
public HspTankGame02() {mp = new MyPanel();
this.add(mp);// 把面板(就是游戏的绘图区域)
this.setSize(1000, 750);
// 一个接口的援用能够指向实现该接口的对象(mp 实现了该接口) this.addKeyListener(KeyListener I);
this.addKeyListener(mp); // 让 JFrame 监听 mp 的键盘事件
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
package com.hspedu.tankgame2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* 坦克大战的绘图区域
*/
// 为了监听 键盘事件,实现 KeyListener
public class MyPanel extends JPanel implements KeyListener {
// 定义我的坦克
Hero hero = null;
// 定义敌人坦克,放入到 Vector
Vector<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);
}
}
@Override
public void paint(Graphics g) {super.paint(g);
g.fillRect(0, 0, 1000, 750);// 填充矩形,默认彩色
// 画出本人坦克 - 封装办法
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
// 画出敌人的坦克, 遍历 Vector
for (int i = 0; i < enemyTanks.size(); i++) {
// 取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
}
}
// 编写办法,画出坦克
/**
* @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("临时没有解决");
}
}
@Override
public void keyTyped(KeyEvent e) { }
// 解决 wdsa 键按下的状况
@Override
public void keyPressed(KeyEvent e) {System.out.println(e.getKeyCode());
if (e.getKeyCode() == KeyEvent.VK_W) {// 按下 W 键
// 扭转坦克的方向
hero.setDirect(0);//
// 批改坦克的坐标 y -= 1
hero.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();}
// 让面板重绘,如果不重绘的话,则不会有扭转
this.repaint();}
@Override
public void keyReleased(KeyEvent e) {}}
package com.hspedu.tankgame2;
public class Tank {
private int x;// 坦克的横坐标
private int y;// 坦克的纵坐标
private int direct = 0;// 坦克方向 0 上 1 右 2 下 3 左
private int speed = 1;
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 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;}
}
作业
画出三辆敌人的坦克, 留神色彩。如图所示剖析:
- 因为敌人的坦克,是在 MyPanel 上所以咱们的代码在 MyPanel
- 因为敌人的坦克,前面有本人非凡的属性和办法,能够单开一个 EnemyTank
- 敌人坦克数量多,能够放入到汇合 Vector,因为思考多线程问题
代码同上。