java零基础Ⅱ-- 7.坦克大战[1]
一、坦克大战游戏演示
游戏演示
为什么写这个项目
好玩、涉及到java各个方面的技术
1、java面向对象编程
2、多线程
3、文件i/o操作
4、数据库
巩固旧知识,学习新知识
写项目前的提醒
1、编写坦克大战游戏,你需要有一定java基础,是完成可以听懂的
2、记住一点:成为一个编程高手的秘诀就是:思考 – 编程 – 思考 – 编程
二、java绘图坐标体系
坐标体系 - 介绍
下面说明了Java坐标系。坐标原点位于左上角,以像素为单位。在Java坐标系中,第一个是x坐标,表示当前位置水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。
坐标体系 - 像素
1、绘图还必须要搞清楚一个非常重要的概念 – 像素 一个像素等于多少厘米?
2、计算机在屏幕上显示的内容是由屏幕上的每个像素组成的。例如,计算机显示器的分辨率是 800x600,表示计算机屏幕的每一行是有800个点组成,共有600行,整个计算机屏幕共有 480 000 个像素。像素是一个密度单位,而厘米是长度单位,两者无法比较。
三、java绘图技术
介绍 - 快速入门
先给大家写一个小案例,在面板上画一个小圆,然后借这个案例,来讲解java绘图技术原理
package com.zzpedu.draw;
import javax.swing.*;
import java.awt.*;
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);
//当点击窗口的小x,程序完全退出
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
public void paint(Graphics g){//绘图方法
super.paint(g);//调用父类的方法完成初始化
System.out.println("paint 被调用~");
//画出一个圆形
g.drawOval(10,10,100,100);
}
}
运行结果
绘图原理
Component类提供了两个和绘图相关最重要的方法:
1、paint(Graphics g)绘制组件的外观
2、repaint() 刷新组件的外观
当组件第一次在屏幕显示的时候,程序会自动的调用paint() 方法来绘制组件。
在以下情况paint()将会被调用:
1、窗口最小化,在最大化
2、窗口的大小发生变化
3、repaint方法被调用
Graphic类
Graphic类你可以理解就是画笔,为我们提供了各种绘制图形的方法:参考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、画图片 drawImage(Image img, int x, int y)
7、画字符串 drawString(String str, int x, int y)
8、设置画笔的字体 setFont(Font font)
9、设置画笔的颜色 setColor(Color c)
演示下具体用法
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(600,500);
//当点击窗口的小x,程序完全退出
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);
//演示绘制不同的图形
//1、画出直线 drawLine(int x1, int y1, int x2, int y2)
// g.drawLine(10,10,100,100);
//2、画矩形边框 drawRect(int x, int y, int width, int height)
// g.drawRect(10,10,100,100);
//3、画椭圆边框 drawOval(int x, int y, int width, int height)
// g.drawOval(10,10,100,100);
//4、填充矩形 fillRect(int x, int y, int width, int height)
//先设置画笔的颜色
// g.setColor(Color.BLUE);
// g.fillRect(10,10,100,100);
//5、填充椭圆 fillOval(int x, int y, int width, int height)
// g.setColor(Color.RED);
// g.fillOval(10,10,100,100);
//6、画图片 drawImage(Image img, int x, int y)
//1.获取图片资源 /11.jpg 表示在该项目的跟目录去获取11.jpg 图片资源
Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/11.jpg"));
g.drawImage(image,10,10,400,300,this);
//7、画字符串 drawString(String str, int x, int y) 写字
//给画笔设置颜色和字体
// g.setColor(Color.RED);
// g.setFont(new Font("宋体",Font.BOLD,50));
// //这里设置的 100,100,是 "中国你好" 左下角
// g.drawString("中国你好",100,100);
//8、设置画笔的字体 setFont(Font font)
//9、设置画笔的颜色 setColor(Color c)
}
}
运行效果:
绘出坦克
坦克大战游戏中,我们会绘制到坦克,现在我们就利用java绘图技术来画出一个小坦克,完成我们坦克大战游戏1.0版本!
绘制坦克游戏窗口
先定义一个Tank类(作用:父类)
package com.zzpedu.tankgame;
/**
* @author zzp
*/
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; }
}
定义自己的坦克MyTank类:
package com.zzpedu.tankgame;
/**
* 自己的坦克
* @author zzp
*/
public class MyTank extends Tank{
public MyTank(int x, int y) {
super(x, y);
}
}
定义坦克大战绘图区域MyPanel类
package com.zzpedu.tankgame;
import javax.swing.*;
import java.awt.*;
/**
* 坦克大战的绘图区域
* @author zzp
*/
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);//填充矩形,默认是黑色
}
}
定义游戏的窗口,主方法ZzpTankGame01类
package com.zzpedu.tankgame;
import javax.swing.*;
/**
* @author zzp
*/
public class ZzpTankGame01 extends JFrame {
//定义MyPanel
MyPanel mp = null;
public static void main(String[] args) {
ZzpTankGame01 zzpTankGame01 = new ZzpTankGame01();
}
public ZzpTankGame01() {//构造器
mp = new MyPanel();
this.add(mp);//把面板(就是游戏的绘图区域)
this.setSize(1000,750);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
运行效果:游戏面板
绘制坦克图形
坦克草稿设计:
代码:
/**
* 坦克大战的绘图区域
* @author zzp
*/
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,1);
drawTank(myTank.getX() + 60,myTank.getY(),g,0,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;
}
//根据坦克的方向,来绘制坦克
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;
default:
System.out.println("暂时没有处理");
}
}
}
执行效果:
四、java事件处理机制
事件处理机制 - 看一个问题
请大家看一个小程序,怎么让小球受到键盘的控制,上下左右移动
代码演示:
package com.zzpedu.event_;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* 演示小球通过键盘上下左右的移动 -> 讲解Java的事件控制
* @author zzp
*/
public class BallMove extends JFrame{//窗口
MyPanel mp = null;
public static void main(String[] args) {
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() + "被按下...");
//根据用户按下的不同键,来处理小球的移动 (上下左右的键)
//在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++;
}
//让面板重绘
this.repaint();
}
//当某个键按释放(松开),该方法会触发
@Override
public void keyReleased(KeyEvent e) {
}
}
执行效果:
基本说明
java事件处理是采用 “委派事件模型”。当事件发生时,产生事件的对象,会把此 “信息” 传给 “事件的监听者” 处理,这里所说的 “信息” 实际上就是 java.awt.event
事件类库里某个类所创建的对象,把它称为 “事件的对象”。
示意图:
机制分析
事件处理机制深入理解
1、前面我们提到几个重要的概念 事件源,事件,事件监听器,我们下面来全面的介绍它们
2、事件源:事件源是一个产生事件的对象,比如按钮,窗口等
3、事件:事件就是承载事件源状态改变时的对象,比如键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保存着当前事件很多信息,比如 KeyEvent 对象有含有被按下键的 Code的值。java.awt.event
包 和javax.swing.event
包中定义了各种事件类型
4、事件类型:查jdk文档
事件类 | 说明 |
---|---|
ActionEvent | 通常在按下,或双击一个列表项或选中某个菜单时发生 |
AdjustmentEvent | 当操作一个滚动条件时发生 |
ComponentEvent | 当一个组件隐藏,移动,改变大小时发生 |
ContainerEvent | 当一个组件从容器中加入或者删除时发生 |
FocusEvent | 当一个组件获得或是失去焦点时发生 |
ItemEvent | 当一个复选框或是列表项被选中时,当一个选择框或选择菜单被选中 |
KeyEvent | 当从键盘的按键被按下,松开时发生 |
MouseEvent | 当鼠标被拖动,移动,点击,按下 … |
TextEvent | 当文本区和文本域的文本发生改变时发生 |
WindowEvent | 当一个窗口被激活,关闭,失效,恢复,最小化… |
5、事件监听器接口:
(1)当事件源产生一个事件,可以传递给事件监听者处理
(2)事件监听者实际上就是一个类,该类实现了某个事件监听器接口,比如前面我们案例中的MyPanel
就是一个类,它实现了KeyListener
接口,它就可以作为一个事件监听者,对接口受到的事件,进行处理
(3)事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件,一个类可以实现多个监听接口
(4)这些接口在java.awt.event
包和javax.swing.event
包中定义。
列出常用的事件监听器接口,查看jdk文档:
五、坦克大战游戏(1.0版)
让坦克动起来
现在我们学习java事件处理机制和java绘图技术,请试试如何让坦克可以通过按键控制上下左右(wdsa表示)的移动。
package com.zzpedu.tankgame2;
/**
* @author zzp
*/
public class Tank {
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 void moveUp(){
y -= speed;
}
public void moveRight(){
x += speed;
}
public void moveDown(){
y += speed;
}
public void moveLeft(){
x -= speed;
}
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 getDirect() { return direct; }
public void setDirect(int direct) { this.direct = direct; }
public int getSpeed() { return speed; }
public void setSpeed(int speed) { this.speed = speed; }
}
package com.zzpedu.tankgame2;
/**
* 自己的坦克
* @author zzp
*/
public class MyTank extends Tank {
public MyTank(int x, int y) {
super(x, y);
}
}
package com.zzpedu.tankgame2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* 坦克大战的绘图区域
* @author zzp
*/
//为了监听键盘事件,要实现KeyListener
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);//填充矩形,默认是黑色
//画出坦克-封装方法
drawTank(myTank.getX(),myTank.getY(),g,myTank.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("暂时没有处理");
}
}
@Override
public void keyTyped(KeyEvent e) {
}
//处理按键事件 wdsa
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_W){//按下W键
//改变坦克方向 上
myTank.setDirect(0);
//修改坦克的坐标 y -= 1
myTank.moveUp();
}else if(e.getKeyCode() == KeyEvent.VK_D){//按下D键
//改变坦克方向 右
myTank.setDirect(1);
myTank.moveRight();
}else if(e.getKeyCode() == KeyEvent.VK_S){//按下S键
//改变坦克方向 下
myTank.setDirect(2);
myTank.moveDown();
}else if(e.getKeyCode() == KeyEvent.VK_A){//按下A键
//改变坦克方向 左
myTank.setDirect(3);
myTank.moveLeft();
}
//让面板重绘
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
}
package com.zzpedu.tankgame2;
import javax.swing.*;
/**
* @author zzp
*/
public class ZzpTankGame02 extends JFrame {
//定义MyPanel
MyPanel mp = null;
public static void main(String[] args) {
ZzpTankGame02 zzpTankGame01 = new ZzpTankGame02();
}
public ZzpTankGame02() {//构造器
mp = new MyPanel();
this.add(mp);//把面板(就是游戏的绘图区域)
this.setSize(1000,750);
//让JFrame 监听mp键盘事件
this.addKeyListener(mp);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
运行效果:
绘制敌人的坦克
请大家再ZzpTankGame02.java 基础上画出三辆敌人坦克,注意颜色。下图所示:
分析:
1、因为敌人的坦克,是在MyPanel 上,所以我们的代码在MyPanel
2、因为敌人的坦克,后面有自己的特殊属性和方法,可以单开一个EnemyTank
3、敌人坦克数量多,可以放入到集合 Vector,因为考虑多线程问题
package com.zzpedu.tankgame2;
/**
* 敌人的坦克
* @author zzp
*/
public class EnemyTank extends Tank{
public EnemyTank(int x, int y) {
super(x, y);
}
}
package com.zzpedu.tankgame2;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* 坦克大战的绘图区域
* @author zzp
*/
//为了监听键盘事件,要实现KeyListener
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);//初始化自己的坦克
myTank.setSpeed(5);
//初始化敌人的坦克
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(myTank.getX(),myTank.getY(),g,myTank.getDirect(),1);
//画出敌人的坦克,遍历Vector
for(int i = 0; i < enemyTankSize; i++){
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(),enemyTank.getY(),g,enemyTank.getDirect(),0);
}
}
.....
}
运行效果:
小结
java坐标体系
java画图技术
java事件处理机制
随着知识点的讲解,我们的坦克大战游戏也在不停的完善
ZzpTankGame01.java 包含功能:
1、绘制出我方坦克
ZzpTankGame02.java 包含功能:
1、绘制了我方坦克
2、通过按键可以控制我方坦克上下左右移动