Im trying to code a 2D pong game with java. The problem that i have is about fluidity. Im using images (As most of the 2D games do) for background and paddles and the ball. Created the game with the Actionlistener and attached it with a timer to the game. For now i just have this code in the game loop for collision.
#Override
public void actionPerformed(ActionEvent e) {
if (ball.y <= 0 || ball.y + ball.height + 35 > HEIGHT) { // Horizontal
// Walls
// collision
ballspeedY = -ballspeedY;
}
if (ball.x <= 0 || ball.x + ball.width + 5 >= WIDTH) { // Vertical Walls
// collision
ballspeedX = -ballspeedX;
}
ball.y += ballspeedY;
ball.x += ballspeedX;
renderer.repaint();
}
And this code for drawing
public void repaint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(backgroundImage, 0, 0, WIDTH, HEIGHT, null);
g2d.drawImage(ball.image, ball.x, ball.y, ball.width, ball.height, null);
g2d.drawImage(paddle.image, paddle.x, paddle.y, paddle.width, paddle.height, null);
}
The problem is, when i increase the ball's speed, it starts to move not smoothly. Ive done a research and tried to cancel drawing the background image. And the ball started to move a bit more smooth. The thing im confused about is, what if i have moving background or 100 balls or something else. Im doing something wrong and please someone tell me which way i should use to move these objects on the screen smoothly. The games which are using a lot bigger images or backgrounds or even 3D games move so fluidly but my simple pong game doesn't. What am i doing wrong?
SOLVED: The problem is just with this line:
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
This line cracks the animations fluidity. When i delete it, everthing runs pretty fast and smooth
I can't fully answer your question, because that is actually a complicated subject. There are multiple different aspects to talk about, but each of them is in itself studying material. I'll try to cover the basic ideas:
Multithreading
So first of all, your code is probably executed all sequential. Each time actionPerformed runs, it will call redraw at the end. Now, basically this will define your 'framerate'. So the more time consuming (more complex code) your actionPerformed is, the lower your framerate will get.
To counter this, one thread on it's own can take care to draw the actual state of the game.
Ball speed is not related to the framerate
I am not sure how to read your code. But from what I see, your ballspeed is an integer. Take in account the thing I wrote above. So in your current implementation ballspeed actually defines how far the ball moves per frame. That is a problem, because it would mean, that the game runs faster/slower depending on the complexity of actionPerformed. Even if I read your code wrong, this is a thing to consider.
One way to solve it, is to separate calculation and drawing with multiple threads like mentioned above. But you should probably also have a look at frame independent physics.
100 balls problem
If you would calculate the positions of all x balls in your actionPerformed method, your framerate becomes increasingly slower the bigger x gets. This, again, is because of the things above. But I guess at one point you have to do further optimizations, otherwise your recalculation process can't keep up. Though the number should be quite high - but this is where I lack experience.
Graphics2D is not OpenGL
I am not too familiar with how the Java Graphics2D works, but I have basic knowledge about OpenGL. For real games OpenGL is almost a must. Even webgames start to use WebGL (browser supported interface for similar functionality). This is where you actually use the graphics card for its capabilities. Especially for 3D games, but even for simple 2D games it is really the best for performance. By the way, smartphones games as well.
Covering that would be impossible here, so I just leave it as is. If you are interested in those things, you would need to learn them more in depth. I guess your next step would be to use a framerate independent calculation. I'm not sure about the multithreading stuff, because it usually comes along with a bunch of difficulties...
Related
im trying do develop a Zelda like game. So far i am using bitmaps and everything runs smooth. At this point the camera of the hero is fixed, meaning, that he can be anywhere on the screen.
The problem with that is scaling. Supporting every device and keeping every in perfect sized rects doesnt seem to be that easy :D
To prevent that i need a moving camera. Than i can scale everything to be equally sized on every device. The hero would than be in the middle of the screen for the first step.
The working solution for that is
xCam += hero.moveX;
yCam += hero.moveY;
canvas.translate(xCam,yCam);
drawRoom();
canvas.restore();
drawHero();
I do it like this, because i dont wand to rearrange every tile in the game. I guess that could be too much processing on some devices. As i said, this works just fine. the hero is in the middle of the screen, and the whole room is moving.
But the problem is collision detection.
Here a quick example:
wall.rect.intersects(hero.rect);
Assuming the wall was originally on (0/0) and the hero is on (screenWitdh/2 / screenHeight/2) they should collide on some point.
The problem is, that the x and y of the wall.rect never change. They are (0/0) at any point of the canvas translation, so they can never collide.
I know, that I can work with canvas.getClipBounds() and then use the coordinates of the returned rect to change every tile, but as I mentioned above, I am trying to avoid that plus, the returned rect only works with int values, and not float.
Do you guys know any solution for that problem, or has anyone ever fixed something like this?
Looking forward to your answers!
You can separate your model logic and view logic. Suppose your development dimension for the window is WxH. In this case if your sprite in the model is 100x100 and placed at 0,0, it will cover area from 0,0 to 100, 100. Let's add next sprite (same 100x100 dimension) at 105,0 (basically slightly to the right of the first one), which covers area from 105,0 to 205,100. It is obvious that in the model they are not colliding. Now, as for view if your target device happens to be WxH you just draw the model as it is. If your device has a screen with w = 2*W, h = 2*H, so twice as big in each direction. You just multiply the x and y by w / W and h / H respectively. Therefore we get 2x for x and y, which on screen becomes 1st object - from 0,0 to 200, 200, 2nd object - from 210,0 to 410, 200. As can be seen they are still not colliding. To sum up, separate your game logic from your drawing (rendering) logic.
I think you should have variables holding the player's position on the "map". So you can use this to determine the collision with the non changing wall. It should look something like (depensing on the rest of your code):
canvas.translate(-hero.rect.centerX(), -.rect.centerY());
drawRoom();
canvas.restore();
drawHero();
Generally you should do the calculations in map coordinates, not on screen. For rendering just use the (negative) player position for translation.
I'm new to Java's graphics library and I'm still coming to grips with its limitations. Below I have a draw function for a Grid class, which draws a 2D array of tiles as filled rectangles.
Not that it's pertinent to the question but the the scale and offset arguments are there to adjust the Grid's tiles and tileSize variables such that it's drawn in the correct position and scale on the screen.
My question is it normal for this to lag considerably when the tiles variable is quite big? I normally get around 500 fps without any Grids on screen, and no noticeable reduction with a Grid of tiles[10][10] to [50][50]. But at tiles[1000][1000], or 1,000,000 total rectangles to draw, fps drops to 7.
I know a million is a lot, but they are just rectangles after all, and my pc can play a game like Skyrim on full settings no problem. I'd imagine there's more than a million polygons on display in Skyrim, and they comes with all sorts of highres textures and lighting and all so, why should a million gray squares be such a problem? Is Java's graphics library really poor? Am I expecting too much? Or, as I suspect, is there a much better way of drawing something like this?
I can provide the main class's paintComponent if that's important, but it's just a call to _Grid.draw() so I don't think the problem's there..
public Graphics draw(Graphics g, double scale, Point offset) {
Graphics2D g2 = (Graphics2D) g;
for(int i = 0; i != this.tiles.length; i++) {
for(int j = 0; j != this.tiles[0].length; j++) {
boolean draw = true;
if(this.tiles[i][j].type.equals("EMPTY")) {
draw = false;
} else if(this.tiles[i][j].type.equals("PATH")) {
g2.setColor(Color.LIGHT_GRAY);
} else if(this.tiles[i][j].type.equals("WALL")) {
g2.setColor(Color.DARK_GRAY);
}
if(draw) {
g2.fillRect((int)((this.xPos+i*this.tileSize)*scale + offset.x),
(int)((this.yPos+j*this.tileSize)*scale + offset.y),
(int)(this.tileSize*scale),
(int)(this.tileSize*scale));
}
}
}
return g2;
}
Java's BufferedImage class is slow. It's a known fact. If you want to do fast image manipulation then it's the wrong tool to use.
Additionally games like Skyrim are using the graphics card to do most of the work, using the Java Image stuff it is all being done in the CPU.
You should really look into using a game framework - there are 2d ones like Slick2d and 3d ones like jMonkeyEngine3.
A few things you might consider trying:
If your original Graphics object is being created by a BufferedImage, consider using VolatileImage instead. As others have said, everything in this draw method is happening on the CPU via the Swing Event Dispatch Thread, so you are limited to a single CPU for drawing (unless you spawn more Threads somewhere). VolatileImage, when used for double-buffering, takes advantage of the graphics hardware and can be MUCH faster for this kind of thing.
You're doing a TON of String comparisons, which can be very slow. I'd consider refactoring your type field to be a custom enum instead, then have that enum define its own draw method that takes a Graphics object and draws itself. That way, you could call this.tyles[i][j].type.draw(g2) and rely on late binding to draw the right colored rectangle, thereby eliminating the String comparisons.
Make sure you're only drawing the stuff that's on screen. I don't know anything about your tiles array, but if it's significantly larger than what's actually being rendered to screen, you could be wasting a lot of CPU cycles there.
This might sound silly, but you're actually doing twice as many memory accesses as you really need. I'm going to assume you have Tile[][] tiles ... defined somewhere in your source code. In the outer loop, grab the row first by writing something like Tile[] row = tiles[i];, then in your inner loop, get the type by calling row[j].type. This will cut your memory access count in half when iterating over the tile array.
I am developing a small Game in Java, and I'm rewriting the Player Movement system to not be Grid-Based. It is a 2D side-scroller, and what I'm trying to achieve is basic Player Movement, so that when the user presses, and holds, the right Key the Player moves right, and the same for the Left Key. The problem I am having is that the Paint Component in another Class draws the image of the Player on the screen, with positions X and Y stored in a Settings Class. Then a KeyListener Class gets when the user is pressing Right, and adds to the X value (And the same for Left, but minuses 1 every time). This creates a slowly moving Character on the screen, and what I want to do is have him move faster without adding more than 1px each time as it would seem like he was skipping pixels (I've already tried).
I was wondering if there was a better way to go about this, as the code I'm using is bare-minimum, and my outcome would be a smoothly moving Player.
KeyListener Snippet:
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x++;
}else if(key == 37) { // Left Key
Settings.player_pos_x--;
}
main.Game.redo();
}
Drawing User on-screen:
g.drawImage(player_image, Settings.player_pos_x, Settings.player_pos_y, this);
Any help is appreciated, if you need any more information or code please feel free to ask.
Let's try again :)
Double buffering
Quote: http://msdn.microsoft.com/en-us/library/b367a457.aspx
Flicker is a common problem when programming graphics. Graphics operations that require
multiple complex painting operations can cause the rendered images to appear to flicker
or have an otherwise unacceptable appearance.
When double buffering is enabled, all paint operations are first rendered to a memory
buffer instead of the drawing surface on the screen. After all paint operations are
completed, the memory buffer is copied directly to the drawing surface associated with
it. Because only one graphics operation is performed on the screen, the image
flickering associated with complex painting operations is eliminated.
Quote: http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html
Suppose you had to draw an entire picture on the screen, pixel by pixel or line by
line. If you were to draw such a thing directly to the screen (using, say,
Graphics.drawLine), you would probably notice with much disappointment that it takes
a bit of time. You will probably even notice visible artifacts of how your picture is
drawn. Rather than watching things being drawn in this fashion and at this pace, most
programmers use a technique called double-buffering.
Perhaps you could write the app to iterate multiple redraws to the right, each only 1 or 2 pixels per keyboard input received. This way, you're kind of artificially setting how fast you want it to move. Otherwise, you'd be limited to how the user has their keyboard iteration speed set up.
Also, java's jPanel is not exactly the place to look for video game efficiency. Just saying; you might want to look to something like openGL for that.
At best, you could optimize it to have a transition buffer outside of the jpanel drawing logic, with the same draw functionality as a jPanel, this way you write to the buffer, then copy the whole buffer during a writeImage call... I don't know if you're already doing that, but I think it avoids flicker or something... read about it a long time ago, it's called double buffering.
While double buffering will probably help you should first address how you are calculating the distance you move each time.
Change in distance (dx) can be defined as the velocity(v) times the change in time(dt), dx = v * dt. From what I can see you are omitting dt (change in time). You should calculate the time difference from the last time the character was moved and multiply that by your velocity. This way if your distance processing code is executed 10 time or 100 times in 10 seconds the character will always move the same distance.
The code will look something like this:
int currentTime = System.nanoTime();
int deltaTime = currentTime - previousTime;
Settings.player_pos_x += velX * deltaTime;
previousTime = currentTime;
With this code you will probably need to significantly increase velX. Typically I would have a constant that I would multiply by the velocity so they are smaller numbers.
Additionally if you want better movement and physics look into JBox2D although it's probably over kill for you right now. Their documentation might also help to understand basic physics.
In order to answer your question about the speed, most games i've seen store a global int for the velocity in each plane (x and y planes), then apply the velocity to the current position rather than simply incrementing it as you have done above.
public int velX = 2;
public int velY = 2;
public void keyPressed(KeyEvent arg0) {
int key = arg0.getKeyCode();
if(key == 39) { // Right Key
Settings.player_pos_x += velX;
}else if(key == 37) { // Left Key
Settings.player_pos_x -= velX;
}
main.Game.redo();
}
Once you have that set up, try adding operations which may increase the velocity (i.e. holding the key down for a long time, or something else);
Also, try to implement a jump operation, where the players Y position goes up, and try to add a gravity field to make it come back down.
I am making a game where for the purpose of this question i have a a class that extends JFrame that adds a class that extends JPanel(GamePanel). in GamePanel i have a run method that has two functions update(); and repaint(); and then a Thread.sleep(20). The update function takes about 1-2ms. I have all my drawing stuff in paintComponent(Graphics g) which seems to correctly get called when i use repaint() since stuff shows up on screen.
My problem is that it is increeedibly laggy. When i didn't have Thread.sleep(20) it was unplayable with like 2fps. I read that this was because repaint() wasnt given enough time to finish or something so i added a delay before next loop. anything above or under 20ms seems to make it more laggy.
I've tried using graphics configuration stuff, double buffering and more but it stays laggy. On my home pc, which is an intel i5, quad core, 3.2GHz i'm only getting around 100fps, and on a school computer i get around 15fps (ok pc, like amd dual core i believe). The paintComponent loop is Super lightweight! just drawmap with offset depending on player position, then draw player in middle of screen! I am using a map that is 2000x2000, 0.8mb. Tried switching to 1000x1000 0.4mb and no difference.
Here is the code:
#Override
public void paintComponent(Graphics g) {
//Rendering settings and stuff
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
//Draw map gameMap.gameMap is a bufferedImage
g2d.drawImage(gameMap.gameMap, gameMap.x, gameMap.y, null);
//Draw health bar
g2d.setColor(healthBarColor);
g2d.setComposite(alphaHealthBarBackground);
g2d.fillRect(100, 19, 200, 23);
g2d.setFont(font);
g2d.setComposite(alphaNormal);
g2d.setColor(healthBarColor);
g2d.drawString("HP: ", 20, 40);
g2d.fillRect(100, 19, player.health * 2, 23);
//Draw player player.playerImage is a BufferedImage
rotatePlayer.setToRotation(rotation, rotationAnchor.x, rotationAnchor.y);
rotatePlayer.translate(xResolution / 2, yResolution / 2);
g2d.drawImage(player.playerImage, rotatePlayer, this);
g2d.setColor(Color.white);
}
This results in 100fps on relatively awesome computer and 15fps on descent computer!
Not pretending to be ultimate solution, just some tips:
Make sure your buffered images are have same color model as default of GraphicsDevice on which they drawn. The method GraphicsConfiguration.createCompatibleImage could create such images.
If it is possible, try to split whole huge map to tiles and skip rendering off-screen (out of game view) parts.
It seems you use passive rendering, as you mentioned calling of repaint(). Event Dispatch Thread used for many things in Swing and AWT, and it can't guarantee acceptable timing for actively rendered games. Maybe it worth to redesign rendering part of game to use Active Rendering. Tutorial and example can be found here.
Well, for game development I donot think drawing a huge image in a Swing paintComponent is ever going to win any speed records. 100 FPS is still amazing (and about 40 FPS more than the refresh rate of most LCD displays).
For more speed it is essential that the drawing primitives you are using are accelerated. On your school PC's they may simply lack a decent graphics card that provides the 2D acceleration needed (how do these PC's feel doing normal 2D operations in say browsers and paint programs?)
You may want to look into a framework that uses OpenGL instead, like JOGL. Another option is to try out JavaFX 2 -- it may have a better rendering pipeline.
I am also developing a Java game and have done many things the same as you (extending JPanel and calling repaint()). One difference is that I am using a javax.swing.Timer to call an ActionPerformed(ActionEvent e) method to update my game every 5ms. At the end of the method it calls repaint() to do the rendering.
Also if you are using Graphics2D to do your rendering you can use RenderingHints to guide the computer on how you want your game to be rendered (Eg, speed or quality, stuff like that).
I haven't measured the FPS exactly but I get no lag on my Ubuntu 12.10 system and JUST a bit slower on Windows 8.
I apologize if my response isn't exactly what you wanted but this is what has worked for me so far without problems.
I'm currently working on a simple 2D top-down shooter in which you maneuver your character around a map. The graphics are still very much in the "testing" phase (solid colors and rectangles). The drawing process is as follows:
I have the map (this is basically just walls or free space) stored as an integer array which is then looped through and painted onto my JPanel.
Once the map is drawn, I go through each "entity" on the map (powerups, other characters) and place them.
Finally, I draw the user's character
I experimented with different methods, and finally decided that the user's character would stay in the center of the screen with the map moving around it.
Currently, it works but it seems slow. I only have 4 walls, 2 entities, and the user's character being drawn and it's at 90FPS, spiking around 60-70 quite often. This would be acceptable if the game were finished, however I still have fancier graphics, many more entities, walls, bullets, etc. to be rendered onto the map. I've tried increasing the speed of my gameLoop, which can get it as high as 230 FPS, but even then when I move into areas with entities or walls it spikes down to 50-70.
I've tried limiting what is drawn to only what is visible but the results were overall negligible and even negative.
The current paintComponent() method looks something like this:
public void painComponent(Graphics g) {
for(int x = 0; x < Map.size.getWidth() ; x++) {
for(int y = 0; y < Map.size.getHeight(); y++) {
if(mapArray[y][x] == WALL) {
g.setColor(Color.BLACK);
g.fillRect(x, y, wallSize, wallSize);
}
}
}
for(int i = 0; i < map.countEntities(); i++) {
Entity obj = map.getEntity(i);
g.setColor(Color.WHITE);
g.fillRect(obj.getCoords().x, obj.getCoords().y, obj.getSize().width, obj.getSize().height);
}
Robot bot = map.getPlayerBot();
g.setColor(Color.BLACK);
g.fillRect(bot.getCoords().x, bot.getCoords().y, bot.getSize().width, bot.getSize().height);
}
Do any of you gurus out there have any tips to speed this up a bit?
Beyond the answers mentioned in the following links:
Java 2D Drawing Optimal Performance,
Java2D Performance Issues,
Java 2D game graphics
and How to display many SVGs in Java with high performance
A common and simple trick to improve 2D performance is to draw all the static objects ('background') into a buffered image, then when drawing the next image you just offset the pixels of your background image from the previous screen accordingly, then you need only draw the pixels around at most two edges of the screen rather than the entire screen, this only really matters when you're drawing a lot of image layers to the screen.
Beyond all of that make sure you have double buffering enabled so that the drawing doesn't try and draw to the actual screen while you're still drawing the next image.
Seph's answer was taken into consideration and implemented, but it did not increase the speed. Apparently, the image being drawn was not the problem at all. The problem was the timer that I was using to call the update methods. I was using an instance of the Timer class which seems to be very unreliable, especially at small intervals such as these. Upon removing the timer and calling my update methods based on the system clock, I was able to reach and maintain incredibly high FPS.