什么是无限期停止线程的有效方法?

时间:2014-05-28 16:15:55

标签: java multithreading

现在,我正在制作一个大理石游戏,其中两个玩家尝试将他们的弹珠射入一个圆圈。圈中有更多弹珠的玩家获胜。

轮到它的玩家会在他们希望球移动的相反方向上点击并拖动(有点像吊索)。在我的Marble class中,有一个线程在释放鼠标时启动。大理石将移动并从墙壁反弹,慢慢达到零和X的Y和Y速度。

但是当发生这种情况时,我的线程会继续循环并通过计算机CPU进食。

你可以知道线程仍然在运行,因为即使球停止移动它也会继续打印速度。

我需要帮助尝试找出一种有效的方法来停止线程而不使用Thread类的stop()方法。

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Ball
{
    float x, y, lastX, lastY;
    int width, height;
    float xVelocity, yVelocity;
    float speed;
    public Ball()
    {        
        width = (int) (Math.random() * 50 + 10);
        height = (int) (Math.random() * 50 + 10);
        x = (float) (Math.random() * (Marble.gamePanel.getWidth() - width) + width/2);
        y = (float) (Math.random() * (Marble.gamePanel.getHeight() - height) + height/2);
        lastX = x;
        lastY = y;
        xVelocity = (float) Math.random() * speed*2 - speed;
        yVelocity = (float) Math.random() * speed*2 - speed;
    }

    public void update()
    {
        lastX = x;
        lastY = y;
        x += xVelocity;
        y += yVelocity;

        if(x + width/2 >= Marble.gamePanel.getWidth())
        {
            xVelocity *= -0.75;
            x = Marble.gamePanel.getWidth() - width/2;
        }
        else if (x - width/2 <= 0)
        {
            xVelocity *= -0.75;
            x = width/2;
        }

        if (y + height/2 >= Marble.gamePanel.getHeight())
        {
            yVelocity *= -0.75;
            y = Marble.gamePanel.getHeight() - height/2;
        }
        else if (y - height/2 <= 0)
        {
            yVelocity *= -0.75;
            y = height/2;
        }
    }
}

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.ArrayList;
/**
 * Write a description of class Marble here.
 * 
 * @author (Ryan Lawes) 
 * @version (5-23-14)
 */
public class Marble extends JFrame implements MouseListener, MouseMotionListener
{
    public static GamePanel gamePanel = new GamePanel();
    private boolean running = false;
    private int fps = 60;
    private int frameCount = 0;
    private int mouseXC;//X coordinate of where the mouse was clicked
    private int mouseYC;//Y coordinate of where the mouse was clicked
    private int mouseXR;//X coordinatte of where the mouse was released
    private int mouseYR;//Y coordinatte of where the mouse was released
    public Marble()
    {
        super("Fixed Timestep Game Loop Test");
        Container cp = getContentPane();
        cp.setLayout(new BorderLayout());
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(1,2));
        cp.add(gamePanel, BorderLayout.CENTER);
        cp.add(p, BorderLayout.SOUTH);
        setSize(500, 500);
        addMouseListener(this);
        addMouseMotionListener(this);
    }      

    public static void main(String[]args)
    {   
        Marble mrb = new Marble();
        mrb.setVisible(true);
    }      

    //Starts a new thread and runs the game loop in it.   
    public void runGameLoop()   
    {
        Thread loop = new Thread()
            {
                public void run()
                {
                    gameLoop();
                }
            };
        loop.start();
    } 

    //Only run this in another Thread!
    private void gameLoop()
    {      
        //This value would probably be stored elsewhere.      
        final double GAME_HERTZ = 30.0;
        //Calculate how many ns each frame should take for our target game hertz.
        final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
        //At the very most we will update the game this many times before a new render.
        //If you're worried about visual hitches more than perfect timing, set this to 1.
        final int MAX_UPDATES_BEFORE_RENDER = 5;
        //We will need the last update time.
        double lastUpdateTime = System.nanoTime();
        //Store the last time we rendered.
        double lastRenderTime = System.nanoTime();
        //If we are able to get as high as this FPS, don't render again.
        final double TARGET_FPS = 60;
        final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
        //Simple way of finding FPS.
        int lastSecondTime = (int) (lastUpdateTime / 1000000000);
        while(running)
        {
            double now = System.nanoTime();
            int updateCount = 0;

            //Do as many game updates as we need to, potentially playing catchup.
            while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER)
            {   
                gamePanel.update();
                lastUpdateTime += TIME_BETWEEN_UPDATES;
                updateCount++;
            }
            //If for some reason an update takes forever, we don't want to do an insane number of catchups.
            //If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.

            if(now - lastUpdateTime > TIME_BETWEEN_UPDATES)
            {
                lastUpdateTime = now - TIME_BETWEEN_UPDATES;
            }

            //Render. To do so, we need to calculate interpolation for a smooth render.
            float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES));
            drawGame(interpolation);
            lastRenderTime = now;
            //Update the frames we got.
            int thisSecond = (int) (lastUpdateTime / 1000000000);
        }

        //////if(gamePanel.ballXVel == 0 && gamePanel.ballYVel == 0)
        //////{
        //////    running = !running;
        //////}
    }

    private void drawGame(float interpolation)
    {
        gamePanel.setInterpolation(interpolation);
        gamePanel.repaint();
    }

    public void mouseClicked(MouseEvent e)
    {

    }

    public void mousePressed(MouseEvent e)
    {
        /**
         * I want to make it so you have to wait until the marble is done rolling before you can click again
         */
        mouseXC = (int) e.getPoint().getX();
        mouseYC = (int) e.getPoint().getY();
    }

    public void mouseReleased(MouseEvent e)
    {
        mouseXR = (int) e.getPoint().getX();
        mouseYR = (int) e.getPoint().getY();
        gamePanel.ballXVel = (mouseXC - mouseXR)/2;
        gamePanel.ballYVel = (mouseYC - mouseYR)/2;
        //////////while(gamePanel.ballXVel != 0 && gamePanel.ballYVel != 0)
        //////////{
        running = !running;
        runGameLoop();
        ////////}
    }

    public void mouseEntered(MouseEvent e)
    {

    }

    public void mouseExited(MouseEvent e)
    {

    }

    public void mouseDragged(MouseEvent e)
    {

    }

    public void mouseMoved(MouseEvent e)
    {

    }
}

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
public class GamePanel extends JPanel
{
    float interpolation;
    float ballX, ballY, lastBallX, lastBallY;
    int ballWidth, ballHeight;
    float ballXVel, ballYVel;
    int lastDrawX, lastDrawY;
    int ballSpeed = 10;
    public GamePanel()
    {
        ballX = lastBallX = 100;
        ballY = lastBallY = 100;
        ballWidth = 25;
        ballHeight = 25;
    }

    public void setInterpolation(float interp)
    {
        interpolation = interp;
    }

    public void update()
    {
        lastBallX = ballX;
        lastBallY = ballY;
        ballX += ballXVel;
        ballY += ballYVel;

        //Slowly decreases the ball's velocity
        ballXVel *= 0.975;
        ballYVel *= 0.975;
        //Prints out the velocity
        //////////if(ballXVel > 0.3 && ballYVel > 0.3)
        //////////{
        System.out.println(ballXVel);
        System.out.println(ballYVel);
        //////////}
        //If the ball is at a slow enough velocity, it will stop moving alltogether
        if(ballXVel > 0 && ballYVel > 0)//if x and y velocity are positive
        {
            if(ballXVel < 0.3 && ballYVel < 0.3)
            {
                ballXVel = 0;
                ballYVel = 0;
            }
        }
        if(ballXVel < 0 && ballYVel < 0)//if x and y velocity are negative
        {
            ballXVel *=-1;//Changes to a positive number so it is easier to check
            ballYVel *=-1;//Changes to a positive number so it is easier to check
            if(ballXVel < 0.3 && ballYVel < 0.3)
            {
                ballXVel = 0;
                ballYVel = 0;
            }
            ballXVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
            ballYVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
        }
        if(ballXVel < 0 && ballYVel > 0)//if x velocity is negative and y velocity is positive
        {
            ballXVel *=-1;//Changes to a positive number so it is easier to check
            if(ballXVel < 0.3 && ballYVel < 0.3)
            {
                ballXVel = 0;
                ballYVel = 0;
            }
            ballXVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
        }
        if(ballXVel > 0 && ballYVel < 0)//if x velocity is positive and y velocity is negative
        {
            ballYVel *=-1;//Changes to a positive number so it is easier to check
            if(ballXVel < 0.3 && ballYVel < 0.3)
            {
                ballXVel = 0;
                ballYVel = 0;
            }
            ballYVel *=-1;//Changes the velocity back if it doesn't get set to 0. Even if it does, 0*-1 is still 0
        }

        if(ballX + ballWidth/2 >= getWidth())
        {
            ballXVel *= -0.8;
            ballX = getWidth() - ballWidth/2;
        }
        else if(ballX - ballWidth/2 <= 0)
        {
            ballXVel *= -0.8;
            ballX = ballWidth/2;
        }

        if(ballY + ballHeight/2 >= getHeight())
        {
            ballYVel *= -0.8;
            ballY = getHeight() - ballHeight/2;
        }
        else if(ballY - ballHeight/2 <= 0)
        {
            ballYVel *= -0.8;
            ballY = ballHeight/2;
        }
    }

    public void paintComponent(Graphics g)
    {
        //BS way of clearing out the old rectangle to save CPU.
        g.setColor(getBackground());
        g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
        g.setColor(Color.RED);
        int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
        int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
        g.fillOval(drawX, drawY, ballWidth, ballHeight);
        lastDrawX = drawX;
        lastDrawY = drawY;
        g.setColor(Color.BLACK);
    }
}

2 个答案:

答案 0 :(得分:1)

您评论过的代码:

//////if(gamePanel.ballXVel == 0 && gamePanel.ballYVel == 0)
//////{
//////    running = !running;
//////}

将它放在while(running)循环中,如:

float EPSILON = 0.00000001f;
if(Math.abs(gamePanel.ballXVel - 0.0f) < EPSILON && Math.abs(gamePanel.ballYVel - 0.0f) < EPSILON)
    {
        running = false;
    }

你把它放在了while循环之外。解释器永远不会到达代码,因为running=true;

编辑:

我自己测试了解决方案,但它确实有效。见:

enter image description here

答案 1 :(得分:0)

我建议您更改gamePanel.update()方法以返回boolean值,以指示奇迹是否仍在移动。

然后,您可以使用该方法的结果在奇迹停止后突破循环。

GamePanel#update()之类的东西。

final float epsilon = 0.00001f;
return (Math.abs(ballXVel) < epsilon) && (Math.abs(ballXVel) < epsilon);

Marble#gameLoop之类的东西:

running = !gamePanel.update();