0


Java小游戏练习---超级玛丽代码实现

B站教学视频:

01_超级玛丽_创建窗口_哔哩哔哩_bilibili

素材提取:

【超级会员V2】我通过百度网盘分享的文件:Java游戏项目…
链接:百度网盘 请输入提取码
提取码:k6j1
复制这段内容打开「百度网盘APP 即可获取」
百度网盘 请输入提取码
百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间

百度网盘 请输入提取码

百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间

游戏构造思路:

首先需要创建一个窗口,游戏需要在该窗口中运行,其次我们需要添加游戏中要用到的图片,将其添加到一个专属包中,并在类中实现。(有些物品一样或只用一次,如旗杆,城堡等,普通添加即可;有些如:玛丽,蘑菇敌人等,需要移动以及其他动作,故需要创建列表方便实现不同图片来回使用)。此外,我们还需要给游戏添加背景图,设定游戏的关卡数,以及不同关卡之间的切换。

在以上基础步骤之后,就需要在场景中添加游戏中最关键的两个角色:玛丽和敌人,这两个角色需要运动以及一系列其他操作,因此我们需要分两个类来写,并通过线程来调用。

以下代码中额外添加了一个休眠用来进行判定,在运行之后如果在较长时间(5秒左右)后按下空格键,则敌人类将不会再运动,同时,若在运行开始后,立即按下空格,敌人类会在经历一小段休眠后再进行运动(因为添加了休眠,故经过一小段休眠后才会进行判断)

以上休眠的添加是因为在添加暂停功能时本人无法实现暂停功能而写的一个妥协方案,欢迎各位大佬在评论区提出有关添加暂停功能的方法

代码实现:

MyFrame类:

  1. package com.sxt;
  2. import javazoom.jl.decoder.JavaLayerException;
  3. import javax.swing.*;
  4. import java.awt.*;
  5. import java.awt.event.KeyEvent;
  6. import java.awt.event.KeyListener;
  7. import java.io.FileNotFoundException;
  8. import java.util.ArrayList;
  9. import java.util.List;
  10. import static java.lang.Thread.sleep;
  11. //创建窗口需要继承JFrame类,
  12. //添加键盘监听,需要该类实现KeyListener接口,并重写其中抽象方法
  13. public class MyFrame extends JFrame implements KeyListener,Runnable {
  14. //用于储存所有的背景,定义一个列表用于存储背景
  15. private List<BackGround> allBg = new ArrayList<>();
  16. //用于储存当前背景 , 用于记录当前场景
  17. private BackGround nowBg = new BackGround();
  18. //用于双缓存,创建了一个变量
  19. private Image offScreenImage = null;
  20. //马里奥对象
  21. private Mario mario = new Mario();
  22. //定义线程对象用于实现马里奥的运动
  23. private Thread thread = new Thread(this);
  24. static boolean isStart = false;
  25. //创建该类的空参构造
  26. public MyFrame() {
  27. //设置窗口大小
  28. this.setSize(800, 600);
  29. //设置窗口居中显示
  30. this.setLocationRelativeTo(null);
  31. //设置窗口可见性
  32. this.setVisible(true);
  33. //设置窗口关闭键
  34. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  35. //设置窗口大小不可变
  36. this.setResizable(false);
  37. //给窗口对象添加键盘监听
  38. this.addKeyListener(this);
  39. //设置窗口名称
  40. this.setTitle("超级马里奥");
  41. //初始化图片
  42. StaticValue.init();
  43. //初始化马里奥
  44. mario = new Mario(10, 355);
  45. //创建全部的场景,一共有三个场景,因此需要用for循环来创建
  46. for (int i = 1; i <= 3; i++) {
  47. //要传入两个参数:当前关卡数i和是否为最后一关的布尔变量
  48. //要用三目运算符进行判断,判断这时的i是否是第三关
  49. allBg.add(new BackGround(i, i == 3 ? true : false));
  50. }
  51. //将第一个背景设置为当前场景
  52. nowBg = allBg.get(0);
  53. mario.setBackGround(nowBg);
  54. //绘制图像
  55. repaint();
  56. //启动线程并调用start方法
  57. thread.start();//绘制图像开始时同步播放音乐
  58. try {
  59. new Music();
  60. } catch (FileNotFoundException e) {
  61. e.printStackTrace();
  62. } catch (JavaLayerException e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. //重写一下paint方法
  67. @Override
  68. public void paint(Graphics g) {
  69. //首先要判断,判断变量是否为null
  70. if (offScreenImage == null) {
  71. offScreenImage = createImage(800, 600);
  72. }
  73. //定义Graphics(画笔)对象
  74. Graphics graphics = offScreenImage.getGraphics();
  75. //调用graphics的fillRect方法对我们的图像进行填充
  76. graphics.fillRect(0, 0, 800, 600);
  77. //绘制背景,调用graphics的drawImage方法,将当前背景图像进行绘画
  78. //此时是将图像绘制到了缓冲区上
  79. graphics.drawImage(nowBg.getBgImage(), 0, 0, this);
  80. //绘制敌人
  81. for (Enemy e : nowBg.getEnemyList()) {
  82. graphics.drawImage(e.getShow(), e.getX(), e.getY(), this);
  83. }
  84. //绘制障碍物
  85. for (Obstacle ob : nowBg.getObstacleList()) {
  86. graphics.drawImage(ob.getShow(), ob.getX(), ob.getY(), this);
  87. }
  88. //绘制城堡
  89. graphics.drawImage(nowBg.getTower(), 620, 270, this);
  90. //绘制旗杆
  91. graphics.drawImage(nowBg.getGan(), 500, 220, this);
  92. //绘制马里奥
  93. graphics.drawImage(mario.getShow(), mario.getX(), mario.getY(), this);
  94. //添加分数
  95. Color c = graphics.getColor();
  96. graphics.setColor(Color.BLACK);
  97. graphics.setFont(new Font("黑体", Font.BOLD, 25));
  98. graphics.drawString("当前分数为:" + mario.getScore(), 300, 100);
  99. if (isStart == false) {
  100. graphics.setFont(new Font("宋体", Font.BOLD, 25));
  101. graphics.drawString("按下空格开始游戏", 270, 160);
  102. }
  103. //还原画笔颜色
  104. graphics.setColor(c);
  105. //接下来将缓冲区的图片绘制到窗口中,调用g.drawImage方法
  106. g.drawImage(offScreenImage, 0, 0, this);
  107. }
  108. public static void main(String[] args) {
  109. MyFrame myFrame = new MyFrame();
  110. }
  111. @Override
  112. public void keyTyped(KeyEvent e) {
  113. }
  114. //当键盘按下按键时调用
  115. @Override
  116. public void keyPressed(KeyEvent e) {
  117. if (isStart == false) {
  118. if (e.getKeyCode() == 32) {
  119. isStart = true;
  120. }
  121. } else {
  122. //向右移动
  123. if (e.getKeyCode() == 39) {
  124. mario.rightMove();
  125. }
  126. //向左移动
  127. if (e.getKeyCode() == 37) {
  128. mario.leftMove();
  129. }
  130. //跳跃
  131. if (e.getKeyCode() == 38) {
  132. mario.jump();
  133. }
  134. }
  135. }
  136. //当键盘松开按键时调用
  137. @Override
  138. public void keyReleased (KeyEvent e) {
  139. //向左停止
  140. if (isStart == true) {
  141. if (e.getKeyCode() == 37) {
  142. mario.leftStop();
  143. }
  144. //向右停止
  145. if (e.getKeyCode() == 39) {
  146. mario.rightStop();
  147. //接下来让MyFrame类来实现Runnable接口并且重写抽象方法
  148. }
  149. }
  150. }
  151. @Override
  152. public void run () {
  153. //写一个死循环,在循环里调用repaint()方法,用于重新绘制我们的图像
  154. while (true) {
  155. repaint();
  156. //让线程休眠
  157. try {
  158. sleep(50);
  159. //判断此时马里奥是否移动到了最右端,是的话则要进行场景的切换
  160. //当前窗口大小为800,马里奥宽度为25
  161. if (mario.getX() >= 775) {
  162. //满足就要切换当前场景,在这里可以调用now的sort,他表示第几关,索引为1和2
  163. nowBg = allBg.get(nowBg.getSort());
  164. //将当前场景对象传递给马里奥类
  165. mario.setBackGround(nowBg);
  166. mario.setX(10);
  167. mario.setY(355);
  168. }
  169. //判断马里奥是否死亡
  170. if (mario.isDeath()) {
  171. JOptionPane.showMessageDialog(this, "马里奥死亡,游戏失败");
  172. System.exit(0);
  173. }
  174. //判断游戏是否结束,即判断mario的isOK是否为true,若true则游戏结束
  175. if (mario.isOK()) {
  176. JOptionPane.showMessageDialog(this, "恭喜你!通关了");
  177. System.exit(0);
  178. }
  179. } catch (InterruptedException e) {
  180. e.printStackTrace();
  181. }
  182. }
  183. }
  184. }

BackGround类:

  1. package com.sxt;
  2. import java.awt.image.BufferedImage;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. public class BackGround {
  6. //当前场景要实现的图像,该对象用于显示当前场景的图片,赋初值为null
  7. private BufferedImage bgImage=null; //在bgImage上设置getter方法
  8. //记录当前是第几个场景,储存当前是第几关
  9. private int sort;
  10. //判断是否是最后一个场景
  11. private boolean flag;
  12. //创建一个列表用于存放所有的障碍物
  13. private List<Obstacle>obstacleList=new ArrayList<>();
  14. //用于存放所有敌人
  15. private List<Enemy>enemyList=new ArrayList<>();
  16. //用于显示旗杆
  17. private BufferedImage gan=null;
  18. //用于显示城堡
  19. private BufferedImage tower=null;
  20. //判断马里奥是否到达旗杆位置
  21. private boolean isReach=false;
  22. //判断旗子是否落地
  23. private boolean isBase=false;
  24. //创建空参构造
  25. public BackGround(){
  26. }
  27. public BackGround(int sort,boolean flag){
  28. this.sort=sort;
  29. this.flag=flag;
  30. if (flag){
  31. bgImage=StaticValue.bg2; //旗帜——最后一关背景
  32. }else{
  33. bgImage=StaticValue.bg; //第一关和第二关背景
  34. //下一步生成getter方法
  35. }
  36. //判断是否为第一关
  37. if (sort==1){
  38. //绘制第一关的地面,上地面type=1,下地面type=2,
  39. // 窗口横坐标为800,一个地面为30
  40. for (int i=0;i<27;i++){
  41. obstacleList.add(new Obstacle(i*30,420,1,this));
  42. }
  43. for (int j=0;j<=120;j+=30){
  44. for (int i=0;i<27;i++){
  45. obstacleList.add(new Obstacle(i*30,570-j,2,this));
  46. }
  47. }
  48. //绘制砖块A
  49. for (int i=120;i<=150;i+=30){
  50. obstacleList.add(new Obstacle(i,300,7,this));
  51. //括号里是:横坐标,纵坐标,类型,this这个背景。
  52. }
  53. //绘制砖块B~~F
  54. for(int i=300;i<=570;i+=30){
  55. if (i==360||i==390||i==480||i==510||i==540) {
  56. obstacleList.add(new Obstacle(i,300,7,this));
  57. }else {
  58. obstacleList.add(new Obstacle(i,300,0,this));
  59. }
  60. }
  61. //绘制砖块G
  62. for(int i=420;i<=450;i+=30){
  63. obstacleList.add(new Obstacle(i,240,7,this));
  64. }
  65. //绘制水管
  66. for (int i=360;i<=600;i+=25){
  67. if (i==360){
  68. obstacleList.add(new Obstacle(620,i,3,this));
  69. obstacleList.add(new Obstacle(645,i,4,this));
  70. }else{
  71. obstacleList.add(new Obstacle(620,i,5,this));
  72. obstacleList.add(new Obstacle(645,i,6,this));
  73. //接下来实现Obstacle列表的getter方法
  74. }
  75. }
  76. //绘制第一关的蘑菇敌人
  77. enemyList.add(new Enemy(580,385,true,1,this));
  78. enemyList.add(new Enemy(300,385,true,1,this));
  79. enemyList.add(new Enemy(100,385,true,1,this));
  80. enemyList.add(new Enemy(400,385,true,1,this));
  81. enemyList.add(new Enemy(400,265,true,1,this));
  82. //绘制第一关的食人花敌人
  83. enemyList.add(new Enemy(635,420,true,2,328,428,this));
  84. }
  85. //判断是否为第二关
  86. if(sort==2){
  87. //直接复制第一关的,三关的地砖一样
  88. //绘制第二关的地面,上地面type=1,下地面type=2,
  89. // 窗口横坐标为800,一个地面为30
  90. for (int i=0;i<27;i++){
  91. obstacleList.add(new Obstacle(i*30,420,1,this));
  92. }
  93. for (int j=0;j<=120;j+=30){
  94. for (int i=0;i<27;i++){
  95. obstacleList.add(new Obstacle(i*30,570-j,2,this));
  96. }
  97. }
  98. //绘制第一个水管,复制第一关的过来改一下坐标即可
  99. for (int i=360;i<=600;i+=25) {
  100. if (i == 360) {
  101. obstacleList.add(new Obstacle(60, i, 3, this));
  102. obstacleList.add(new Obstacle(85, i, 4, this));
  103. } else {
  104. obstacleList.add(new Obstacle(60, i, 5, this));
  105. obstacleList.add(new Obstacle(85, i, 6, this));
  106. }
  107. }
  108. //第二根水管
  109. for (int i=330;i<=600;i+=25) {
  110. if (i == 330) {
  111. obstacleList.add(new Obstacle(620, i, 3, this));
  112. obstacleList.add(new Obstacle(645, i, 4, this));
  113. } else {
  114. obstacleList.add(new Obstacle(620, i, 5, this));
  115. obstacleList.add(new Obstacle(645, i, 6, this));
  116. }
  117. }
  118. //绘制砖块C
  119. obstacleList.add(new Obstacle(300,330,0,this));
  120. //绘制砖块B,E,G
  121. for (int i=270;i<=330;i+=30){
  122. if (i==270||i==330){
  123. obstacleList.add(new Obstacle(i,360,0,this));
  124. }else{
  125. obstacleList.add(new Obstacle(i,360,7,this));
  126. }
  127. }
  128. //绘制砖块A,D,F,H,I
  129. for (int i=240;i<=360;i+=30){
  130. if (i==240||i==360){
  131. obstacleList.add(new Obstacle(i,390,0,this));
  132. }else{
  133. obstacleList.add(new Obstacle(i,390,7,this));
  134. }
  135. }
  136. //绘制妨碍1砖块
  137. obstacleList.add(new Obstacle(240,300,0,this));
  138. //绘制空1~4砖块
  139. for (int i=360;i<=540;i+=60){
  140. obstacleList.add(new Obstacle(i,270,7,this));
  141. }
  142. //绘制第二关的第一个蘑菇敌人
  143. enemyList.add(new Enemy(200,385,true,1,this));
  144. //绘制第二个蘑菇敌人
  145. enemyList.add(new Enemy(500,385,true,1,this));
  146. //第三个
  147. enemyList.add(new Enemy(400,205,true,1,this));
  148. //绘制第二关第一个食人花敌人
  149. enemyList.add(new Enemy(75,420,true,2,328,418,this));
  150. //绘制第二个食人花敌人
  151. enemyList.add(new Enemy(635,420,true,2,298,388,this));
  152. }
  153. //判断是否为第三关
  154. if (sort==3) {
  155. //绘制地面
  156. //绘制第三关的地面,上地面type=1,下地面type=2,
  157. // 窗口横坐标为800,一个地面为30
  158. for (int i = 0; i < 27; i++) {
  159. obstacleList.add(new Obstacle(i * 30, 420, 1, this));
  160. }
  161. for (int j = 0; j <= 120; j += 30) {
  162. for (int i = 0; i < 27; i++) {
  163. obstacleList.add(new Obstacle(i * 30, 570 - j, 2, this));
  164. }
  165. }
  166. //绘制第三个背景的A~O砖块
  167. int temp=290;
  168. for (int i=390;i>=270;i-=30){
  169. for (int j=temp;j<=410;j+=30){
  170. obstacleList.add(new Obstacle(j,i,7,this));
  171. }
  172. temp+=30;
  173. }
  174. //绘制第三个背景的P~R砖块
  175. temp=60;
  176. for (int i=390;i>=360;i-=30){
  177. for (int j=temp;j<=90;j+=30){
  178. obstacleList.add(new Obstacle(j,i,7,this));
  179. }
  180. temp+=30;
  181. }
  182. //绘制旗杆
  183. gan=StaticValue.gan;
  184. //绘制城堡
  185. tower=StaticValue.tower;
  186. //添加旗子到旗杆上
  187. obstacleList.add(new Obstacle(515,220,8,this));
  188. //接下来生成旗杆和城堡的getter方法
  189. //绘制第三关蘑菇敌人
  190. enemyList.add(new Enemy(150,385,true,1,this));
  191. }
  192. }
  193. public BufferedImage getBgImage() {
  194. return bgImage;
  195. }
  196. public int getSort() {
  197. return sort;
  198. }
  199. public boolean isFlag() {
  200. return flag;
  201. }
  202. public List<Obstacle> getObstacleList() {
  203. return obstacleList;
  204. }
  205. public BufferedImage getGan() {
  206. return gan;
  207. }
  208. public BufferedImage getTower() {
  209. return tower;
  210. }
  211. public boolean isReach() {
  212. return isReach;
  213. }
  214. public void setReach(boolean reach) {
  215. isReach = reach;
  216. }
  217. public boolean isBase() {
  218. return isBase;
  219. }
  220. public void setBase(boolean base) {
  221. isBase = base;
  222. }
  223. public List<Enemy> getEnemyList() {
  224. return enemyList;
  225. }
  226. }

加载图片的StaticValue类:

  1. package com.sxt;
  2. import javax.imageio.ImageIO;
  3. import java.awt.image.BufferedImage;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.nio.Buffer;
  7. import java.util.ArrayList;
  8. import java.util.List;
  9. //常量类
  10. public class StaticValue {
  11. //定义需要用到的变量
  12. //背景
  13. public static BufferedImage bg=null; //前两个场景
  14. public static BufferedImage bg2=null; //第三关场景
  15. //马里奥向左跳跃
  16. public static BufferedImage jump_L=null;
  17. //马里奥向右跳跃
  18. public static BufferedImage Jump_R=null;
  19. //马里奥向左站立
  20. public static BufferedImage stand_L=null;
  21. //马里奥向右站立
  22. public static BufferedImage stand_R=null;
  23. //城堡
  24. public static BufferedImage tower=null;
  25. //旗杆
  26. public static BufferedImage gan=null;
  27. //障碍物,较多,需要列表
  28. public static List<BufferedImage> obstacle=new ArrayList<>();
  29. //马里奥向左跑
  30. public static List<BufferedImage> run_L=new ArrayList<>();
  31. //马里奥向右跑
  32. public static List<BufferedImage> run_R=new ArrayList<>();
  33. //蘑菇敌人
  34. public static List<BufferedImage> mogu=new ArrayList<>();
  35. //食人花敌人
  36. public static List<BufferedImage> flower=new ArrayList<>();
  37. //获取路径, 定义path变量,除了照片的名字以外前缀一直都一样,因此定义出来路径方便调用
  38. public static String path=System.getProperty("user.dir")+"/src/images/";
  39. //初始化方法
  40. public static void init(){
  41. //利用ImageIO流的read方法,注意在read处捕获异常all+enter
  42. try {
  43. //加载背景图片
  44. bg= ImageIO.read(new File(path+"bg.png"));
  45. bg2=ImageIO.read(new File(path+"bg2.png"));
  46. //加载玛丽向左站立
  47. stand_L=ImageIO.read(new File(path+"stand_L.png"));
  48. //加载玛丽向右站立
  49. stand_R=ImageIO.read(new File(path+"stand_R.png"));
  50. //加载马里奥向左跳跃
  51. jump_L=ImageIO.read(new File(path+"jump1_l.png"));
  52. //加载马里奥向右跳跃
  53. Jump_R=ImageIO.read(new File(path+"Jump1_R.png"));
  54. //加载城堡
  55. tower=ImageIO.read(new File(path+"tower.png"));
  56. //加载旗杆
  57. gan=ImageIO.read(new File(path+"gan.png"));
  58. } catch (IOException e) {
  59. e.printStackTrace();
  60. }
  61. //加载马里奥向左跑
  62. for (int i=1;i<=2;i++){
  63. //向列表中添加
  64. try {
  65. run_L.add(ImageIO.read(new File(path+"run"+i+"_L.png")));
  66. } catch (IOException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. //加载马里奥向右跑
  71. for (int i=1;i<=2;i++){
  72. //向列表中添加
  73. try {
  74. run_R.add(ImageIO.read(new File(path+"run"+i+"_R.png")));
  75. } catch (IOException e) {
  76. e.printStackTrace();
  77. }
  78. }
  79. try {
  80. //加载障碍物,上下地面
  81. obstacle.add(ImageIO.read(new File(path+"brick.png")));
  82. obstacle.add(ImageIO.read(new File(path+"soil_up.png")));
  83. obstacle.add(ImageIO.read(new File(path+"soil_base.png")));
  84. } catch (IOException e) {
  85. e.printStackTrace();
  86. }
  87. //加载水管
  88. for (int i=1;i<=4;i++) {
  89. try {
  90. obstacle.add(ImageIO.read(new File(path+"pipe"+i+".png")));
  91. } catch (IOException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. //加载不可破坏的砖块和旗子
  96. try {
  97. obstacle.add(ImageIO.read(new File(path+"brick2.png")));
  98. obstacle.add(ImageIO.read(new File(path+"flag.png")));
  99. } catch (IOException e) {
  100. e.printStackTrace();
  101. }
  102. //加载蘑菇敌人
  103. for(int i=1;i<=3;i++){
  104. try {
  105. mogu.add(ImageIO.read(new File(path+"fungus"+i+".png")));
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. }
  109. }

Obstacle类:该类设置了到旗子处后的后续操作

  1. package com.sxt;
  2. import java.awt.image.BufferedImage;
  3. public class Obstacle implements Runnable{ //让该类实现Runnable方法,并重写其抽象方法
  4. //用于表示坐标
  5. private int x;
  6. private int y;
  7. //用于记录障碍物类型
  8. private int type;
  9. //用于显示图像
  10. private BufferedImage show=null;
  11. //定义当前的场景对象
  12. private BackGround bg=null;
  13. //定义一个线程对象,用于完成旗子下落的过程
  14. private Thread thread=new Thread(this);
  15. //创建带参构造函数
  16. public Obstacle(int x,int y,int type,BackGround bg){
  17. //赋值
  18. this.x=x;
  19. this.y=y;
  20. this.type=type;
  21. this.bg=bg;
  22. show=StaticValue.obstacle.get(type); //初始化show,得到该类型的障碍物图像
  23. //下一步,生成这四个变量的getter方法
  24. //如果是旗子的话,启动线程
  25. if (type==8){
  26. thread.start();
  27. }
  28. }
  29. public int getX() {
  30. return x;
  31. }
  32. public int getY() {
  33. return y;
  34. }
  35. public int getType() {
  36. return type;
  37. }
  38. public BufferedImage getShow() {
  39. return show;
  40. }
  41. @Override
  42. public void run() {
  43. while(true){ //一个死循环
  44. //让线程休眠,休眠时间:50毫秒
  45. if (this.bg.isReach()) { //判断马里奥是否到达旗子位置
  46. if (this.y<374){ //判断此时旗子是否落到了地上
  47. this.y+=5; //若没有,则让旗子慢慢下降
  48. }else{
  49. this.bg.setBase(true);//将isBase设置为true,表示旗子已经成功落地
  50. }
  51. }
  52. try {
  53. Thread.sleep(50);
  54. } catch (InterruptedException e) {
  55. e.printStackTrace();
  56. }
  57. }
  58. }
  59. }

Strong类:

  1. package com.sxt;
  2. import java.awt.image.BufferedImage;
  3. public class Strong implements Runnable{
  4. //储存食物坐标
  5. private int x,y;
  6. //显示图像
  7. private BufferedImage show;
  8. //定义一个背景对象
  9. private BackGround bg;
  10. //实现线程对象
  11. private Thread thread=new Thread(this);
  12. public Strong(int x,int y,BackGround bg){
  13. this.x=x;
  14. this.y=y;
  15. this.bg=bg;
  16. }
  17. @Override
  18. public void run() {
  19. }
  20. }

Mario类:

  1. package com.sxt;
  2. import jdk.nashorn.internal.ir.SplitReturn;
  3. import java.awt.image.BufferedImage;
  4. public class Mario implements Runnable{
  5. //用于表示横纵坐标
  6. private int x;
  7. private int y;
  8. //用于表示当前状态
  9. private String status;
  10. //用于显示当前状态对应的图像
  11. private BufferedImage show=null;
  12. //定义一个BackGround对象来获取障碍物信息
  13. private BackGround backGround=new BackGround();
  14. //创建线程对象用来实现马里奥动作
  15. private Thread thread=null;
  16. //马里奥移动速度
  17. private int xSpeed;
  18. //马里奥跳跃速度
  19. private int ySpeed;
  20. //定义一个索引,用于取得马里奥的运动图像
  21. private int index;
  22. //表示马里奥的上升时间
  23. private int upTime=0;
  24. //判断马里奥是否走到了城堡门口
  25. private boolean isOK;
  26. //用于判断马里奥是否死亡
  27. private boolean isDeath=false;
  28. //设置积分
  29. private int score=0;
  30. public Mario(){
  31. }
  32. public Mario(int x,int y){
  33. this.x=x;
  34. this.y=y;
  35. show=StaticValue.stand_R;
  36. this.status="stand--right"; //下一步使马里奥类实现Runnable接口并重写其中的抽象方法
  37. //初始化线程
  38. thread=new Thread(this);
  39. //启动线程
  40. thread.start();
  41. }
  42. //马里奥死亡的方法
  43. public void death(){
  44. isDeath=true;
  45. }
  46. //向左移动
  47. public void leftMove(){
  48. //改变速度
  49. xSpeed=-5;
  50. //判断马里奥是否碰到了旗子
  51. if (backGround.isReach()){ //若是,则马里奥不可再移动
  52. xSpeed=0;
  53. }
  54. //判断马里奥是否在空中
  55. if (status.indexOf("jump")!=-1){
  56. status="jump--left";
  57. }else{
  58. status="move--left";
  59. }
  60. }
  61. //向右移动
  62. public void rightMove(){
  63. xSpeed=5;
  64. //判断马里奥是否碰到了旗子
  65. if (backGround.isReach()){ //若是,则马里奥不可再移动
  66. xSpeed=0;
  67. }
  68. if (status.indexOf("jump")!=-1){
  69. status="jump--right";
  70. }else{
  71. status="move--right";
  72. }
  73. }
  74. //向左停止
  75. public void leftStop(){
  76. xSpeed=0;
  77. if(status.indexOf("jump")!=-1) {
  78. status="jump--left";
  79. }else{
  80. status="stop--left";
  81. }
  82. }
  83. //向右停止
  84. public void rightStop(){
  85. xSpeed=0;
  86. if(status.indexOf("jump")!=-1) {
  87. status="jump--right";
  88. }else{
  89. status="stop--right";
  90. }
  91. }
  92. //马里奥跳跃
  93. public void jump() {
  94. //判断是否是跳跃状态,判断status调用它的indexOF方法
  95. if (status.indexOf("jump")==-1) {
  96. //判断方向,不等于-1则方向向左
  97. if (status.indexOf("left")!=-1){
  98. status="jump--left";
  99. }else{
  100. status="jump--right";
  101. }
  102. //改变下降速度向上跳跃y值减少
  103. ySpeed=-10;
  104. //设置向上跳的高度
  105. upTime=7; //向上跳一下70
  106. }
  107. //判断马里奥是否碰到了旗子
  108. if (backGround.isReach()){ //若是,则马里奥不可再移动
  109. ySpeed=0;
  110. }
  111. }
  112. //马里奥下落
  113. public void fall(){
  114. //下落时可能是从障碍物上掉下也可能是跳起后落下
  115. if (status.indexOf("left")!=-1){
  116. status="jump--left";
  117. }else{
  118. status="jump--right";
  119. }
  120. //下落时速度应该是正的,因为y值要++
  121. ySpeed=10;
  122. }
  123. @Override
  124. public void run() {
  125. while (true){
  126. //判断是否处于障碍物上
  127. boolean onObstacle=false;
  128. //判断是否可以向右走
  129. boolean canRight=true;
  130. //判断是否可以向左走
  131. boolean canLeft=true;
  132. //判断马里奥是否到达旗杆位置
  133. if (backGround.isFlag()&&this.x>=500){
  134. //setReach设置为true,表明此刻马里奥到达了旗杆的位置
  135. this.backGround.setReach(true);
  136. //判断旗子是否下落完成
  137. if (this.backGround.isBase()){
  138. status="move--right"; //若是,则让马里奥开始向城堡移动
  139. if (x<690){ //判断马里奥是否到了城堡中间
  140. x+=5;
  141. }else { //马里奥已经到了城堡处
  142. isOK=true; //表示马里奥已经走到了城堡处
  143. }
  144. }else { //如果旗子没有下落完成
  145. if (y<395){ //判断马里奥是否在空中
  146. xSpeed=0;
  147. this.y+=5; //让马里奥逐渐下落
  148. status="jump--right"; //改变状态
  149. }
  150. if (y>395){ //判断马里奥是否落到了地上,大于则说明已经到了地上,不该再下降
  151. this.y=395;
  152. status="stop--right"; //设置状态:向右站立
  153. }
  154. }
  155. }else {
  156. //遍历当前场景中的所有障碍物
  157. for (int i = 0; i < backGround.getObstacleList().size(); i++) {
  158. //定义临时变量储存当前障碍物
  159. Obstacle ob = backGround.getObstacleList().get(i);
  160. //判断马里奥是否处于障碍物上
  161. if (ob.getY() == this.y + 25 && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {
  162. onObstacle = true;
  163. }
  164. //判断是否跳起来顶到砖块
  165. if ((ob.getY() >= this.y - 30 && ob.getY() <= this.y - 20)
  166. && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {
  167. //判断此时顶到的砖块类型是否是普通砖块(0),若是则移除
  168. if (ob.getType() == 0) {
  169. //如果是则调用backGround.getObstacleList().remove()方法将ob移出去
  170. backGround.getObstacleList().remove(ob);
  171. //破坏砖块+1分
  172. score+=1;
  173. }
  174. upTime = 0; //使马里奥顶到砖块后立刻下落
  175. }
  176. //判断是否可以向右走
  177. if (ob.getX() == this.x + 25 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {
  178. //如果符合该判断条件,则说明马里奥右侧有障碍物,无法往右走,将canRight置为false
  179. canRight = false;
  180. }
  181. //判断是否可以往左走
  182. if (ob.getX() == this.x - 30 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {
  183. canLeft = false;
  184. }
  185. }
  186. //判断马里奥是否碰到敌人死亡或者踩死蘑菇敌人
  187. //通过for循环来遍历每一个敌人
  188. for (int i=0;i<backGround.getEnemyList().size();i++){
  189. Enemy e=backGround.getEnemyList().get(i); //用e来储存我们当前的敌人
  190. //判断马里奥是否位于敌人的头上
  191. if (e.getY()==this.y+20&&(e.getX()-25<=this.x&&e.getX()+35>=this.x)){
  192. //判断是蘑菇敌人还是食人花敌人
  193. if (e.getType()==1){
  194. e.death();
  195. score+=2;
  196. upTime=3;
  197. ySpeed=-10;
  198. }else if (e.getType()==2){
  199. //马里奥死亡
  200. death();
  201. }
  202. }
  203. //碰到敌人,马里奥死亡
  204. if ((e.getX()+35>this.x&&e.getX()-25<this.x)
  205. &&(e.getY()+35>this.y&&e.getY()-20<this.y)){
  206. death();
  207. }
  208. }
  209. //进行马里奥跳跃的操作
  210. //先判断马里奥此时是否在障碍物上
  211. if (onObstacle && upTime == 0) {
  212. if (status.indexOf("left") != -1) {
  213. if (xSpeed != 0) {
  214. status = "move--left";
  215. } else {
  216. status = "stop--left";
  217. }
  218. } else {
  219. if (xSpeed != 0) {
  220. status = "move--right";
  221. } else {
  222. status = "stop--right";
  223. }
  224. }
  225. } else {
  226. //如果不符合上个外层条件,则说明此时马里奥处于上升阶段
  227. if (upTime != 0) {
  228. upTime--;
  229. } else {
  230. //不是,则说明到了最高点,该下落了
  231. fall();
  232. }
  233. //改变坐标值
  234. y += ySpeed;
  235. }
  236. }
  237. if ((canLeft&&xSpeed<0)||(canRight&&xSpeed>0)){
  238. x+=xSpeed;
  239. //判断马里奥是否到了最左边
  240. if(x<0){
  241. x=0;
  242. }
  243. }
  244. //判断当前是否是移动状态
  245. if (status.contains("move")){
  246. index=index==0?1:0;
  247. }
  248. //判断是否向左移动
  249. if("move--left".equals(status)){
  250. show=StaticValue.run_L.get(index);
  251. }
  252. //判断是否向右移动
  253. if ("move--right".equals(status)){
  254. show=StaticValue.run_R.get(index);
  255. }
  256. //判断是否向左停止
  257. if ("stop--left".equals(status)){
  258. show=StaticValue.stand_L;
  259. }
  260. //判断是否向右停止
  261. if ("stop--right".equals(status)){
  262. show=StaticValue.stand_R;
  263. }
  264. //判断是否向左跳跃
  265. if("jump--left".equals(status)){
  266. show=StaticValue.jump_L;
  267. }
  268. //判断是否向右跳跃
  269. if ("jump--right".equals(status)){
  270. show=StaticValue.Jump_R;
  271. }
  272. //设置线程休眠50毫秒
  273. try {
  274. Thread.sleep(50);
  275. } catch (InterruptedException e) {
  276. e.printStackTrace();
  277. }
  278. }
  279. }
  280. public int getX() {
  281. return x;
  282. }
  283. public int getY() {
  284. return y;
  285. }
  286. public BufferedImage getShow() {
  287. return show;
  288. }
  289. public void setShow(BufferedImage show){
  290. this.show=show;
  291. }
  292. public void setBackGround(BackGround backGround) {
  293. this.backGround = backGround;
  294. }
  295. public void setX(int x) {
  296. this.x = x;
  297. }
  298. public void setY(int y) {
  299. this.y = y;
  300. }
  301. public boolean isOK() {
  302. return isOK;
  303. }
  304. public boolean isDeath() {
  305. return isDeath;
  306. }
  307. public int getScore() {
  308. return score;
  309. }
  310. }

Enemy(敌人类):

  1. package com.sxt;
  2. import javax.xml.ws.BindingType;
  3. import java.awt.image.BufferedImage;
  4. import static java.lang.Thread.sleep;
  5. public class Enemy implements Runnable{
  6. //储存当前坐标
  7. private int x,y;
  8. //储存敌人类型
  9. private final int type;
  10. //判断敌人的运动方向
  11. private boolean face_to=true;
  12. //用于显示敌人当前的图像
  13. private BufferedImage show;
  14. //定义一个背景对象
  15. private final BackGround bg;
  16. //食人花运动的极限范围
  17. private int max_up=0;
  18. private int max_down=0;
  19. //实现线程对象,用于实现食人花和蘑菇的运动
  20. private final Thread thread=new Thread(this);
  21. //定义当前图片的状态
  22. private int image_type=0;
  23. // 创建MyFrame对象
  24. // MyFrame myFrame = new MyFrame();
  25. //蘑菇敌人的构造函数
  26. public Enemy(int x, int y,boolean face_to,int type,BackGround bg){
  27. this.x=x;
  28. this.y=y;
  29. this.face_to=face_to;
  30. this.type=type;
  31. this.bg=bg;
  32. show=StaticValue.mogu.get(0);
  33. thread.start(); //调用start方法实现线程
  34. }
  35. //食人花敌人的构造函数
  36. public Enemy(int x,int y,boolean face_to,int type,int max_up,int max_down,BackGround bg){
  37. this.x=x;
  38. this.y=y;
  39. this.face_to=face_to;
  40. this.type=type;
  41. this.max_up=max_up;
  42. this.max_down=max_down;
  43. this.bg=bg;
  44. show=StaticValue.flower.get(0);
  45. thread.start();
  46. }
  47. //死亡方法
  48. public void death(){
  49. show=StaticValue.mogu.get(2); //蘑菇敌人死亡时的图片
  50. this.bg.getEnemyList().remove(this);
  51. }
  52. public int getX(){
  53. return x;
  54. }
  55. public int getY(){
  56. return y;
  57. }
  58. public BufferedImage getShow() {
  59. return show;
  60. }
  61. public int getType(){
  62. return type;
  63. }
  64. @Override
  65. public void run() {
  66. try {
  67. sleep(2000);
  68. } catch (InterruptedException e) {
  69. throw new RuntimeException(e);
  70. }
  71. if (MyFrame.isStart == true) {
  72. while (true) {
  73. //判断是否是蘑菇敌人,type是蘑菇敌人
  74. if (type == 1) {
  75. if (face_to) { //true是向左移动,false时向右移动
  76. this.x -= 2;
  77. } else {
  78. this.x += 2;
  79. }
  80. //使用三目运算符
  81. image_type = image_type == 1 ? 0 : 1;
  82. show = StaticValue.mogu.get(image_type);
  83. }
  84. //定义两个布尔变量
  85. boolean canLeft = true;
  86. boolean canRight = true;
  87. //通过for循环来遍历每一个障碍物
  88. for (int i = 0; i < bg.getObstacleList().size(); i++) {
  89. Obstacle ob1 = bg.getObstacleList().get(i);
  90. //判断是否可以向右走
  91. if (ob1.getX() == this.x + 36 && (ob1.getY() + 65 > this.y && ob1.getY() - 35 < this.y)) {
  92. //如果符合该判断条件则说明敌人右侧有障碍物,那么它就无法右走
  93. canRight = false;
  94. }
  95. //判断是否可以左走
  96. if (ob1.getX() == this.x - 36 && (ob1.getY() + 65 > this.y && ob1.getY() - 35 < this.y)) {
  97. canLeft = false;
  98. }
  99. }
  100. if (face_to && !canLeft || this.x == 0) { //判断是否是向左走且碰到了障碍物或是走到了屏幕最左侧
  101. face_to = false;
  102. } else if ((!face_to) && (!canRight) || this.x == 764) {
  103. face_to = true;
  104. }
  105. //判断敌人是否是食人花敌人
  106. if (type == 2) {
  107. if (face_to) {
  108. this.y -= 2;
  109. } else {
  110. this.y += 2;
  111. }
  112. image_type = image_type == 1 ? 0 : 1;
  113. //判断食人花是否到达极限位置
  114. if (face_to && (this.y == max_up)) {
  115. face_to = false;
  116. }
  117. if ((!face_to) && (this.y == max_down)) {
  118. face_to = true;
  119. }
  120. show = StaticValue.flower.get(image_type);
  121. }
  122. try {
  123. sleep(50);
  124. } catch (InterruptedException e) {
  125. e.printStackTrace();
  126. }
  127. }
  128. }
  129. }
  130. }

Music类:

  1. package com.sxt;
  2. import javazoom.jl.decoder.JavaLayerException;
  3. import javazoom.jl.player.Player;
  4. import java.io.BufferedInputStream;
  5. import java.io.FileInputStream;
  6. import java.io.FileNotFoundException;
  7. public class Music {
  8. public Music() throws FileNotFoundException, JavaLayerException {
  9. Player player;
  10. String str=System.getProperty("user.dir")+"/src/Music/music.wav";//拼接路径,寻找音乐
  11. BufferedInputStream name=new BufferedInputStream(new FileInputStream(str));//读取音乐
  12. player=new Player(name);//实例化对象。同上一步都需要抛出异常
  13. player.play();//调用方法播放音乐
  14. }
  15. }
标签: java jvm intellij-idea

本文转载自: https://blog.csdn.net/weixin_71766825/article/details/127060235
版权归原作者 Hello1Java 所有, 如有侵权,请联系我们删除。

“Java小游戏练习---超级玛丽代码实现”的评论:

还没有评论