这篇文章基于随机小球游戏V1的那篇博客来进行修改,我们想要的是点击一下就有一个小球随机方向移动,而不是和V1一样,发射一串小球。接下来就来实现这个效果。

       首先,我们这次的思想是不是再像V1一样,点击一次增加一个线程,而是点击一次的话 就在点击位置增加一个小球,然后就让小球在窗体内随机运动,然后再点击,就再生成一个小球,要做到这一点的话,线程的添加就不能再放在监听器方法中,而是在GameUI的showUI中就添加一个线程,并且只用这一个即可。所以首先在showUI方法中进行修改,先修改窗体名称,用setTitle方法,然后添加新的线程对象,ThreadBall threaball = new ThreadBall();Thread tf = new Thread(threadball);tf.start();这就增加了线程对象。接下来就写一下GameUI类中的mouseClick方法,这里我们打算每次点击一下就添加一个小球,所以要生成一个具备一定属性的小球对象,然后存入到我们设定好的一个存储的地方,这里我们用List来保存。

       我们用面向对象的思维来思考这个问题,那么我们就可以把这一个一个的小球看成一个一个的对象,我们自然就可以创建一个关于小球的类,取名为Ball,设定属性int x,y表示小球的初始位置,int speedx,speedy表示小球的位移的偏移量,画笔属性Graphics用来画出小球,接着设置颜色属性Color来表示小球的颜色,最后是size来表示小球的大小。接着就创建构造方法便于小球的创建,代码为public Ball(int x,int y,int speedx,int speedy,int size,Color color){this.x=x;this.y=y;this speedx=speedx;this.speedy=speedy;this.size=size;this.color=color;},这就可以在点击时方便地创建一个小球。然后刚刚只是给出了小球的属性,接下来要让小球动起来,设置drawBall方法,方法体内设置画笔颜色,并且是用画笔画圆即可,方法体代码为g.setColor(color);g.drawOval(x,y,size,size);这就完成了drawBall的方法体的撰写,方法的参数我们用画笔,即Graphics g。接着要保证小球一直运动并且不出边界,此处我们设置move方法,对小球的当前x和y进行判定,当比边界大时设置反向移动,代码为if(x<0||x>1000){speedx= -speedx};if(y<0||y>800){speedy= -speedy};x+=speedx;y+=speedy;这就完成了小球的移动,或者说是每次遍历时每个小球(对象)的x和y的更新,从而显现出小球在窗体上不断移动的效果,先附上完整的Ball类代码

package com.ms.ThreadGameV2;

import java.awt.*;

public class Ball {
    int x,y;
    Color color;
    int size;//球的大小
    int speedx;
    int speedy;

    public Ball(int x,int y,Color color,int size,int speedx,int speedy){
        this.x=x;
        this.y=y;
        this.color=color;
        this.size=size;

        this.speedx=speedx;
        this.speedy=speedy;
    }

    public void  drawBall(Graphics g){
        g.setColor(color);
        g.fillOval(x,y,size,size);
    }

    public void move(){
        if(x<0||x>1000){
            speedx = -speedx;
        }
        if (y<0||y>800){
            speedy = -speedy;
        }
        x+=speedx;
        y+=speedy;

    }







}

然后我们就接着来补充线程类的代码,首先我们创建线程类ThreadRun,并让它继承于Runnable类,接着设置属性,因为在run方法中要遍历存储小球的List,所以设置属性List<Ball>=new balllist[10000];然后要得到画笔,所以设置画笔Graphics g;然后这里我们也设置GameUI类的属性,目的是同步balllist的信息,否则可能会造成balllist的初始值空指针问题,(这里注意一下也可以还是设置构造方法传的是balllist,但是在run方法中要加上balllist中对象是否为空指针的判断,为null的话就continue即可)代码为public GameUI gui;接着我们就创建构造方法为的是能够把GameUI中的对象传递进来,同时传递画笔g,代码为public ThreadRun(GameUI gui,Graphics g){this.gui = gui;this,g=g;}然后写run方法,我们使用while true的无限循环,对balllist进行无限次的循环遍历,对其中的球进行绘画。方法体为while(true){for(int i=0;i<gui.getIndex;i++){Ball ball=balllist[i];ball.drawBall(g);ball.move();}},然后为了形成小球移动而不是发射一串小球的效果,我们这里每次循环遍历之前都画白板吧上一次遍历画出的小球覆盖一下买就可以得到我们想要的效果。代码为g.setColor(Color.WHITE);g.drawRect(0,0,1000,800);并且加一个30ms的延时,代码为Thread.sleep(30);这里附上ThreadRun的完整代码

package com.ms.ThreadGameV2;

import java.awt.*;

public class ThreadRun implements Runnable {
    Ball[] balllist;
    Graphics g;
    GameUI gui;

    public ThreadRun(GameUI gui,Graphics g){
        this.balllist=gui.balllist;
        this.g=g;
        this.gui=gui;
    }

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            g.setColor(Color.white);
            g.fillRect(0,0,1000,800);

            //从数据结构中取出来球,所以我们需要一个Ball类
            for ( int i=0;i< gui.getIndex();i++){
                Ball ball =balllist[i];
                ball.drawBall(g);
                ball.move();
            }

        }
    }
}

接着就回过头来完善GameUI的内容,我们在一开始是创建了ThreadRun的对象,显然这里要修改一下,构造线程对象时,要加上一些参数,修改的代码为ThreadRun thread  = new ThreadRun(gui,g);然后在showUI的创建线程类对象代码的前面,获得窗体的画笔,并且创建新的GameUI类的对象,代码为GameUI gui = new GameUI ;这就改完了showUI的方法体。接着我们要添加方法getIndex,使gui能够调用该方法来得到当前的balllist的非null元素的个数,接着就可以在run方法中完成对balllist的遍历。附上完整的GameUI类的代码

package com.ms.ThreadGameV2;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;

public class GameUI extends JFrame {
    private Graphics g;
    Ball[] balllist =new Ball[10000];
    int index =0;
    public int getIndex(){
        return index;
    }


    public void showUI(){
        setSize(1000,800);
        setTitle("线程游戏V2");
        setVisible(true);
        g = getGraphics();
        ThreadRun trun = new ThreadRun(this,g);
        Thread tf = new Thread(trun);
        tf.start();


        //直接实例化一个匿名内部类对象,用Lamda来写更方便,先用第一种方法
        addMouseListener(new MouseAdapter() {
            Random random =new Random();
            @Override
            public void mouseClicked(MouseEvent e) {
                //每次点击都创建一个球
                int speedx = random.nextInt(8)-5;
                int speedy = random.nextInt(7)-3;
                Color color =new Color(random.nextInt(500000000));
                int size = random.nextInt(50);
                Ball ball =new Ball(e.getX(),e.getY(),color,size,speedx,speedy);
                //把球存进去
                balllist[index++]=ball;


            }
        });
    }



    public void paint(Graphics g){
        super.paint(g);


    }

    public static void main(String[] args) {
        GameUI gf = new GameUI();
        gf.showUI();



    }








}

最后附上游戏效果截图

java小球反弹_java实现小球碰撞反弹 java小球游戏_构造方法