记得在红白机(FC)年代,还刚刚上小学的我对马里奥、冒险岛、洛克人、魂斗罗等游戏几乎可说是痴迷,每天放学回家就是想去游戏,就是要通关,就是想和关底论个胜负高低。

许多年过去了,沧海桑田,FC曾经的荣耀早已不再,只留下我们对曾经少年时的点点记忆。即使当时在FC上看上去多么复杂,多么高不可攀的游戏,在当今,即使最普通的程序员都可以轻易实现。

本着向经典学习、向经典致敬的心情,我也准备用Java在PC机再现当年马里奥的风采。

下面我在代码中所演示的,是一个简单的ACT游戏动作及地图构成原型。

Map.java
package org.test.mario;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:地图绘制及描述用类
 * </p>
 * <p>
 * Copyright: Copyright (c) 2008
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 *
 * @author chenpeng
 * @email:[email]ceponline@yahoo.com.cn[/email]
 * @version 0.1
 */
public class Map ...{

// 在以前的blog文章中我介绍过,游戏开发中通常以数组描述地图
    // 此处1描绘为一个障碍物,0描绘为一个可通行空间
    final static private int[][] map = ...{
            ...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
            ...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };

// 地面瓦片的宽度
    final static private int TILE_SIZE = 32;

// 行
    final static private int ROW = 15;

// 列
    final static private int COL = 20;

/** *//**
     * 构造函数
     *
     */
    public Map() ...{
    }

public void draw(Graphics g) ...{
         g.setColor(Color.ORANGE);
        for (int i = 0; i < ROW; i++) ...{
            for (int j = 0; j < COL; j++) ...{
                switch (map[i][j]) ...{
                case 1:
                    g.fillRect(tilesToPixels(j), tilesToPixels(i), TILE_SIZE,
                            TILE_SIZE);
                    break;
                }
            }
        }
    }

/** *//**
     * 换算角色与地板的撞击,并返回Point用以描述新的x,y
     *
     * @param player
     * @param newX
     * @param newY
     * @return
     */
    public Point getTileHit(Role player, double newX, double newY) ...{
        // 取最小的整数但不能小于自身,用以换算坐标
        newX = Math.ceil(newX);
        newY = Math.ceil(newY);

double fromX = Math.min(player.getX(), newX);
        double fromY = Math.min(player.getY(), newY);
        double toX = Math.max(player.getX(), newX);
        double toY = Math.max(player.getY(), newY);

int fromTileX = pixelsToTiles(fromX);
        int fromTileY = pixelsToTiles(fromY);
        int toTileX = pixelsToTiles(toX + Role.WIDTH - 1);
        int toTileY = pixelsToTiles(toY + Role.HEIGHT - 1);

// 返回Point,用以描述x,y坐标点
        for (int x = fromTileX; x <= toTileX; x++) ...{
            for (int y = fromTileY; y <= toTileY; y++) ...{
                if (x < 0 || x >= COL) ...{
                    return new Point(x, y);
                }
                if (y < 0 || y >= ROW) ...{
                    return new Point(x, y);
                }
                if (map[y][x] == 1) ...{
                    return new Point(x, y);
                }
            }
        }

return null;
    }

/** *//**
     * 将Tiles转为Pixels
     *
     * @param pixels
     * @return
     */
    public static int pixelsToTiles(double pixels) ...{
        return (int) Math.floor(pixels / TILE_SIZE);
    }

/** *//**
     * 将Pixels转为Tiles
     *
     * @param pixels
     * @return
     */
    public static int tilesToPixels(int tiles) ...{
        return tiles * TILE_SIZE;
    }
}

Role.java
package org.test.mario;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:角色描述及绘制用类
 * </p>
 * <p>
 * Copyright: Copyright (c) 2008
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 *
 * @author chenpeng
 * @email:[email]ceponline@yahoo.com.cn[/email]
 * @version 0.1
 */
public class Role ...{

// 坐标的x,y
    private double _x;

private double _y;

// 显示的x,_y
    private double _vx;

private double _vy;

// 是否在平地
    private boolean isFlat;

// 自定义的地图描述类
    private Map _map;

// 角色宽
    final static public int WIDTH = 32;

// 角色高
    final static public int HEIGHT = 32;

// 移动速度
    final static public int SPEED = 6;

// 跳越速度
    final static public int JUMP_SPEED = 25;

/** *//**
     * 构造函数,注入初始的角色x,y及map
     *
     * @param _x
     * @param _y
     * @param _map
     */
    public Role(double x, double _y, Map _map) ...{
        this._x = x;
        this._y = _y;
        this._map = _map;
        _vx = 0;
        _vy = 0;
        isFlat = false;
    }

/** *//**
     * 停止动作
     *
     */
    public void stop() ...{
        _vx = 0;
    }

/** *//**
     * 向左
     *
     */
    public void left() ...{
        _vx = -SPEED;
    }

/** *//**
     * 向右
     *
     */
    public void right() ...{
        _vx = SPEED;
    }

/** *//**
     * 跳越动作
     *
     */
    public void jump() ...{
        // 当角色立于平地时
        if (isFlat) ...{
            _vy = -JUMP_SPEED;
            isFlat = false;
        }
    }

/** *//**
     * 变更位置
     *
     */
    public void update() ...{
        // 加入偏差值
        _vy += 1.0;

// 获得新的newX
        double newX = _x + _vx;

// 获得地板x,_y
        Point tile = _map.getTileHit(this, newX, _y);
        // 不存在时则默认为newX
        if (tile == null) ...{
            _x = newX;
        } else ...{
            if (_vx > 0) ...{
                _x = Map.tilesToPixels(tile.x) - WIDTH;
            } else if (_vx < 0) ...{
                _x = Map.tilesToPixels(tile.x + 1);
            }
            _vx = 0;
        }

double newY = _y + _vy;

tile = _map.getTileHit(this, _x, newY);
        if (tile == null) ...{
            _y = newY;
            isFlat = false;
        } else ...{
            if (_vy > 0) ...{
                _y = Map.tilesToPixels(tile.y) - HEIGHT;
                _vy = 0;
                isFlat = true;
            } else if (_vy < 0) ...{
                _y = Map.tilesToPixels(tile.y + 1);
                _vy = 0;
            }
        }
    }

/** *//**
     * 将角色绘制于指定Graphics上
     *
     * @param g
     */
    public void draw(Graphics g) ...{
        // 目前以一个红色方块代替
        g.setColor(Color.RED);
        g.fillRect((int) _x, (int) _y, WIDTH, HEIGHT);
    }

public double getX() ...{
        return _x;
    }

public double getY() ...{
        return _y;
    }

}

启动类:Main.java
package org.test.mario;

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Panel;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import org.loon.framework.game.p_w_picpath.Bitmap;

/** *//**
 * <p>
 * Title: LoonFramework
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * Copyright: Copyright (c) 2008
 * </p>
 * <p>
 * Company: LoonFramework
 * </p>
 *
 * @author chenpeng
 * @email:[email]ceponline@yahoo.com.cn[/email]
 * @version 0.1
 */
public class Main extends Panel implements Runnable, KeyListener ...{

/** *//**
     *
     */
    private static final long serialVersionUID = 1L;

public static final int _WIDTH = 640;

public static final int _HEIGHT = 480;

private Map _map;

private Role _role;

private Thread _sleep;

private Image _screen = null;

private Graphics _graphics = null;

// 方向控制,由于是自然落体所以没有down
    private boolean LEFT;

private boolean RIGHT;

private boolean UP;

public Main() ...{
        setSize(_WIDTH, _HEIGHT);
        setFocusable(true);
        _screen = new Bitmap(_WIDTH, _HEIGHT).getImage();
        _graphics = _screen.getGraphics();
        _map = new Map();
        
        _role = new Role(100, 32, _map);

// 监听窗体
        addKeyListener(this);

// 启动线程
        _sleep = new Thread(this);
        _sleep.start();
    }

/** *//**
     * 运行
     */
    public void run() ...{
        while (true) ...{
            //改变方向
            if (LEFT) ...{
                _role.left();
            } else if (RIGHT) ...{
                _role.right();
            } else ...{
                _role.stop();
            }
            if (UP) ...{
                _role.jump();
            }
            _role.update();
            repaint();
            try ...{
                Thread.sleep(20);
            } catch (InterruptedException e) ...{
                e.printStackTrace();
            }
        }
    }

public void update(Graphics g) ...{
        paint(g);
    }

public void paint(Graphics g) ...{
        _graphics.setColor(Color.BLACK);
        _graphics.fillRect(0, 0, _WIDTH, _HEIGHT);
        _map.draw(_graphics);
        _role.draw(_graphics);
        g.drawImage(_screen, 0, 0, null);
    }

public void keyPressed(KeyEvent e) ...{
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT) ...{
            LEFT = true;
        }
        if (key == KeyEvent.VK_RIGHT) ...{
            RIGHT = true;
        }
        if (key == KeyEvent.VK_UP) ...{
            UP = true;
        }
    }

public void keyReleased(KeyEvent e) ...{
        int key = e.getKeyCode();
        if (key == KeyEvent.VK_LEFT) ...{
            LEFT = false;
        }
        if (key == KeyEvent.VK_RIGHT) ...{
            RIGHT = false;
        }
        if (key == KeyEvent.VK_UP) ...{
            UP = false;
        }
    }

public void keyTyped(KeyEvent e) ...{
    }

public static void main(String[] args) ...{
        Frame frame = new Frame();
        frame.setTitle("Java来做马里奥(1)—让精灵舞动");
        frame.setSize(_WIDTH, _HEIGHT);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.add(new Main());
        frame.setVisible(true);
        frame.addWindowListener(new WindowAdapter() ...{
            public void windowClosing(WindowEvent e) ...{
                System.exit(0);
            }
        });
    }

}

运行效果如下图:

现在开始,我会在blog中逐步构建马里奥中的一关,有关心java pc游戏开发者敬请留意。

转载于:https://blog.51cto.com/cping1982/130043

Java来做马里奥[0]—让精灵再次舞动相关推荐

  1. 【错误记录】Android Studio 编译报错 ( Could not determine java version from ‘11.0.8‘. | 仅做参考 | 没有解决实际问题 )

    文章目录 一.报错信息 二.解决方案 一.报错信息 编译 VirtualApp 报如下错误 : Could not determine java version from '11.0.8'.The p ...

  2. 警惕参数变化,MySQL 8.0 升级避免再次踩坑

    MySQL 8.0新特性专栏目录 <MySQL开发规范>过时了,视图查询性能提升了一万倍 你真的会用EXPLAIN么,SQL性能优化王者晋级之路 索引三剑客之降序索引和不可见索引 千呼万唤 ...

  3. Java编码规范V1.0

     Java编码规范V1.0 1 代码总体原则 1. 清晰第一 清晰性是易于维护.易于重构的程序必需具备的特征.代码首先 是给人读的,其次才给机器用来执行. 目前软件维护期成本占整个生命周期成本的 40 ...

  4. Java能做什么?学完Java可以从事什么工作呢?

    如果你是一个Java初学者,你可能对Java应用在什么地方感到困惑.除了"马里奥""贪吃蛇"等经典游戏,其他领域好像也找不到Java的踪迹,那么Java究竟能做 ...

  5. 自动安装 Java Access Bridge 2.0.2 的批处理脚本

    为了方便自己使用,写了一个自动安装 JAB 的批处理脚本,可以自动判断系统版本,判断默认使用的 java 版本. 下载压缩包后,解压,然后点击install.cmd 即可完成安装. 这个批处理脚本只是 ...

  6. 我的世界1 11java,Editing Java版Alpha v1.0.11

    Anti-spam check. Do not fill this in!{{version nav |title=Alpha v1.0.11 |edition=java |image=Alpha v ...

  7. java多线程做一件事_关于Java的十件事

    java多线程做一件事 那么,您从一开始就一直在使用Java? 还记得那些被称为" Oak"的日子,OO仍然是热门话题,C ++人士认为Java没有机会,Applet还是一件事吗? ...

  8. opengles 2.0 点精灵 多边形偏移等备忘

    opengles 2.0 点精灵 多边形偏移等备忘 opengl es 2.0只支持三种绘画单元:     三角形,直线和点精灵.         GL_TRIANGLES     GL_TRIANG ...

  9. java基础做简单的医院管理系统,附带课程设计说明书

    最近做了一个简单的医院管理系统,查阅资料时发现,网上的很多java语言做的语言管理系统项目过于庞大,代码过于繁杂,不适于初学者更好的理解,下面我把我做的简单的医院管理系统分享给大家.当然了大牛直接略过 ...

  10. 帮做Java_代写Heuristics、代做SAT、代写Java语言设计、代做Java代做Prolog|帮做Haskell程序...

    代写Heuristics.代做SAT.代写Java语言设计.代做Java代做Prolog|帮做Haskell程序Heuristics and OptimizationLab assignment #2 ...

最新文章

  1. 强化学习AI:它菜了,我慌了
  2. 51Nod:1085 背包问题
  3. Java如何打印异常的堆栈?
  4. Java黑皮书课后题第7章:*7.33(文化:中国生肖)使用一个字符串数组存储动物名称来简化程序清单3-9的程序
  5. Android 点击应用外的Url拉起应用
  6. SAP Spartacus url里默认electronics-spa的由来
  7. 论文浅尝 | 融合多层次领域知识的分子图对比学习
  8. 【树叶识别】基于matlab PNN神经网络树叶类别【含Matlab源码 798期】
  9. 教程-Delphi7 自带控件安装对应表
  10. 淘宝客APP源码/社交电商自营商城源码/前端基于Uniapp开发
  11. 全球最大同性交友网站 GitHub 10 岁了!
  12. Xpose模块抓取函数功能学习笔记
  13. Quasar的配置文件说明
  14. 阿里性能调优工具Arthas
  15. 微信公众平台与微信公众平台的区别与联系
  16. Dragonfly 修改数据像素点大小
  17. 以首尾交换的方法交换字符串中以'A'开头和以'N'结尾的单词
  18. 851-40亿个号码如何去重?
  19. 一份小白前端可视化学习指南——附思维导图
  20. 压缩脚本linux shell

热门文章

  1. html插入mv,视屏如何插入html
  2. centos7 默认启动项_如何在centos7中搭建 ISCSI 服务
  3. uniapp 生成二维码长按保存_工程设备巡检如何用二维码管理?
  4. 计算机网络每日一题,【计算机考研】每日一题-数网操-35
  5. Flutter学习 — 使用不同类型的子项创建列表
  6. Linux的Sysfs文件系统简要说明
  7. MyBatis基础入门《十七》动态SQL
  8. XJOI 3281 A * B Problem again 题解
  9. Deep Learning Tutorial - Multilayer perceptron
  10. 课时2 一些默认样式