当前位置:首页 » 《随便一记》 » 正文

Java 小游戏《超级马里奥》

24 人参与  2024年11月18日 14:02  分类 : 《随便一记》  评论

点击全文阅读


文章目录

一、效果展示二、代码编写1. 素材准备2. 创建窗口类3. 创建常量类4. 创建动作类5. 创建关卡类6. 创建障碍物类7. 创建马里奥类8. 编写程序入口

一、效果展示

在这里插入图片描述

二、代码编写

1. 素材准备

首先创建一个基本的 java 项目,并将本游戏需要用到的图片素材 image 导入。

图片素材如下:
https://pan.baidu.com/s/1db_IcPvPKWKbVPtodPWO5Q?pwd=03kv
提取码:03kv

在这里插入图片描述

2. 创建窗口类

在这里插入图片描述

① Java 内部已经给我们封装了窗口的各种方法,我们只需创建一个窗口类并重写父类的方法,即可使用;
② Alt + Enter 键 → implement methods 可一键补全所有的重写方法;
③ 实现多线程有两种方法,分别是继承 Thread 类和实现 Runnable 接口,这里我们用 Runnable 方法,因为 Java 不支持多继承。

重写 paint 方法,实现场景、物体的绘制,使用多线程无限绘制窗口。

完整代码如下:

package com.zxe.beans;import javax.swing.*;import java.awt.*;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.util.ArrayList;import java.util.List;/** * 窗口类 */public class MyFrame extends JFrame implements KeyListener, Runnable {    //定义一个集合用于所有的关卡    private List<LevelMap> levelMaps = new ArrayList<>();    //定义一个变量,存放当前背景    private LevelMap levelMap = new LevelMap();    //定义变量,记录马里奥    private Mario mario;    //重写paint方法,实现场景、物体的绘制    @Override    public void paint(Graphics g) {        //创建一张图片        Image image = createImage(1045, 500);        //设置图片        Graphics graphics = image.getGraphics();        graphics.drawImage(levelMap.getBg(), 0, 0, 1045, 500, this);        //绘制障碍物        for (Obstacle obstacle : levelMap.getObstacles()){            graphics.drawImage(obstacle.getObstacleImage(), obstacle.getX(), obstacle.getY(), this);        }        //绘制马里奥        graphics.drawImage(mario.getMarioImage(), mario.getX(), mario.getY(), this);        //将图片描绘到当前窗口中        g.drawImage(image, 0, 0, this);    }    @Override    public void keyTyped(KeyEvent e) {    }    @Override    public void keyPressed(KeyEvent e) {        if (e.getKeyCode() == 37) {            mario.runLeft();        } else if (e.getKeyCode() == 39) {            mario.runRight();        } else if (e.getKeyCode() == 38) {            mario.jump();        }    }    public Mario getMario() {        return mario;    }    public void setMario(Mario mario) {        this.mario = mario;    }    @Override    public void keyReleased(KeyEvent e) {        if (e.getKeyCode() == 37) {            mario.runLeftStop();        } else if (e.getKeyCode() == 39) {            mario.runRightStop();        } else if (e.getKeyCode() == 38) {            mario.jumpDown();        }    }    @Override    public void run() {        //无限次绘制马里奥        while (true) {            repaint();            try {                Thread.sleep(50);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }            //判断一下马里奥是否通关            if (mario.getX() > 1040) {                levelMap = levelMaps.get(levelMap.getLevel());                mario.setLevelMap(levelMap);                mario.setX(50);                mario.setY(420);            }        }    }    public List<LevelMap> getLevelMaps() {        return levelMaps;    }    public void setLevelMaps(List<LevelMap> levelMaps) {        this.levelMaps = levelMaps;    }    public LevelMap getLevelMap() {        return levelMap;    }    public void setLevelMap(LevelMap levelMap) {        this.levelMap = levelMap;    }}

3. 创建常量类

小游戏中的各种元素画面其实都是一张张的图片,而这些图片路径的定义都将放在常量类中完成。

package com.zxe.beans;import javax.imageio.ImageIO;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.util.ArrayList;import java.util.List;/** * 常量类 */public class Constant {    //给窗口定义一张图片    public static BufferedImage bg;    //右跳图片    public static BufferedImage jumpR;    //左跳图片    public static BufferedImage jumpL;    //右边站立    public static BufferedImage standR;    //左边站立    public static BufferedImage standL;    //定义一个集合,存放右跑动作    public static List<BufferedImage> runR = new ArrayList<>();    //定义一个集合,存放左跑动作    public static List<BufferedImage> runL = new ArrayList<>();    //为障碍物定义一个集合    public static List<BufferedImage> onstacles = new ArrayList<>();    //定义一个变量,记录文件路径前缀    public static String prefix = "C:\\Users\\Lenovo\\Desktop\\demo\\file\\image\\";    //初始化图片到系统中    public static void initImage() {        try {            //加载图片            bg = ImageIO.read(new File(prefix + "bg2.jpeg"));            jumpR = ImageIO.read(new File(prefix + "mario_jump_r.png"));            jumpL = ImageIO.read(new File(prefix + "mario_jump_l.png"));            standR = ImageIO.read(new File(prefix + "mario_stand_r.png"));            standL = ImageIO.read(new File(prefix + "mario_stand_l.png"));            runR.add(ImageIO.read(new File(prefix + "mario_run_r1.png")));            runR.add(ImageIO.read(new File(prefix + "mario_run_r2.png")));            runL.add(ImageIO.read(new File(prefix + "mario_run_l1.png")));            runL.add(ImageIO.read(new File(prefix + "mario_run_l2.png")));            for (int i = 1; i <= 6; i++) {                onstacles.add(ImageIO.read(new File(prefix + "ob" + i + ".png")));            }        } catch (IOException e) {            e.printStackTrace();            throw new RuntimeException(e);        }    }}

常量用 static 修饰,外部可直接通过类名调用常量,而无需创建对象。

4. 创建动作类

记录玛丽的动作状态,具体的常量名与代码分离,可以降低代码的耦合度,更规范化。

在这里插入图片描述

5. 创建关卡类

每个关卡的障碍物是不一样的,这里需要外部传入关卡号,在关卡类中把不同的障碍物拼成自定义的关卡。

完整代码如下:

package com.zxe.beans;import com.zxe.utils.Constant;import java.awt.image.BufferedImage;import java.util.ArrayList;import java.util.List;/** * 关卡地图类 */public class LevelMap {    //记录当前场景需要的图片    private BufferedImage bg;    //记录当前关卡    private int level;    //创建一个集合,用于存放障碍物    private List<Obstacle> obstacles = new ArrayList<>();    public LevelMap() {    }    public LevelMap(int level) {        this.level = level;        bg = Constant.bg;        if (level == 1) {            //绘制方块            obstacles.add(new Obstacle(100, 370, 0, this));            obstacles.add(new Obstacle(130, 370, 1, this));            obstacles.add(new Obstacle(160, 370, 0, this));            obstacles.add(new Obstacle(190, 370, 1, this));            obstacles.add(new Obstacle(300, 260, 0, this));            obstacles.add(new Obstacle(330, 260, 1, this));            obstacles.add(new Obstacle(360, 260, 1, this));            obstacles.add(new Obstacle(800, 300, 0, this));            obstacles.add(new Obstacle(830, 300, 0, this));            obstacles.add(new Obstacle(860, 300, 1, this));            obstacles.add(new Obstacle(890, 300, 1, this));            //绘制水管            obstacles.add(new Obstacle(420, 420, 4, this));            obstacles.add(new Obstacle(450, 420, 5, this));            obstacles.add(new Obstacle(415, 390, 2, this));            obstacles.add(new Obstacle(435, 390, 2, this));            obstacles.add(new Obstacle(455, 390, 3, this));            obstacles.add(new Obstacle(600, 420, 4, this));            obstacles.add(new Obstacle(630, 420, 5, this));            obstacles.add(new Obstacle(600, 390, 4, this));            obstacles.add(new Obstacle(630, 390, 5, this));            obstacles.add(new Obstacle(595, 360, 2, this));            obstacles.add(new Obstacle(615, 360, 2, this));            obstacles.add(new Obstacle(635, 360, 3, this));        } else if (level == 2) {            int i = 0;            for (int y = 420; y >= 300; y -= 30) {                for (int x = 100; x <= 190 - 30 * i; x += 30) {                    obstacles.add(new Obstacle(x + 30 * i, y, 0, this));                }                for (int x = 300; x <= 390 - 30 * i; x += 30) {                    obstacles.add(new Obstacle(x, y, 0, this));                }                for (int x = 550; x <= 640 - 30 * i; x += 30) {                    obstacles.add(new Obstacle(x + 30 * i, y, 0, this));                }                for (int x = 670; x <= 790 - 30 * i; x += 30) {                    obstacles.add(new Obstacle(x, y, 0, this));                }                i++;            }        }    }    public BufferedImage getBg() {        return bg;    }    public void setBg(BufferedImage bg) {        this.bg = bg;    }    public int getLevel() {        return level;    }    public void setLevel(int level) {        this.level = level;    }    public List<Obstacle> getObstacles() {        return obstacles;    }    public void setObstacles(List<Obstacle> obstacles) {        this.obstacles = obstacles;    }}

6. 创建障碍物类

障碍物的属性包括图片以及横纵坐标。

完整代码如下:

package com.zxe.beans;import com.zxe.utils.Constant;import java.awt.image.BufferedImage;/** * 障碍物类 */public class Obstacle {    //记录障碍物的坐标    private int x;    private int y;    //定义一个变量,记录当前障碍物的图片信息    private BufferedImage obstacleImage;    //定义障碍物类型    private int type;    //定义变量存放当前的背景    private LevelMap bg;    public Obstacle(int x, int y, int type, LevelMap bg) {        this.x = x;        this.y = y;        this.type = type;        this.bg = bg;        //根据障碍物的编号,从常量中的障碍物集合中获取对应的图片        this.obstacleImage = Constant.onstacles.get(type);    }    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 BufferedImage getObstacleImage() {        return obstacleImage;    }    public void setObstacleImage(BufferedImage obstacleImage) {        this.obstacleImage = obstacleImage;    }    public int getType() {        return type;    }    public void setType(int type) {        this.type = type;    }    public LevelMap getBg() {        return bg;    }    public void setBg(LevelMap bg) {        this.bg = bg;    }}

7. 创建马里奥类

马里奥的无限行走动作由多线程实现,定义一个状态量status,用于标记马里奥当前的运动状态,以便进行不同动作的来回切换。

完整代码如下:

package com.zxe.beans;import com.zxe.utils.Action;import com.zxe.utils.Constant;import java.awt.image.BufferedImage;/** * 马里奥类 */public class Mario implements Runnable {    //记录马里奥坐标信息    private  int x;    private int y;    //记录马里奥状态    private String status;    //定义一个变量,记录马里奥当前动作所对应的图片信息    private BufferedImage marioImage;    //定义变量,记录当前的关卡地图,也可以获取障碍物的信息    private LevelMap levelMap = new LevelMap();    //创建线程执行马里奥的动作    private Thread thread;    //定义变量,记录马里奥的移动速度    private int xSpeed;    //定义变量,记录马里奥的跳跃速度    private int ySpeed;    //定义变量,记录马里奥的上升状态    private int up;    public Mario() {}    public Mario(int x, int y) {        this.x = x;        this.y = y;        //默认马里奥的动作是朝右站立        status = Action.STAND_RIGHT;        marioImage = Constant.standR;        thread = new Thread(this);        thread.start();    }    //马里奥向左移动的方法    public void runLeft() {        //判断当前是否为跳跃状态,如果不是,就改变状态        if ( !status.contains("jump") ) {            status = Action.RUN_LEFT;        } else {            status = Action.JUMP_LEFT;        }        xSpeed = -5;    }    //马里奥向右移动的方法    public void runRight() {        //判断当前是否为跳跃状态,如果不是,就改变状态        if ( !status.contains("jump") ) {            status = Action.RUN_RIGHT;        } else {            status = Action.JUMP_RIGHT;        }        xSpeed = 5;    }    public void jump() {        if (status.contains("left")) {            status = Action.JUMP_LEFT;        } else {            status = Action.JUMP_RIGHT;        }            ySpeed = -12;    }    public void jumpDown() {        ySpeed = 12;    }    private void jumpStop() {        if (status.contains("left")) {            status = Action.STAND_LEFT;        } else {            status = Action.STAND_RIGHT;        }        ySpeed = 0;    }    //马里奥向左移动停止的方法    public void runLeftStop() {        if (!status.contains("jump")) {            status = Action.STAND_LEFT;        } else {            status = Action.JUMP_LEFT;        }        xSpeed = 0;    }    //马里奥向右移动停止的方法    public void runRightStop() {        if (!status.contains("jump")) {            status = Action.STAND_RIGHT;        } else {            status = Action.JUMP_RIGHT;        }        xSpeed = 0;    }    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 String getStatus() {        return status;    }    public void setStatus(String status) {        this.status = status;    }    public BufferedImage getMarioImage() {        return marioImage;    }    public void setMarioImage(BufferedImage marioImage) {        this.marioImage = marioImage;    }    public LevelMap getLevelMap() {        return levelMap;    }    public void setLevelMap(LevelMap levelMap) {        this.levelMap = levelMap;    }    public Thread getThread() {        return thread;    }    public void setThread(Thread thread) {        this.thread = thread;    }    public int getxSpeed() {        return xSpeed;    }    public void setxSpeed(int xSpeed) {        this.xSpeed = xSpeed;    }    public int getySpeed() {        return ySpeed;    }    public void setySpeed(int ySpeed) {        this.ySpeed = ySpeed;    }    public int getUp() {        return up;    }    public void setUp(int up) {        this.up = up;    }    @Override    public void run() {        int index = 0;        //控制马里奥无限移动        while (true) {            //判断当前是否移动,xSpeed<0左移动,xSpeed>0右移动            if (xSpeed < 0 || xSpeed > 0) {                x += xSpeed;                if (x < 0) {                    x = 0;                }            }            if (ySpeed < 0 || ySpeed > 0) {                y += ySpeed;                if (y > 420) {                    y = 420;                    jumpStop();                }                if (y < 280) {                    y = 280;                }            }            //判断移动状态,跑步状态图片切换            if (status.contains("run")) {                index = index == 0 ? 1 : 0;            }            //根据马里奥的状态切换不同的图片            if (Action.RUN_LEFT.equals(status)) {                marioImage = Constant.runL.get(index);            }            if (Action.RUN_RIGHT.equals(status)) {                marioImage = Constant.runR.get(index);            }            if (Action.STAND_LEFT.equals(status)) {                marioImage = Constant.standL;            }            if (Action.STAND_RIGHT.equals(status)) {                marioImage = Constant.standR;            }            if (Action.JUMP_LEFT.equals(status)) {                marioImage = Constant.jumpL;            }            if (Action.JUMP_RIGHT.equals(status)) {                marioImage = Constant.jumpR;            }            // 控制线程的速度            try {                Thread.sleep(30);            } catch (InterruptedException e) {                throw new RuntimeException(e);            }        }    }}

8. 编写程序入口

创建游戏窗口,并对窗口的基本属性进行设置,创建三个关卡,并调用 repaint 方法绘制场景。

package com.zxe;import com.zxe.beans.LevelMap;import com.zxe.beans.Mario;import com.zxe.utils.Constant;import com.zxe.beans.MyFrame;import javax.swing.*;public class Main {    public static void main(String[] args) {        //创建窗口对象        MyFrame myFrame = new MyFrame();        //设置窗口大小        myFrame.setSize(1045,500);        //设置窗口居中        myFrame.setLocationRelativeTo(null);        //设置窗口可见性        myFrame.setVisible(true);        //设置窗口关闭程序        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        //设置键盘监听事件        myFrame.addKeyListener(myFrame);        //设置窗口的大小不可改变        myFrame.setResizable(false);        //设置窗口标题        myFrame.setTitle("超级玛丽");        //加载图片        Constant.initImage();        //创建三个关卡地图        for (int i = 1; i <= 3; i++) {            myFrame.getLevelMaps().add(new LevelMap(i));        }        //设置当前关卡地图        myFrame.setLevelMap(myFrame.getLevelMaps().get(0));        //创建马里奥        Mario mario = new Mario(50, 420);        myFrame.setMario(mario);        mario.setLevelMap(myFrame.getLevelMap());        //绘制场景        myFrame.repaint();        Thread thread = new Thread(myFrame);        thread.start();    }}

点击全文阅读


本文链接:http://m.zhangshiyu.com/post/188181.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1