java画图板之四——滑稽大作战(鼠标可移动小球,发射子弹)

先上效果图,子弹左边的滑稽就是我的鼠标控制的,网上搜了好久的在csdn上插入视频,没解决,从爱奇艺上传,现在还在转码……,有没有大佬教下方法

这里写图片描述

其实只是用了“滑稽”的图片而已,把图片拿掉本质上就是用鼠标控制一个可以发射很多小球的小球,以及一堆随机产生的带有随机速度的小球而已,前面是讲解,代码放在了最后

随机产生的小球思路在java画图板之三中有过详细描述,我们创建了四个类
窗体类Framball——建立画图板,开启线程的主方法
球类Ball——描述了球的属性,包括横纵坐标xy,半径R,运动方法run()
画球线程drawBall——将产生的球放在一个队列中,存到一定数量
动球线程moveBall——将队列中的球依次释放,每个球都执行Ball中的run,crash(),注意,此时要放在循环中,延迟时间0.1秒,这样球每run一次坐标就改变一次,花费0.1秒,看起来就像运动一样

其实稍微改一改就是球球大作战,飞机大战,大鱼吃小鱼这类游戏了。现在我们升级到“滑稽”版本,我们需要一下四步操作


在Ball类中增加crash()方法,使用勾股定理判定距离,当接近时双方速度交换(有一个问题没有处理好,就是有时候新出生的两个球靠得很近,导致黏在一起抖动,因为速度一直在交换,以其中一个消失告终,用图片代替后又多了一个问题是图片的背景色和窗体背景色一致,看不出来是图片,但是有时候撞在一起时其中一张的图片背景色会掩盖另一张的表情)
添加blood属性,传入crash()中进行值的变化,同时添加一个方法画出血条,

public void crash(ArrayList<Ball> list) {
}
public void blood(Graphics g) {
}


增加子弹类Bullet,和球类相似,但是血量设为1,纵坐标速度设为0,大小恒定,同时设置自己的队列ListB,每次点击就新建一个Bullet

public class Bullet {
}

最重要的是Bullet是在点击后才产生,所以我们还需要


增加鼠标监听器ListenerBall,新建一个Ball的实例(作为我们控制的球),在mouseMoved事件中将鼠标的坐标传给这个球,并且加到包含球的队列List中,也就是说,他是List的第一个球。

public class ListernerBall implements MouseListener,MouseMotionListener{
}

同时,在mousePressed事件中新建Bullet的实例,注意,Ball的实例新建是放在属性中,因为我们只需要控制一个Ball,而Bullet的实例是放在mousePressed中,每点击一次,就产生一个bullet,并且加到队列ListB里面去

Bullet bullet=new Bullet(fb,g,listB);
        bullet.x=e.getX();
        bullet.y=e.getY();
        listB.add(bullet);

这个队列同样放在moveBall中执行,当然是新建一个for循环,因为这个队列和Ball队列的长度不一样


在moveBall中我们做了另外一个关键性的改动——引入次画布ig,还记得我们让小球动起来的本质是什么吗?在Ball类中的run()方法每执行一次就改变一次圆心坐标,但是这样其实原来的小球还在,就会使得图形变成一条射线,在画图板之三中我用的方法是在run()中一开始还没改坐标时画一个原坐标的背景色的小球来擦除,这样的方法在增加crash()方法后容易判断不准原坐标,导致擦除偏移留下像残月一样的痕迹

 g.setColor(fb.getContentPane().getBackground());//切换为背景色
        g.fillOval(x - R - speedX, y - R - speedY, R, R);//减掉R表示在坐标替换为圆心,减掉速度表示将上一个小球掩盖掉

而次画布ig比较奇特,所有的球都花在次画布上,画完之后,次画布消失,也就是所一切都消失不见了,无论上面有多少个球,包括子弹,然后再在极短的时间之内重画出来,然后各种球和子弹再画出他们变换坐标后的位置,这样可以永久性,无残留的擦除上一次所留下的一切痕迹,毕竟皮之不存毛将焉附?

所以他应该放在哪呢?当然是moveBall中了,因为他画的频率一定和画Ball和Bullet的频率一样,才能保证画面的正常,所以要一起放在run()中,而且要放在前面,先画出来,在run()的最后,别忘了次画布ig是我们新造的,所以要移花接木,将ig画在g上

Image img = fb.createImage(fb.getWidth(), fb.getHeight()); // 图片要多次创建!!!!!!
Graphics2D ig = (Graphics2D) img.getGraphics();
……
g.drawImage(img, 0, 0, fb);//等小球画完之后一次性清除一遍,如果在for循环中会闪烁

以下是代码
窗体类,相对于画图板之三,需要增加一些代码:创建控制的小球,传入监听器

package BallGame;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.util.ArrayList;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Framball extends JFrame{
    private static ArrayList <Ball> list = new ArrayList<Ball>();//新建的list一定要初始化
    static ArrayList<Bullet>listB=new ArrayList<Bullet>();
    public  Graphics g;


    public void showUI() {

        this.setTitle("滑稽大作战");
        this.setSize(1200, 700);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        // 设置布局,流式布局
        this.setLayout(new FlowLayout());

        this.setVisible(true);
        g=this.getGraphics();

        //添加监听器
//      g.setColor(Color.black);
        Ball ball=new Ball(this,g);
        ball.set(600, 500, 40);
        list.add(ball);
        ListernerBall l=new ListernerBall(this,g,ball);
        l.setB(list,listB);
        this.addMouseListener(l);
        this.addMouseMotionListener(l);
    }

    public static void main(String[] args) {
        Framball fb=new Framball();
        fb.showUI();//可见才能得到画布

        drawBall db=new drawBall();
        db.setG(fb,fb.g,list);//传三个参数
        db.start();//启动线程,线程启动后,他会自动运行,并且只运行run方法,想要运行其他方法必须另外调用

        moveBall mb=new moveBall();
        mb.setL(fb, fb.g,list,listB);
        mb.start();//启动线程

    }

}

Ball类

package BallGame;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.Icon;
import javax.swing.ImageIcon;

public class Ball {
    Random rand = new Random();
    int R = rand.nextInt(70) + 20;// 半径
    int x = rand.nextInt(1200);
    int y = rand.nextInt(600);
    int blood = rand.nextInt(5) + 3;
    int fblood = blood;//满血值
    int choose=rand.nextInt(4);
    //球的几种颜色的不停变换
    int []arr= {255,192,128,64,0};
    int i=rand.nextInt(arr.length);
    int j=rand.nextInt(arr.length);
    int k=rand.nextInt(arr.length);

    int speedX =  70-R-20, speedY = 70-R-20;// 小球运动速度

    private int r = 1200, d = 700;// 右限,下限
    public Graphics g;
    public Framball fb;

    public Ball(Framball fb,Graphics g) {//这个是必须要传的,放在构造方法中
        this.g = g;
        this.fb = fb;

    }
    public void set(int x,int y,int R) {//这个是可选传的
        this.x=x;
        this.y=y;
        this.R=R;
    }


    public void draw(Graphics g) {  
        Color C=new Color(arr[k],arr[j],arr[i]);
        g.setColor(C);

        ImageIcon image = null;
        //这里可以使用不同的图片,从而出现不同的滑稽表情,为了方便我全部用了一种
        if(choose==0) image = new ImageIcon("D:\\Pictures\\项目图片\\滑稽2.jpg");
        else if(choose==1)image = new ImageIcon("D:\\Pictures\\项目图片\\滑稽2.jpg");
        else if(choose==2)image = new ImageIcon("D:\\Pictures\\项目图片\\滑稽2.jpg");
        else if(choose==3)image = new ImageIcon("D:\\Pictures\\项目图片\\滑稽2.jpg");

        Image img=image.getImage();
        g.drawImage(img, x-R, y-R,2*R,2*R, null);
//      g.fillOval(x - R, y - R, 2*R, 2*R);//这里是宽和高,所以要写直径,否则就会在奇怪的地方相撞

        //设置字体,将血量值显示在图片内部
        Font mf=new Font(Font.DIALOG,Font.BOLD,15);
        g.setFont(mf);
        g.setColor(Color.black);
        String  Blood=String.valueOf(blood);//同步球的血量和血值
        g.drawString(Blood, x-6, y+5);

    }

    public void run() {// 表示小球的运动,可反弹,必须放在while循环中才能跑起来

        if (y >= d)// 当y接触到下限时
            speedY *= -1;// 速度反向
        else if (y <= 0)
            speedY *= -1;
        if (x >= r)
            speedX *= -1;
        else if (x <= 0)
            speedX *= -1;
        x += speedX;// 每一次run,就移动一次速度值
        y += speedY;
        // 只执行一次run方法,小球是不会动的

    }

    public void crash(ArrayList<Ball> list) {
        for (int i = 0; i < list.size(); i++) {
            Ball ball = list.get(i);
            if (this != ball) {
                int d = (x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y);// 横纵坐标平方和
                int dis = (R + ball.R) * (R + ball.R);// 半径之和的平方
                if (d <= dis) {
                    int sx=speedX;
                    int sy=speedY;
                    speedX=ball.speedX;
                    speedY=ball.speedY;
                    ball.speedX=sx;
                    ball.speedY=sy;
                }
            }
        }
    }

    public void blood(Graphics g) {
        g.setColor(Color.red);
        g.fillRect(x - R, y - 5 - R, blood * 10, 5);
        g.setColor(Color.gray);//失血时空去的血条
        g.fillRect(x - R + blood * 10, y - R - 5, (fblood - blood) * 10, 5);
    }
}

drawBall线程

package BallGame;

//import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;

public class drawBall extends Thread{
    ArrayList <Ball> list;//注意后面的写法,写错会报空指针异常
    Graphics g;
    Framball fb;
    int count=0;
    public void setG(Framball fb,Graphics g,ArrayList <Ball> list) {
        this.g=g;
        this.fb=fb;
        this.list=list;
    }

    public void run() {
        while(true) {
            if(list.size()==10) {
                break;//设置小球存储的个数
            }

            Ball b=new Ball(fb,g);//循环中不断新建一个球

            list.add(b);//把球放进队列    
        }
    }
}

moveBall线程

package BallGame;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.ImageIcon;

public class moveBall extends Thread  {
    int i = 0;
    private ArrayList<Ball> list;// =new ArrayList<Ball>();
    private ArrayList<Bullet> listB;
    private Framball fb;
    private Graphics g;
    private int x,y,r=20;
    Color c = new Color(238, 238, 238);//背景色
    int count=1;//控制球的数量

    public void setL( Framball fb, Graphics g,ArrayList<Ball> list,ArrayList<Bullet> listB) {
        this.list = list;
        this.listB=listB;
        this.fb = fb;
        this.g = g;
    }

    public void run() {

        while (true) {//创建次画布放在循环中,每次循环完一次队列中的小球之后就重新添加画布

            Image img = fb.createImage(fb.getWidth(), fb.getHeight()); // 图片要多次创建!!!!!!
            Graphics2D ig = (Graphics2D) img.getGraphics();
//          ImageIcon imgi=new ImageIcon("D:\\Pictures\\项目图片\\滑稽2.jpg");
//          ig.drawImage(imgi.getImage(), 0, 0,fb.getWidth(),fb.getHeight(),fb);//设置背景图片

            ig.setColor(Color.white);
            ig.fillRect(0, 0, fb.getWidth(), fb.getHeight());
            Ball mball=list.get(0);
            mball.draw(ig);
            mball.blood(ig);
            mball.crash(list);

            if(list.size()<7) {
                drawBall db=new drawBall();
                db.setG(fb,fb.g,list);//传参数
                db.start();//启动线程,线程启动后,他会自动运行,并且只运行run方法,想要运行其他方法必须另外调用
            }
            //执行队列中的小球
            for (i = 1; i < list.size(); i++) {
                // list.get(i).clear();
                Ball ball=list.get(i);
                ball.draw(ig);
                ball.blood(ig);
                ball.crash(list);
                ball.run();

            }
            //执行子弹
            for(i=0;i<listB.size();i++) {
                Bullet bullet=listB.get(i);
                bullet.draw(ig);
                bullet.crash(list);
                bullet.run();
            }

            g.drawImage(img, 0, 0, fb);//等小球画完之后一次性清除一遍,如果在for循环中会闪烁

            try {
                sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Bullet类


package BallGame;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.Icon;
import javax.swing.ImageIcon;

public class Bullet {
    ArrayList<Bullet>listB;
    int x,y;
    int R = 5;// 半径
    private int blood =  1;
    private int speedX =  10, speedY = 0;// 子弹运动速度
    Graphics g;
    Framball fb;

    public Bullet(Framball fb,Graphics g,ArrayList<Bullet>listB) {
        this.g = g;
        this.fb = fb;
        this.listB=listB;

    }
    public void set(int x,int y,int R) {
        this.x=x;
        this.y=y;
        this.R=R;
    }

    public void draw(Graphics g) {  
        ImageIcon image =new ImageIcon("D:\\Pictures\\项目图片\\滑稽图标.png");
        Image img=image.getImage();
        g.drawImage(img, x-R, y-R,2*R,2*R, null);
    }

    public void run() {// 子弹运动
        x += speedX;// 每一次run,就移动一次速度值
        y += speedY;
        // 只执行一次run方法,子弹是不会动的
    }

    public void crash(ArrayList<Ball> list) {
        for (int i = 1; i < list.size(); i++) {
            Ball ball = list.get(i);

                int d = (x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y);// 横纵坐标平方和
                int dis = (R + ball.R) * (R + ball.R);// 半径之和的平方
                if (d <= dis) {
                    blood--;
                    ball.blood--;
                    if (ball.blood <= 0 && list != null)
                        list.remove(i);// 不能和下行调换,否则会指针溢出,因为下面的可能为空
                    if (blood <= 0)
                        listB.remove(this);

                }

        }
    }

}

监听器ListenerBall

package BallGame;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

public class ListernerBall implements MouseListener,MouseMotionListener{
    private int x,y,r=20;
    public Graphics g;
    Framball fb;
    Ball ball=new Ball(fb,g);

    ArrayList<Bullet>listB;
    ArrayList<Ball> list;
    public ListernerBall(Framball fb,Graphics g,Ball ball) {
        // TODO Auto-generated constructor stub
        this.g=g;
        this.fb=fb;
        this.ball=ball;

    }
    public void setB(ArrayList<Ball> list,ArrayList<Bullet>listB) {
        this.list=list;
        this.listB=listB;
    }
    public void mouseClicked(MouseEvent e) {    

    }

    public void mouseDragged(MouseEvent e) {
//      x=ball.x;
//      y=ball.y;
    }
    @Override
    public void mouseMoved(MouseEvent e) {
        // TODO Auto-generated method stub
        ball.x=e.getX();
        ball.y=e.getY();
    }
    @Override
    public void mousePressed(MouseEvent e) {
        // TODO Auto-generated method stub
        Bullet bullet=new Bullet(fb,g,listB);
        bullet.x=e.getX();
        bullet.y=e.getY();
        listB.add(bullet);
    }
    @Override
    public void mouseReleased(MouseEvent e) {

    }
    @Override
    public void mouseEntered(MouseEvent e) {

    }
    @Override
    public void mouseExited(MouseEvent e) {

    }

}

看代码多累啊,再“滑稽”一下,玩了一个小时哈哈
这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_37465638/article/details/81255658
今日推荐