Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (499)
Games in Android Showcase (118)
games submitted by our members
Games in WIP (568)
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  
  null objects in ArrayList  (Read 897 times)
0 Members and 1 Guest are viewing this topic.
Offline songangel

Junior Member


Medals: 1



« Posted 2014-02-23 02:31:56 »

Hey guys,

I am getting an occasional null pointer exception in my games ObjectManager class.  I can't seem to find a pattern as to why this is happening and could use some help.  Every object in my game is added and removed by this ObjectManager class by checking the objects alive property.  This property is set in collision methods when two objects collide so that the ObjectManager knows when to remove them.

Below is the ObjectManager update method:
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  
public void update()
   {
      GameObject a, b;
     
      // Update the objects and cull the dead
     
      for(int i=0; i<objects.size(); i++)
      {
     
         objects.get(i).update();
         
         if(objects.get(i).isAlive() == false)
         {
            objects.get(i).destroy();
            objects.remove(i);
     
         }
         
      }
     
      // Check for collision
     
      for(int i=0; i<objects.size(); i++)
      {
         for(int j=i; j<objects.size(); ++j)
         {
            a = objects.get(i);
            b = objects.get(j);
           
            if(a == b) continue;
           
            // Finally do the test and alert both objects
           
            if(collision(a,b))
            {
               a.collision(b);
               b.collision(a);
            }
           
           
         }
         
      }
     
     
   }


Here is my relevant stack trace:
1  
2  
3  
4  
5  
6  
7  
8  
Exception in thread "Thread-3" java.lang.NullPointerException
   at shooter.engine.ObjectManager.collision(ObjectManager.java:110)
   at shooter.engine.ObjectManager.update(ObjectManager.java:83)
   at shooter.game.PlayScreen.update(PlayScreen.java:33)
   at shooter.engine.ScreenManager.update(ScreenManager.java:15)
   at shooter.engine.GamePanel.update(GamePanel.java:137)
   at shooter.engine.GamePanel.run(GamePanel.java:127)
   at java.lang.Thread.run(Unknown Source)


Here is the collision method from the stack trace:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
   private boolean collision(GameObject a, GameObject b)
   {
         
      double dx = a.getX() - b.getX();
      double dy = a.getY() - b.getY();
     
      double dist = Math.sqrt(dx * dx + dy * dy);
      if(dist < a.getR() + b.getR()) return true;
     
      return false;
   }


The line that it's pointing to is in the collision method.  I don't see how it could be null because "dead" objects are removed before checking for collision.  The crash doesn't always occur in the collision method, sometimes it occurs in the update method posted above.  Any ideas?  I would be happy to post more code if needed.
Offline HeroesGraveDev

JGO Kernel


Medals: 260
Projects: 11
Exp: 2 years


┬─┬ノ(ಠ_ಠノ)(╯°□°)╯︵ ┻━┻


« Reply #1 - Posted 2014-02-23 02:52:06 »

Make sure you aren't adding null objects to the arraylist.

Offline songangel

Junior Member


Medals: 1



« Reply #2 - Posted 2014-02-23 02:55:28 »

I'm not.  I'm only adding with the new operator.   Huh
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Drenius
« Reply #3 - Posted 2014-02-23 03:00:25 »

1  
objects.remove(i);

If you do so, the list size is one object shorter.
This should not be a problem since you are always checking objects.size() again, but you are at least skipping one object.
A little "i--" is necessary.
Offline BurntPizza
« Reply #4 - Posted 2014-02-23 03:01:42 »

What's your destroy() look like?

Also, some quick tips:
Try to have variables in the narrowest possible scope, meaning, don't hoist your GameObject a, b out of the loop.

You don't have to test boolean expressions with == false, so just if(!objects.get(i).isAlive()) { ...
Also just return the result of a boolean expression: return dist < a.getR() + b.getR();

Edit: Drenius is right, you need remove(i--); or else it will skip objects. Alternatively, you can iterate backwards.
Offline songangel

Junior Member


Medals: 1



« Reply #5 - Posted 2014-02-23 03:04:27 »

For my Enemy class:
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  
@Override
   public void destroy()
   {
      SoundManager.play("explosion");
      ObjectManager.add(new Explosion(x,y));
     
      // Type 4 and 5 enemies break into smaller types
     
      if(type == MAMA_YELLOW)
      {
         ObjectManager.add(new Enemy(x,y,BABY_YELLOW));
         ObjectManager.add(new Enemy(x,y,BABY_YELLOW));
         ObjectManager.add(new Enemy(x,y,BABY_YELLOW));
      }
     
      if(type == MAMA_MAGENTA)
      {
         for(int i=0; i<10; i++)
         {
            ObjectManager.add(new Enemy(x,y,BABY_MAGENTA));
         }
      }
     
      // Chance for powerup
     
      int chance = (int) (Math.random() * 100);
     
      if(chance < 5) ObjectManager.add(new PowerUp(x,y,PowerUp.EXTRA_LIFE));
      else if(chance < 20) ObjectManager.add(new PowerUp(x,y,PowerUp.SLOW_DOWN));
      else if(chance < 60) ObjectManager.add(new PowerUp(x,y,PowerUp.POWER));
     
   }


The destroy method was intended to allow one object to "change" into another.  Most of the logic for setting an object as "dead" occurs in the collision events.

EDIT:  @BurntPizza:  Thanks for the info on i--.  I've often wondered why people do this.  This doesn't seem to be the cause of the problem though as the crash occurs with or without it.
Offline songangel

Junior Member


Medals: 1



« Reply #6 - Posted 2014-02-23 03:29:50 »

Any other ideas?  This is annoying bug that's slowing my progress on the game and I'm not even sure how to start debugging it.  Sometimes I can play through the game 10 times without incident and then suddenly the crash occurs. 
Online LiquidNitrogen
« Reply #7 - Posted 2014-02-23 03:33:20 »

perhaps put in some checks for null pointers and print an error instead of calling the desired function, then you might be able to see whats going wrong without it actually crashing.

Offline songangel

Junior Member


Medals: 1



« Reply #8 - Posted 2014-02-23 03:41:54 »

Could this be some sort of multi threading issue?  I am posting my GamePanel class 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  
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  
package shooter.engine;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;

import shooter.game.PlayScreen;

@SuppressWarnings("serial")
public class GamePanel extends Canvas implements Runnable, KeyListener, MouseMotionListener, MouseListener
{
   public static final String GAME_NAME = "SHOOTER v1.0";
   public static final int WIDTH = 1024;
   public static final int HEIGHT = 768;
   
   private Thread thread;
   private boolean running;
   
   private int FPS = 60;
   private int targetTime = 1000 / FPS;
   
   private Graphics2D g;
   private BufferStrategy bs;
   
   private ScreenManager scm;
   private GameLoop gameLoop;
   
   public GamePanel()
   {
      super();
      setPreferredSize(new Dimension(WIDTH, HEIGHT));
      setFocusable(true);
      requestFocus();
      setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
   }
   
   private void init()
   {
      running = true;
     
      createBufferStrategy(2);
      bs = getBufferStrategy();
      setIgnoreRepaint(true);
     
      Graphics2D gTemp = (Graphics2D) bs.getDrawGraphics();
     
      gTemp.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      gTemp.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
     
      scm = new ScreenManager();
      scm.changeScreen(new PlayScreen(scm));
     
      gameLoop = new GameLoop(targetTime);
     
      // Load game sounds
     
      SoundManager.add("laser");
      SoundManager.add("explosion");
      SoundManager.add("powerup");
      SoundManager.add("hit");
      SoundManager.add("powerup");
      SoundManager.add("boost");
     
   }
   
   @Override
   public void addNotify()
   {
      super.addNotify();
      addKeyListener(this);
      addMouseListener(this);
      addMouseMotionListener(this);
      init();
     
      // THREAD START
     if(thread == null)
      {
         thread = new Thread(this);
         thread.setDaemon(true);
         thread.start();
      }
   }

   @Override
   public void run()
   {
      /*long startTime;
      long urdTime;
      long waitTime;
         
      while(running)
      {
         startTime = System.nanoTime();
         
         update();
         draw();
         Toolkit.getDefaultToolkit().sync();
         
         urdTime = (System.nanoTime() - startTime) / 1000000;
         waitTime = (long) (targetTime - urdTime);
         
         if(waitTime < 0) waitTime = 5;
         try
         {
            Thread.sleep(waitTime);
         }
         
         catch(Exception e)
         {
            e.printStackTrace();
         }
      }*/

     
      while(running)
      {
         if(gameLoop.redraw())
         {
            update();
            draw();
            gameLoop.setRedraw(false);
         }
           
      }  
   }
   
   private void update()
   {
      scm.update();
   }
   
   private void draw()
   {
      g = (Graphics2D) bs.getDrawGraphics();
      g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
     
      // Clear the screen
     
      g.setColor(Color.BLACK);
      g.fillRect(0, 0, WIDTH, HEIGHT);
     
      // Draw game content here
     
      scm.draw(g);
     
      // Render to the screen
     
      g.dispose();
      bs.show();
   }
   
   @Override
   public void keyPressed(KeyEvent key)
   {
      int keyCode = key.getKeyCode();
      scm.keyPressed(keyCode);
   }

   @Override
   public void keyReleased(KeyEvent key)
   {
      int keyCode = key.getKeyCode();
      scm.keyReleased(keyCode);
   }

   @Override
   public void keyTyped(KeyEvent key)
   {
     
   }

   @Override
   public void mouseDragged(MouseEvent e)
   {
      scm.mouseMoved(e);
   }

   @Override
   public void mouseMoved(MouseEvent e)
   {
      scm.mouseMoved(e);
   }

   @Override
   public void mouseClicked(MouseEvent e)
   {
     
   }

   @Override
   public void mouseEntered(MouseEvent e)
   {
     
   }

   @Override
   public void mouseExited(MouseEvent e)
   {
     
   }

   @Override
   public void mousePressed(MouseEvent e)
   {
      scm.mousePressed(e);
   }

   @Override
   public void mouseReleased(MouseEvent e)
   {
     
   }
}
Offline Drenius
« Reply #9 - Posted 2014-02-23 03:51:37 »

How often/when does the error occur?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline songangel

Junior Member


Medals: 1



« Reply #10 - Posted 2014-02-23 03:55:00 »

It's pretty rare and random which is what makes it so annoying.  At one point I thought that it seemed to occur when there were many objects on the screen at once, but there's no definite pattern that I can find.  

EDIT: At no point in the game do I actually set any object to null, I only set it's alive boolean to false.  In my mind, this means that only the ObjectManager class will remove objects from the list which I guess is where garbage collection comes in.
Offline BurntPizza
« Reply #11 - Posted 2014-02-23 03:56:07 »

It's pretty rare and random which is what makes it so annoying.  At one point I thought that it seemed to occur when there were many objects on the screen at once, but there's no definite pattern that I can find. 

Yep, typical symptoms of a race condition, aka a multithreading issue like you said. I'm pretty busy right now, but I'll try to find it here in a sec.
Offline songangel

Junior Member


Medals: 1



« Reply #12 - Posted 2014-02-23 04:03:08 »

Thanks I appreciate your time.  The GamePanel is my engine which runs on it's own thread.  The GameLoop class is just a class which extends java.util.Timer and java.util.TimerTask to tell me when to update and redraw.  It doesn't do any heavy lifting and was just an experiment because I wasn't happy with the commented game loop code you see above.  I should mention that the bug occurred even without a third thread (i.e. the GameLoop class).

I have yet to find an "ideal" game loop that works for me without occasional lag, but that's another story.   Wink
Offline opiop65

JGO Kernel


Medals: 154
Projects: 7
Exp: 3 years


JumpButton Studios


« Reply #13 - Posted 2014-02-23 04:06:01 »

Quote
third thread
I know I'm not helping, but damn man. Three threads for what? That's just over the top, you don't need that many and you will get issues like this from using too many threads.

Offline Drenius
« Reply #14 - Posted 2014-02-23 04:07:17 »

You should always avoid using iteration through a list that can be modified in another Thread. You could try to clone the list or adding everything to remove to a "toRemove" list so you could manage it someway...
But @opiop64:
Yep, normally multithreading is not even necessary.
Offline songangel

Junior Member


Medals: 1



« Reply #15 - Posted 2014-02-23 04:07:42 »

@opiop64: Well I'm assuming there is three based on the output of the stack trace.  The application itself, the GamePanel class, and the GameLoop class which as I mentioned is just an experiment and the bug occurred before I implemented that.
Offline songangel

Junior Member


Medals: 1



« Reply #16 - Posted 2014-02-23 04:09:56 »

@Drenius should I remove the Runnable interface altogether?  I'm actually more experienced with LWJGL than I am with Java 2D, but every tutorial/example code I've ever seen implements Runnable.
Offline BurntPizza
« Reply #17 - Posted 2014-02-23 04:12:00 »

I would recommend several things:

'Proper' game loops: it's a bit old, but this is alright: http://www.java-gaming.org/topics/game-loops/24220/view.html
Stay away from Timer/TimerTask, System.nanoTime() is all you ever need.

Read the Oracle tutorials about Swing and it's threading model, and also the tuts on concurrency. They will make you a better programmer.
Offline songangel

Junior Member


Medals: 1



« Reply #18 - Posted 2014-02-23 04:14:44 »

@BurntPizza:  I have read that post and found it interesting.  My issue seemed to be due to a bad implementation of the Thread.sleep on windows platform (or so I've read) which caused the sleep time to be innacurate so I just wanted to see how the game would run from a steady timer "tick" if you will.  Also, I'm not using swing with the exception of JFrame I suppose.
Offline BurntPizza
« Reply #19 - Posted 2014-02-23 04:26:26 »

bad implementation of the Thread.sleep on windows platform (or so I've read)

It's pretty much a non-issue, but of course you can code defensively, compensate for any inaccuracies.
Also, I would not be at all surprised if TimerTask uses sleep().
Apparently, ScheduledThreadPoolExecutor is good, although I have not used it.
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.

Pippogeek (40 views)
2014-09-24 16:13:29

Pippogeek (31 views)
2014-09-24 16:12:22

Pippogeek (21 views)
2014-09-24 16:12:06

Grunnt (47 views)
2014-09-23 14:38:19

radar3301 (29 views)
2014-09-21 23:33:17

BurntPizza (65 views)
2014-09-21 02:42:18

BurntPizza (37 views)
2014-09-21 01:30:30

moogie (43 views)
2014-09-21 00:26:15

UprightPath (53 views)
2014-09-20 20:14:06

BurntPizza (55 views)
2014-09-19 03:14:18
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
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!