线程游戏

Java线程创意游戏之——海绵宝宝版超级玛丽

 

【整体创意简介】

海绵宝宝出发,键盘控制其移动(上下左右),途中遭遇阻碍(水母、海胆),碰到就掉血,途中获得奖励(汉堡包),吃掉可增加血量,到达终点后,与派大星会面,解救出派大星。

 

【分块思路】

1.入口窗体/面板

       (游戏进行、游戏帮助、退出游戏、播放开始背景音乐)

2.帮助窗体/面板

       (游戏简介、游戏作者、返回入口)

3.游戏进行主窗体/面板

         1人物:海绵宝宝、派大星、水母、海胆、汉堡包

         2背景:海滩、土壤(各两块以便于背景移动)

         3音效:关闭游戏开始音,播放游戏进行音

         4移动:水母、海胆、背景的移动,键盘监听控制海绵宝宝

4.死亡窗体/面板

       (图片背景,文字介绍,死亡音乐,返回重新开始游戏通道)

5.胜利窗体/面板

       (图片背景,文字介绍,胜利音乐,下一关通道)

 

【游戏截图介绍】

1.欢迎界面

 

游戏的三个按钮采用画笔g画上去

 

 

2.帮助按钮界面

 

点击help按钮进入这个界面,点击返回返回入口界面

点击exit按钮退出游戏。

 

3.进入游戏界面

1)初进入

 

左上方:血量条以及血量具体数值显示,随着游戏的进行,血量条和数值会相应的增减。

右上方:海绵宝宝所走路程的显示

中央:海胆(碰到掉血),汉堡包(吃掉可增加血量),水母(不断由界面右边飘向左边,碰到掉血)

左下方:主人公海绵宝宝,它有左右两个方向,这里是向右走,当按键盘上的向左箭头时,可变为向左。

2)游戏进行

键盘上下左右箭头控制前进与跳起,松开向上箭头时,自动下落。

 

当走到界面一半路程时,背景及海滩,海盗,汉堡都开始向左移动,营造出前进的动态感。

 

3)血量掉光时死亡

弹出死亡界面,游戏音乐关闭,死亡音乐开启。

 

4)点击返回游戏,重新开始

5)走到一定路程后,被困的派大星出现,解救出派大星后,弹出游戏胜利界面。

 

游戏胜利音乐开启,游戏进行音乐关闭。

6)点击下一关,开始第二关。界面类似于第一关,但是阻碍更多,设计的阻碍路线使通过更困难。

 

 

【游戏设计详细介绍】

1.所需的类、图片、音乐

 


 

2.最主要的类-线程类代码

public class playThread extends JPanel implements Runnable, KeyListener {

 

// 背景图片、人物图片

private Image background0;

private Image background1;

private Image sand0, sand1;

private Image babyright, babyleft;

private Image paidaxing;

// 记录两张背景图片时时更新的X坐标

private int backX0 = 0;

private int backX1 = 0;

// 背景移动速度

private int backv = 5;

// 海绵宝宝走过的路程

private int load = 0;

// 记录游戏状态

private boolean state = false;

// 记录左右走的状态

private boolean left = false;

private boolean right = false;

// 游戏主线程

private Thread mThread = null;

// 向上跳初始化

private int upjump = 0;

// baby最低位置

private int babyY0 = 475;

 

private Graphics g;

// 将结束和胜利定义为公用

public over ov;

public win wn;

// 设置血条长度

private int width = 300;

private int blood = 100;

// 游戏进行的音乐

public AudioClip playsound;

// 实例化对象

JFrame jf;

Baby baby = new Baby();

haidan[] hai = new haidan[3];

hamburger[] ham = new hamburger[3];

shuimu[] shui = new shuimu[6];

 

public playThread(JFrame jf) {

playsound = Applet.newAudioClip(this.getClass().getResource("playGame.au"));

playsound.play();

 

jf.addKeyListener(this);

 

initUI();

/** 启动游戏线程 **/

 

state = true;

mThread = new Thread(this);// 实例化线程

 

mThread.start();

this.setVisible(true);

 

}

 

public void initUI() {

 

/************** 绘制游戏背景 **************/

// 初始化背景图片

background0 = new ImageIcon(this.getClass().getResource("界面2.png")).getImage();

background1 = new ImageIcon(this.getClass().getResource("界面3.png")).getImage();

// 第一张图片贴在屏幕0,0点,第二张图片在第一张图片右边

backX0 = 0;

backX1 = 1194;

// 加载地面背景图

sand0 = new ImageIcon(this.getClass().getResource("sand0.png")).getImage();

sand1 = new ImageIcon(this.getClass().getResource("sand1.png")).getImage();

 

// 加载baby向左、右的动图

babyleft = new ImageIcon(this.getClass().getResource("spongeL.gif")).getImage();

babyright = new ImageIcon(this.getClass().getResource("spongeR.gif")).getImage();

//

paidaxing = new ImageIcon(this.getClass().getResource("派大星.png")).getImage();

 

/************** 初始化汉堡和海胆的数值对象 **************/

for (int i = 0; i < hai.length; i++) {

hai[i] = new haidan();

}

for (int i = 0; i < hai.length; i++) {

ham[i] = new hamburger();

}

for (int i = 0; i < shui.length; i++) {

shui[i] = new shuimu();

shui[i].y = 200;

}

hai[0].x = 200;

hai[1].x = 700;

hai[2].x = 1200;

 

ham[0].x = 400;

ham[1].x = 900;

ham[2].x = 1400;

 

shui[0].x = 100;

shui[1].x = 700;

shui[2].x = 1300;

shui[3].x = 1900;

shui[4].x = 2500;

shui[5].x = 3100;

}

 

public void paint(Graphics g) {

 

super.paint(g);

 

/*************** 绘制游戏背景 *******************/

g.drawImage(background0, backX0, 0, jf);

g.drawImage(background1, backX1, 0, jf);

 

/*************** 绘制海绵宝宝、海胆、汉堡 ********************/

baby.draw(g, jf);

for (int i = 0; i < hai.length; i++) {

hai[i].draw(g, jf);

ham[i].draw(g, jf);

 

}

for (int i = 0; i < shui.length; i++) {

shui[i].draw(g, jf);

shui[i].x -= 7;

}

/*************** 绘制地面背景 *******************/

g.drawImage(sand0, backX0, 545, jf);

g.drawImage(sand1, backX1, 545, jf);

/*************** 绘制路程显示框 *******************/

g.setColor(Color.blue);

 

g.setFont(new Font("", Font.BOLD, 20));// 设置字体样式以及大小

g.drawString("路程:" + load, 1000, 80);

/*************** 绘制血量条 *******************/

g.setColor(Color.RED);

g.drawString("血量:", 60, 70);

g.drawString("" + blood, 430, 70);

 

g.fillRect(120, 50, width, 20);

g.drawRect(120, 50, 300, 20);

 

/*************** 绘制派大星 *******************/

if (load > 2000) {// 当走过一段路程后,派大星才出现

g.drawImage(paidaxing, 1000, 440, 150, 150, jf);

}

/***************** 控制上下跳 *************/

jump();

/*********** 调用碰撞方法 ******************/

 

collisionBall();

 

}

 

public void jump() {// 跳跃方法

/***************** 控制上下跳 *************/

if (upjump > 0) {// 上跳

 

baby.y -= 8;

upjump--;

}

if (babyY0 != baby.y && upjump == 0) // 下降

baby.y += 8;

/***************** 控制左右跳 *************/

if (left) {// 打开左走开关

baby.x -= baby.v;

if (baby.x <= 80) {

baby.x = 80;

backX0 += backv;

backX1 += backv;

for (int i = 0; i < hai.length; i++) {

hai[i].x += backv;

ham[i].x += backv;

 

}

if (backX0 > 0) // 背景循环

backX1 = -1 * backX0;

//

// if (backX1 > 0)

// backX0 = - backX1;

}

}

if (right) {// 打开右走开关

baby.x += baby.v;

load += baby.v;

 

if (baby.x >= 600 && load < 2000) {

baby.x = 600;

backX0 -= backv;

backX1 -= backv;

for (int i = 0; i < hai.length; i++) {

hai[i].x -= backv;

ham[i].x -= backv;

 

}

/**

 * 背景问题修改:注释部分为原代码,这样做不能保证backX1正好是0,因为是每次减去速度,减完之后不一定恰好是0.

 * 而修改后解决了这一问题

 */

// if(backX1==0)backX0=1194;

// if(backX0==0)backX1=1194;

if (backX1 < 0) // 背景循环

backX0 = 1194 + backX1;

 

if (backX0 <= 0)

backX1 = 1194 + backX0;

 

}

// baby走过某段路程后,baby开始重新动

if (load >= 2000) {

baby.x += 3;

// baby停在界面上的末位置

if (baby.x >= 920 && baby.y >= 440) {

baby.x = 920;

 

state = false;

 

wn = new win(playsound);

wn.setVisible(true);// 游戏胜利了!

 

}

 

}

}

}

 

public void Draw() {

repaint(); // 绘制游戏界面(包括背景、海绵宝宝、海胆、蟹黄堡、水母)

 

}

 

public void collisionBall() {

// 当碰到海胆

for (int i = 0; i < hai.length; i++) {

double d = Math.sqrt((baby.x - hai[i].x) * (baby.x - hai[i].x) + (baby.y - hai[i].y) * (baby.y - hai[i].y));

 

if (d < 80) {

AudioClip hurtsound = Applet.newAudioClip(this.getClass().getResource("eat.au"));

hurtsound.play();

width -= 5;

blood -= 1;

}

}

// 当碰到汉堡

for (int j = 0; j < ham.length; j++) {

double f = Math.sqrt((baby.x - ham[j].x) * (baby.x - ham[j].x) + (baby.y - ham[j].y) * (baby.y - ham[j].y));

if (f < 72) {

AudioClip jumpsound = Applet.newAudioClip(this.getClass().getResource("jump.au"));

jumpsound.play();

width += 5;

blood += 1;

ham[j].x = 1194 + ham[j].x;// 让汉堡在原来的位置上消失

}

}

// 当碰到水母

for (int k = 0; k < shui.length; k++) {

double g = Math

.sqrt((baby.x - shui[k].x) * (baby.x - shui[k].x) + (baby.y - shui[k].y) * (baby.y - shui[k].y));

if (g < 82) {

AudioClip hurtsound = Applet.newAudioClip(this.getClass().getResource("eat.au"));

hurtsound.play();

width -= 5;

blood -= 2;

}

}

// 血量为0

if (width == 0) {

state = false;

 

ov = new over(playsound);

ov.setVisible(true);// 开启结束面板

 

}

 

}

 

public void keyPressed(KeyEvent e) {

int key = e.getKeyCode();

if (key == KeyEvent.VK_UP) {

upjump = 10;

 

}

if (key == KeyEvent.VK_LEFT) {// 假如是向左键被按下

baby.babyimage = babyleft;

left = true;// 打开左走开关

/*****

 * 注意不要repaint了,不然打开开关后还是会画

 ********/

// repaint();

}

 

if (key == KeyEvent.VK_RIGHT) {

// 假如是向右键被按下

 

baby.babyimage = babyright;

right = true;// 打开右走开关

 

// repaint();

}

}

 

// 线程执行

public void run() {

 

while (state) {

Draw();

 

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

 

public void keyReleased(KeyEvent e) {// 释放时候关闭开关

int key = e.getKeyCode();

if (key == KeyEvent.VK_LEFT) {

left = false;

}

if (key == KeyEvent.VK_RIGHT) {

right = false;

}

if (key == KeyEvent.VK_UP) {

upjump = 0;

}

}

 

public void keyTyped(KeyEvent e) {

// TODO Auto-generated method stub

 

}

}

 


 

 

【游戏感悟】

1.各类间的调用是一大难题,尤其是在游戏死亡后重新开始游戏,以及游戏胜利后某些类的关闭及某些类的重新调用。

2.游戏入口时,如果用流式布局的话,达不到美观的效果,所以用g画笔实现,在playgress类,根据所画的大小来确定监听范围,从而到达按钮的效果

3.游戏音乐设置,调用Applet.newAudioClip

设置无标题super.setUndecorated(true);

设置字体样式以及大小g.setFont(new Font("", Font.BOLD, 20));

4.画游戏背景以及海滩时,都用了两张图片,第一张图片贴在屏幕0,0点,第二张图片在第一张图片右边

5.海绵宝宝为了营造出向左右走的效果,也调用了两张图片。

6.人物上下左右跳时,遇到一个问题,就是不能同时两个方向走,为了实现同时向右且向上跳的效果,设置boolean开关

7.背景循环时,遇到问题

 * 代码

// if(backX1==0)backX0=1194;

// if(backX0==0)backX1=1194;

,这样做不能保证backX1正好是0,因为是每次减去速度,减完之后不一定恰好是0.修改后:

 

if (backX1 < 0) // 背景循环

backX0 = 1194 + backX1;

 

if (backX0 <= 0)

backX1 = 1194 + backX0;

8.还有一些不断改进的问题,时间比较久远我有些不太记得了……总之不断的想办法改进游戏,使其更流畅更美观。

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值