线程游戏总结

多线程游戏总结:
首先说一下什么是线程。
每个java程序都至少有一个线程----主线程。当程序启动时,JVM会创建主线程,并在该线程中调用程序的main()方法。
线程可理解为“程序内部一个独立的运行单位”
我们之前写过的代码其实都是单线程,特点就是按顺序执行
public void ma(){
mb();
mc();
}
public void mb(){

}
public void mc(){

}
这段代码就要等mb()执行完之后再执行mc()。
public void ma(){
在线程中调用mb();-------->
在线程中调用mc();-------->
}
public void mb(){

}
public void mc(){

}
这是多线程模型,它将会并行执行mb()和mc()。
java中多线程是如何实现的?
可通过继承Thread类实现,也可通过实现Runnable接口实现,但是那种方法会更好一点呢?
个人觉得还是实现Runnable接口好一点,因为java中一个类只能继承一个父类,但可以实现多个接口,所以一个继承Thread的多线程
类就不能再继承其他类,可能会造成局限。
下面来说一下线程游戏
坦克大战游戏开发思路:
首先创建一个窗体,在上面画一个矩形,再通过ImageIcon和Image的方法把图片加到矩形上,这个矩形就是一个线程,先让这个矩形
在窗体上运动。
坦克这个线程创建好以后我们需要想的是它有哪些属性,哪些方法?
首先,坦克要有运行速度,方向这些属性,还要有坦克行走的方法和坦克发子弹的方法,初步实现只要用这些方法,之后还要用到坦克与坦克之间是否相撞的
方法,与墙相撞的方法。
下面是坦克类中的方法
/**
*坦克行走的方法
*/
public void forward(){
//画坦克的初始位置
g.drawImage(image, x,y, Wide, High,null);
g.setColor(Color.black);
try {
Thread.sleep(200);
//将原来的坦克覆盖掉
g.fillRect(x, y, Wide, High);
if(direction.equals("Down")){
y+=speed;setDirection("Down");
}
if(direction.equals("Up")){
y-=speed;setDirection("Up");
}
if(direction.equals("Left")){
x-=speed;setDirection("Left");
}
if(direction.equals("Rigth")){
x+=speed;setDirection("Right");
}
//画坦克图片
g.drawImage(image, x, y, Wide, High,null);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(y+High>550||x+Wide>750||x<0||y<0){
speed=-speed;
}
}
/**
* 设置坦克方向
*/
public void setDirection(String direction){
this.direction=direction;
//得到坦克对应方向的图片
if(direction.equals("Up")){
image=iconU.getImage();
}
if(direction.equals("Down")){
image=iconD.getImage();
}
if(direction.equals("Left")){
image=iconL.getImage();
}
if(direction.equals("Right")){
image=iconR.getImage();
}
}
/**
* 返回坦克方向
* @return
*/
public String getDirection(){
return direction;
}
/**
* 坦克发子弹的方法
*/
public void attacks(){
//创建子弹的对象
Bullet bu=new Bullet(g,x,y,direction);
Thread thread=new Thread(bu);
thread.start();
}
这里是创建一个我们可以用键盘控制的坦克-----玩家坦克
public class MykeyListener extends Frame implements KeyListener, Infor {

Graphics g;
Tank mytank;
// 判断是否前进
boolean state = true;

/**
* 构造器
*/
public MykeyListener(Graphics g, Tank mytank) {
this.g = g;
this.mytank = mytank;
this.panel.addKeyListener(this);
}

public void keyPressed(KeyEvent e) {
// 得到键盘上按下键的值
int key = e.getKeyCode();
// 设置坦克的名字
mytank.setName("Mytank");
// 一按下就设置为true
state = true;
while (state) {// 如果为真
if (key == 37) {// 如果按下的是左
mytank.setDirection("Left");
mytank.forward();
}
if (key == 39) {// 按下的是右
mytank.setDirection("Right");
mytank.forward();
}
if (key == 38) {// 按下的是上
mytank.setDirection("Up");
mytank.forward();
}
if (key == 40) {// 按下的是下
mytank.setDirection("Down");
mytank.forward();
}
if (key == 88) {// 按下的是x,即发子弹
mytank.attack();
}
if (state) {// 完成一次跳出循环
break;
}
}
}

public void keyReleased(KeyEvent e) {
state = false;// 按键松下停止前进
}

public void keyTyped(KeyEvent e) {
}

}
这样一个简单的坦克类就写好了,下面要做的就是如何让坦克发子弹。所以我们先要定义一个子弹类。
子弹的属性有速度,方向。还有一个发射的方法。下面是代码:
/**
* 构造方法
*/
public Bullet(Graphics g, int x, int y, String direction) {
this.g = g;
this.x = x;
this.y = y;
this.direction = direction;
}

/**
* 子弹射击的方法
*/
public void shot(){
// 根据坦克的方向和坐标设置子弹发射的初始坐标
if (direction.equals("Right")) {
x= x + Wide + 5;
y = y + High / 2 - 2;
}
if (direction.equals("Left")) {
y = y + High / 2 - 2;
x -= 10;
}
if (direction.equals("Up")) {
x = x + Wide / 2 - 2;
y -= 10;
}
if (direction.equals("Down")) {
x = x + Wide / 2 - 2;
y = y + High + 5;
}
//子弹运动
while(bl){
//画子弹
g.drawImage(image, x, y, Wide_B, High_B,null);
g.setColor(Color.black);
try {
Thread.sleep(40);
//把原来的子弹盖住
g.fillRect(x, y, Wide_B, High_B);
//判断坦克的方法
if (direction.equals("Right")) {
x += speed;
}
if (direction.equals("Left")) {
x -= speed;
}
if (direction.equals("Up")) {
y -= speed;
}
if (direction.equals("Down")) {
y += speed;
}
//再画一颗子弹
g.drawImage(image, x, y, Wide_B, High_B, null);
} catch (InterruptedException e) {
e.printStackTrace();
}
//判断子弹是否打到坦克
for(int i=0;i<tanklist.size();i++){
//获取坦克对象
Tank tank=tanklist.get(i);
//获取坦克线程对象
Thread thread=tankthread.get(i);
//得到坦克的矩形框
Rectangle rect_T = new Rectangle(tank.x, tank.y,Wide, High);
//得到子弹的矩形框
Rectangle rect_B = new Rectangle(x, y,Wide_B, High_B);
//判断两个矩形是否相交
boolean bool = rect_T.intersects(rect_B);
if(bool){ //如果相交
//把子弹遮住
g.setColor(Color.black);
g.drawRect(x, y, Wide_B, High_B);
//将坦克的线程停止
thread.stop();
//把坦克遮住
g.fillRect(tank.x, tank.y,Wide, High);
//把坦克对象从集合中移除
tanklist.remove(i);
//把坦克线程从集合中移除
tankthread.remove(i);
//循环放出爆炸图片
for(int k=1;k<9;k++){
ImageIcon icon = new ImageIcon("Image/blast"+k+".gif");
Image image = icon.getImage();
g.drawImage(image,tank.x,tank.y,Wide,High,null);
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//把最后一张爆炸图片清除
g.setColor(Color.black);
g.fillRect(tank.x, tank.y, Wide, High);
bl=false;
}
}
}
}

/**
* 重写子弹的run方法
*/
public void run() {
shot();
}
现在在窗体上的坦克可以发子弹了,但它们之间还是可以相互穿过的,那是因为我们还没让坦克和子弹进行判断是否遇到东西了。下面就该写一个
方法来让它们可以进行判断。
/**
* 判断坦克是否和其他会坦克相撞
* @return true:会相撞 false:不会相撞
*/
public int isSeparate(int m, int n) {
// 得到坦克中心点所在行列值
int i = ((m / 30) * 30 + Wide / 2) / 30;
int j = ((n / 30) * 30 + High / 2) / 30;
// 初始定义为没相撞
int bool = 0;
// 矩形隔离带的坐标和尺寸
int x = 0, y = 0, wide = 0, high = 0;
// 判断坦克方向,设置隔离带的坐标和尺寸
if (this.getDirection().equals("Right")) {
x = this.x + Wide + 1;
y = this.y;
wide = 5;
high = High;

}
if (this.getDirection().equals("Left")) {
x = this.x - 5 - 1;
y = this.y;
wide = 5;
high = High;

}
if (this.getDirection().equals("Up")) {
x = this.x;
y = this.y - 5 - 1;
wide = Wide;
high = 5;

}
if (this.getDirection().equals("Down")) {
x = this.x;
y = this.y + 5 + 1 + High;
wide = Wide;
high = 5;

}

// 根据方向确定隔离带矩形
Rectangle rect_R = new Rectangle(x, y, wide, high);
// 判断是否与坦克相撞
// 循环与每个坦克对象比较
for (int k = 0; k < list_alltank.size(); k++) {
// 获取坦克对象
Tank othertank = list_alltank.get(k);
// 绘制坦克矩形
Rectangle rect_O = new Rectangle(othertank.x, othertank.y, Wide,
High);
// 判断是否会相交
if (rect_R.intersects(rect_O)) {
bool = 1;
return bool;
}
}
int[][] array = new int[8][2];
for (int p = 0; p < 8; p++) {
for (int q = 0; q < 2; q++) {
array[p][q] = -1;
}
}
if (i > 0 && j > 0 && i < 28 && j < 18) {
// 左上
if (walls[i - 1][j - 1] == 2 || walls[i - 1][j - 1] == 4
|| walls[i - 1][j - 1] == 6) {
array[0][0] = i - 1;
array[0][1] = j - 1;
}// 上
if (walls[i][j - 1] == 2 || walls[i][j - 1] == 4
|| walls[i][j - 1] == 6) {
array[1][0] = i;
array[1][1] = j - 1;
}// 右上
if (walls[i + 1][j - 1] == 2 || walls[i + 1][j - 1] == 4
|| walls[i + 1][j - 1] == 6) {
array[2][0] = i + 1;
array[2][1] = j - 1;
}// 左
if (walls[i - 1][j] == 2 || walls[i - 1][j] == 4
|| walls[i - 1][j] == 6) {
array[3][0] = i - 1;
array[3][1] = j;
}// 右
if (walls[i + 1][j] == 2 || walls[i + 1][j] == 4
|| walls[i + 1][j] == 6) {
array[4][0] = i + 1;
array[4][1] = j;
}// 左下
if (walls[i - 1][j + 1] == 2 || walls[i - 1][j + 1] == 4
|| walls[i - 1][j + 1] == 6) {
array[5][0] = i - 1;
array[5][1] = j + 1;
}// 下
if (walls[i][j + 1] == 2 || walls[i][j + 1] == 4
|| walls[i][j + 1] == 6) {
array[6][0] = i;
array[6][1] = j + 1;
}// 右下
if (walls[i + 1][j + 1] == 2 || walls[i + 1][j + 1] == 4
|| walls[i + 1][j + 1] == 6) {
array[7][0] = i + 1;
array[7][1] = j + 1;
}
for (int k = 0; k < 8; k++) {
if (array[k][0] != -1) {
Rectangle rect_w = new Rectangle(array[k][0] * 30,
array[k][1] * 30, Wide_wall, High_wall);
if (rect_R.intersects(rect_w)) {
bool = 1;
return bool;
}
}

}

}
return bool;
}
思路:我们这里的每个坦克都是一个矩形,所以在进行坦克之间是否相撞的判断中用到了Rectangle里面的方法,判断两个矩形是否相交。
我们首先判断现在坦克的方向,找出相对于它前方的一个小矩形,在创建坦克对象的时候我们已经把坦克对象加入了一个集合中,这时坦克每
走一步就进行一次判断是否与其他坦克相撞,如果相撞就让坦克改变方向,让它们向别的方向前进。把这个方法放入子弹类中也可以进行子弹
是否与坦克相撞,道理都是一样的。只是子弹与坦克相撞时让先让坦克这个线程停止并把它盖住然后在坦克的位置加上爆炸的图片,最后再把子弹的
线程停止。代码如下:
public void isHitTank() {
// 判断子弹是否撞到坦克
for (int i = 0; i < list.size(); i++) {
// 获取坦克对象
Tank tank1 = list.get(i);
// if(tank.name.equals("enemy")){

// }

// 创建两个矩形框
Rectangle rect_T = new Rectangle(tank1.x, tank1.y, Wide, High);
Rectangle rect_B = new Rectangle(x, y, WideB, HighB);
// 判断两个矩形是否相交
boolean bool = rect_T.intersects(rect_B);
// 如果子弹打到坦克
if (bool) {
System.out.println(tank.countBomb);
tank.countBomb++;
// 播放爆炸声效
File file = new File("C:\\Users\\aa\\Desktop\\蓝杰 java\\坦克大战素材\\music\\blast.wav");
URL rul;
try {
rul = file.toURL();
AudioClip ac = Applet.newAudioClip(rul);
ac.play();
} catch (MalformedURLException e) {
e.printStackTrace();
}
// 把子弹遮住
g.setColor(Color.BLACK);
g.fillRect(x, y, WideB, HighB);
// 获取坦克线程对象
Thread thread = Threadlist.get(i);
// 坦克线程停止,把坦克遮住
thread.stop();
g.fillRect(tank1.x, tank1.y, Wide, High);
// 把坦克,线程对象从list移除,清除对象
list.remove(i);
list_alltank.remove(i);// 从所有坦克队列中移除,
Threadlist.remove(i);
// 循环放出爆炸效果图
for (int k = 1; k < 9; k++) {
ImageIcon icon = new ImageIcon("Image/blast" + k + ".gif");
Image image = icon.getImage();
g.drawImage(image, tank1.x, tank1.y, Wide, High, null);
try {
this.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 把爆炸图清除
g.fillRect(tank1.x, tank1.y, Wide, High);
Food fd=new Food(g);
list_food.add(fd);
// //道具出现
fd.ShowUp();
if(tank.Allcount>0){
// 随机得到起始坐标
int x = rood.nextInt(1000);
int y = 0;
// 创建tank对象
Tank newtank = new Tank(g, x, y);
// 将坦克对象加入到list集合中
list_alltank.add(newtank);
list_enemy.add(newtank);
// 设置坦克初始图片
newtank.setImage();
// 创建坦克线程对象
TankRunnable tr = new TankRunnable(newtank, g);
Thread newthread = new Thread(tr);
// 将线程对象添加到list集合中
Threadlist.add(newthread);
// 线程启动
newthread.start();
//坦克总数减一
tank.Allcount--;
System.out.println(tank.Allcount);
}
// 判断输赢
if (tank1.name.equals("Mytank")) {
javax.swing.JOptionPane.showMessageDialog(null,
"GAME OVER !!");
}
if (list_enemy.size() == 0) {
javax.swing.JOptionPane.showMessageDialog(null,
"Very good !!");
}
this.stop();// 停止该子弹线程

}

}
}
现在坦克之间不会相撞,遇到子弹也会爆炸,下面要做的就是加墙壁。
最开始我们是把墙壁加到一个集合中,在进行判断时与坦克和子弹都是一样的。但最后运行的时候出现了卡的问题,因为墙的对象比较多
坦克每走一步都要判断是否遇到墙,子弹没走一步也要进行判断,而且子弹还要判断是墙还是草地或是水,是墙还要判断是哪一种墙,能不能
打过去,所以造成了卡的问题。后来我们把墙的对象存到数组里,这样坦克和子弹走一步只要判断当前方法是否有东西与它相撞就可以了。
下面是墙的类:
public class Wall {

// 墙的坐标
public int x;
public int y;
// 墙的尺寸
public int wide, high;
// 已经确定的图片属性
public Image image;
// 可以选择的图片
ImageIcon icon1 = new ImageIcon("Image/wall.gif");
ImageIcon icon2 = new ImageIcon("Image/walls.gif");
ImageIcon icon3 = new ImageIcon("Image/tugai0.gif");
ImageIcon icon4 = new ImageIcon("Image/tugai3.gif");
ImageIcon icon5 = new ImageIcon("Image/grass.gif");
ImageIcon icon6 = new ImageIcon("Image/water.gif");
// 墙的类型
public int type; // 1——黄色1/4 2 ——黄色 3 ——白色1/4 4——白色 5——草地 6——水

/**
* 构造器
*/
public Wall(int type, int x, int y, int wide, int high) {
this.x = x;
this.y = y;
this.wide = wide;
this.high = high;
this.type = type;
if (this.type == 1) {
image = icon1.getImage();
}
if (this.type == 2) {
image = icon2.getImage();
}
if (this.type == 3) {
image = icon3.getImage();
}
if (this.type == 4) {
image = icon4.getImage();
}
if (this.type == 5) {
image = icon5.getImage();
}
if (this.type == 6) {
image = icon6.getImage();
}

}
}
这样一个简单的坦克大战就差不多了,剩下的就是优化问题了,我们的优化也只是做了道具的这个方法。
下面是道具的类:
public class Food implements Infor{
Graphics g;
Random rd=new Random();
Random rand=new Random();
int x=rd.nextInt(30);
int y=rd.nextInt(20);
ImageIcon icon;
String name;
/**
* 构造方法
*/
public Food(Graphics g){
this.g=g;
}
/**
* 道具出现的方法
* @param x x表示它的横坐标
* @param y y表示它的纵坐标
*/
public void ShowUp(){
Random rand=new Random();
int count=rand.nextInt(10);
if(count<5){
//设为星星的图片
icon=new ImageIcon("image/star.gif");
name="star";
}else if(count>=5){
//设为钟表的图片
icon=new ImageIcon("image/timer.gif");
name="timer";
}
g.drawImage(icon.getImage(), x*30, y*30, WideU, HighU, null);
}
/**
* 返回道具横坐标
*/
public int getX(){
return x*30;
}
/**
* 返回道具横坐标
*/
public int getY(){
return y*30;
}
/**
* 返回道具名字
*/
public String getName(){
return name;
}
/**
* 道具是钟表所进行的方法
*/
public void Timer(){
System.out.println("dddddddd");
int n=rand.nextInt(3);
//遍历坦克线程集合
for(int i=0;i<n;i++){
//得到坦克线程
Thread thread=Threadlist.get(i);
// try {
// thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
thread.stop();
}
}
/**
* 道具是星星所进行的方法
*/
public void Star(){

}
}
思路:当我们的坦克打到一个坦克时会在窗体上选一个随机的位置来出现这个道具,坦克吃道具时就把道具当成墙,当坦克遇到道具时马上让
道具消失,根据道具的不同也有不同的效果。当遇到时钟时会随机停止窗体上的坦克。遇到星星时坦克发子弹的个数会增加,这里我们没写它的
实现,只写了时钟的方法。

我们做的坦克大战并不完善,而且还有一些问题有待优化,这里讲的只是一种思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值