Github标星11.5K,清华学长强推经典落地项目,坦克大战附源码

该项目通过坦克大战游戏,深入浅出地介绍了应用层和运输层网络协议的使用,以及面向对象编程的概念。玩家可以通过游戏理解TCP和UDP连接,以及自定义应用层协议。此外,游戏逻辑中涉及坦克和子弹的封装,以及网络联机的实现,包括TCP连接、UDP端口号交换、应用层协议设计。项目源码在GitHub上可供参考。
摘要由CSDN通过智能技术生成

介绍

通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简单的应用层协议来让自己应用进行网络通信,文末附源码。

本项目的Github地址

基础版本

游戏的原理, 图形界面(非重点)

  • 多张图片快速连续地播放, 图片中的东西就能动起来形成视频, 对视频中动起来的东西进行操作就变成游戏了. 在一个坦克对战游戏中, 改变一辆坦克每一帧的位置, 当多帧连续播放的时候, 视觉上就有了控制坦克的感觉. 同理, 改变子弹每一帧的位置, 看起来就像是发射了一发炮弹. 当子弹和坦克的位置重合, 也就是两个图形的边界相碰时, 在碰撞的位置放上一个爆炸的图片, 就完成了子弹击中坦克发生爆炸的效果.
  • 在本项目借助坦克游戏认识网络知识和面向对象思想, 游戏的显示与交互使用到了Java中的图形组件, 如今Java已较少用于图形交互程序开发, 本项目也只是使用了一些简单的图形组件.
  • 在本项目中, 游戏的客户端由TankClient类控制, 游戏的运行和所有的图形操作都包含在这个类中, 下面会介绍一些主要的方法.
//类TankClient, 继承自Frame类

//继承Frame类后所重写的两个方法paint()和update()
//在paint()方法中设置在一张图片中需要画出什么东西. 
@Override
public void paint(Graphics g) {
    //下面三行画出游戏窗口左上角的游戏参数
    g.drawString("missiles count:" + missiles.size(), 10, 50);
    g.drawString("explodes count:" + explodes.size(), 10, 70);
    g.drawString("tanks    count:" + tanks.size(), 10, 90);
    
    //检测我的坦克是否被子弹打到, 并画出子弹
    for(int i = 0; i < missiles.size(); i++) {
        Missile m = missiles.get(i);
        if(m.hitTank(myTank)){
            TankDeadMsg msg = new TankDeadMsg(myTank.id);
            nc.send(msg);
            MissileDeadMsg mmsg = new MissileDeadMsg(m.getTankId(), m.getId());
            nc.send(mmsg);
        }
        m.draw(g);
    }
    //画出爆炸
    for(int i = 0; i < explodes.size(); i++) {
        Explode e = explodes.get(i);
        e.draw(g);
    }
    //画出其他坦克
    for(int i = 0; i < tanks.size(); i++) {
        Tank t = tanks.get(i);
        t.draw(g);
    }
    //画出我的坦克
    myTank.draw(g);
}

/*
 * update()方法用于写每帧更新时的逻辑. 
 * 每一帧更新的时候, 我们会把该帧的图片画到屏幕中.
 * 但是这样做是有缺陷的, 因为把一副图片画到屏幕上会有延时, 游戏显示不够流畅
 * 所以这里用到了一种缓冲技术.
 * 先把图像画到一块幕布上, 每帧更新的时候直接把画布推到窗口中显示
 */
@Override
public void update(Graphics g) {
    if(offScreenImage == null) {
        offScreenImage = this.createImage(800, 600);//创建一张画布
    }
    Graphics gOffScreen = offScreenImage.getGraphics();
    Color c = gOffScreen.getColor();
    gOffScreen.setColor(Color.GREEN);
    gOffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
    gOffScreen.setColor(c);
    paint(gOffScreen);//先在画布上画好
    g.drawImage(offScreenImage, 0, 0, null);//直接把画布推到窗口
}


//这是加载游戏窗口的方法
public void launchFrame() {
    this.setLocation(400, 300);//设置游戏窗口相对于屏幕的位置
    this.setSize(GAME_WIDTH, GAME_HEIGHT);//设置游戏窗口的大小
    this.setTitle("TankWar");//设置标题
    this.addWindowListener(new WindowAdapter() {//为窗口的关闭按钮添加监听

        @Override
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    });
    this.setResizable(false);//设置游戏窗口的大小不可改变
    this.setBackground(Color.GREEN);//设置背景颜色
    this.addKeyListener(new KeyMonitor());//添加键盘监听, 
    this.setVisible(true);//设置窗口可视化, 也就是显示出来
    new Thread(new PaintThread()).start();//开启线程, 把图片画出到窗口中
    dialog.setVisible(true);//显示设置服务器IP, 端口号, 自己UDP端口号的对话窗口
}

//在窗口中画出图像的线程, 定义为每50毫秒画一次. 
class PaintThread implements Runnable {

    public void run() {
        while(true) {
            repaint();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 以上就是整个游戏图形交互的主要部分, 保证了游戏能正常显示后, 下面我们将关注于游戏的逻辑部分.

游戏逻辑

  • 在游戏的逻辑中有两个重点, 一个是坦克, 另一个是子弹. 根据面向对象的思想, 分别把这两者封装成两个类, 它们所具有的行为都在针对应有相应的方法.
  • 坦克的字段
public int id;//作为网络中的标识

public static final int XSPEED = 5;//左右方向上每帧移动的距离
public static final int YSPEED = 5;//上下方向每帧移动的距离
public static final int WIDTH = 30;//坦克图形的宽
public static final int HEIGHT = 30;//坦克图形的高

private boolean good;//根据true和false把坦克分成两类, 游戏中两派对战
private int x, y;//坦克的坐标
private boolean live = true;//坦克是否活着, 死了将不再画出
private TankClient tc;//客户端类的引用
private boolean bL, bU, bR, bD;//用于判断键盘按下的方向
private Dir dir = Dir.STOP;//坦克的方向
private Dir ptDir = Dir.D;//炮筒的方向
  • 由于在TankClient类中的paint方法中需要画出图形, 根据面向对象的思想, 要画出一辆坦克, 应该由坦克调用自己的方法画出自己.
  public void draw(Graphics g) {
        if(!live) {
            if(!good) {
                tc.getTanks().remove(this);//如果坦克死了就把它从容器中去除, 并直接结束
            }
            return;
        }
        //画出坦克
        Color c = g.getColor();
        if(good) g.setColor(Color.RED);
        else g.setColor(Color.BLUE);
        g.fillOval(x, y, WIDTH, HEIGHT);
        g.setColor(c);
        //画出炮筒
        switch(ptDir) {
            case L:
                g.drawLine(x + WIDTH/2, y + HEIGHT/2, x, y + HEIGHT/2);
                break;
            case LU:
                g.drawLine(x + WIDTH/2, y + HEIGHT/2, x, y);
                break;
            case U:
                g.drawLine(x + WIDTH/2, y + HEIGHT/2, x + WIDTH/2, y);
                break;
            //...省略部分方向
        }
        move();//每次画完改变坦克的坐标, 连续画的时候坦克就动起来了
    }
  • 上面提到了改变坦克坐标的move()方法, 具体代码如下:
private void move() {
    switch(dir) {//根据坦克的方向改变坐标
        case L://左
            x -= XSPEED;
            break;
        case LU://左上
            x -= XSPEED;
            y -
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值