Java 小游戏《超级马里奥》

文章目录

  • 一、效果展示
  • 二、代码编写
    • 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方法,实现场景、物体的绘制@Overridepublic 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);}@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic 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;}@Overridepublic void keyReleased(KeyEvent e) {if (e.getKeyCode() == 37) {mario.runLeftStop();} else if (e.getKeyCode() == 39) {mario.runRightStop();} else if (e.getKeyCode() == 38) {mario.jumpDown();}}@Overridepublic 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;}@Overridepublic 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://www.rhkb.cn/news/450302.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小马识途海外媒体推广有何优势?

互联网让地球变得像一个村子一样&#xff0c;信息可以瞬间变得人尽皆知&#xff0c;商品和服务也同样习惯了跨国合作。中国不少物美价廉的产品在世界各地都很受欢迎&#xff0c;国内小资群体对国外的服饰和美妆更是偏爱有加。小马识途营销顾问认为&#xff0c;中国品牌不出走国…

“趋势买点”,智能捕捉市场底部的工具指标

“趋势买点”&#xff0c;智能捕捉市场底部的工具指标 分享的这个指标包含副图与主图&#xff0c;不含未来函数&#xff0c;旨在通过分析市场波动找到可靠的买点信号&#xff0c;以便在底部进行抄底操作。 "趋势买点"的副图信号作为判断市场底部的重要依据&#xff0…

只想简单跑个 AI 大模型,却发现并不简单

之前我用 Ollama 在本地跑大语言模型&#xff08;可以参考《AI LLM 利器 Ollama 架构和对话处理流程解析》&#xff09;。这次想再捣鼓点进阶操作&#xff0c;比如 fine-tuning。 我的想法是&#xff1a;既然有现成的大模型&#xff0c;为什么不自己整理些特定领域的数据集&am…

6云图书管理系统-图书展示

1 /src/store中新增userInfo.js&#xff0c;用于保存用户的登录信息 import { defineStore } from "pinia" import { ref } from vueexport const userInfoStore defineStore(userInfo, () > {//1.定义用户信息const info ref({})const isAdmin ref(false)//2…

css 仿微信朋友圈图片自适应九宫格

不好用请移至评论区揍我 原创代码,请勿转载,谢谢! 示例效果 1 ~ 5张图与5 ~ 9张图 代码实现 <view style="

卸载Python

1、查看安装框架位置并删除 Sudo rm -rf /Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8 2、查看应用并删除 在 /Applications/Python 3.x 看是否存在&#xff0c;如果存在并删除。 3、删除软连接 ls -l /usr/bin/py* 或 ls -…

什么是分布式锁?Redis的分布式锁又是什么?

什么是分布式锁&#xff1f; 分布式锁是一种用于解决分布式系统中多节点对共享资源并发访问问题的机制。在分布式系统中&#xff0c;多个服务器实例或服务进程可能同时操作某个共享资源&#xff08;如数据库记录、缓存条目、文件等&#xff09;&#xff0c;导致数据不一致或竞…

千鹿 AI:强大的抠图神器,让你的工作效率飙升99%

宝子们&#xff0c;今天一定要给大家分享一款超厉害的抠图工具 —— 千鹿 AI。千鹿 AI 用起来真的是极其方便&#xff0c;仅仅上传一张图片&#xff0c;短短几秒钟的时间&#xff0c;就能够获得一张边缘超级清晰的抠图成品&#xff0c;实在是令人惊叹。 鹿 AI 的厉害之处有很多…

【Linux系统编程】环境基础开发工具使用

目录 1、Linux软件包管理器yum 1.1 什么是软件包 1.2 安装软件 1.3 查看软件包 1.4 卸载软件 2、Linux编辑器-vim 2.1 vim的概念 2.2 vim的基本操作 2.3 vim的配置 3、Linux编译器-gcc/g 3.1 gcc编译的过程​编辑​编辑​编辑 3.2 详解链接 动态链接 静态链接 4…

二百六十九、Kettle——ClickHouse清洗ODS层原始数据增量导入到DWD层表中

一、目的 清洗ClickHouse的ODS层原始数据&#xff0c;增量导入到DWD层表中 二、实施步骤 2.1 newtime select( select create_time from hurys_jw.dwd_statistics order by create_time desc limit 1) as create_time 2.2 替换NULL值 2.3 clickhouse输入 2.4 字段选择 2.5 …

UDP反射放大攻击防范手册

UDP反射放大攻击是一种极具破坏力的恶意攻击手段。 一、UDP反射放大攻击的原理 UDP反射放大攻击主要利用了UDP协议的特性。攻击者会向互联网上大量的开放UDP服务的服务器发送伪造的请求数据包。这些请求数据包的源IP地址被篡改为目标受害者的IP地址。当服务器收到这些请求后&…

『网络游戏』服务器启动逻辑【16】

新建Visual Studio工程命名为NetGameServer 重命名为ServerStart.cs 创建脚本&#xff1a; 编写脚本&#xff1a;ServerRoot.cs 编写脚本&#xff1a;ServerStart.cs 新建文件夹 调整脚本位置 新建文件夹 新建文件夹网络服务 创建脚本&#xff1a;NetSvc.cs 编写脚本&#xff1…

【word】文章里的表格边框是双杠

日常小伙伴们遇到word里插入的表格&#xff0c;边框是双杠的&#xff0c;直接在边框和底纹里修改边框的样式就可以&#xff0c;但我今天遇到的这个有点特殊&#xff0c;先看看表格在word里的样式是怎么样&#xff0c;然后我们聊聊如何解决。 这个双杠不是边框和底纹的设置原因…

linux线程 | 一点通你的互斥锁 | 同步与互斥

前言&#xff1a;本篇文章主要讲述linux线程的互斥的知识。 讲解流程为先讲解锁的工作原理&#xff0c; 再自己封装一下锁并且使用一下。 做完这些就要输出一堆理论性的东西&#xff0c; 但博主会总结两条结论&#xff01;&#xff01;最后就是讲一下死锁。 那么&#xff0c; 废…

《量子之歌》

第一章&#xff1a;曙光 在2045年的未来&#xff0c;人工智能不再是科幻作品中的虚构&#xff0c;而是成为了日常生活的一部分。在这个时代&#xff0c;深度学习模型已经变得如此庞大和复杂&#xff0c;以至于即使是最快的超级计算机也需要数小时才能完成一次完整的训练。 陈欣…

大华智能云网关注册管理平台 SQL注入漏洞复现(CNVD-2024-38747)

0x01 产品简介 大华智能云网关注册管理平台是一款专为解决社会面视频资源接入问题而设计的高效、便捷的管理工具,平台凭借其高效接入、灵活部署、安全保障、兼容性和便捷管理等特点,成为了解决社会面视频资源接入问题的优选方案。该平台不仅提高了联网效率,降低了联网成本,…

【前端】制作一个自己的网页(4)

刚才我们完成了网页中标题与段落元素的学习。在实际开发时&#xff0c;一个网页通常会包含多个相同元素&#xff0c;比如多个标题与段落。 对于相同标签的元素&#xff0c;我们又该如何区分定位呢&#xff1f; 对多个相同的标签分类 比如右图设置了七个段落元素&#xff0c;它…

DS堆的实际应用(10)

文章目录 前言一、堆排序建堆排序 二、TopK问题原理实战创建一个有一万个数的文件读取文件并将前k个数据创建小堆用剩余的N-K个元素依次与堆顶元素来比较将前k个数据打印出来并关闭文件 测试 三、堆的相关习题总结 前言 学完了堆这个数据结构的概念和特性后&#xff0c;我们来看…

react18中实现简易增删改查useReducer搭配useContext的高级用法

useReducer和useContext前面有单独介绍过&#xff0c;上手不难&#xff0c;现在我们把这两个api结合起来使用&#xff0c;该怎么用&#xff1f;还是结合之前的简易增删改查的demo&#xff0c;熟悉vue的应该可以看出&#xff0c;useReducer类似于vuex&#xff0c;useContext类似…

wxml 模板语法-数据绑定

mustache 语法的应用场景&#xff1a; 动态绑定内容&#xff1a; 动态绑定属性&#xff1a; 三元运算&#xff1a; 算数运算&#xff1a;