写了一个线程小游戏,放上来供有需要童鞋的参考.
1.界面
2. Mario类的几个方法
Mario切换方向和图片的方法:
public void run() {
while (true) {
ThreadSleep.threadSleep(10);
// 切换Mario走的方向的实际操作,同时要判断是否撞到障碍物
if (isLeft) {
currentImg = img_Left;
hit = isHit("left");
if (!hit) {
if (this.x > 0) {
this.x -= speedX;
}
}
} else if (isRight) {
currentImg = img_Right;
hit = isHit("right");
if (!hit) {
if (this.x > 300) {
DeX -= speedX; // 背景图动
} else {
this.x += speedX;
}
}
}
if (isUp) {
hit = isHit("up");
if (!hit) {
if (!isJump) {
if (!isGravite) {
isJump = true;
jump();
}
}
}
}
if (isStop) {
currentImg = img_Stop;
}
isHit("down");
}
}
判断是否死亡:
void isDie() {
while (true) {
ThreadSleep.threadSleep(10);
if (this.y < constNum.frameWidth) {
this.y += this.speedY;
} else {
break;
}
}
ms.stopMusic();
}
判断是否撞击到障碍物:
public boolean isHit(String direction) {
Rectangle rectangle = null;
// 构造一个预判断的Mario长方形
if ("left".equals(direction)) {
rectangle = new Rectangle(this.x - this.speedX - DeX, this.y,
this.width, this.height);
} else if ("right".equals(direction)) {
rectangle = new Rectangle(this.x + this.speedX - DeX, this.y,
this.width, this.height);
} else if ("up".equals(direction)) {
rectangle = new Rectangle(this.x - DeX, this.y - this.speedY,
this.width, this.height);
} else if ("down".equals(direction)) {
rectangle = new Rectangle(this.x - DeX, this.y + this.speedY,
this.width, this.height);
}
// 遍历所有的障碍物
List<entity> list = EntitySingleton.getInstance().getList();
for (int i = 0; i < list.size(); i++) {
entity entitys = list.get(i);
// 自带的长方形判断是否重叠的方法
boolean intersects = entitys.intersects(rectangle);
if (intersects) {
// 判断碰撞的障碍物是否是金币
if (entitys instanceof Coin) {
music ms = new music("musics//coin.wav", false);
list.remove(entitys);
}
// 判断撞到的是否是墙壁
if (entitys instanceof Brick) {
isDie();
}
return true;
}
}
return false;
}
引力方法,使Mario不会悬浮在空中不掉下来:
public void isGravite() {
while (true) {
ThreadSleep.threadSleep(10);
// 判断Mario在高空中
if (constNum.marioY > this.y) {
if (!isHit("down")) {
if (!isJump) {
this.y += this.speedY;
isGravite = true;
continue;
}
}
}
isGravite = false;
}
}
Mario跳跃的方法:
private void jump() {
new Thread() {
public void run() {
for (int i = 0; i < 130; i++) {
hit = isHit("up");
if (!hit) {
y -= speedY;
ThreadSleep.threadSleep(10);
} else {
break;
}
}
for (int i = 0; i < 130; i++) {
hit = isHit("down");
if (!hit) {
if (y < constNum.marioY) {
y += speedY;
ThreadSleep.threadSleep(10);
}
} else {
break;
}
}
isJump = false;
};
}.start();
}
3.主窗口paint方法的重写:
public void paint(Graphics g) {
// super.paint(g);
// 画一张空图,把Mario这些画在空图上,再把空图画在窗口上,这样解决了刷新是一闪一闪的问题
Image img = createImage(getWidth(), getHeight());
Graphics gg = img.getGraphics();
gg.drawImage(backgroundImg, mario.getDeX(), 0, null);// 画背景图
mario.drawSelf(gg);// 画Mario
list = EntitySingleton.getInstance().getList();// 遍历障碍物,并画出来
for (int i = 0; i < list.size(); i++) {
entity entitys = list.get(i);
entitys.drawSelf(gg, mario.getDeX());
}
if (MyListener.listBullet != null) {// 画子弹
for (int i = 0; i < MyListener.listBullet.size(); i++) {
Bullet bullet = MyListener.listBullet.get(i);
if (bullet.isDie) {
MyListener.listBullet.remove(bullet);
}
bullet.drawSelf(gg, 0);
}
}
g.drawImage(img, 0, 0, null);// 把创的空图画在窗口上
}
4.我们最喜欢的源码:
package frame;
import java.awt.LayoutManager;
import javax.swing.JFrame;
public abstract class ParentFrame extends JFrame {
private static final long serialVersionUID = 1L;
public ParentFrame(int width, int height) {
this(null, width, height, null);
}
public ParentFrame(String title, int width, int height,
LayoutManager manager) {
setTitle(title);
setSize(width, height);
setLocationRelativeTo(null);
// setUndecorated(true);
// getGraphicsConfiguration().getDevice().setFullScreenWindow(this);//
// 全屏必须在去修饰之后
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(manager);
initUI();
setVisible(true);
initListener();
}
/**
* 添加组件
*/
public abstract void initUI();
/**
* 添加监听
*/
public abstract void initListener();
}
package frame;
import java.awt.Graphics;
import java.awt.Image;
import java.util.List;
import Entity.Bullet;
import Entity.Coin;
import Entity.Mario;
import Entity.Pipe;
import Entity.constNum;
import Entity.entity;
import MyListener.MyListener;
import Singleton.EntitySingleton;
import Util.ThreadSleep;
public class MainFrame extends ParentFrame {
Image backgroundImg;
Mario mario;
Pipe pipe;
Coin coin;
List<entity> list;
/**
*
* @param width
* 窗口的宽度
* @param height
* 窗口的高度
*/
public MainFrame(int width, int height) {
super(width, height);
// 开一个线程不断刷新界面
new Thread() {
public void run() {
while (true) {
ThreadSleep.threadSleep(100);
// 重绘
repaint();
}
};
}.start();
mario.isGravite();
}
// 初始化Mario
public void initUI() {
backgroundImg = GetImage.getImages("image\\startBack.jpg");
mario = new Mario();
}
// 添加键盘监听
public void initListener() {
this.addKeyListener(new MyListener(mario));
}
// 重写paint方法
public void paint(Graphics g) {
// super.paint(g);
// 画一张空图,把Mario这些画在空图上,再把空图画在窗口上,这样解决了刷新是一闪一闪的问题
Image img = createImage(getWidth(), getHeight());
Graphics gg = img.getGraphics();
gg.drawImage(backgroundImg, mario.getDeX(), 0, null);// 画背景图
mario.drawSelf(gg);// 画Mario
list = EntitySingleton.getInstance().getList();// 遍历障碍物,并画出来
for (int i = 0; i < list.size(); i++) {
entity entitys = list.get(i);
entitys.drawSelf(gg, mario.getDeX());
}
if (MyListener.listBullet != null) {// 画子弹
for (int i = 0; i < MyListener.listBullet.size(); i++) {
Bullet bullet = MyListener.listBullet.get(i);
if (bullet.isDie) {
MyListener.listBullet.remove(bullet);
}
bullet.drawSelf(gg, 0);
}
}
g.drawImage(img, 0, 0, null);// 把创的空图画在窗口上
}
public static void main(String[] args) {
MainFrame frame = new MainFrame(constNum.frameWidth,
constNum.frameHeight);
}
}
package Entity;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.List;
import Singleton.EntitySingleton;
import Util.ThreadSleep;
import frame.GetImage;
public class Mario extends Rectangle implements Runnable {
int speedX = 1;
int speedY = 1;
// 标记是否暂停游戏
public static boolean pause = false;
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getSpeedX() {
return speedX;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public int getSpeedY() {
return speedY;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
public Image getCurrentImg() {
return currentImg;
}
public void setCurrentImg(Image currentImg) {
this.currentImg = currentImg;
}
// 分别是当前的图片,向左走时的图片,向右走时的图片,不动时的图片
private Image currentImg;
private Image img_Left;
private Image img_Right;
private Image img_Stop;
public Mario() {
super(10, constNum.marioY, 32, 34);
// Mario一初始化,就播放背景音乐
ms = new music("musics//backgroundmusic_new.wav", true);
// 加载图片
img_Left = GetImage.getImages("image\\mari_left.gif");
img_Right = GetImage.getImages("image\\mari_right.gif");
img_Stop = GetImage.getImages("image\\mari1.png");
currentImg = img_Stop;
new Thread(this).start();
}
/**
* 把自己画到画布上
*
* @param gg
* 画笔Graphics
*/
public void drawSelf(Graphics gg) {
gg.drawImage(currentImg, this.x, this.y, width, height, null);
}
// 记录偏移量
private int DeX = 0;
// 标记是否撞到障碍物
boolean hit;
public void run() {
while (true) {
ThreadSleep.threadSleep(10);
// 切换Mario走的方向的实际操作,同时要判断是否撞到障碍物
if (isLeft) {
currentImg = img_Left;
hit = isHit("left");
if (!hit) {
if (this.x > 0) {
this.x -= speedX;
}
}
} else if (isRight) {
currentImg = img_Right;
hit = isHit("right");
if (!hit) {
if (this.x > 300) {
DeX -= speedX; // 背景图动
} else {
this.x += speedX;
}
}
}
if (isUp) {
hit = isHit("up");
if (!hit) {
if (!isJump) {
if (!isGravite) {
isJump = true;
jump();
}
}
}
}
if (isStop) {
currentImg = img_Stop;
}
isHit("down");
}
}
/**
* 判断是否死亡
*/
void isDie() {
while (true) {
ThreadSleep.threadSleep(10);
if (this.y < constNum.frameWidth) {
this.y += this.speedY;
} else {
break;
}
}
ms.stopMusic();
}
/**
* 判断是否撞击到障碍物,并且要进行预判断,不然很有可能会卡在障碍物里面出不来
*
* @param direction
* 预判断需要的Mario的下一步的方向
* @return
*/
public boolean isHit(String direction) {
Rectangle rectangle = null;
// 构造一个预判断的Mario长方形
if ("left".equals(direction)) {
rectangle = new Rectangle(this.x - this.speedX - DeX, this.y,
this.width, this.height);
} else if ("right".equals(direction)) {
rectangle = new Rectangle(this.x + this.speedX - DeX, this.y,
this.width, this.height);
} else if ("up".equals(direction)) {
rectangle = new Rectangle(this.x - DeX, this.y - this.speedY,
this.width, this.height);
} else if ("down".equals(direction)) {
rectangle = new Rectangle(this.x - DeX, this.y + this.speedY,
this.width, this.height);
}
// 遍历所有的障碍物
List<entity> list = EntitySingleton.getInstance().getList();
for (int i = 0; i < list.size(); i++) {
entity entitys = list.get(i);
// 自带的长方形判断是否重叠的方法
boolean intersects = entitys.intersects(rectangle);
if (intersects) {
// 判断碰撞的障碍物是否是金币
if (entitys instanceof Coin) {
music ms = new music("musics//coin.wav", false);
list.remove(entitys);
}
// 判断撞到的是否是墙壁
if (entitys instanceof Brick) {
isDie();
}
return true;
}
}
return false;
}
/**
* Mario正在跳跃
*/
private void jump() {
new Thread() {
public void run() {
for (int i = 0; i < 130; i++) {
hit = isHit("up");
if (!hit) {
y -= speedY;
ThreadSleep.threadSleep(10);
} else {
break;
}
}
for (int i = 0; i < 130; i++) {
hit = isHit("down");
if (!hit) {
if (y < constNum.marioY) {
y += speedY;
ThreadSleep.threadSleep(10);
}
} else {
break;
}
}
isJump = false;
};
}.start();
}
private boolean isGravite = false;
/**
* 地面对Mario的引力 当Mario跳上去跳到障碍物上,之后不再掉下来的时候,要把Mario吸下来
*/
public void isGravite() {
while (true) {
ThreadSleep.threadSleep(10);
// 判断Mario在高空中
if (constNum.marioY > this.y) {
if (!isHit("down")) {
if (!isJump) {
this.y += this.speedY;
isGravite = true;
continue;
}
}
}
isGravite = false;
}
}
private boolean isLeft = false;
private boolean isRight = false;
private boolean isUp = false;
private boolean isDown = false;
private boolean isJump = false;
private boolean isStop = true;
public music ms;
public void setLeft(boolean isLeft) {
this.isLeft = isLeft;
}
public void setRight(boolean isRight) {
this.isRight = isRight;
}
public void setUp(boolean isUp) {
this.isUp = isUp;
}
public void setDown(boolean isDown) {
this.isDown = isDown;
}
public void setStop(boolean isStop) {
this.isStop = isStop;
}
public void setDeX(int DeX) {
this.DeX = DeX;
}
public int getDeX() {
return DeX;
}
public boolean getUp() {
return isUp;
}
public boolean getLeft() {
return isLeft;
}
public boolean getRight() {
return isRight;
}
}
package MyListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import Entity.Bullet;
import Entity.Mario;
import Util.ThreadSleep;
public class MyListener implements KeyListener {
Mario mario;
public static ArrayList<Bullet> listBullet = new ArrayList<Bullet>();
public MyListener(Mario mario) {
this.mario = mario;
}
public void keyTyped(KeyEvent e) {
}
/**
* 鼠标按下时,改变标记Mario走的方向的标记变量
*/
public void keyPressed(KeyEvent e) {
// 87 w
// 83 s
// 65 a
// 68 d
// 71 g
if (e.getKeyCode() == 87) {
mario.setUp(true);
mario.setStop(false);
} else if (e.getKeyCode() == 83) {
mario.setDown(true);
mario.setStop(false);
} else if (e.getKeyCode() == 65) {
mario.setLeft(true);
mario.setStop(false);
} else if (e.getKeyCode() == 68) {
mario.setRight(true);
mario.setStop(false);
} else if (e.getKeyCode() == 32) {// 游戏暂停
mario.pause = !mario.pause;
System.out.println(mario.pause);
if (mario.pause) {
mario.ms.stopMusic();
} else {
mario.ms.startMusic();
}
new Thread() {
public void run() {
ThreadSleep.threadSleepOther(500);
};
}.start();
} else if (e.getKeyCode() == 71) {// 发射子弹
Bullet bullet = new Bullet(mario.x, mario.y, mario.getLeft());
listBullet.add(bullet);
}
}
/**
* 鼠标松开时,改变标记Mario走的方向的标记变量
*/
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 87) {
mario.setUp(false);
mario.setStop(true);
} else if (e.getKeyCode() == 83) {
mario.setDown(true);
mario.setStop(false);
} else if (e.getKeyCode() == 65) {
mario.setLeft(false);
mario.setStop(true);
} else if (e.getKeyCode() == 68) {
mario.setRight(false);
mario.setStop(true);
}
}
}
package Singleton;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import Entity.Brick;
import Entity.Coin;
import Entity.Pipe;
import Entity.entity;
public class EntitySingleton {
/**
* 单例模式,不能在外面初始化
*/
private EntitySingleton() {
}
static EntitySingleton instance = new EntitySingleton();
public static EntitySingleton getInstance() {
return instance;
}
List<entity> list = null;
public List<entity> getList() {
if (list == null) {
list = new ArrayList<entity>();
readMap();
}
return list;
}
List<String> listLine = new ArrayList<String>();
/**
* 读地图
*/
public void readMap() {
BufferedReader br = null;
String lines = null;
try {
FileReader reader = new FileReader("marioMap.txt");
br = new BufferedReader(reader);
while ((lines = br.readLine()) != null) {
listLine.add(lines);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
br = null;
}
}
}
int row = 0;
int col = 0;
if (listLine != null) {
row = listLine.size();
col = listLine.get(0).length();
}
for (int i = 0; i < row; i++) {
String line = listLine.get(i);
for (int j = 0; j < col; j++) {
char eg = line.charAt(j);
int x = j * 30;
int y = i * 30;
if ('1' == eg) { // 1代表墙,2代表金币,3代表水管,5代表墙
;
} else if ('2' == eg) {
Coin coin = new Coin(x, y);
list.add(coin);
} else if ('3' == eg) {
Pipe pipe = new Pipe(x, y);
list.add(pipe);
} else if ('5' == eg) {
Brick brick = new Brick(x, y);
list.add(brick);
}
}
}
}
}
package Util;
import Entity.Mario;
public class ThreadSleep {
/**
* 休眠 控制游戏暂停的
*
* @param millis
*/
public static void threadSleepOther(long millis) {
while (Mario.pause) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// try {
// Thread.sleep(millis);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
/**
* 其他地方用来休眠的
*
* @param millis
*/
public static void threadSleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package frame;
import java.awt.Image;
import javax.swing.ImageIcon;
public class GetImage {
// 通过路径得到图片
public static Image getImages(String path) {
// 做缓存
return new ImageIcon(path).getImage();
}
}
package Entity;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class music implements Runnable {
private String filename;
SourceDataLine auline = null;
public music(String wavfile, boolean isLoop) {
filename = wavfile;
this.isLoop = isLoop;
// 开启自己
new Thread(this).start();
}
boolean isLoop = true;
public void run() {
do {
File soundFile = new File(filename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
// FloatControl
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format); // 打开
} catch (Exception e) {
e.printStackTrace();
return;
}
auline.start(); // 开始播放
int nBytesRead = 0;
byte[] abData = new byte[512];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream
.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
} while (isLoop);
}
public void stopMusic() {
isLoop = false;
if (auline != null) {
auline.stop();
}
}
public void startMusic() {
if (auline != null)
auline.start();
}
}
package Entity;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
public class entity extends Rectangle {
// 继承了长方形,主要用来判断俩个长方形是否有重叠部分
// 长方形的只要特征点是:一定有它的x坐标,y坐标,width宽度,height高度
Image img;
public entity(int x, int y, int width, int height, String path) {
super(x, y, width, height);// 初始化自己
this.img = new ImageIcon(path).getImage();
}
/**
* 用来把自己放到画布上
*
* @param gg
* 画笔Graphics
* @param deX
* 相对窗口的偏移量
*/
public void drawSelf(Graphics gg, int deX) {
gg.drawImage(img, this.x + deX, y, width, height, null);
}
}
package Entity;
import java.awt.Graphics;
public class Pipe extends entity {// 水管
public Pipe(int x, int y) {
super(x, y, 60, 120, "image\\pipe.png");
}
public void drawself(Graphics gg, int deX) {
drawSelf(gg, deX);
}
}
package Entity;
import java.awt.Graphics;
public class Coin extends entity {
public Coin(int x, int y) {
super(x, y, 15, 25, "image//coin_brick_null.png");
}
public void drawself(Graphics gg, int deX) {
drawSelf(gg, deX);
}
}
package Entity;
import java.awt.Graphics;
import Util.ThreadSleep;
public class Bullet extends entity implements Runnable {
int speedX = 2;
public boolean isDie = false;
public Bullet(int x, int y, boolean isLeft) {
super(x, y, 10, 10, "image//brick.png");
if (!isLeft) {
speedX = -2;
} else {
speedX = 2;
}
new Thread(this) {
}.start();
}
public void drawself(Graphics gg, int deX) {
drawSelf(gg, deX);
}
public void run() {
while (!isDie) {
ThreadSleep.threadSleep(10);
x += speedX;
}
}
public void isDie() {// 当子弹飞出视野,要即使清除掉它,减少内存的浪费
if (this.x < 0 || this.x > constNum.frameWidth) {
isDie = true;
}
}
}
package Entity;
import java.awt.Graphics;
public class Brick extends entity {
public Brick(int x, int y) {
super(x, y, 30, 30, "image//brick.png");
}
public void drawself(Graphics gg, int deX) {
drawSelf(gg, deX);
}
}
package Entity;
public class Blank { // 各种障碍物
}
package Entity;
public class constNum {
// 存放常量,很大的方便之处
// eg:这样方便对窗体大小调整后,不用再修改判断是否离开了视野
public static final int marioY = 358;
public static final int frameWidth = 550;
public static final int frameHeight = 450;
}