[LibGDX & Artemis] Random Null Pointer Exception in System

I’ve been learning how to use the LibGDX game API, and I’ve enjoyed using it so far… but I found this blog and it introduced me to Artemis and the Entity-System framework, which I also have begun to enjoy using.

Whilst trying to reconstruct Artemis’s “Spaceship Warrior” demo, I’ve come across a problem. I can render stuff onto the screen, and I’m reading successfully from the atlas and everything is working just fine… to a certain point.

As it is, you can move the ship around no problem. When you start shooting though, it gives you about 3/5 seconds before it drops this error and stack.


Exception in thread "LWJGL Application" java.lang.NullPointerException
	at game.madsplash.spacewar.Systems.SpriteRenderSystem.process(SpriteRenderSystem.java:91)
	at game.madsplash.spacewar.Systems.SpriteRenderSystem.processEntities(SpriteRenderSystem.java:68)
	at com.artemis.EntitySystem.process(EntitySystem.java:57)
	at game.madsplash.spacewar.GameScreen.render(GameScreen.java:53)
	at com.badlogic.gdx.Game.render(Game.java:46)
	at game.madsplash.spacewar.SpaceWarrior.render(SpaceWarrior.java:21)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:206)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

It’s telling me that an error is occurring in my sprite rendering system, around the part I determine it’s position.


public class SpriteRenderSystem extends EntitySystem {
	@Mapper ComponentMapper<Position> PosMap;
	@Mapper ComponentMapper<Sprite> SpriteMap;
	
	private OrthographicCamera camera;
	private SpriteBatch batch;
	
	private TextureAtlas atlas;
	private HashMap<String, AtlasRegion> regions;
	private Bag<AtlasRegion> regionsByEntity;
	
	private List<Entity> sortedEntities;
	
	
	
	@SuppressWarnings("unchecked")
	public SpriteRenderSystem(OrthographicCamera camera) {
		super(Aspect.getAspectForAll(Position.class, Sprite.class));
		
		this.camera = camera;
	}
	
	@Override
	protected void initialize() {
		batch = new SpriteBatch();
		
		atlas   = new TextureAtlas(Gdx.files.internal("Images/Atlases/pack.atlas"));
		regions = new HashMap<String, AtlasRegion>();
		
		for(AtlasRegion r: atlas.getRegions()) {
			regions.put(r.name, r);
			
			System.out.println("[NOTICE] " + r.name + " was added to the region list.");
		}
		
		regionsByEntity = new Bag<AtlasRegion>();
		
		sortedEntities = new ArrayList<Entity>();
	}

	@Override
	protected void processEntities(ImmutableBag<Entity> entities) {
		for(int i = 0; entities.size() > i; i++) {
			process(sortedEntities.get(i));
		}
	}

	@Override
	protected boolean checkProcessing() {
		return true;
	}
	
	@Override 
	protected void begin() {
		batch.setProjectionMatrix(camera.combined);
		batch.begin();
	}
	
	protected void process(Entity e) {
		if(PosMap.has(e)) {
			Position position = PosMap.getSafe(e);
			Sprite sprite     = SpriteMap.get(e);
			
			AtlasRegion spriteRegion = regionsByEntity.get(e.getId());
			
			batch.setColor(sprite.r, sprite.g, sprite.b, sprite.a);
			>>> float posX = position.x - (spriteRegion.getRegionWidth() / 2 * sprite.scaleX);
			float posY = position.y - (spriteRegion.getRegionHeight() / 2 * sprite.scaleY);
			
			batch.draw(spriteRegion, posX, posY, 0, 0, spriteRegion.getRegionWidth(), spriteRegion.getRegionHeight(), sprite.scaleX, sprite.scaleY, sprite.rotation);
		}
	}
	
	@Override
	protected void end() {
		batch.end();
	}
	
	@Override
	protected void inserted(Entity e) {
		Sprite sprite = SpriteMap.get(e);
		
		regionsByEntity.set(e.getId(), regions.get(sprite.name));
		
		sortedEntities.add(e);
		
		Collections.sort(sortedEntities, new Comparator<Entity>() {

			@Override
			public int compare(Entity e1, Entity e2) {
				Sprite s1 = SpriteMap.get(e1);
				Sprite s2 = SpriteMap.get(e2);
				
				return s1.layer.compareTo(s2.layer);
			}
			
		});
	}
	
	@Override
	protected void removed(Entity e) {
		regionsByEntity.set(e.getId(), null);
	}

}

The error then changes, when I remove spriteRegion.getRegionWidth() and cousin.


Exception in thread "LWJGL Application" java.lang.NullPointerException
	at com.badlogic.gdx.graphics.g2d.SpriteBatch.draw(SpriteBatch.java:632)
	at game.madsplash.spacewar.Systems.SpriteRenderSystem.process(SpriteRenderSystem.java:94)
	at game.madsplash.spacewar.Systems.SpriteRenderSystem.processEntities(SpriteRenderSystem.java:68)
	at com.artemis.EntitySystem.process(EntitySystem.java:57)
	at game.madsplash.spacewar.GameScreen.render(GameScreen.java:53)
	at com.badlogic.gdx.Game.render(Game.java:46)
	at game.madsplash.spacewar.SpaceWarrior.render(SpaceWarrior.java:21)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:206)
	at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)

I need help. :emo:

Null pointer is thrown when some parameters you pass to a method are null or when youre trying to do:

null.something

Well, I get that… but I don’t know what’s null. The SpriteRegion? The Atlas?

Is there any way to troubleshoot this problem?

If it’s this line:


>>> float posX = position.x - (spriteRegion.getRegionWidth() / 2 * sprite.scaleX);

then the only dereferenced pointers are position, spriteRegion and sprite.

Break that line into 3:


float posXtmp = position.x;
float rWidth = spriteRegion.getRegionWidth();
float posX = posXtmp - (rWidth / 2 * sprite.scaleX);

Now see which line generates the exception.

Add a breakpoint at the line that your error occurs, or do

System.out.println(atlas);

Alright, did what BurntPizza said to do. Error moved, to the inserted() function, in the comparing bit that returns the comparing value. To me, it means something in the Sprite component isn’t reading over…


public class Sprite extends Component {
	public enum Layer {
		DEFAULT,
		BACKGROUND,
		ACTORS1,
		ACTORS2,
		ACTORS3,
		PARTICLES;
		
		public int GetLayerID() {
			return ordinal();
		}
	}
	
	public String name;
	public Layer layer = Layer.DEFAULT;
	public float r = 1, g = 1, b = 1, a = 1; // Texture colors!
	public float scaleX = 1, scaleY = 1; // Texture scale.
	public float rotation; // Texture rotation.
	
	public Sprite(String name, Layer layer) {
		this.name  = name;
		this.layer = layer;
	}
	
	public Sprite(String name) {
		this(name, Layer.DEFAULT);
	}
	
	public Sprite() {
		this("default", Layer.DEFAULT);
	}
}

You can also do like troll said and use the debugger for breakpoints, google for more.

“In the comparing bit” isn’t really much information. Is it still a NPE? If it is, then what is null? It could be e1, e2, the SpriteMap, s1, s2, or the layers of s1 and s2.

Whoops, sorry for being so vague. It’s still NPE, where return s1.layer.compareTo(s2.layer); is. It only errors out on the return, not on defining s1 and s2, which makes me assume it’s the layers, however, layer is definitely defined in the Sprite class. So then, makes me think it’s the Sprites themselves, but then s1 and s2 wouldn’t have been defined.

It means they’re defined as null. (EDIT: at least one of them is null, doesn’t have to be both) SpriteMap.get(e) returned null, then when you try to get (null).layer.compareTo(…), it throws a NPE.

Well, that’s troublesome, because I’m lost as to where to go next. It only occurs after I start shooting, and I don’t know what’s specifically triggering it.

Have fun. Thats the real programming right there.

Sounds like some Sprites that are created/destroyed when the shooting mechanic is used are causing entities (at least one) in sortedEntities to have null Sprites, or more likely, the SpriteMap doesn’t have an entry for that entity, and SpriteMap.get(e) then returns null.
Just hope to god you aren’t manipulating state across threads.

Learn to debug before using 3rd party libs.

Alright, so after I spent some time rereading the errors and looking back through my code, I had noticed that some sprites were trying to default to a region of the atlas that didn’t exist. Fixed that problem, so getRegionHeight() and cousin no longer gives me NPE…

However, now, when I compare the layers, it gives me NPE. Sprites default to Sprite.Layers.DEFAULT unless specifically stated, which leads me to believe that somehow the Sprites being created no longer know which layer they belong to. I dunno how, though. It still only occurs after I begin shooting, which tells me once again that something is amiss with how I shoot.

Odd thing is that I CAN shoot without the error occurring. After waiting for the expiring system to kick in (the thing that deletes bullets from the world after a few seconds), I can shoot once again no problem, and am still able to move. However, when I make more than one shot in a 2/3 second period, the error occurs.

Basically, I’m lost with the new problem.

Well then the code path that is triggered when your shoot is dependent on state, specifically the state that there is 1 or fewer bullets in the world.
Seriously I’d run it in debug mode watching the contents of sortedEntities or whatever.

The only control on that is when you’re allowed to fire. I have recently discovered, however, that the most of the problem comes from when the bullet entity is deleted. Same for the enemy ships I added; they’re deleted and OH SHIZZ THE END HAPPENS.

Tried that. I must not know how to work that thing. :cranky:

Learn using the debugger. It’s one of the most valuable tools you have. You can for example advice the debugger to stop at a custom defined condition and even directly when a NPE occurs. Then you can just inspect all the values currently in scope and see the cause of your problem right away.

Seriously, take your time, create a little test-program and check out the various debugger features.

As a quick check; if you make the SpriteRenderSystem the first system to be processed (ie; add it to the world instance before any other systems), does it still throw a NullPointerException?