I've been working on a java game recently, and I have a lot of it figured out. One thing still plagues me, however. The way it's set up, a player moves across a background (the game board). Currently, every time the player moves, it repaints the whole frame, including the background. This causes a brief, yet annoying screen flicker whenever the player moves.
I've separated out my code to draw the background separately from the things that need to be repainted:
public void drawMap(Graphics pane) {...}
public void drawPlayer(Graphics pane) {...}
The problem is that I can't find a way to make the board stay on the screen when I use repaint(); , a necessity for the player to move. Any suggestions?
You should look into double buffering, basically you paint an image to the buffer, then paint the buffer. It should remove the flickering effect you are talking about. Below are a few helpful links:
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering
http://www.ecst.csuchico.edu/~amk/classes/csciOOP/double-buffering.html
Just comment if your having trouble understanding it.
UPDATE: I would also suggest you look in 'Killer game programming in java'. You can get a free ebook of one of the older versions. Some of it is a bit out dated, but the first few chapters about setting up a game loop and drawing to the screen etc are still very much relevant.
UPDATE 2: From the second link, try something like this:
private void drawStuff() {
BufferStrategy bf = this.getBufferStrategy();
Graphics g = null;
try {
g = bf.getDrawGraphics();
drawMap(g);
drawPlayer(g);
} finally {
// It is best to dispose() a Graphics object when done with it.
g.dispose();
}
// Shows the contents of the backbuffer on the screen.
bf.show();
//Tell the System to do the Drawing now, otherwise it can take a few extra ms until
//Drawing is done which looks very jerky
Toolkit.getDefaultToolkit().sync();
}
UPDATE 3: This post here gives a nice code sample that you can play with and adapt, that should give you the best idea on how to do double buffering
I suggest to avoid redrawing everything with every change. Instead draw the whole frame at a fixed interval, e.g. every 50ms. Just keep the status of every element in a class and if something changes just change the data value. Due to the fixed redrawing interval the display will pick up any changes at the next redraw.
Related
I am building a simple game rendering method. I have a static images like map background that isn't really changing at all . I am just using like 30% of the whole screen for changing graphics. And I don't feel like it is necessary to render all that stuff over and over again if it isn't changing..
on internet I found something about passive rendering - You draw what you need and wait... until something updates .. after that you update it and again wait.
Looks like a great method for this situation . But now ...
I have a render method like that :
public class X extends Canvas{
//some method
this.createBufferStrategy(3);
//....
public void render(){
BufferStrategy buffer = this.getBufferStrategy();
Graphics g = buffer.getDrawGraphics();
//Draws black background...
g.setColor(Color.BLACK);
g.fillRect(....
for(... //cycles every object and calls their render methods...
}
This is unable to maintain draw and wait method...becouse the background is redrawn every 1/30 of second so If you don't have anything to draw, you get black canvas with nothing on it.. so you have to always redraw everything each update...If I don't draw the background , buffer start to blink like hell... so I have to have something to cover that stuff up.
I searched on internet and found nothing much about other solutions for this rendering type.. Tuns of stuff for active rendering but nothing for that Draw and wait method..
I think it's impossible to do that with BufferStrategy. Is there something else to serve for this ? Also is there other source for Graphics g ? On internet was something about calling it directyl from Canvas but I didn't get it running for this. Or am I thinking about it from wrong angle?
You're confusing passive rendering with partial updates. In "passive rendering" you can redraw everything, but you don't do it 30fps. you only do it when something moves. In "partial rendering", you only draw on the part of the canvas that changed.
The two can be put together, so that you only draw what changes, and only when it changes. That's the ideal case.
But passive rendering doesn't work if something is always moving, and partial updates don't work if the whole background is updating constantly.
so i am dabbling in making a game and i am a fairly messy worker and then i go back and tidy it up. So please ignore any stupid naming conventions or methods or anything. As I go back later and tidy up.
So basically I am trying to make a main menu, i just want to draw something to the screen and have it stay there. I am writing the graphics to a image ATM and displaying the image as i tried just drawing to the canvas and it just flickered up onto the screen and then it went blank. I changed it to a runnable so i could put in sleep() to try and error hunt. I've removed all my error hunting code now. It turns out when i have a sleep() before or after the image is put on the screen for longer than 50ms the rectangle stays on screen and doesn't flicker away like 1ms after it was drawn.
I'm not sure what's going on. I am probably not that clear and I apologies.
import java.awt.Canvas;
import java.awt.Graphics;
import javax.swing.JFrame;
public class test extends Thread{
public static void main(String[] args){
test t = new test();
t.start();
}
#Override
public void run() {
JFrame f = new JFrame("Test");
Canvas c = new Canvas();
Graphics g;
c.setSize(400, 400);
f.add(c);
f.pack();
f.setVisible(true);
c.setVisible(true);
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g = c.getGraphics();
g.drawRect(10, 10, 40, 68);
}
}
Basically, this is not how painting in AWT works. Painting should be done within the context of a component's paint method. In AWT Canvas seems to be the preferred base component to use.
You should never use getGraphics, apart from been able to return null, it will only represent the Graphics context from the last paint cycle, anything added to it could be wiped about by newer paint events...which is probably happening to you.
Take a look at Painting in AWT and Swing for details about how painting works.
Having said all that, I would discourage you from using AWT based API as it was replaced by Swing some 15 years ago.
Take a look at Performing Custom Painting for more details about painting in Swing
You're kind of getting the idea, but there are a few standard design patterns of creating a game, for starters check out this code.
Basically, you'll need something called a game loop which will update and render to the display a certain amount of times every second (framerate or fps). When it renders, it will clear and redraw the image in a new location at a certain interval, if your fps was 60 you would be rendering (clearing, and redrawing) 60 times a second. We can also introduce more advanced concepts such as buffer strategies (shown in the example above) to reduce tearing and flickering.
To sum it up, you're kind of there, you just need to constantly do that g = c.getGraphics() so...
while (true) {
g = c.getGraphics();
// set color
// draw a rectangle
g.dispose();
}
Note how I added the dispose, this will just free up any unused memory. Just to clarify, this is by no means good code, but it will give you a place to start :)
The flickering is due to only having 1 screen, to stop this flickering you need a buffer strategy. Basically having two buffers is like having two screens, whilst you render on one 'screen' you can clear and draw the next step on the second screen and switch between the two buffers; this reduces the flickering.
I have a little Android game I've been working on and have also implemented some pixel perfect collision checking based on code here (I say based on, the only thing I changed was the class name). This used to work fine a few months ago but I stopped development for a while, came back and had to rebuild a lot of code including that. Now when I implement it to collisions seem horribly off. If the large weight is on the left of the player, there's a significant distance and yet it still counts as a collision. I have also noticed that the player can pass through some of the smaller weights but not all the way through. I know the player has some non-transparent white pixels which don't help, but I tried with a transparent smaller image and the same thing happened. I edited the player image to remove the white pixels, but I'm still getting the odd collision detection. Any ideas?
The second issue, when the player collides with a weight, it goes to the next activity (which it does) however it jerks all the objects on screen for about a second, and then proceeds to the next activity. Any ideas on this? I have a feeling it's to do with the way I handle the threads, or the way I close them. Check GitHub link below for the GameThread class which handles the main operations on my surface thread.
Collision code can be found in above link, my calling of the class can be found below:
// handles game over circumstances
if (CollisionUtil.isCollisionDetected(player.getBitmap(), player.getX(), player.getY(), weight.getBitmap(), weight.getX(), weight.getY())) {
Intent gameOverIntent = new Intent(this.getContext(), GameOverActivity.class);
player.setTouched(false);
this.getContext().startActivity(gameOverIntent);
((Activity) this.getContext()).finish();
save(score, time);
try {
gameTimers.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
gameOver = true;
}
The rest of the classes and all my other code can be found here: https://github.com/addrum/Francois/tree/master/src/com
Thanks for your help in advance.
Edit: Forgot to mention that if you need to test it (you probably will to see what I'm talking about) you can download it here: https://play.google.com/store/apps/details?id=com.main.francois
Edit: I've now uploaded a short recording on YouTube to make it easier to see what I'm talking about: http://youtu.be/vCjKmTmhabY
Thanks for the video; now I understand the "jerk" part.
The explanation for that is simple: your code is doing this (simplified):
while (running) {
canvas = this.surfaceHolder.lockCanvas();
this.gameLogic.update();
this.gameLogic.render(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
public void render(Canvas canvas) {
if (!gameOver) {
...draw...
}
}
If gameOver is true, your render() function doesn't draw anything, so you get the previous contents of the buffer.
You might expect this to show whatever was drawn from the frame just before this one. However, the SurfaceView surface is double- or triple-buffered. So if you just keep calling lock/post without drawing anything, you'll cycle through the last two or three frames.
Simple rule: if you lock the surface, you need to draw. If you don't want to draw when the game is over, you need to put your "game over" test back before you lock the canvas.
I'm working on a game framework inspired by XNA. Almost every day I find a new problem with this or that. Today, it's going from window mode to full-screen and back. The gist of it is:
If I start the game in windowed mode it works fine. When I go full-screen in works fine too. After switching back from full-screen something goes wrong and nothing is drawn on the screen. Going to full-screen again works fine. I just get this problem in windowed mode unless the game started in it.
I set the window this way:
public void setW(){
jFrame.setVisible(false);
if(graphicDevice.getFullScreenWindow() != null && jFrame.isDisplayable())graphicDevice.getFullScreenWindow().dispose();
graphicDevice.setFullScreenWindow(null);
jFrame.setResizable(false);
jFrame.setUndecorated(false);
jFrame.getContentPane().setPreferredSize(new Dimension(width, height));
jFrame.setVisible(true);
jFrame.pack();
graphics = (Graphics2D)jFrame.getContentPane().getGraphics();
fullScreen = false;
}
Full-screen is set like this
public void setFS(){
jFrame.setVisible(false);
if(jFrame.isDisplayable())jFrame.dispose();
jFrame.setResizable(false);
jFrame.setUndecorated(true);
graphicDevice.setFullScreenWindow(jFrame);
graphicDevice.setDisplayMode(new DisplayMode(width, height, 32, 60));
graphics = (Graphics2D)jFrame.getContentPane().getGraphics(); // graphicDevice.getFullScreenWindow().getGraphics(); does the same thing
fullScreen = true;
}
Then I use this method to draw... deviceManager.getGraphics().draw...(actually i use an intermediary bufferImage) I use a game loop so this happens continuously.
public Graphics2D getGraphics(){
return graphics;
}
Now if I use this method:
public Graphics2D getGraphics(){
if(fullScreen)
return (Graphics2D)graphics;
else
return (Graphics2D)jFrame.getContentPane().getGraphics();
}
I'm sure I'm doing something wrong. Thew windowed mode works, this I know. Why does it go pear-shaped when I return from full-screen. The window just stays gray with nothing being painted on it.
However if I create a method like this:
public void assignGraphics(){
graphics = (Graphics2D)jFrame.getContentPane().getGraphics()
}
And call it later(one game cycle passed) it fixes the problem. That's why the second mode-switching method works, since it gets Graphics from the JFrame every cycle.
Worked on the problem quite a bit since starting this question and I thing here is the real crux of it: Why can't I get the Graphics for a Window in the same cycle I leave full-screen?
EDT. How come the problem is always where I'm not looking. Yes, the problem was that my game-loop is swing.timer-based. It's a rookie mistake and the fact, that it took me almost a week to figure it out, came down heavy on my ego.
Apparently many of swing operations go of on the EDT and using it for updating and drawing not only clogged the sucker up, but resulted in this questions' problem.
Now I'm going for variable time-step loop using while() in the main thread. (Even with a basic while loop with only update() and draw() the problem is gone)
Should have at least read one article on game loops before starting on my program. Live and learn(and waste a week being stumped).
-Hi all! I'm making a Java applet that simulates wave interference, which I have almost finished (will license under GPL). However, I have two questions regarding the AWT paint cycle that I am having difficulty finding answers to.
I want to make an 'about' overlay that appears when I press a button. The way I want to do this is to draw over the entire applet window with my static message and legend objects. The problem is stopping the AWT components from drawing themselves in the foreground without using remove(). Is there a way I can stop AWT from drawing itself temporarily?
For my standing waves mode I want to have node and anti-node markers calculated and drawn to a secondary graphics every time the standing wave reaches a maximum amplitude (all of which I can do myself), but drawn to the primary graphics (and thus displayed) every paint cycle. Could someone explain the steps to do so? I imagine it would involve creating a second graphics object, drawing to it once, then drawing it to the primary graphics every cycle.
If you are able to answer either of my questions I would be very grateful!
Cheers, Jack Allison
Responding to your first question:
You can't disable the paint()/paintComponent() method if you've inlcluded it in your code. If its there, it runs. However, you can create a flag so that only if the flag is true, the stuff gets drawn. Let me show you what I mean:
boolean flag;
...
public void paintComponent(Graphics comp) {
if (flag) {
Graphics2D comp2D = (Graphics2D) comp;
//drawing statements
}
}
public void actionPerformed(ActionEvent event) {
flag = true;
repaint();
}