Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (475)
Games in Android Showcase (106)
games submitted by our members
Games in WIP (530)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Embedded JPanel as it's own thread?  (Read 1033 times)
0 Members and 1 Guest are viewing this topic.
Offline cbech

Junior Newbie





« Posted 2009-07-08 17:47:25 »

So, I've been trying to figure out a way of making my custom JPanel class run as it's own thread, because I believe that it'll solve my problem of "showing the user a sequence of colours" (it's a version of the game Simon) without hanging the rest of the program. (or more correctly, hanging everything until it comes up with the new sequence, instead of actually showing it.)

I just can't figure out how. Should I make the class extend Thread? Should I declare it as a thread in my "outer" class? (pseudo code below)


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
public class outer extends JFrame

private inner customClass;

constructor{

initialize inner;
add(inner)
}

public main
{
new outer.setVisible(true)
}


class CustomClass extend JPanel

constructor
{
build JPanel
}

@override
private paint(g)
{

The entire game runs in this method.
//disable mouseEvents
in a loop
{
     highlight a sequence
     g.drawSquare(brighter color than already there)
     pause thread
     repeat for the length of the sequence
}
//reenable mouse events

}
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #1 - Posted 2009-07-08 19:17:44 »

You don't need to put the JPanel itself in its own Thread, you should be fine just putting calls to repaint() (as that will almost always be the most expensive operation in a Java2D game) in its own Thread, or whatever the bottleneck action is. Just putting the class in another Thread (which isn't really possible, because Threads just do work, a class itself isn't wok to do) wouldn't help anything.

Chances are, though, that you don't need another Thread at all, and shouldn't have one because it greatly increases the overhead of your game.

Things I typically use multiple Threads for:

  • Showing progress for loading or other time-consuming (100%-processor-using) tasks, like loading in images or levels or whatever.
  • Multi-player networking, so the whole game doesn't hang when waiting for packets from another player.
  • UI that should give good feedback no matter how much going on elsewhere.
  • Any tasks that should not hang or freeze and need to not be interrupted, such as system tasks, reading/writing files, etc.

See my work:
OTC Software
Offline cbech

Junior Newbie





« Reply #2 - Posted 2009-07-08 19:48:47 »

The only reason I bring up the idea of threads is because when I initially designed the custom JPanel class, I initially ran it as a JFrame and it was the sole thing on the screen. And it worked exactly as I wanted. But as soon as I made it a JPanel and put it inside a JFrame, that's when everything went to hell.

Now, my first reaction was that JPanels and JFrames are different, and that could be why the difference when running a loop that is supposed to "pause the current thread", that being the one running all the UI. But I could not for the life of me figure it out, and just tried moving onto making a thread that runs seperately from the rest of the ui.

I'm also not 100% sure how I would cast a "repaint()" into it's own thread...seeing as there's only 2 repaint calls that I would want to run seperately from the rest of the UI, this seems like a good idea....

But how to I do it?

new Thread(this.repaint()).run();


ok...so that didn't solve anything...

Can anyone explain why this code works when it's run as the "Top" class, but not when it's run as an embedded component Class?
(works as Frame, but not as a JPanel WITHIN a JFrame)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
//partially pseudo-code

@override
public paint(Graphics g)

     for (//length of sequence))
    {
               Char whichZone = charAt(sequence);

            if(letter == 'a')
                {
                    g.setColor(zoneA.getColor().brighter()); //from custom zone class
                   g.fill3DRect(0,0,180,180,true);
                }

            //3 other similar methods for 3 other zones

                try
                {
                Thread.sleep(300);
                }
                catch(Exception e){}
               
                reset(g); //does the same as above, just reverts back to g.setColor(zoneA.getColor());


                }


Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #3 - Posted 2009-07-08 23:28:10 »

You've got have something calling repaint, no? That would typically be some sort of loop, so the loop itself is what would go in another Thread. A Thread has no magic to decide whether it should repeat or not. Creating a new Thread that has a run method like this: run() {panel.repaint();} will literally just call repaint once, then finish and get cleaned up by the GC. If you had this, however: run() {while (true) {panel.repaint();}} then it would never exit and would repaint as fast as it possibly could. A Thread is just like any other block of code, except that the processor allows it to execute "in parallel" with everything else.

There must be something else wrong, I've embedded JPanel's into JFrame's many, many, many times with no problems at all. And no offense to you, but you don't seem very familiar with the fundamentals of at least Java2D and Swing, so it's no surprise that you're having issues finding the proper way of doing it.

See my work:
OTC Software
Offline cbech

Junior Newbie





« Reply #4 - Posted 2009-07-09 04:32:14 »

Well, this is the Clueless Newbie forum, and I don't make any claims to being an expert. I'll provide you the (abeit trimmed) code of the code in question class, and any other code that I feel is important to it in my JFrame class.

So I'll try to explain everything here. So the way this works is you have Dorothy, and you have the Wizard. The Wizard controls the game that Dorothy is playing. (Currently the Game "Simon" 4 coloured squares, and a sequence to remember)

THe communication that I have designed works perfectly and the Zone Attributes, update exactly as planned, without any issues.

The ONLY problem I'm having is whenever a "newGame()" gets called. At the start of each new game, a sequence is supposed to be shown to the user, by drawing a brighter coloured zone (triangle, square, circle, whatever it may be at that time) for the time specified of "Thread.sleep(300)". This should be doing it for each letter(see "Zone") in the sequence. However, when I run the program, when I click the start button, the game hangs for a moment...then waits for the users input. (Instead of highlighting each zone)

Now that you see the code, do you see any glaring problems?

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
public class Dorothy extends JFrame{

    public  ConcurrentLinkedQueue changes = new  ConcurrentLinkedQueue();
     /*
    *Variable definitions
    */

    private Zones gameArea;

    public Dorothy() {
    //Code that builds the frame
   buildUI();
    setVisible(true);

    //code for setting up socket communication (this works just fine)

    }


    private void buildUI() {
        //other components(a couple of labels, and a button or two.) PERHAPS THESE INTERFERE SOMEHOW?

        gameArea = new Zones(this);
        add(gameArea);
        gameArea.setBounds(20,40,360,360);
        gameArea.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        gameArea.setBackground(Color.BLACK);
        gameArea.setOpaque(true);
        gameArea.setVisible(true);      
    }

    private void butStartActionPerformed(ActionEvent evt) {                                        
        if(butStart.getText().equals("Retry?")){
            //if player loses
           gameArea.newGame();

        }else{
            //first time user plays.
           gameArea.newGame();
        }
}


    public void addEvent(String s){
       //this is for the communication via sockets
      //this works as intended
      changes.add(s);
       gameArea.repaint();
    }
    public String updateBoard(){
//adds new "Zone attribute change via socket communication with another user
//This works perfectly.
       if (changes.isEmpty()){return null;}
        return changes.remove().toString();

    }

    public static void main(String args[]) {

        new Dorothy().setVisible(true);
    }

}
class Zones extends JPanel {
 
   //a whole bunch of variables.

    public Zones(Dorothy mainGame) {

        myParent = mainGame;
        setLayout(null);
       //setup mouse listeners
      //setup "each zone"

    }
    public void newGame(){
        //Prepare a new game
       gameOver = false;
        killMouse = false;
        gridSize = 4;
        seqSize = 3;
        sequence = "";
        attempt = "";
        clicks = 0;
        highlight = 0;
        newSequence = true;
        repaint();
    }

     private void zonePressed(MouseEvent evt) {
       
        if(killMouse){return;}
        clicks++;
        //zone a
       if(evt.getX()< 180 && evt.getY() < 180)
        {
            highlight = 1;
            attempt += "a";
        }
        //zone b
       if(evt.getX()>180 && evt.getY()<180)
        {
            highlight = 2;
            attempt += "b";
        }
        //zone c
       if(evt.getX()<180 && evt.getY()>180)
        {
            highlight = 3;
            attempt += "c";
        }
        //zone d
       if(evt.getX()>180 && evt.getY()>180)
        {
            highlight = 4;
            attempt += "d";
        }
        myParent.sendMsg(highlight,'h');
        repaint();

        String codeSeg = sequence.substring(0,clicks);

        if(!codeSeg.equals(attempt))
        {
            gameOver = true;//if the user was wrong
           clicks =0;
            highlight = 0;
            attempt = "";
            repaint();
        }
        else
        {
           if(clicks == sequence.length()){
               //if the sequence was remembered correctly
              attempt = "";
               clicks = 0;
               newSequence = true;
           }
        }
     }

     private void zoneRelease(MouseEvent evt) {
        if(killMouse){return;}
        if(!gameOver){
           
            if(newSequence){
                System.out.println("Time for a New Sequence!");
                repaint();
            }
            else{
                System.out.println("Still Guessing!");
                repaint();
            }
        }

     }
//THIS METHOD IS USED TO RESET THE COLOURED ZONE AREAS DURING A repaint() CALL. (ALSO TO UPDATE
//ZONE ATTRIBUTES WHEN THE CONNECTED USER MAKES CHANGES.
    private synchronized void reset(Graphics g){
//everything that happens here is to handle when zone attribute needs to be changed. (colour, shape, etc)
           for (int i = 0; i<4 ;i++){
                g.setColor(zoneGroup[i].getColor());
                switch(zoneGroup[i].getShape()){
                    case 4:g.fill3DRect(178*(i%2)+2, 178*(i/2)+2, zoneGroup[i].getDimension(), zoneGroup[i].getDimension(), true);break;
                    case 5:g.fillPolygon(new int[]{178*(i%2)+2,178*(i%2)+92,178*(i%2)+180},new int[]{178*(i/2)+182,178*(i/2)+2,178*(i/2)+182}, 3); break;
                    case 6:break;
                    case 7:break;
                }
            }
       
     }

    @Override
    public  void paint(Graphics g) {
        super.paint(g);
        reset(g);

        if(killMouse){return;}

        if (gameOver){
            killMouse = true;
            myParent.resetGame();

        }else if (highlight > 0){
            highlight--;
                g.setColor(zoneGroup[highlight].getColor().brighter());
                g.fill3DRect(178*(highlight%2)+2, 178*(highlight/2)+2,
                        zoneGroup[highlight].getDimension(), zoneGroup[highlight].getDimension(), true);
            highlight = 0;
        }else if(newSequence){
            setEnabled(false);
//-------------------------------------------------------------------------------------------------
//THIS IS WHERE THE SEQUENCE IS SUPPOSED TO LIGHT UP BUT ISN'T WORKING
//setEnabled was used to disable key clicks by the user, and has no effect to the problem in question
//as the problem existed before it was put in.
//--------------------------------------------------------------------------------------------------
           makeSequence();

            //iterate through the code
           for(int i = 0; i < sequence.length(); i++)
            {

                //light up the next square

                char letter = sequence.toCharArray()[i];
                if(letter=='a')
                {
                    g.setColor(zoneGroup[0].getColor().brighter());
                    g.fill3DRect(178*(0%2)+2, 178*(0/2)+2, zoneGroup[0].getDimension(), zoneGroup[0].getDimension(), true);
                }
                if(letter=='b')
                {
                    g.setColor(zoneGroup[1].getColor().brighter());
                    g.fill3DRect(178*(1%2)+2, 178*(1/2)+2, zoneGroup[1].getDimension(), zoneGroup[1].getDimension(), true);
                }
                if(letter=='c')
                {
                    g.setColor(zoneGroup[2].getColor().brighter());
                    g.fill3DRect(178*(2%2)+2, 178*(2/2)+2, zoneGroup[2].getDimension(), zoneGroup[2].getDimension(), true);
                }
                if(letter=='d')
                {
                    g.setColor(zoneGroup[3].getColor().brighter());
                    g.fill3DRect(178*(3%2)+2, 178*(3/2)+2, zoneGroup[3].getDimension(), zoneGroup[3].getDimension(), true);
                }
                try
                {
                Thread.sleep(300);
                }
                catch(Exception e){}

                reset(g);
            }
            clicks = 0;
           
            newSequence = false;
            setEnabled(true);
        }
       
    }
    private void makeSequence() {
      //creates the letterized sequence used to display the colours to the user.
     //"abcdcba"
}

}
class zone{
   //custom class that handles each zone and it's currently active attributes

}
Offline GKW

Senior Member




Revenge is mine!


« Reply #5 - Posted 2009-07-09 05:22:59 »

You shouldn't be calling sleep in the paint method.  That's a great way to make the system less responsive.  Try setting it up as an animation that lasts 300ms rather than locking the system up for 300ms.
Offline Eli Delventhal

JGO Kernel


Medals: 42
Projects: 11


Game Engineer


« Reply #6 - Posted 2009-07-09 20:36:13 »

Yup, GKW is right, that's definitely your problem. I think probably you're not understanding exactly how repaint works - you're putting the sleep at the end of paint() because you're thinking that makes it display that picture for 300 ms, right? In actuality, repaint calls a few methods that, in the end, erase the screen and redraw everything. If you put a sleep somewhere in that process, that it's all going to look delayed. In your case, the actual update happens when paint() finishes, and you're doing all your draw changes and sleeps within the same paint - so it's just going to hang with no updates until finally you're at the last iteration, and everything will just be drawn on top of each other.

ex.)
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
public void paint(Graphics g)
{
     g.setColor(Color.RED);
     g.fillRect(0,0,10,10);
     try {Thread.sleep(100);} catch (Exception e) {}
     g.setColor(Color.BLUE);
     g.fillRect(0,0,10,10);
     try {Thread.sleep(100);} catch (Exception e) {}
     g.setColor(Color.GREEN);
     g.fillRect(0,0,10,10);
     try {Thread.sleep(100);} catch (Exception e) {}
}

public void somewhereElse()
{
     repaint();
}

When somewhereElse() is called, to all intensive purposes, it will wait 300ms (100ms + 100ms + 100ms) and then draw a green square. Why? Because the actual painting doesn't happen as you're imagining it - think of it as updating the canvas once every time you call repaint(), in one definitive state. So the paint() above will draw the red and the blue squares, yes, but by the end of the state the green square is what has been drawn on top.

The code below is a different story.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
private Color color;

public void paint(Graphics g)
{
     g.setColor(color);
     g.fillRect(0,0,10,10);
}

public void somewhereBetter()
{
     color = Color.RED;
     repaint();
     try {Thread.sleep(100);} catch (Exception e) {}
     color = Color.BLUE;
     repaint();
     try {Thread.sleep(100);} catch (Exception e) {}
     color = Color.GREEN;
     repaint();
}


When somewhereBetter() is called, a red square will immediately be drawn to the screen, then 100ms later a blue square will be drawn, then 100ms later a green square will be drawn. This will look correct, but chances are we're sleeping the main Thread so everything else will lock up in between each color being drawn - not what we want anyway.

What I recommend you do (and what GKW hinted at) is just create a variable to track time that has passed (possible only if you have some sort of loop that continually runs within which you can increment time that has passed), or come up with some other way of making time go by. My suggestion (purely because it's the easiest given what you seem to have already) is to either use a Timer, or make another Thread and put your loop there.

Making a timer:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
private Color color;

public void inMyConstructor()
{
     //Start the timer with a delay of 100, because that's our pause length in this example.
    //this refers to the containing class, but it must implement ActionListener. You could
    //also make another class that implements ActionListener and use that instead.
    javax.swing.Timer timer = new Timer(100,this);
}

public void somewhereEvenBetter()
{
     timer.start();
}

public void paint(Graphics g)
{
     g.setColor(color);
     g.fillRect(0,0,10,10);
}

public void actionPerformed(ActionEvent e)
{
     //Each 100ms when the timer gets fired, it can check to see what its
    //old color was in order to decide what new color to give it.
    if (color == null)
     {
          color = Color.RED;
     }
     else if (color == Color.RED)
     {
          color = Color.BLUE;
     }
     else if (color == Color.BLUE)
     {
          color = Color.GREEN;
     }
     else
     {
          //When it has been 100ms since green was displayed, stop the timer.
         stop();
     }

     repaint();
}


The above will do exactly what you want, displaying red for 100ms, then blue for 100ms, then green for 100ms, before it stops. The rest of the program will be unaffected and will continue to run smoothly.

Here's how you could do it with another Thread. Note that I don't really recommend using Threads for something like this, typically, but your animation is structured such that this will probably be easiest for you.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
public void somewhereThatWorks()
{
     new Thread(new ColorThread(this)).start();
}

private class ColorThread implements Runnable
{
     JPanel panel;

     public ColorThread(JPanel p)
     {
          panel = p;
     }

     public void run()
     {
          p.color = Color.RED;
          p.repaint();
          try {Thread.sleep(100);} catch (Exception e) {}
          p.color = Color.BLUE;
          p.repaint();
          try {Thread.sleep(100);} catch (Exception e) {}
          p.color = Color.GREEN;
          p.repaint();
     }
}


Hopefully all that helps?

See my work:
OTC Software
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

ctomni231 (33 views)
2014-07-18 06:55:21

Zero Volt (29 views)
2014-07-17 23:47:54

danieldean (24 views)
2014-07-17 23:41:23

MustardPeter (26 views)
2014-07-16 23:30:00

Cero (41 views)
2014-07-16 00:42:17

Riven (43 views)
2014-07-14 18:02:53

OpenGLShaders (31 views)
2014-07-14 16:23:47

Riven (30 views)
2014-07-14 11:51:35

quew8 (29 views)
2014-07-13 13:57:52

SHC (65 views)
2014-07-12 17:50:04
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!