图形 2d 对象的参考在孤儿线程中不起作用 [英] Reference of graphics 2d object doesn't work in orphan Thread

查看:54
本文介绍了图形 2d 对象的参考在孤儿线程中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 JPanel 中使用 Graphics2D 设计一个简单的游戏.我可以通过覆盖 paintComponent() 方法来绘制普通对象.但是当我在孤儿线程中引用 Graphics2D 对象时,它不起作用.我哪里出错了?

I am trying to design a simple game using Graphics2D in a JPanel. I am able to draw normal objects by overriding the paintComponent() method. But when I reference the Graphics2D object inside a orphan Thread, it does not work. Where am I going wrong?

public void paintComponent(Graphics g) {

    super.paintComponent(g);
    g2d = (Graphics2D) g;

    g2d.drawString("sample",60,100); //Works fine

    if(<Certain Condition>){
       new Thread(new Runnable(){
            //Some Code Here
            public void run() {
               try{
                 g2d.drawString("sample2",60,100); //Does not work.. :(
                 System.out.println("Test Print"); //Shows Output
               }
               catch (Exception e)
               {
               }
             }
       }).start();
   }
}

这里是完整的代码供参考.这本质上是一个乒乓球"游戏.它运作良好,但当球击中前锋时,我无法强调得分的增加.代码的重要部分被突出显示.这是 SSCCE.

Here is the complete code for reference. This is essentially a 'ping pong ball' game. Its working well but I am not able to highlight an increase in score when the ball hits the striker. The important part of code is highlighted. It's SSCCE.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

import java.util.Random;

public class MovingBall extends JPanel {
    int XPos, YPos;
    int speedX, speedY;
    int diameter;
    private JButton jButton1 = new JButton();
    private JButton jButton2 = new JButton();
    private JLabel jLabel1 = new JLabel();
    private static Timer timer;
    private static MovingBall movingball;
    private int w,h;

    private int strikerHeight;
    private int strikerWidth;

    private int score;
    private boolean isBallMoving;

    int strikerYPos;
    Graphics2D g2d;

    public MovingBall() {

        //Striker Properties
        strikerHeight = 100;
        strikerWidth = 20;
        strikerYPos = strikerHeight/2;

        //Ball Properties
        isBallMoving = false;
        XPos = strikerWidth + 5;
        YPos = 0;
        Random r = new Random();
        speedX = 2+ Math.abs(r.nextInt()) % 5;
        speedY = 2+ Math.abs(r.nextInt()) % 5;
        diameter = 50;

        //UI Objects
        try {
            jbInit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        movingball = this; //Helps to access the current class object in inner classes

        //Create a timer for animation
        timer = new Timer(1, new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                movingball.repaint();
            }    
        });
        timer.start();
    }

    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        g2d = (Graphics2D) g;

        Dimension size = getSize();
        Insets insets = getInsets();

        w =  size.width - insets.left - insets.right;
        h =  size.height - insets.top - insets.bottom;

        //Paint the striker
        g2d.setColor(Color.DARK_GRAY);
        if(strikerYPos < strikerHeight/2) //Top End
            g2d.fillRect(0,0, strikerWidth, strikerHeight);
        else if(strikerYPos > (h-strikerHeight/2)) //Bottom End
            g2d.fillRect(0,h-strikerHeight, strikerWidth, strikerHeight);
        else //Anywhere in the middle
            g2d.fillRect(0,strikerYPos - (strikerHeight/2), strikerWidth, strikerHeight);

        //Paint the ball
        if (isBallMoving) {
            XPos += speedX;
            YPos += speedY;

            g2d.drawOval(XPos, YPos, diameter,diameter);

            if((XPos+diameter) >= w)
            {
                //speedX *= -1;
                speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
                XPos = w-diameter-1;
            }
            if(XPos <= strikerWidth)
            {
                if((YPos+diameter/2) >= (strikerYPos-strikerHeight/2) && (YPos+diameter/2) <= (strikerYPos+strikerHeight/2))
                {
                    score++;

                    //////////////////////////////////////////////////////////////////////
                    /////THIS IS THE PART TO FOCUS ON///////////////////////////////////////
                    /////WHEN THE BALL HITS THE STRIKER, I SHOW A '+1' TEXT FADING UPWARDS FROM THE POINT OF HIT
                    /////(THIS IS TO HIGHLIGHT A +1 INCREASE IN SCORE)///////////////////
                    //////NOW SINCE THE BALL MAY HIT THE STRIKER AGAIN BEFORE THE PREVIOUS +1 HAS COMPLETELY FADED,
                    //////I HAVE MADE THIS SIMPLE THREAD TO CREATE A +1 EVERY TIME THERE IS A HIT. SO THERE CAN BE MULTIPLE
                    //////+1 ON THE SCREEN.
                    //-------------------------------SADLY, SOMETHING IS WRONG-------------------

                    //Print a '+1' to show score increase
                    new Thread(new Runnable(){
                        int yStart = strikerYPos;
                        int fadeLength = 0;
                        Timer pointTimer;
                        int MAX_FADE_LEN = 50;

                        public void run() {
                            try
                            {

                                pointTimer = new Timer(1, new ActionListener() {
                                         public void actionPerformed(ActionEvent evt) {
                                            if(fadeLength >= MAX_FADE_LEN)
                                                pointTimer.stop();
                                            g2d.setColor(new Color(0,0,0,255));
                                            g2d.setFont(new Font("Times",Font.BOLD,20));
                                            g2d.drawString("+1",60,yStart - fadeLength);
                                            g2d.drawOval(100,100,50,50);
                                            System.out.println("Drawn +1 at x = " + 60 + " y = " + (yStart - fadeLength));
                                            fadeLength++;
                                         }    
                                        });
                                pointTimer.start();
                            }
                            catch (Exception e)
                            {

                            }
                        }

                    }).start();
                    ////////////////THREAD ENDS HERE//////////////////////
                }
                else
                {
                    score--;
                }

                //SHOW THE SCORE ON THE LABEL
                jLabel1.setText("Score: " + score);
                speedX = ((int)Math.signum((double)speedX))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
                XPos = strikerWidth+1;
            }

            if(YPos <= 0)
            {
                speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
                YPos = 0;
            }
            if((YPos+diameter) >= h)
            {
                speedY = ((int)Math.signum((double)speedY))*(-1) * (2+ Math.abs(new Random().nextInt()) % 5);
                YPos = h-diameter;
            }
        } else {
            g2d.drawOval(XPos,YPos,diameter,diameter);
            return;
        }
    }

    public static void main(String[] args) {

        JFrame frame = new JFrame("Magic Ball");
        movingball = new MovingBall();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(movingball);
        frame.setSize(450, 700);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private void jbInit() throws Exception {
        jButton1.setText("Start");
        jButton1.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        jButton1_actionPerformed(e);
                    }
                });
        jButton2.setText("Stop");
        jButton2.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        jButton2_actionPerformed(e);
                    }
                });
        jLabel1.setText("Score:0");
        this.add(jButton1, null);
        this.add(jButton2, null);
        this.add(jLabel1, null);
        this.setBackground(Color.white);
        this.addMouseMotionListener(new MouseMotionListener() {
                    public void mouseMoved(MouseEvent e) {
                        int coordX = e.getX();
                        if(coordX < 200)
                            strikerYPos = e.getY();
                    }

                    public void mouseDragged(MouseEvent e) {
                    }
                });
    }

    private void jButton1_actionPerformed(ActionEvent e) {
        if(!isBallMoving)
            isBallMoving = true;
    }

    private void jButton2_actionPerformed(ActionEvent e) {
        isBallMoving = false;
    }
}

推荐答案

我不认为很多人会认为几乎 250 LOC 是短的"(尽管我必须承认我在编写 SSCCE 文档时故意含糊其辞).OTOH 我将我在这里看到的较短的源代码改编为一个动画示例,该示例显示鼠标点击时的淡化效果".让它适应您的需求留给...您练习.

I don't think many people would consider almost 250 LOC to be 'short' (though I must admit I was deliberately vague when writing the SSCCE document). OTOH I adapted my shorter source seen here to an animated example that shows a 'fade effect' on mouse clicks. Adapting it to your needs is left as an exercise for ..you.

此源显示如何在 5 秒内更改绘制的字符串.它对主要(弹跳球)和淡入淡出动画使用相同的Thread(EDT).

This source shows how to change the drawn string over a period of 5 seconds. It uses the same Thread (the EDT) for both the main (bouncing ball) and fade animation.

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.*;

class ShapeCollision {

    private BufferedImage img;
    private Area walls;
    int x;
    int y;
    int xDelta = 3;
    int yDelta = 2;
    ArrayList<Strike> strikes;

    /**
     * A method to determine if two instances of Area intersect
     */
    public boolean doAreasCollide(Area area1, Area area2) {
        boolean collide = false;

        Area collide1 = new Area(area1);
        collide1.subtract(area2);
        if (!collide1.equals(area1)) {
            collide = true;
        }

        Area collide2 = new Area(area2);
        collide2.subtract(area1);
        if (!collide2.equals(area2)) {
            collide = true;
        }

        return collide;
    }

    ShapeCollision() {
        int w = 400;
        int h = 200;
        img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        final JLabel imageLabel = new JLabel(new ImageIcon(img));
        x = w / 2;
        y = h / 2;

        strikes = new ArrayList<Strike>();

        MouseListener strikeListener = new MouseAdapter() {

            @Override
            public void mouseClicked(MouseEvent e) {
                Strike s = new Strike(e.getPoint(),System.currentTimeMillis());
                strikes.add(s);
            }
        };
        imageLabel.addMouseListener(strikeListener);

        walls = new Area(new Rectangle(0, 0, w, h));

        ActionListener animate = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                animate();
                imageLabel.repaint();
            }
        };
        Timer timer = new Timer(50, animate);

        timer.start();
        JOptionPane.showMessageDialog(null, imageLabel);
        timer.stop();
    }

    public void animate() {
        Graphics2D g = img.createGraphics();
        g.setRenderingHint(
                RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.BLACK);
        g.fillRect(0, 0, img.getWidth(), img.getHeight());
        x += xDelta;
        y += yDelta;
        int s = 15;
        Area player = new Area(new Ellipse2D.Double(x, y, s, s));

        // Acid test of edge collision;
        if (doAreasCollide(player, walls)) {
            if (x + s > img.getWidth() || x < 0) {
                xDelta *= -1;
            }
            if (y + s > img.getHeight() || y < 0) {
                yDelta *= -1;
            }
        }
        g.setColor(Color.ORANGE);
        g.setColor(Color.YELLOW);
        g.fill(player);

        for (Strike strike : strikes) {
            strike.draw(g);
        }

        g.dispose();
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                new ShapeCollision();
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}

class Strike {

    private Point point;
    private long started;
    private final long DURATION = 5000;
    private boolean expired = false;

    Strike(Point point, long time) {
        this.point = point;
        started = time;
    }

    public void draw(Graphics g) {
        long now = System.currentTimeMillis();
        long age = now - started;
        if (age>DURATION) {
            expired = true;
            return;
        }
        double fraction = 1d-((double)age/(double)DURATION);
        int alpha = (int)(fraction*255d);
        Color c = new Color(255,255,255,alpha);
        g.setColor(c);
        String s = point.x + "," + point.y;
        g.drawString( s, point.x, point.y );
    }

    public boolean isExpired() {
        return expired;
    }
}

这篇关于图形 2d 对象的参考在孤儿线程中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆