Java 小游戏《超级马里奥》

栈老师不回家 2024-10-23 16:05:01 阅读 87

文章目录

一、效果展示二、代码编写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 方法,实现场景、物体的绘制,使用多线程无限绘制窗口。

完整代码如下:

<code>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. 创建关卡类

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

完整代码如下:

<code>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();

}

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。