Какой эффективный способ остановить поток на неопределенный срок?

1

Прямо сейчас, я в настоящее время делаю игру с мрамором, где два игрока пытаются стрелять в мрамор в круг. Побеждает игрок с большим количеством шариков в круге.

Игрок, чей ход он будет, нажимает и перетаскивает в противоположном направлении, в котором они хотят, чтобы мяч двигался (вроде как прострел). В моем Marble class есть поток, который начинается, когда вызывается щелчок мыши. Мрамор будет двигаться и отскакивать от стен, медленно достигая нулевой скорости X и Y.

Однако, когда это происходит, мой поток продолжает циклироваться и питаться через процессор компьютеров.

Вы можете сказать, что поток все еще запущен, потому что он продолжает печатать скорость, даже когда мяч остановился.

Мне нужна помощь, пытаясь найти эффективный способ остановки потока без использования метода stop() класса Thread.

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 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);
    }
}
Теги:
multithreading

2 ответа

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;

РЕДАКТИРОВАТЬ :

Я сам тестировал решение, он работал. Видеть:

Изображение 174551

  • 0
    Не сравнивайте float с == .
  • 0
    @ OrtwinAngermeier Отредактировано.
Показать ещё 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();

Ещё вопросы

Сообщество Overcoder
Наверх
Меню