How to reduce lag in my java 2d platformer? - java

I am making a 2d platformer in Swing java, and I am wondering how to reduce the lag I get from it. I mean, it doesnt lag too bad but it is noticable that it slows down sometimes. I have a Swing timer ticking at 12 milliseconds, a cycle function, and a paint function.
public void cycle() {
if (guy.getJumpState() == false) {
if (canExecuteMovement(0, 4)) {
onGround = false;
if (guy.getY() > 300) {
// if you are in the middle, move the platforms.
for (int i = 0; i < platformCount; i++) {
platform[i].setY(platform[i].getY() - 4);
}
} else {
// or just move the guy if not.
guy.moveY(4);
}
} else {
onGround = true;
}
} else {
if (canExecuteMovement(0, -8)) {
if (guy.getY() < 300) {
// if you are in the middle, move the platforms.
for (int i = 0; i < platformCount; i++) {
platform[i].setY(platform[i].getY() + 8);
}
} else {
// or just move the guy if not.
guy.moveY(-8);
}
jumpCount++;
if (jumpCount >= 15) {
jumpCount = 0;
guy.setJumpState(false);
}
} else {
jumpCount = 0;
guy.setJumpState(false);
}
}
if (guy.getDirection() == "left") {
if (canExecuteMovement(-3, 0)) {
if (guy.getX() < 450) {
// if you are in the middle, move the platforms.
for (int i = 0; i < platformCount; i++) {
platform[i].setX(platform[i].getX() + 3);
}
} else {
// or just move the guy if not.
guy.moveX(-3);
}
}
} else if (guy.getDirection() == "right") {
if (canExecuteMovement(3, 0)) {
if (guy.getX() > 450) {
// if you are in the middle, move the platforms.
for (int i = 0; i < platformCount; i++) {
platform[i].setX(platform[i].getX() - 3);
}
} else {
// or just move the guy if not.
guy.moveX(3);
}
}
}
}
public void paint(Graphics g) {
super.paint(g); // something important
Graphics2D g2d = (Graphics2D) g;
// draw platforms
for (int i = 0; i < platformCount; i++) {
if (platform[i].getX() > -50 && platform[i].getX() < 950 && platform[i].getY() > -50 && platform[i].getY() < 650) {
g2d.drawImage(platform[i].getImage(), platform[i].getX(), platform[i].getY(), this);
}
}
// draw guy
g2d.drawImage(guy.getImage(), guy.getX(), guy.getY(), this);
// destroy unneeded processes
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
What can I do to optimize this and cause less lag? When I make a thread for the cycle function itself, the platforms sometimes seperate for a split second. I assume because since the thread is asynchronous, half of it is done while the paint function goes on.

Some loose thoughts (it's been years since I did some animation in Swing), and you didn't posted some compilable code.
Have you tried do paintComponent() --- paint does a lot of other stuff. And then maybe you need to add repaint() to tick function. Every time I reloaded paint it enden in a mess.
Also try increasing tick time --- youll waste less time repaiting.
Also I assume you are doing ticks by Timers.
I have no idea why you dispose graphics object
Also try just dropping sync (Ive done animations that work on many oeses without it) Toolkit.getDefaultToolkit().sync()
If it doesn't help use profiler to find a bottleneck. Visual VM is quite nice. Also Visual VM is part of the jdk for some time --- just go into bin folder and launch jvisualvm.
EDIT: (thread issues)
Some people suggested using threads --- which I diasgree. If you want do some work outside EDT please use SwingWorker
I assume you are not calling paint() but just call repeaint(). If you do call paint() (whatever black magic you also make to make it work) please just call repaint() that will schedule repaing on appropriate time.

First of all, this bit here is a problem:
// destroy unneeded processes
Toolkit.getDefaultToolkit().sync();
g.dispose();
In general, disposing a resource you did not create is probably a bad idea. In this specific case, the Graphics passed into paint() is probably used by all other components in the hierarchy, so this could cause really odd results.
Calling Toolkit.sync() here is I think your attempt to eliminate the tearing you were seeing when moving things in the background. But all that it does is to flush any pending draw instructions. That has no effect here because you are probably drawing to a back-buffered Swing component that will be drawn fully later.
The correct way to eliminate tearing is to perform any updates on the event thread, so that you are not changing the screen data while drawing it. One simple way to implement this would be to have your timer just call repaint(), and have the paint() method call cycle() before doing anything.
For dealing with lag, one solution might be to allow a variable frame rate. Instead of moving everything a fixed distance each frame, calculate the time since the last frame and move everything accordingly.

I would create your variables outside the method so that it is not being created every time you call that method. The best way to program games is to re-use things instead of destroying and creating because destroying & creating cost a lot of computing power.
Graphics2D g2d = (Graphics2D) g; <---declare it outside your method.
And also try to find redundant conditionals. I saw one where you say (if direction right then ..., else if direction left ...); just say (if direction right then ... else ...). Conditionals do not cost much but when you're calling that conditional 1000 times a second I think it adds up. (idk though, but I do it just in case and for making things fluid)
Where you say setjumpstate(false) it's redundant because no matter what, it is executed - just move it outside the conditional.

Related

How to use Thread.sleep in a basic RPG game

I've been working on this really basic Java RPG game, and when I tried using Thread.sleep for my monster to move slower, it seems to affect the player as well, in other words, the movement of the player had slow down when the monster appears, but turns back to normal when I move to another part of the map where the monster is not there
Here's the code for the monster's movement
int wolfRandNum;
if (isKilled() == false){
wolfRandNum = (int) (Math.random()*4);
if (wolfRandNum == 1){ // up
System.out.println("up");
if (playerRow -1 <= 0){
setCurrentEnemySprite(3);
} else {
moveUp();
playerRow--;
}
} else if (wolfRandNum == 2){ // down
System.out.println("down");
if (playerRow +1 >= 8){
setCurrentEnemySprite(0);
} else {
moveDown();
playerRow++;
}
} else if (wolfRandNum == 3){ // left
System.out.println("left");
if (playerColumn -1 <= 0){
setCurrentEnemySprite(1);
} else {
moveLeft();
playerColumn--;
}
} else{ // right
System.out.println("right");
if (playerColumn + 1 >= 9){
setCurrentEnemySprite(2);
} else {
moveRight();
playerColumn++;
}
}
}
The code above is in a method called update, which is from an enemy class.
Here's the where I call the method:
public void paintComponent(Graphics g) {
super.paintComponent(g); //required to ensure the panel is correctly redrawn
map.draw(g);
player.draw(g);
for (Character character : characterList) {
character.draw(g);
}
for (Enemy enemy : enemyList){
enemy.draw(g);
try {
enemy.update();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (Item item : bagList) {
item.draw(g);
}
repaint();
}
Is there anyway to use thread.sleep without affecting the player's movement? If not, what other methods can I use to make the monster move slower but not the player?
I'm not really sure how to fix this problem, any advise would help a lot! Thank you :)
This is a event-driven Swing GUI, and so the answer to the question:
How to use Thread.sleep(...)
is that you don't.
You're coding this as if it were a linear console program, and this design won't work for event-driven non-linear programs. Calling Thread.sleep on the Swing event thread and especially in any painting method is a guarantee to put your entire GUI to sleep so that it becomes totally unresponsive. The proper solution is to create a non-GUI Model class to go with your GUI (your "View"), and to change how the view responds based on the state of the Model, i.e., the state of its key fields.
Also, if your game is being run in "real time", then you will need a game loop to drive your game. This can be done easily via a Swing Timer, although it does not provide absolute precision. If greater precision is required, then use the Swing Timer, but don't assume that each time slice is accurate, and instead measure each delta time and base your physics on the measured slice, not the assumed slice. Other ways to create a game loop include use of a background thread with a while loop and Thread.sleep within this thread.
All you are slowing down is the rendering (drawing) of your monster. As all drawing is done on the UI thread, this will, as you have rightly noticed, have a huge performance impact on your entire application. As a general rule, you should never call a blocking operation such as sleep in the UI thread.
Given you only have two characters, what I would consider trying is creating two threads, one for your character and one for your monster. You will do what you need to do in each of these threads, and then call repaint() when you need to redraw. If you want to slow down the monster, for example, you can call sleep in the monster thread.

Optimize rendering Canvas

I struggle with problem relayed on code (rendering) optimization.
I have 650 hexagons on screen and I need to move them all together and zoom them freely.
As far I have 2d map that stores hexagon objects that contains some parameters as vertexes, center point etc.
my onDraw method looks like this:
#Override
public void onDraw(final Canvas canvas) {
if(hexMap == null || hexMap.length <= 0) {
populateHexMap();
}
if(radiusChanged) {
changeRadius();
}
for(int i = 0; i < hexMap.length; i++) {
for(int j = 0; j < hexMap[i].length; j++) {
if(hexMap[i][j] != null && hexMap[i][j].isToDraw()) {
paint.setColor(hexMap[i][j].getColor());
drawPath.rewind();
drawPath.moveTo(hexMap[i][j].getPoint(0).x, hexMap[i][j].getPoint(0).y);
drawPath.lineTo(hexMap[i][j].getPoint(1).x, hexMap[i][j].getPoint(1).y);
drawPath.lineTo(hexMap[i][j].getPoint(2).x, hexMap[i][j].getPoint(2).y);
drawPath.lineTo(hexMap[i][j].getPoint(3).x, hexMap[i][j].getPoint(3).y);
drawPath.lineTo(hexMap[i][j].getPoint(4).x, hexMap[i][j].getPoint(4).y);
drawPath.lineTo(hexMap[i][j].getPoint(5).x, hexMap[i][j].getPoint(5).y);
drawPath.lineTo(hexMap[i][j].getPoint(0).x, hexMap[i][j].getPoint(0).y);
canvas.drawPath(drawPath, paint);
}
}
}
}
Drawing works great as far. To zoom I catch pinch event gesture and change radius of hexagon then recalculate its vertexes. To move I simply add shift value to center points and recalculate vertexes.
Everything has worked great when I had for example ~30 hexagons. With 650 everything is super slow.
I've tried to enable hardware acceleration but it didn't help. I've tried to reduce amount of invalidate() calls but in my opinion it's not the problem. The problem lays on drawPath.
Is there any way to speed things up? I don't have much time to switch on opengl right now or maybe you know some opensource solutions for drawing hexagons with opengl?
Thanks in advance!

Execute a part of code in specified time

I want to make an animation using java swing. I have a for loop that changes the location of an image slightly. My question is How can I make the each loop execution takes the specified amount of time?.
Its what I've done but I don't know how to use wait() and don't know is there a better way.
// inside of a MyJPanel class extending JPanel
for (int i = 0; i < FJframe.FRAMES; i++){
long start = System.currentTimeMillis();
animationFrame = i;
this.repaint();
long end = System.currentTimeMillis();
long remainder = (end - start);
System.out.println(remainder);
try {
this.wait(200 - remainder);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Edit
here my overrided Jpanel PaintComponent():
//some drawing
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
And inside drawChanges(Graphics g, ArrayList deltas, int frame, int frames):
// a switch_case with cases similar to this one.
case AGENT_ATTACK:
// tu phase badi animation e kill
//drawImage(g2d, map[delta.getCell().x][delta.getCell().y], attacker);
source = map[delta.getSource().x][delta.getSource().y];
dest = map[delta.getDestination().x][delta.getDestination().y];
distanceX = dest.getCenter().x -
source.getCenter().x;
distanceY = dest.getCenter().y -
source.getCenter().y;
if (counter < frames / 2){
g2d.drawImage(ImageHolder.attacker, source.getBounds().x + (int)(((float)counter/frames) * distanceX),
source.getBounds().y + (int)(((float)counter/frames) * distanceY),
null);
}
else{
g2d.drawImage(ImageHolder.attacker, dest.getBounds().x - (int)(((float)counter/frames) * distanceX),
dest.getBounds().y - (int)(((float)counter/frames) * distanceY),
null);
}
break;
I want each loop takes, for example, exactly 200 miliseconds. How can I achieve this?
Look into using a Timer. For example, the scheduleAtFixedRate() method.
Probably not an acceptable answer, but too long for a comment:
There are several options, depending on the actual intention. The pattern that you described is not uncommon for a "simple game loop". In this case, a code that is similar to the one that you posted is run in an own thread, and regularly triggers a repaint() in order to paint the updated game state. In your case, it seems that only the animationFrame variable is increased. For such a simple action, the alternatives that have already been mentioned may be sufficient:
As suggested by whiskeyspider in https://stackoverflow.com/a/21860250/ : You could use a java.util.Timer with a TimerTask that only updated the animationFrame
Alternatively, you could use a javax.swing.Timer whose ActionListener updates the animationFrame. This might be advantageous here, because you can be sure that the update of the animationFrame happens on the event dispatch thread. Thus, the update of this variable can not interfere with its usage in the painting method, for example
But as mentioned in the comments: An own thread that executed the code that you already posted (with the minor adaptions) is also feasible and not "wrong" per se - but note that in this case (similar to when using a java.util.Timer) you might have to take care of the synchronization on your own
EDIT: Based on the edited question: Similar to what I expected, you are using the animationFrame in your painting method. The crucial point here are the details about how the animationFrame variable is used. For example, if your paintComponent method looks like this
class MyJPanel extends JPanel {
private int animationFrame = 0;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawChanges(g, getDelta(), animationFrame, FJPanle.FRAMES);
drawSomethingElse(g, animationFrame, ...);
drawEvenMore(g, animationFrame, ...);
}
...
}
then it may happen that the value of animationFrame is changed (by another thread, possibly the java.util.Timer thread) while the paintComponent method is executed. That means that drawChanges and drawSomethingElse may receive different values for animationFrame. This may cause rendering artefacts (misplaced images, tearing between tiles etc).
This could either be avoided by using a javax.swing.Timer (because then, the updates of animationFrame will be done on the same thread as the one that executes paintComponent), or by making sure that all painting operations use the same value of animationFrame - roughly like this:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int animationFrameUsedForAllPainting = animationFrame;
drawChanges(g, getDelta(), animationFrameUsedForAllPainting , FJPanle.FRAMES);
drawSomethingElse(g, animationFrameUsedForAllPainting , ...);
drawEvenMore(g, animationFrameUsedForAllPainting , ...);
}
But apart from that, there is not sooo much difference between the aforementioned approaches in this case. So for simplicity, you could use the javax.swing.Timer (or the java.util.Timer when you make sure that the update is "thread safe" regarding the painting operations).

JAVA Background Animation (LinearGradientPaint)

A LinearGradientPaint object from java.awt may appear nice once painted, but there's a problem I'm having with it for painting an animated background in some kind of game model that's taking me long to build.
I want to paint an animated rainbow gradient on the background using the Graphics2D paint object, except that when I do so, I notice a lot of lag in repainting the panel. It should repaint itself at least 30 frames per second, which is only possible if the Paint object the graphics object uses is not a rainbow gradient.
Even running it as a separate thread will not do the trick. Below is the code for what I am trying to do at the end of each frame:
gamePanel.executor.execute(new Runnable(){
public void run()
{
while(true)
{
if (Background.selectedBackgroundIndex >= Background.SKY_HORIZON_GRADIENT_PAINT &&
Background.selectedBackgroundIndex < Background.SPACE_PAINT)
{
float displacementValue = 1.0f;
if (Background.backgroundShape.y < ((-2990.0f) + CannonShooterModel.gamePanel.getSize().height) && gamePanel.horizonGoingDown)
gamePanel.horizonGoingDown = false;
else if (Background.backgroundShape.y > (-10.0f) && !gamePanel.horizonGoingDown)
gamePanel.horizonGoingDown = true;
Point2D.Double startPoint = (Point2D.Double)(((LinearGradientPaint)Background.background).getStartPoint()),
endPoint = (Point2D.Double)(((LinearGradientPaint)Background.background).getEndPoint());
if (gamePanel.horizonGoingDown)
Background.backgroundShape.y -= displacementValue;
else
Background.backgroundShape.y += displacementValue;
startPoint.setLocation(0, Background.backgroundShape.y);
endPoint.setLocation(0, Background.horizonGradientPaintHeight + Background.backgroundShape.y);
// Should be done in another thread, particularly in arithmetic calculations.
Background.background = new LinearGradientPaint(startPoint, endPoint,
((LinearGradientPaint)Background.background).getFractions(),
((LinearGradientPaint)Background.background).getColors());
}
for (int a = 0; a < PlayerUnit.weapon.bullets.length; a++)
{
if (PlayerUnit.weapon.bullets[a] != null)
{
if (PlayerUnit.weapon instanceof Pistol &&
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).x + ((Pistol)PlayerUnit.weapon).bulletWidth >= 0 &&
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).x <= CannonShooterModel.gamePanel.getSize().width &&
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).y + ((Pistol)PlayerUnit.weapon).bulletWidth >= 0)
{
if (PlayerUnit.weapon.weaponAngles[a] >= 0)
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).x +=
PlayerUnit.weapon.bulletSpeed * Math.cos(PlayerUnit.weapon.weaponAngles[a]);
else
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).x -=
PlayerUnit.weapon.bulletSpeed * Math.cos(PlayerUnit.weapon.weaponAngles[a]);
if (PlayerUnit.weapon.weaponAngles[a] >= 0)
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).y -=
PlayerUnit.weapon.bulletSpeed * Math.sin(PlayerUnit.weapon.weaponAngles[a]);
else
((Ellipse2D.Float)PlayerUnit.weapon.bullets[a]).y +=
PlayerUnit.weapon.bulletSpeed * Math.sin(PlayerUnit.weapon.weaponAngles[a]);
}
else
PlayerUnit.weapon.bullets[a] = null;
}
}
//System.out.println(Background.backgroundShape.y);
repaint();
try
{
Thread.sleep(1000 / 60);
}
catch (InterruptedException ex)
{
}
}
}
});
The classes Background, PlayerUnit, and CannonShooterModel are important to my game model. It's an upright shooting game supposed to be designed with various weapons and enemies.
This rainbow gradient I have uses an array of eight different Color objects. For every frame passed through, I change the y-coordinate for both Point2D.Float objects required for the gradient paint as desired. In order for the animation to work, I have to actually instantiate another object of LinearGradientPaint again, with some of the previous properties from the previous object, and have it be referenced by the variable background of type Paint.
Problem is, LinearGradientPaint does not have a method to where you can do a translate on the two end points, and the get methods do not return the actual object that LinearGradientPaint object contains. (what I mean is, the get methods return a new object of Point2D with the same values as those part of the LinearGradientPaint object.)
For every frame passed, I have to change not only the y-coordinate property of the shape that's associated with the gradient, but also set the locations of the two Point2D objects that are needed to instantiate LinearGradientPaint once again.
I would love to re-explain this much simpler, because I can have trouble with some knowledge of English, even though this is my primary language. Please let me know if you need re-explanation.
There are a couple of solutions you might try.
Instead of filling the entire paintable area, you could create a BufferedImage whose width is 1 pixel and height equal to the area you want to fill (assuming you are fill vertically). You would then apply the LinearGradientPaint to this BufferedImage's Graphics2D and fill it (don't forget to dispose of the Graphics context when your done).
Then, you would simply use Graphics#drawImage(Image, x, y, width, height, ImageObserver) to actually paint the image. Generally speaking, it appears that it's faster to rescale an image then it is to fill it with a LinearGradientPaint, especially when you consider you are only stretching the image horizontally.
The other choice would be to generate a basic BufferedImage which had the LinearGradientPaint already applied and you would simply paint this offset as required. This would probably require you to paint it at least twice to allow it to "stitch" toegther...
If you are just making the background gradient move up and down, could you write it to an image at initialization and then move and wrap the image vertically?

Drawing lots of particles efficiently

I have written a particle system applet; currently I am creating, and drawing each particle separately.
(Here is the code)
BufferedImage backbuffer;
Graphics2D g2d;
public void init(){
backbuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
g2d = backbuffer.createGraphics();
setSize(WIDTH, HEIGHT);
//creates the particles
for (int i = 0; i < AMOUNTPARTICLES; i++) {
prtl[i] = new particleO();
prtl[i].setX(rand.nextInt(STARTX));
prtl[i].setY(rand.nextInt(STARTY));
prtl[i].setVel(rand.nextInt(MAXSPEED)+1);
prtl[i].setFAngle(Math.toRadians(rand.nextInt(ANGLESPREAD)));
}
//other code
}
public void update(Graphics g) {
g2d.setTransform(identity);
//set background
g2d.setPaint(BGCOLOUR);
g2d.fillRect(0,0,getSize().width,getSize().height);
drawp();
paint(g);
}
public void drawp() {
for (int n = 0; n < AMOUNTPARTICLES; n++) {
if (prtl[n].getAlive()==true){
g2d.setTransform(identity);
g2d.translate(prtl[n].getX(), prtl[n].getY());
g2d.setColor(prtl[n].getColor());
g2d.fill(prtl[n].getShape());
}
}
}
It's performance was alright, I could get ~40FPS with 20,000 particles (although, I have a decent laptop). But after I added collision detection (see below), that number plummeted to less than 2000,
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
public boolean hasCollided(int prt1, int prt2){
double dx = prtl[prt1].getX() - prtl[prt2].getX();
double dy = prtl[prt1].getY() - prtl[prt2].getY();
int edges = prtl[prt1].getRadius() + prtl[prt2].getRadius();
double distance = Math.sqrt( (dx*dx) + (dy*dy) );
return (distance <= edges);
}
I have searched quite a bit for a better way of drawing the particles to the screen, but the examples either confused me, or were not applicable.
I am doing a boat load of calculations (too many). But I couldn’t think of another way of doing it, suggestions are welcome.
First of all, adding in something like collision detection always takes a lot of memory. However, let's look at your collision detection algorithm
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
Let's pretend there was only 2 particles, 1 and 2. You will check, in order
1,1
1,2
2,1
2,2
The truth is, you only really needed to check 1 vs 2 in this case. If 1 hits 2, 2 will also hit 1. So change your for loop skip previously tested, and the same number for that matter.
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = i+1; j < AMOUNTPARTICLES; j++) {
Another thing I notice is that you do a sqrt operation, but only to compare to what looks like a static number. If you remove that, and compare it to the number squared, you'll get a large improvement, especially with something you do so much.
double distance_squared = ( (dx*dx) + (dy*dy) );
return (distance <= edges*edges);
Keep looking for improvements like this. Then you might carefully look at other options, like using a different class, threading, etc, which all might improve the system. But make sure you optimize the code where you can first. Here is a list of other things that I would try, roughly in order.
Check to see if particle i is alive before you even calculate anything else after i comes into view.
Do a quick pass over the pairs, only even bothering to check in detail if they are close. A simple way would be to detect if they are within the x and y dimensions first, before taking a sqrt operation. Always do the cheapest test first, before doing complex ones.
Look at your code, to see if you really use all of the values calculated, or if you might be able to reduce the number of operations somehow.
Perhaps you could cluster the images on a regular basis with a broad stroke, then refine only objects which passed the initial cluster for a period of time, then do a broad cluster algorithm.
You could thread the collision detection. However, if you are going to do this, you should only thread the checking to see if something has collided, and after all of those threads are done, update the objects on the view.
Look into alternative architectures, which might speed things up some.
Painting is a complex process and paint requests can be triggered for any number of reasons, the OS might want the window to update, the repaint manager might want to repaint, the programer might want to repaint.
Updating the particles within the paint process is bad idea. What you want to do is update the particles in a separate thread on a separate buffer. When you're ready, request that the component responsible for painting the buffer perform a repaint, passing a new copy of the buffer to repainted (you don't want to be painting on the buffer that is begin updated to the screen, you'll end up with dirty paints).
It's hard to tell from you code, but it would appear you're using java.awt.Applet, personally, I'd upgrade to javax.swing.JApplet.
I'd move the painting to a java.swing.JPanel. Swing components provide double buffering (as well as other buffering strategies). The only job this panel has is to paint a buffer to the screen when the particles engine has a new frame.
The particles engine is responsible for updating all the particles and painting these results to a backing buffer (BufferedImage), this would then be handed to the panel and the panel would make a copy and schedule an update.
Swing is NOT THREAD SAFE. That is, you shouldn't make changes to the UI from any thread other then the Event Dispatching Thread. To this end, you might like to have a read through Concurrency in Swing for solutions to resync the off screen buffer to the client.
You are checking all particless colliding with all particlesand this is quite a requeriment, of the order of n^2 (2,000 particles means 4,000,000 combinations, for each frame).
The issue is not java but the algorithm. There must be better options, but to begin with you could reduce the comparations; if you have a maximum speed S and the time in your world increments by T, with T*S you get the maximum distance D of a particle that can collide with the particle you are considering. Reduce the search to those particle which are at a distance equal or less than that. Maybe it will be easier to restrict the search to those in an square centered in your particle an with height/widht D (this will include some particles that are too far, but will make the checks easier).
Additionally, in your code you are checking collisions of P1 vs P2 and P2 vs P1 which is the same, that is a redundance that you can avoid easily.

Categories