超级玛丽游戏开发(角色与敌人的互动)
目录
实现功能:
角色移动时遇到障碍物无法穿过,需要进行跳跃,角色撞击砖块的时候你,砖块被消除;角色遇到敌人的时候生命值-1,角色踩到敌人的时候敌人被消灭掉。
1.与敌人的撞击,生命减少
- 在游戏过程中需要不断检测与队列里面的敌人是否撞击(根据mario和所有enemy的位置及大小判断),因此这里需要用到线程。但是MFrame里面已经用线程来进行图像的绘制了,因此我的处理是,在MFrame里面加一个内置类Collide实现Runbbale接口。
- 与敌人的撞击检测,如果不做处理的画,会因为检测的时间间隔很短而在一次撞击到同一个敌人的时候检测到多次撞击,导致损失超过1的生命值,因此这里用到System.currentTimeMillis()方法获得当前撞击的时间,根据与前一次检测到撞击的 时间间隔来决定是否算作一次撞击。
- endGame是MFrame的一个方法
/*
* 内部类检测撞击
* */
private class Collide implements Runnable{
private Thread collideThread;
private long t1,t2;
public Collide(){
collideThread = new Thread(this);
collideThread.start();
t1 = t2 = 0;
}
@Override
public void run() {
while(true){
List <Enemy> enemies = bg.getEnemies();
for(int i=0; i<enemies.size(); i++){
Enemy enemy = enemies.get(i);
if(collide(enemy)){
t2 = System.currentTimeMillis();//控制撞击的时间间隔
if(t2 - t1 > 500){
mario.decreaseLife();
if(mario.life==0){
endGame();//结束界面
return;
}
t1 = t2;
}
}
}
}
}
/*
* 检测是否与enemy撞击
* 撞击返回true,没有撞击返回false
* */
public boolean collide( Enemy enemy){
if(mario.posx>=enemy.getX()&&mario.posx<=enemy.getX()+enemy.getWidth()
&&mario.posy+60>=enemy.getY()&&mario.posy+60<=enemy.getY()+enemy.getHeight()){
return true;
}
return false;
}
}
endGame:
- MFrame的方法,在方法内改变mario的生存状态marioDead,然后让游戏在当前界面停留2s后切换到结束界面。
- 清除之前的组件,重新绘制缓冲图片,然后将缓冲图片绘制到到界面上
/*
* 结束游戏
* */
public void endGame(){
marioDead = true;
this.removeKeyListener(this);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.getContentPane().removeAll();//清除原来的组件
buffg.fillRect(0, 0, this.getWidth(), this.getHeight());
buffg.drawImage(new ImageIcon(StaticValue.endImg).getImage(), 0, 0, this.getWidth(),this.getHeight(), null);
g.drawImage(buffimg,0,0,null,null);
}
matio和界面、Colide线程的控制:
- 要控制当角色死亡的时候,所有的线程停止运行,因此在MFrame和Mario中分别设置一个状态变量来标记生存状态(设置为volatile使得每一次使用到变量的时候都保证是最新的值)
- 不用Thread.interrupt()来标记,因为线程在sleep中阻塞,当interrupt后会报异常interruptException,详见:https://blog.csdn.net/weixin_38858037/article/details/88955859
①Mario:
减少生命的方法
/*
* 生命值减少
* */
public void decreaseLife(){
life --;
if(life == 0){
dead = true;//标记线程终止循环
}
}
run方法:用volatile的boolean变量dead标记
/*
* 在线程里面移动角色
* */
public void run(){
while(!dead){
posx += dx;
if(posx<0)
posx=0;
if(posx>screenWidth-50)
posx = screenWidth-50;
switchN--;
if(switchN==0){
index = (index+1)%4;
switchN = 5;
}
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
②Collide的run方法:当检测到life==0直接结束run方法
@Override
public void run() {
while(true){
List <Enemy> enemies = bg.getEnemies();
for(int i=0; i<enemies.size(); i++){
Enemy enemy = enemies.get(i);
if(collide(enemy)){
t2 = System.currentTimeMillis();//控制撞击的时间间隔
if(t2 - t1 > 500){
mario.decreaseLife();
System.out.println("decrease life, life now:"+mario.life);
if(mario.life==0){
System.out.println("quit thread in collide");
endGame();//结束界面
return;
}
t1 = t2;
}
}
}
}
}
③MFrame里面: 用volatile的boolean变量marioDead标记
/*
* 在线程里面绘制图片
* */
@Override
public void run() {
// TODO Auto-generated method stub
List <Enemy> enemies = bg.getEnemies();
List<Object> objects = bg.getObjects();
while(!marioDead){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
Image bgimg = new ImageIcon(bg.getBgImage()).getImage();//背景图片
buffg.drawImage(bgimg, 0,0, null);
buffg.drawImage(mario.getImage(), mario.posx, mario.posy, 50, 100, null);//马里奥
//敌人
for(int i=0; i<enemies.size(); i++){
Enemy enemy = enemies.get(i);
BufferedImage enemyImg = enemy.getImage();
buffg.drawImage(new ImageIcon(enemyImg).getImage(),
enemy.getX(),enemy.getY(),enemy.getWidth(),enemy.getHeight(),null );
}
//障碍物
for(int i=0; i<objects.size(); i++){
Object object = objects.get(i);
BufferedImage objectImg = object.getImage();
buffg.drawImage(new ImageIcon(objectImg).getImage(),
object.getL(),object.getUp(),object.getR()- object.getL(),object.getDown() - object.getUp(),null );
}
g.drawImage(buffimg, 0, 0, null);
}
}
2.马里奥踩踏敌人,敌人死亡
- 在前面检测撞击的基础上,这个很容易判断
- 只要当撞击的时候马里奥的y方向的速度>0,就说明马里奥通过跳跃踩踏到敌人:
Collide的run内:
@Override
public void run() {
while(true){
List <Enemy> enemies = bg.getEnemies();
for(int i=0; i<enemies.size(); i++){
Enemy enemy = enemies.get(i);
if(collide(enemy)){//和敌人又碰撞
t2 = System.currentTimeMillis();//控制撞击的时间间隔
if(t2 - t1 > 500){
if(mario.dy > 0&&enemy.getdy() == 0){//踩踏敌人,排除食人花(上下移动)
enemy.dead();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
enemies.remove(i);//将敌人从背景中删除
}
else{//撞到敌人
mario.decreaseLife();
if(mario.life==0){
endGame();//结束界面
return;
}
t1 = t2;
}
}
}
}
}
}
- 这个时候调用敌人的dead方法,终止敌人的运动线程:
/*
* 敌人死亡终止线程
* */
public void dead(){
dead = true;
}
- 让线程睡眠1s,展示敌人的死亡图片,然后将对应的敌人从背景的敌人队列中删去,对应代码:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
enemies.remove(i);//将敌人从背景中删除