《Java小游戏实现》:坦克大战

《Java小游戏实现》:坦克大战

前面写了一个简单的聊天小程序,今天开始就写一个坦克大战的游戏,算是对Java相关小知识点的一个应用。

这个游戏的完成,我们也是分步完成,逐步累加,一个一个小功能的添加,最后直至完成整个游戏的开发。

第一步:写一个界面

    public class TankClient extends JFrame{

        public static void main(String[] args) {
            new TankClient().launchFrame();
        }

        public void launchFrame(){

            this.setTitle("坦克大战");
            this.setLocation(300, 400);
            this.setSize(300, 400);
            //this.setBackground(Color.RED);
            this.getContentPane().setBackground(Color.RED);
            //为关闭窗口添加响应
            this.addWindowListener(new WindowAdapter(){

                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }

            });
            //设置是否允许用户改变窗口的大小,这里限制下,不允许
            this.setResizable(false);
            this.setVisible(true);
        }

    }

以上就是为坦克大战写了一个界面,这是我们的第一步,算是完成了。

在完成这个界面时,遇到了一点小小的问题,这里有必要说明下,本来我想将这个界面设置一个背景颜色,但是使用

this.setBackground(Color.RED);就是不能成功,于是百度了下,这是由于我这里继承的是JFrame,而不是Frame,如果是继承的是Frame,则就可以通过this.setBackground(Color.RED)来设置颜色。如果我们选择的是继承JFrame,则需要使用this.getContentPane().setBackground(Color.RED)来设置背景颜色。

出现上面的原因如下:Frame和JFrame的窗口层次结构不同,JFrame的窗口包括:JFrame、Root Pane、Layered pane、Content Pane、Glass Pane;而Frmae窗口包括:Frame、Content Pane。

其次,窗口背景颜色是指直接调用JFrame或者Frame的setBackground(Color color)方法设置后显示出来的颜色。其实,JFrame在你直接调用这个方法后,你的确设置了背景颜色,而你看到的却不是直接的JFrame或者Frame,而是JFrame.getContentPane().而JFrame上的contentPane默认是Color.WHITE的,所以,无论你对JFrame或者Frame怎么设置背景颜色,你看到的都只是contentPane.因此,

    this.setBackground(Color.RED);
    his.getContentPane().setVisible(false);//得到contentPane容器,设置为不可见

也可以解决JFram设置的背景颜色的问题。

第二步:在界面上显示一个坦克图标

有界面,没有坦克在上面,肯定不是我们想要的,是吧,因此,我们就先完成这个功能。

在这里我们用一个圆来代表一个坦克。绘图代码如下:

    @Override
    public void paint(Graphics g) {
        Color c = g.getColor();
        g.setColor(Color.RED);
        g.fillOval(50, 50, 30, 30);
        g.setColor(c);
    }

完整的代码如下:

    public class TankClient extends Frame{

        public static void main(String[] args) {
            new TankClient().launchFrame();
        }

        @Override
        public void paint(Graphics g) {
            Color c = g.getColor();
            g.setColor(Color.RED);
            g.fillOval(50, 50, 30, 30);
            g.setColor(c);
        }

        public void launchFrame(){

            this.setTitle("坦克大战");
            this.setLocation(300, 400);
            this.setSize(600, 400);
            //this.getContentPane().setBackground(Color.GRAY);
            this.setBackground(Color.GRAY);
            //为关闭窗口添加响应
            this.addWindowListener(new WindowAdapter(){

                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }

            });
            //设置是否允许用户改变窗口的大小,这里限制下,不允许
            this.setResizable(false);
            this.setVisible(true);
        }

    }

细心的朋友可能会发现,第一个版本窗口是继承的是JFrame,而我这个版本是继承的是Frame,是出现了这样一个问题,在我重写了paint方法之后,继承JFrame实现的窗口使用this.getContentPane().setBackground(Color.GRAY);来设置颜色又不是我们所期望的。

下面左边的图是使用继承Frame+this.setBackground(Color.GRAY);

右边的图是使用继承JFrame+this.getContentPane().setBackground(Color.GRAY);结果居然是白色。

所以说Frame和JFrame还是有一定的差别的,这里不再仔细研究,而是直接使用Frame来完成。

第三步:控制坦克移动

经过第二步的处理,可以在界面上显示一个坦克了,那么应该如何来控制坦克移动了。下面我们来研究并完成。

我们再实现通过键盘的方向键来控制坦克移动之前,我们实现这样一个功能,什么功能呢??就是:坦克在某一个方向上移动。

坦克移动,简单来说,就是坦克所在的位置。即每次改变坦克的位置之后重画调用paint函数。因此,我们将坦克的位置用x/y表示。然后写一个线程专门用于重画。

实现代码如下:

    public class TankClient extends Frame{

        public static void main(String[] args) {
            new TankClient().launchFrame();
        }
        //坦克所在的位置的坐标
        private int x=50;
        private int y=50;
        @Override
        public void paint(Graphics g) {
            Color c = g.getColor();
            g.setColor(Color.RED);
            g.fillOval(x, y, 30, 30);
            g.setColor(c);

            y+=5;//该表坦克的位置
        }

        public void launchFrame(){

            this.setTitle("坦克大战");
            this.setLocation(300, 400);
            this.setSize(600, 400);
            this.setBackground(Color.GRAY);
            //为关闭窗口添加响应
            this.addWindowListener(new WindowAdapter(){

                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }

            });
            //设置是否允许用户改变窗口的大小,这里限制下,不允许
            this.setResizable(false);
            this.setVisible(true);

            new Thread(new MyRepaint()).start();

        }

        private class MyRepaint implements Runnable{

            @Override
            public void run() {
                while(true){
                    //每50ms重画一次
                    repaint();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

    }

通过开启一个线程以及在paint函数中改变坦克的位置(y+=5),这样,坦克就会一直往下面移动。

实现了坦克一直往下移动之后,我们就可以实现通过键盘来控制坦克的移动了。

通过键盘来实现坦克的移动,我们只需要添加一个键盘按键的监听事件即可,根据键盘按键的不同,做相应的一个处理。

1、首先先写一个键盘按键监听类,键盘按键监听类,有两种实现方式,一种是继承KeyAdapter(重写我们需要的方法,比较方便);另一种是实现KeyListener接口(需要实现3个抽象方法)。

2、然后使用组件的 addKeyListener 方法将从该类所创建的侦听器对象向该组件注册。按下、释放或键入键时生成键盘事件。

    private class KeyMonitor extends KeyAdapter{

        @Override
        public void keyPressed(KeyEvent e) {
            int key=e.getKeyCode();
            switch(key){
            case KeyEvent.VK_LEFT:
                x -= 5;
                break;
            case KeyEvent.VK_UP:
                y -= 5;
                break;
            case KeyEvent.VK_RIGHT:
                x += 5;
                break;
            case KeyEvent.VK_DOWN:
                y += 5;
                break;
            }
        }

    }

完整代码如下:

    public class TankClient extends Frame{

        public static void main(String[] args) {
            new TankClient().launchFrame();
        }
        //坦克所在的位置的坐标
        private int x=50;
        private int y=50;
        @Override
        public void paint(Graphics g) {
            Color c = g.getColor();
            g.setColor(Color.RED);
            g.fillOval(x, y, 30, 30);
            g.setColor(c);

        }

        public void launchFrame(){

            this.setTitle("坦克大战");
            this.setLocation(300, 400);
            this.setSize(600, 400);
            this.setBackground(Color.GRAY);
            //为关闭窗口添加响应
            this.addWindowListener(new WindowAdapter(){

                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }

            });
            //设置是否允许用户改变窗口的大小,这里限制下,不允许
            this.setResizable(false);
            this.setVisible(true);

            new Thread(new MyRepaint()).start();
            this.addKeyListener(new KeyMonitor());

        }

        private class MyRepaint implements Runnable{

            @Override
            public void run() {
                while(true){
                    //每50ms重画一次
                    repaint();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        private class KeyMonitor extends KeyAdapter{

            @Override
            public void keyPressed(KeyEvent e) {
                int key=e.getKeyCode();
                switch(key){
                case KeyEvent.VK_LEFT:
                    x -= 5;
                    break;
                case KeyEvent.VK_UP:
                    y -= 5;
                    break;
                case KeyEvent.VK_RIGHT:
                    x += 5;
                    break;
                case KeyEvent.VK_DOWN:
                    y += 5;
                    break;
                }
            }

        }

    }

以上就通过键盘的上下左右键来移动界面上的坦克了。

第四步:面向对象的思想构造坦克类

对于面向对象编程的思想,我们首先要考虑的事情就是有哪些类和对象,每个类中有哪些属性和方法。

上面的版本都还没有利用面向对象的思想。这一小节,我们就来构造坦克类,这样便于面向对象编程。

首先,我们构造一个Tank类,Tank类中有两个属性,分别为坦克位置x和y。包含的方法应该有:draw方法和keyMonitor方法。即自己画图的方法和自己对键盘的响应的方法。

Tank类的代码如下:

    public class Tank {
        //坦克所在的位置坐标
        private int x;
        private int y;

        public Tank(int x, int y) {
            super();
            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 void draw(Graphics g){

            Color c = g.getColor();
            g.setColor(Color.RED);
            g.fillOval(x, y, 30, 30);
            g.setColor(c);
        }
        //响应键盘按键的方法
        public void keyMonitor(KeyEvent e){
            int key=e.getKeyCode();
            switch(key){
            case KeyEvent.VK_LEFT:
                x -= 5;
                break;
            case KeyEvent.VK_UP:
                y -= 5;
                break;
            case KeyEvent.VK_RIGHT:
                x += 5;
                break;
            case KeyEvent.VK_DOWN:
                y += 5;
                break;
            }
        }

    }

TankClient类的方法重构后的代码如下:

    public class TankClient extends Frame{

        private Tank tk=new Tank(50,50);
        public static void main(String[] args) {
            new TankClient().launchFrame();
        }

        @Override
        public void paint(Graphics g) {
            //直接调用坦克至圣的draw方法
            tk.draw(g);
        }

        public void launchFrame(){

            this.setTitle("坦克大战");
            this.setLocation(300, 400);
            this.setSize(600, 400);
            this.setBackground(Color.GRAY);
            //为关闭窗口添加响应
            this.addWindowListener(new WindowAdapter(){

                @Override
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }

            });
            //设置是否允许用户改变窗口的大小,这里限制下,不允许
            this.setResizable(false);
            this.setVisible(true);

            new Thread(new MyRepaint()).start();
            this.addKeyListener(new KeyMonitor());

        }

        private class MyRepaint implements Runnable{

            @Override
            public void run() {
                while(true){
                    //每50ms重画一次
                    repaint();
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        private class KeyMonitor extends KeyAdapter{

            @Override
            public void keyPressed(KeyEvent e) {
                tk.keyMonitor(e);
            }
        }

    }

解决闪烁现象

什么是闪烁现象呢??可以理解为丢帧。即在视觉上少了一些中间过程的变化。

当使用graphics对象在窗体中绘制多种图像时,每次窗体改变都要调用repaint()函数,相当于把所有图像按顺序重新绘制一次,所以就出现了闪烁现象。 这一点在棋类游戏更加明显(想象一下每次落子都需要重新绘制棋盘上的几十上百个棋子是什么概念)

解决的方法是使用双缓冲技术。也就是说,先将所有图像都绘制在一个缓冲区中,形成一张图片,这样每次调用repaint()只需要将该缓冲图片画到窗体上就可以了。

在本例中,只需要引入一个画布,并重写update方法

    private Image offScreenImage =  null;
    @Override
    public void update(Graphics g) {
        if (offScreenImage == null) {
            offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);
        }
        Graphics goffScreen = offScreenImage.getGraphics();// 重新定义一个画虚拟桌布的画笔//
        Color c = goffScreen.getColor();
        goffScreen.setColor(Color.GRAY);
        goffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
        goffScreen.setColor(c);
        //调用paint方法
        paint(goffScreen);
        //最后,将虚拟画布作为整体直接画上去,避免闪烁的发生
        g.drawImage(offScreenImage, 0, 0, null);
    }

    @Override
    public void paint(Graphics g) {
        //直接调用坦克至圣的draw方法
        tk.draw(g);
    }

未完,待续

时间: 2024-10-22 23:30:16

《Java小游戏实现》:坦克大战的相关文章

坦克大战系列(3.0版)

无论头上是怎样的天空,我准备承受任何风暴.--拜伦 本讲内容:坦克3.0版(面向对象的思想) 要求:画出我方坦克会动并且会发射子弹.画出敌人坦克 一.同一个包下建二个文件分别为:MyTankGame.Members(负责其它成员譬如:制造坦克.子弹等) MyTankGame类 /** * 功能:坦克游戏的3.0版本 * 1:画出坦克 * 2:实现我方坦克移动并且會發子彈,并 画出敌人的坦克 */ package a; import javax.swing.*; import java.awt.*

C++代码训练之坦克大战(2)

这一篇中,我们继续继续进行我们的坦克大战. 位置信息数据结构 在游戏设计过程中,需要记录大量的位置信息,如果仅仅使用(x,y)坐标很容易出错.这一篇中,我们先定义两个简单的数据结构用来保存点和矩形的信息. 在项目中新建Model目录,创建下面四个文件: 代码如下: Point.h #ifndef __POINT_H__ #define __POINT_H__ class Point { public: Point(int x = 0, int y = 0) : m_x(x), m_y(y){};

坦克大战(版本2.5-版本2.9)

版本2.5 功能:添加“血块”步骤:        1)添加blood类        2)添加必要的方法:eat方法等        3)让blood对象固定轨迹运动, 并在一定时间后消失 具体代码实现: 新增的blood类: 1 import java.awt.Color; 2 import java.awt.Graphics; 3 import java.awt.Rectangle; 4 5 //模拟血块,坦克吃了可以补血 6 public class Blood { 7 int x, y

脚本游戏之四: 坦克大战源码注释(待续。。。)

要点: 1.echo的高级用法,主要包括:颜色.位置定位输出等 2.trap 信号捕捉, kill 发送信号 3.捕捉用户输入,控制后台进程 参考:坦克大战 文章结尾的几段代码 总体: 多个进程后台并行运行, 前端一个段接受用户输入的代码,根据用户输入通过发送信号控制后台进程. 几乎每一个组件都是一个后台运行的函数. #!/bin/bash # BY: LingYi # DATE: 2016.02.23 #place temporary files tmpdir='/tmp' #u:up d:d

NYOJ 284 坦克大战 【BFS】+【优先队列】

坦克大战 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 Many of us had played the game "Battle city" in our childhood, and some people (like me) even often play it on computer now. What we are discussing is a simple edition of this game. Given a map that co

Java__线程---基础知识全面实战---坦克大战系列为例

今天想将自己去年自己编写的坦克大战的代码与大家分享一下,主要面向学习过java但对java运用并不是很熟悉的同学,该编程代码基本上涉及了java基础知识的各个方面,大家可以通过练习该程序对自己的java进行一下实战. 每个程序版本代码中,都附有相关注释,看完注释大家就可以对本程序设计有个很明显的思路.真的很有趣,想对java重新温习的同学完全不必再对厚厚的java基础书籍进行阅读了,在跟着本次代码练习并分析后,大家一定会对java各方面基础知识 尤其是线程的知识有更深一步的了解!!! 本次坦克大

【Cocos2D-x 3.5实战】坦克大战(1)环境配置

前言: 最近课比较少,空闲时间比较多,一有时间就东想西想,想着想着就突然想到做手机游戏(android)了,学习下CoCos2d.看了一些CoCos2D的相关文档和教程,觉得是时候实战了,但是苦于没有什么新奇的游戏点子,只有写下被儿时玩过的坦克大战了(主要素材好找).而这个系列的文章来记录下我的开发过程. 开发环境: Win7(x64) VS2012  下载地址: http://www.itellyou.cn/ Eclipse  下载地址:http://www.eclipse.org/downl

坦克大战系列(8.0版)

人生在勤,不索何获.--张衡 本讲内容:坦克大战8.0版(面向对象的思想) 一.解决:防止敌人坦克重叠运动 1.定义一个Vector容器,装所有敌人的坦克(为了得到所有坦克坐标) 2.定义一个方法getEts()可以得到敌人的坦克(为了得到所有坦克坐标) 3.在我的我的面板的构造方法调用getEts() 4.定义一个判断是否碰到了别的敌人的坦克的方法isTouchOtherEnemy() 5.在设置敌人坦克随机走动那调用isTouchOtherEnemy() 二.解决:我方坦克死亡(即隐身)后,

【跟我一起学Unity3D】做一个2D的90坦克大战之地图编辑器

从10月20号到现在,Unity3D也学了10天了,对于Unity3D也有了一个大致的了解,有必要做一个小游戏来检测一下自己的学习成果了.经过两天的努力,终于总算是做出来了一个可以玩的坦克大战了.首先讲讲我的设计目标: 1.地图编辑器 2.道具系统 3.简单AI系统 4.计分器 其中,最重要的就是地图编辑器了,其次到AI系统,其他几个都挺简单的. ---------------------------------------------------------------------------

Java坦克大战游戏源代码

转载自: http://blog.csdn.net/java_cxrs/article/details/3860870 经过几天的练习和研究终于自己能写出坦克大战游戏了,写完这个程序后感觉收获了很多东西,对JAVA的知识又有了一定的增长,接下来还准备继续写几个小项目来练习J2SE 由于代码太长就不发在博客里了,我上传到了资源下载里,有需要的朋友大家可以去下载 下载地址:http://download.csdn.net/source/988654