views:

371

answers:

3

Hi all. As I have said in my previous questions, I'm still new to java. I'm developing a tile based game in java. The movement is on a grid. I'm at the stage where I'm implementing character movement. My plan was, to use the code which I pasted in this question (http://stackoverflow.com/questions/772713/call-repaint-from-another-class-in-java), However this didn't work, and I was told I should use SwingWorker.

Being a problem solver, I've been thinking about this over the day, and had a thought. I could archive the same effect by doing things a bit differently. I mentioned that I had a timer that went off every 20mills at current, calling a repaint. My idea was, I could have a method call before the repaint in the event for the timer, which would move all of the people 1 nearer to their destination (next square/tile). Would that sort of thing work? later on in the game, there may be 100 or so people. Would 20mills be enough time to loop through all the people and move them one bit closer to their destination? is 20mills too short a time? Am I just making no sense at all?

Your opinions / answers / thoughts are welcome :)

Thanks in advance!

Rel

+1  A: 

This area of swing has probably the most "Black Magic" of anything in Java. It can be tricky and there are a lot of approaches.

The most important rule is to never modify the screen unless you are in the AWT thread, but don't use the AWT thread for much more than modifying the screen.

Since every swing "Callback" (like button listener) comes in on the AWT thread, you normally don't have to think about this, but for an active rendering situation you have to pay close attention.

Generally in a larger system like this, you do an entire calculation at once (move all your data), then you redraw everything that needs to be drawn in one step.

So for the most part, you calculate outside your paint system altogether in a continual thread, then you'd call a repaint function at a top-level component. Repaint should deliver paint events to all your components on the AWT thread, so that shouldn't be a problem.

Then each component should look at its' state (which is already updated) and use that info to draw itself.

This is how it should be done, the basics, but it can be slow and there are a lot of tricks to speed it up. You might want to look for a book that involves swing programming and games.

Bill K
Thanks for your answer. Allowed me to think things through more in my head as to how it could be done!
Relequestual
+1  A: 

I agree with pretty much everything Bill said, especially the part about Swing being enigmatic (although not really much more so than other graphics environments).

To give a few references, 30 fps (which is what most interlaced screens produce at) is 1 frame every 33 ms. 60 fps is about the highest that humans can perceive (1 frame / 16 ms), and most LCD monitors refresh at 60 or 75 Hz, which would be the absolute fastest you could actually produce. 20 ms/frame is a frame rate of 50 fps, which also coincides with the electric frequency in European territories and is just noticeable by the human eye.

The main thing I would recommend against is to just do everything in a tight while loop. This will cause your game speed to be highly dependent on the system on which you play. On a faster system, you may get frame spewing faster than the player can react, even if it plays reasonably on an older one (or you'll have the converse problem on a decrepit machine). Using a timer will allow you to be more consistent in the rate, but you have to make some defensive checks in case you miss a frame deadline. This means that your timer needs to know when the calculations are finished so if another frame ticks by and it hasn't finished, then it will skip the next frame. Even better, you log how long actually passes between calculations and adjust the actual movement traveled by your characters accordingly.

Another warning: while the drawing must be done on the AWT thread and the calculations off of it (to keep your program responsive), the state also needs to be updated on the AWT thread. If you let the thread doing the calculations update the game state, then the AWT thread will see this in the middle of a repaint, leading to an effect called tearing. Thus, you may need to make a copy of the state to post to the AWT thread. This is what a SwingWorker is for, which you'll probably use in conjunction with a Timer.

Amazingly, most of what I said is actually new when compared to what I posted on your other question.

James
Thank you again James, This has proved most beneficial in my understanding of how things should work. I admit I'm still confused a bit how StringWorker is used, but at least now I understand why its used! I think I will go with what Neil said, and keep to the single thread for now by doing logic-repaint-sleep on each tick. If later on, I need to do SwingWorker, I'm sure You will see my questions again. Thanks for your help! :)
Relequestual
+2  A: 

OK, first to answer the 20 millis question. You can get quite a lot of game logic done in 20ms. But I'd say it's too frequently to be updating the display. You should also bear in mind that the OS generally dishes out CPU in the order of 10-20 ms timeslices-- in other words, another process at any moment could easily delay your process by about that amount of time. Have a look, for example, at my article on the behaviour of Thread.sleep()-- notice from the graph that as the system is moderately busy, the OS can't honour the sleep time we asked for. If you need an average sleep between frames of 100ms, then 20ms or so jitter here and there won't be too bad. But 20ms jitter when you asked for 20ms will probably be more noticeable...

So I'd probably start at 10 frames per second (100ms pauses) and see how that looks.

As far as the code is concerned, if your game logic will take more or less the same amount of time each tick, then I'd start with logic-repaint-sleep on each tick. Remember you need to synchronize on the things you're painting, so in your game logic thread, you ideally need to avoid holding on to locks for too long.

Neil Coffey
Thanks Neil. I actually came across your article and read it quite a few times! It's very informative in the area of Thread.sleep() .I will try as you suggested with 100ms, and see how it goes.Thanks greatly for your help! :)
Relequestual