《面向对象》课程设计--超级玛丽
1. 团队介绍
团队名称:会飞的代码
| 团队成员 | 任务分配 |
|---|---|
| 兰心怡(组长) | 游戏框架搭建、游戏背景设置、静态常量初始化 |
| 王思嘉 | 马里奥的左右移动、跳跃等,判断马里奥是否与障碍物相撞,敌人的活动状态 |
2. 参考代码
https://blog.csdn.net/hj7jay/article/details/54310817
https://blog.csdn.net/weixin_42313980/article/details/114098276
https://blog.csdn.net/jiachunchun/article/details/89670721
3. 前期调查


4. 功能架构图

5. 面向对象设计包图、类图

- images包存放游戏相关的所有图片
- MyFrame--游戏框架的构建、游戏窗口的设置
- BackGround--游戏背景的绘制
- StaticValue--将静态常量初始化,并将图片加载至内存中
- Mario--马里奥(游戏人物)的左右移动、跳跃等,并判断是否会与障碍物相撞
- Enemy--敌人的移动、状态及死亡
- Obstacle--障碍物初始化
6. 项目运行



7. 项目关键代码
马里奥类(游戏人物类):
//马里奥的死亡方法
public void death() {
isDeath = true;
}
//马里奥向左移动
public void leftMove() {
//改变速度
xSpeed = -5;
//判断马里奥是否碰到旗子
if (backGround.isReach()) {
xSpeed = 0;
}
//判断马里奥是否处于空中
if (status.indexOf("jump") != -1) {
status = "jump--left";
}else {
status = "move--left";
}
}
//马里奥向右移动
public void rightMove() {
xSpeed = 5;
//判断马里奥是否碰到旗子
if (backGround.isReach()) {
xSpeed = 0;
}
if (status.indexOf("jump") != -1) {
status = "jump--right";
}else {
status = "move--right";
}
}
//马里奥向左停止
public void leftStop() {
xSpeed = 0;
if (status.indexOf("jump") != -1) {
status = "jump--left";
}else {
status = "stop--left";
}
}
//马里奥向右停止
public void rightStop() {
xSpeed = 0;
if (status.indexOf("jump") != -1) {
status = "jump--right";
}else {
status = "stop--right";
}
}
//马里奥跳跃
public void jump() {
if (status.indexOf("jump") == -1) {
if (status.indexOf("left") != -1) {
status = "jump--left";
}else {
status = "jump--right";
}
ySpeed = -10;
upTime = 7;
}
//判断马里奥是否碰到旗子
if (backGround.isReach()) {
ySpeed = 0;
}
}
//马里奥下落
public void fall() {
if (status.indexOf("left") != -1) {
status = "jump--left";
}else {
status = "jump--right";
}
ySpeed = 10;
}
@Override
public void run() {
while (true) {
//判断是否处于障碍物上
boolean onObstacle = false;
//判断是否可以往右走
boolean canRight = true;
//判断是否可以往左走
boolean canLeft = true;
//判断马里奥是否到达旗杆位置
if (backGround.isFlag() && this.x >= 500) {
this.backGround.setReach(true);
//判断旗子是否下落完成
if (this.backGround.isBase()) {
status = "move--right";
if (x < 690) {
x += 5;
}else {
isOK = true;
}
}else {
if (y < 395) {
xSpeed = 0;
this.y += 5;
status = "jump--right";
}
if (y > 395) {
this.y = 395;
status = "stop--right";
}
}
}else {
//遍历当前场景里所有的障碍物
for (int i = 0; i < backGround.getObstacleList().size(); i++) {
Obstacle ob = backGround.getObstacleList().get(i);
//判断马里奥是否位于障碍物上
if (ob.getY() == this.y + 25 && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {
onObstacle = true;
}
//判断是否跳起来顶到砖块
if ((ob.getY() >= this.y - 30 && ob.getY() <= this.y - 20) && (ob.getX() > this.x - 30 && ob.getX() < this.x + 25)) {
if (ob.getType() == 0) {
backGround.getObstacleList().remove(ob);
score = getScore() + 1;
}
upTime = 0;
}
//判断是否可以往右走
if (ob.getX() == this.x + 25 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {
canRight = false;
}
//判断是否可以往左走
if (ob.getX() == this.x - 30 && (ob.getY() > this.y - 30 && ob.getY() < this.y + 25)) {
canLeft = false;
}
}
//判断马里奥是否碰到敌人死亡或者踩死蘑菇敌人
for (int i = 0;i < backGround.getEnemyList().size();i++) {
Enemy e = backGround.getEnemyList().get(i);
if (e.getY() == this.y + 20 && (e.getX() - 25 <= this.x && e.getX() + 35 >= this.x)) {
if (e.getType() == 1) {
e.death();
score = getScore() + 2;
upTime = 3;
ySpeed = -10;
}else if (e.getType() == 2) {
//马里奥死亡
death();
}
}
if ((e.getX() + 35 > this.x && e.getX() - 25 < this.x) && (e.getY() + 35 > this.y && e.getY() - 20 < this.y)) {
//马里奥死亡
death();
}
}
//进行马里奥跳跃的操作
if (onObstacle && upTime == 0) {
if (status.indexOf("left") != -1) {
if (xSpeed != 0) {
status = "move--left";
} else {
status = "stop--left";
}
} else {
if (xSpeed != 0) {
status = "move--right";
} else {
status = "stop--right";
}
}
} else {
if (upTime != 0) {
upTime--;
} else {
fall();
}
y += ySpeed;
}
}
if ((canLeft && xSpeed < 0) || (canRight && xSpeed > 0)) {
x += xSpeed;
//判断马里奥是否到了最左边
if (x < 0) {
x = 0;
}
}
//判断当前是否是移动状态
if (status.contains("move")) {
index = index == 0 ? 1 : 0;
}
//判断是否向左移动
if ("move--left".equals(status)) {
show = StaticValue.run_L.get(index);
}
//判断是否向右移动
if ("move--right".equals(status)) {
show = StaticValue.run_R.get(index);
}
//判断是否向左停止
if ("stop--left".equals(status)) {
show = StaticValue.stand_L;
}
//判断是否向右停止
if ("stop--right".equals(status)) {
show = StaticValue.stand_R;
}
//判断是否向左跳跃
if ("jump--left".equals(status)) {
show = StaticValue.jump_L;
}
//判断是否向右跳跃
if ("jump--right".equals(status)) {
show = StaticValue.jump_R;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
敌人类:
public void run() {
while(true) {
//是否是蘑菇敌人
if(type == 1) {
if(face_to) {
this.x -= 2;
}else {
this.x += 2;
}
image_type = image_type == 1 ? 0 : 1;
show = StaticValue.mogu.get(image_type);
}
//定义两个布尔变量
boolean canLeft = true;
boolean canRight = true;
for(int i = 0; i< bg.getObstacleList().size(); i++) {
Obstacle ob1 = bg.getObstacleList().get(i);
//判断是否可以向右走
if(ob1.getX() == this.x+36 && (ob1.getY() + 65 > this.y && ob1.getY()-35 < this.y)) {
canRight = false;
}
//判断是否可以向左走
if(ob1.getX() == this.x-36 && (ob1.getY() + 65 > this.y && ob1.getY()-35 < this.y)) {
canLeft = false;
}
}
if(face_to && !canLeft || this.x == 0) {
face_to = false;
}
else if((!face_to)&&(!canRight) || this.x == 764) {
face_to = true;
}
//判断是否是食人花敌人
if(type == 2) {
if(face_to) {
this.y -= 2;
}else {
this.y += 2;
}
image_type = image_type == 1 ? 0 :1;
//食人花是否移动到极限位置
if(face_to && (this.y == max_up)) {
face_to = false;
}
if(!face_to && (this.y == max_down)) {
face_to = true;
}
show = StaticValue.flower.get(image_type);
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
窗口设置:
public MyFrame() {
//设置窗口大小
this.setSize(800, 600);
//设置窗口居中
this.setLocationRelativeTo(null);
//设置窗口可见性
this.setVisible(true);
//设置点击窗口上的关闭键,结束程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口大小不可变
this.setResizable(false);
//向窗口对象添加键盘监听器
this.addKeyListener(this);
//设置窗口名称
this.setTitle("超级玛丽");
//初始化图片
StaticValue.init();
//初始化马里奥
mario = new Mario(10, 355);
//创建全部的场景
for(int i=1; i<=3; i++) {
allBg.add(new BackGround(i, i==3 ? true : false));
}
//将第一个场景设置为当前场景
nowBg = allBg.get(0);
mario.setBackGround(nowBg);
//绘制图像
repaint();
thread.start();
}
背景绘制:
if(sort == 1) {
//绘制第一关地面
for(int i = 0; i<=27; i++) {
obstacleList.add(new Obstacle(i*30, 420, 1, this));
}
for(int j = 0; j <= 120; j += 30) {
for(int i = 0; i<=27; i++) {
obstacleList.add(new Obstacle(i*30, 570-j, 2, this));
}
}
//绘制砖块A
for(int i =120; i <= 150; i += 30) {
obstacleList.add(new Obstacle(i, 300, 7, this));
}
//绘制砖块B-F
for(int i = 300; i <= 570; i+=30) {
if(i==360 || i==390 || i==480 || i==510 || i==540) {
obstacleList.add(new Obstacle(i, 300, 7, this));
}else {
obstacleList.add(new Obstacle(i, 300, 0, this));
}
}
//绘制砖块G
for(int i = 420; i <= 450; i+=30) {
obstacleList.add(new Obstacle(i, 240, 7, this));
}
//绘制水管
for(int i = 360; i <= 600; i+=25) {
if(i == 360) {
obstacleList.add(new Obstacle(620, i, 3, this));
obstacleList.add(new Obstacle(645, i, 4, this));
}else {
obstacleList.add(new Obstacle(620, i, 5, this));
obstacleList.add(new Obstacle(645, i, 6, this));
}
}
//绘制第一关的蘑菇敌人
enemyList.add(new Enemy(580, 385, true, 1, this));
//绘制第一关的食人花敌人
enemyList.add(new Enemy(635, 420, true, 2, 328, 428, this));
}
图片初始化:
public static void init() {
//加载背景图片
try {
bg = ImageIO.read(new File(path + "bg.png"));
bg2 = ImageIO.read(new File(path + "bg2.png"));
stand_L = ImageIO.read(new File(path + "s_mario_stand_L.png"));
stand_R = ImageIO.read(new File(path + "s_mario_stand_R.png"));
tower = ImageIO.read(new File(path + "tower.png"));
gan = ImageIO.read(new File(path + "gan.png"));
jump_L = ImageIO.read(new File(path + "s_mario_jump1_L.png"));
jump_R = ImageIO.read(new File(path + "s_mario_jump1_R.png"));
}catch(IOException e) {
e.printStackTrace();
}
//加载马里奥向左跑
for (int i = 1;i <= 2;i++) {
try {
run_L.add(ImageIO.read(new File(path + "s_mario_run"+ i +"_L.png")));
} catch (IOException e) {
e.printStackTrace();
}
}
//加载马里奥向右跑
for (int i = 1;i <= 2;i++) {
try {
run_R.add(ImageIO.read(new File(path + "s_mario_run"+ i +"_R.png")));
} catch (IOException e) {
e.printStackTrace();
}
}
//加载障碍物
try {
obstacle.add(ImageIO.read(new File(path + "brick.png")));
obstacle.add(ImageIO.read(new File(path + "soil_up.png")));
obstacle.add(ImageIO.read(new File(path + "soil_base.png")));
} catch (IOException e) {
e.printStackTrace();
}
//加载水管
for (int i = 1;i <= 4;i++) {
try {
obstacle.add(ImageIO.read(new File(path + "pipe"+ i +".png")));
} catch (IOException e) {
e.printStackTrace();
}
}
//加载不可破坏的砖块和旗子
try {
obstacle.add(ImageIO.read(new File(path + "brick2.png")));
obstacle.add(ImageIO.read(new File(path + "flag.png")));
} catch (IOException e) {
e.printStackTrace();
}
//加载蘑菇敌人
for (int i = 1;i <= 3;i++) {
try {
mogu.add(ImageIO.read(new File(path + "fungus"+i+".png")));
} catch (IOException e) {
e.printStackTrace();
}
}
//加载食人花敌人
for (int i = 1;i <= 2;i++) {
try {
flower.add(ImageIO.read(new File(path + "flower1."+i+".png")));
} catch (IOException e) {
e.printStackTrace();
}
}
}
8.未来展望
此次课设还是有许多的不足之处,比如在游戏过程中游戏人物有时候会突然卡在一些奇奇怪怪的地方,或砖块,或水管,所以还是需要继续学习,可能找到一种更为合适更为简便的算法来修复这些小Bug,同时希望可以继续完善这个小游戏,例如增加更多的关卡、增加游戏人物的生命值、增加更多地图等。

浙公网安备 33010602011771号