技术选型
平台:Windows开发工具:Intelij IDEA
JDK环境:Java 8
UI界面:基于Swing的桌面编程技术。
绘图技术:Graphics
集合框架
IO流
多线程等
地图配置

地图map.txt文件
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,1,0,0,0,1,1,1,1,0,0,2,2,0,0,0,0,0,0,3
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
读取地图
package cn.tx.util;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* 地图配置类
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class GameMap {
//数据容器
public List<String> list = new ArrayList<>();
// 二维数组元素又是一个一维数组:行列矩阵
public int[][] map = null;
// 单元测试:验证Map类的readMap()方法确实把地图配置文件map.txt
// 加载成了二维数组
@Test
public void testResult() throws Exception {
int[][] result = readMap();
// 二维数组的内容输出,看一下是否是地图的配置信息
for(int i = 0 ; i < result.length ; i++ ){
for(int j = 0 ; j < result[i].length ; j++) {
System.out.print(result[i][j]+" ");
}
System.out.println();
}
}
public int[][] readMap() throws Exception {
// 构造文件输入流
FileInputStream fis = new FileInputStream("map.txt");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
//直接读取一行数据
String value = br.readLine();
while (value != null) {
//将读取到的一行数据加入到容器中
list.add(value);
value = br.readLine();
}
br.close();
//得到多少行多少列
int row = list.size();
int cloum = 0;
for (int i = 0; i < 1; i++) {
String str = list.get(i);
String[] values = str.split(",");
cloum = values.length;
}
map = new int[row][cloum];
//将读到的字符创转换成整数,并赋值给二位数组map
for (int i = 0; i < list.size(); i++) {
String str = list.get(i);
String[] values = str.split(",");
for (int j = 0; j < values.length; j++) {
map[i][j] = Integer.parseInt(values[j]);
}
}
return map;
}
}
定义角色
超级玛丽中的角色包括了,水管,怪物,砖头等。并显示到主界面。
障碍物抽象父类Enemy
package cn.tx.role;
import java.awt.Image;
/**
敌人抽象类
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public abstract class Enemy {
//坐标位置
public int x,y;
//宽高
public int width,height;
//图片
public Image img;
public Enemy(int x, int y, int width, int height, Image img) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.img=img;
}
}
水管类
package cn.tx.role;
import java.awt.Image;
/**
* 水管类
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class Pipe extends Enemy {
public Pipe(int x, int y, int width, int height, Image img) {
super(x, y, width, height, img);
}
}
砖头类
package cn.tx.role;
import java.awt.Image;
/**
* 砖
*
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class Brick extends Enemy {
public Brick(int x, int y, int width, int height, Image img) {
super(x, y, width, height, img);
}
}
金币类
package cn.tx.role;
import java.awt.Image;
/**
金币类
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class Coin extends Enemy {
public Coin(int x, int y, int width, int height, Image img) {
super(x, y, width, height, img);
}
}
子弹类
package cn.tx.role;
/**
子弹类
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class Boom {
//子弹的坐标,大小,速度
public int x,y;
public int width;
public int speed=1;
public Boom(int x, int y, int width) {
super();
this.x = x;
this.y = y;
this.width = width;
}
}
马里奥类
package cn.tx.mario;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
import cn.tx.ui.GameFrame;
import cn.tx.role.Enemy;
/**
* 玛丽
*
* @author 任亮
* @company 拓薪教育
* @QQ:206229531
*/
public class Mario extends Thread {
//窗体对象
public GameFrame gf;
//是否跳起
public boolean jumpFlag = true;
//马里奥的坐标,原点为左上角,所以y周越小玛丽越高
public int x = 50, y = 358;
//马里奥的速度
public int xspeed = 5, yspeed = 1;
//马里奥的宽高
public int width = 30, height = 32;
//马里奥的图片
public Image img = new ImageIcon("image/mari1.png").getImage();
//键盘上的上下左右是否被按下
public boolean left = false, right = false, down = false, up = false;
public String Dir_Up = "Up", Dir_Left = "Left", Dir_Right = "Right", Dir_Down = "Down";
public Mario(GameFrame gf) {
this.gf = gf;
this.Gravity();
}
// 玛丽飞翔的逻辑 ;移动的逻辑都在这里。
public void run() {
while (true) {
//向左走
if (left) {
//碰撞到了
if (hit(Dir_Left)) {
this.xspeed = 0;
}
//没有撞击到障碍物
if (this.x >= 0) {
//马里奥的位置的更新
this.x -= this.xspeed;
//改变马里奥的图片
this.img = new ImageIcon("image/mari_left.gif").getImage();
}
this.xspeed = 5;
}
//向右走
if (right) {
// 右边碰撞物检测应该是往右走的时候检测
// 进行碰撞检测:至少主角(玛丽,碰撞物)
if (hit(Dir_Right)) {
this.xspeed = 0;
}
//任人物向右移动
if (this.x < 400) {
this.x += this.xspeed;
this.img = new ImageIcon("image/mari_right.gif").getImage();
}
//玛丽奥x轴的坐标大于400的时候发生什么
if (this.x >= 400) {
//背景向左移动
gf.bg.x -= this.xspeed;
//障碍物项左移动
for (int i = 0; i < gf.eneryList.size(); i++) {
Enemy enery = gf.eneryList.get(i);
enery.x -= this.xspeed;
}
//图标的变化
this.img = new ImageIcon("image/mari_right.gif").getImage();
}
this.xspeed = 5;
}
//向上跳
if (up) {
if (jumpFlag && !isGravity) {
jumpFlag = false;
new Thread() {
public void run() {
jump();
jumpFlag = true;
}
}.start();
}
}
try {
this.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//向上跳的函数
public void jump() {
//定义高度值
int jumpHeigh = 0;
for (int i = 0; i < 150; i++) {
//玛丽的y轴位置减去玛丽的y轴速度
gf.mario.y -= this.yspeed;
//玛丽的高度递增
jumpHeigh++;
//如果撞到障碍物跳出
if (hit(Dir_Up)) {
break;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//玛丽跳起来后下落
for (int i = 0; i < jumpHeigh; i++) {
//玛丽的y轴高度加上y轴速度
gf.mario.y += this.yspeed;
//如果撞到下面障碍则停止
if (hit(Dir_Down)) {
this.yspeed = 0;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.yspeed = 1;//还原速度
}
//检测碰撞
public boolean hit(String dir) {
// Swing技术中,人家已经提供了!!
Rectangle myrect = new Rectangle(this.x, this.y, this.width, this.height);
Rectangle rect = null;
for (int i = 0; i < gf.eneryList.size(); i++) {
Enemy enery = gf.eneryList.get(i);
if (dir.equals("Left")) {
rect = new Rectangle(enery.x + 2, enery.y, enery.width, enery.height);
} else if (dir.equals("Right")) {
// 右侧碰撞物检测。
rect = new Rectangle(enery.x - 2, enery.y, enery.width, enery.height);
} else if (dir.equals("Up")) {
rect = new Rectangle(enery.x, enery.y + 1, enery.width, enery.height);
} else if (dir.equals("Down")) {
rect = new Rectangle(enery.x, enery.y - 2, enery.width, enery.height);
}
//碰撞检测
if (myrect.intersects(rect)) {
return true;
}
}
return false;
}
//检查是否贴地
public boolean isGravity = false;
// 重力线程!
public void Gravity() {
new Thread() {
public void run() {
while (true) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
//如果没有跳起退出
if (!jumpFlag) {
break;
}
//如果撞到下面障碍跳出
if (hit(Dir_Down)) {
break;
}
//大于385 在地面之下
if (y >= 358) {
isGravity = false;
} else {
//在地面上
isGravity = true;
//y是马里奥的纵向的位置
y += yspeed;
}
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}.start();
}
}
窗体
package cn.tx.ui;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import cn.tx.mario.Mario;
import cn.tx.role.*;
import cn.tx.util.GameMap;
import cn.tx.util.MusicUtil;
/**
主体窗口界面:展示角色。
*/
public class GameFrame extends JFrame{
// 超级玛丽:界面需要一个超级玛丽的。
public Mario mario;
// 分别定义:水管,金币和砖块
public Enemy pipe ,coin , brick;
//背景图片
public BackgroundImage bg ;
//定义一个集合容器装敌人对象
public ArrayList<Enemy> eneryList = new ArrayList<Enemy>();
//定义一个集合容器装子弹
public ArrayList<Boom> boomList = new ArrayList<Boom>();
//子弹的速度
public int bspeed=0;
//地图数据,制定规则,是1画砖头,是2画金币,是3画水管
public int[][] map = null;
//对象代码块: 当前类的创建的时候执行
{
// 实例代码块中初始化地图资源的数据
GameMap mp = new GameMap();
map = mp.readMap();
}
//构造函数里面初始化背景图片和马里奥对象
public GameFrame() throws Exception {
// 创建背景图片
bg = new BackgroundImage();
//初始化窗体相关属性信息数据
// this代表了当前主界面对象。
this.setSize(800,450);
this.setTitle("超级玛丽");
this.setResizable(true);
// 居中展示窗口
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
// 创建玛丽对象
mario = new Mario(this);
// 读取地图,并配置地图
for (int i = 0; i < map.length; i++) {
for (int j = 0; j < map[0].length; j++) {
//读取到的是1,画砖头
if(map[i][j]==1){
// x
brick = new Brick(j*30,i*30,30,30,new ImageIcon("image/brick.png").getImage());
eneryList.add(brick);
}
//读到2画金币
if(map[i][j]==2){
coin = new Coin(j*30,i*30,30,30,new ImageIcon("image/coin_brick.png").getImage());
eneryList.add(coin);
}
//读到3画水管
if(map[i][j]==3){
pipe = new Pipe(j*30,i*30,60,120,new ImageIcon("image/pipe.png").getImage());
eneryList.add(pipe);
}
}
}
mario.start();
//开启一个线程负责界面的窗体重绘线程
new Thread(){
public void run(){
while(true){
//重绘窗体
repaint(); // 自动触发当前窗口中的paint方法
//检查子弹是否出界
//checkBoom();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
//设置背景音乐
new Thread(new Runnable() {
@Override
public void run() {
MusicUtil.playBackground();
}
}).start();
}
/**
* 画窗体
* @param g
*/
@Override
public void paint(Graphics g) {
//利用双缓冲画背景图片和马里奥
BufferedImage bi =(BufferedImage)this.createImage(this.getSize().width,this.getSize().height);
//创建画图对象
Graphics big = bi.getGraphics();
//画背景
big.drawImage(bg.img, bg.x, bg.y, null);
// 开始绘制界面上的敌人。
for (int i = 0; i < eneryList.size(); i++) {
Enemy e = eneryList.get(i);
//绘制敌人
big.drawImage(e.img, e.x, e.y, e.width, e.height,null);
}
//画子弹
for (int i = 0; i < boomList.size(); i++) {
Boom b =boomList.get(i);
Color c =big.getColor();
big.setColor(Color.red);
big.fillOval(b.x+=b.speed, b.y, b.width, b.width);
big.setColor(c);
}
//画人物 玛丽自己
big.drawImage(mario.img, mario.x, mario.y, mario.width, mario.height,null);
g.drawImage(bi,0,0,null);
}
//检查子弹是否出界,出界则从容器中移除,不移除的话,内存会泄漏
public void checkBoom(){
for (int i = 0; i < boomList.size(); i++) {
Boom b = boomList.get(i);
if(b.x<0 || b.x>800){
boomList.remove(i);
}
}
}
}
键盘事件监听
package cn.tx.util;
import cn.tx.role.Boom;
import cn.tx.ui.GameFrame;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
//键盘按下监听类
public class KeyListener extends KeyAdapter {
// 接收到了当前主界面:游戏界面
public GameFrame gf;
public KeyListener(GameFrame gf) {
this.gf = gf;
}
//键盘监听
@Override
public void keyPressed(KeyEvent e) {
//code是按下键盘的键对应的值
int code = e.getKeyCode();
switch (code) {
//向右走
case 39:
gf.mario.right = true; // 信号位
break;
//向左走
case 37:
gf.mario.left = true;
break;
case 66:
addBoom();
break;
//向上跳
case 38:
gf.mario.up = true;
break;
}
}
//添加子弹
public void addBoom() {
Boom b = new Boom(gf.mario.x, gf.mario.y + 5, 10);
if (gf.mario.left) b.speed = -2;
if (gf.mario.right) b.speed = 2;
gf.boomList.add(b);
}
//键盘释放监听
@Override
public void keyReleased(KeyEvent e) {
int code = e.getKeyCode();
//释放右键
if (code == 39) {
gf.mario.right = false;
gf.mario.img = new ImageIcon("image/mari1.png").getImage();
}
//释放左键
if (code == 37) {
gf.mario.left = false;
gf.mario.img = new ImageIcon("image/mari_left1.png").getImage();
}
//释放上键
if (code == 38) {
gf.mario.up = false;
}
}
}
主方法和事件绑定
import cn.tx.ui.GameFrame;
import cn.tx.util.KeyListener;
/**
超级玛丽启动类。
*/
public class Run {
//主函数,程序入口
public static void main(String[] args) throws Exception {
GameFrame gf = new GameFrame();
// 创建监听器对象
KeyListener kl = new KeyListener(gf);
// 给窗体添加键盘监听器
gf.addKeyListener(kl);
}
}
完整源码和讲解QQ加群:685168267
版权声明:本文为renlianggee原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。
