Problems with Rendering Multiple Entities

Started by EB5473, July 05, 2012, 21:21:59

Previous topic - Next topic

EB5473

Hello All!
I am just doing some base work for a entity engine, and i am having problems rendering more than one Entity at a time.
Main File-
package main;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
public class TheGameMain {
	Game game = new Game("TEST");
	InputHandler playinput = new InputHandler(game.player);
	RenderHandler gameren = new RenderHandler(game);
	
 public void start() {
	try {
		Display.setDisplayMode(new DisplayMode(800,600));
		Display.create();
	} 
	catch(LWJGLException e) {
		e.printStackTrace();
		System.exit(0);
	}
	
	init();
	
	while(!Display.isCloseRequested()) {
		gameren.render(game.player);
		gameren.render(game.entity1);
		playinput.input();
		Display.update();
		
	}
	Display.destroy();
 }

 
 public void init() {
	 glMatrixMode(GL_PROJECTION);
	 glLoadIdentity();
	 glOrtho(0,800,0,600,1,-1);
 }
 
 public static void main(String[] args) {
	 TheGameMain thegame =  new TheGameMain();
	 thegame.start();
 }
 }


"Game"-
package main;

import java.util.Random;

public class Game {
	public String name;
	public Entity player;
	public Entity entity1;
	public Random entitynumber;
	
	public Game(String name) {
		this.name = name;
		player = new Entity();
		entity1 = new Entity();
	}
	

}


RenderClass -
package main;

public class RenderHandler {
	Game rengame;
	public RenderHandler(Game game) {
		rengame = game;
	}
	public void render(Entity entity) {
		entity.render();
	}
	
}

I would love if someone could correct me in any respect, or suggest a better way to do it!

Thanks a bunch-
EB
Coder, Mastermind, Friend

frostyfrog

I'm not sure if my way is the best or not (I learned it for Mojang's CatacombSnatch), so hopefully someone else will come up with a better solution.

You could try adding this to Game.java, hopefully it will all work with your current setup:
public Set<Entity> entities = new HashSet<Entity>();
	public void addEntity(Entity ent) {
		entities.add(ent); // add the entity to a set of entities
	}

	/*
	You could include your player tick code in with all other entities.
	Or, if you declare it in your constructor, you can just call player.tick() separately.
	*/
	public void tick() {
		for(Entity ent : entities) { // for EVERY entity
			if(ent instanceof Player) { // or replace for your own player checking
				//insert player specific tick code
			}
			ent.tick();
		}
	}


Then for your render class:
public void render() {
		for(Entity ent : rengame.entities) {
			ent.render();
		}
	}


Hope this helps. =)

CodeBunny

FYI this is very, very, very bad:

for(Entity ent : entities) { // for EVERY entity
			if(ent instanceof Player) { // or replace for your own player checking
				//insert player specific tick code
			}
			ent.tick();
		}


Instead, all entities should have a tick() method, and the player should override it and put the player-specific calls there.

frostyfrog

Hmm... I should of thought of that, thanks for pointing out my error CodeBunny. :)

EB5473

Well guys, thanks for all your help! Looks like with the proper work i should be on the right track. Now to just work out how to serialize this along with world data...

EDIT:Aaaaanddd...I've run into a problem >.<
The code is a bit hard to use when it comes to assessing specific entities, without using a "instanceof" statement. Would there be any way to append a string to each as a ID for a call or something similar?
Coder, Mastermind, Friend

frostyfrog

As CodeBunny stated, you should override the 'tick' function of each entity that need specific code. The player, for example, would need to be in Player.java. Like so:
package your.package.here

// Your import statements would go here...

public class Player {
	public tick() {
		super.tick();
		// Custom code for the player on tick
	}
	public render() {
		// Player specific render code, super.tick() if you need it.
		// If you only need super.tick(), remove this function
	}
}


But, if you want to disregard our advice on this matter, you could use the following (messier) code:
// Somewhere else in your code...
	Entity player = new Entity();
	player.id = 7;
	addEntity(player);
	/***********************/
	public void addEntity(Entity ent) {
		entities.add(ent); // add the entity to a set of entities
	}
	public void tick() {
		for(Entity ent : entities) { // for EVERY entity
			if(ent.id == 7) {
				//insert player specific tick code
			}
			ent.tick();
		}
	}


Please note: All the above code was hand typed and there may be some issues with it, since I haven't tested it thoroughly...

EB5473

Finally got everything working...but the game will still only render one entity...
Here's the code i am using in TheGameMain, sorry if i am being completley stupid about this  :-[
package main;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
public class TheGameMain {
	Game game = new Game("TEST");
	RenderHandler gameren = new RenderHandler(game);
	EntityPlayer player = new EntityPlayer();
	Entity entity = new Entity();
	
 public void start() {
	try {
		Display.setDisplayMode(new DisplayMode(800,600));
		Display.create();
	} 
	catch(LWJGLException e) {
		e.printStackTrace();
		System.exit(0);
	}
	
	init();
	
	while(!Display.isCloseRequested()) {
		gameren.render();
		game.tick();
		Display.update();
		
	}
	Display.destroy();
 }

 
 public void init() {
	 glMatrixMode(GL_PROJECTION);
	 glLoadIdentity();
	 glOrtho(0,800,0,600,1,-1);
	 glMatrixMode(GL_MODELVIEW);
	 
	 game.addEntity(entity);
	 game.addEntity(player);
	 
	 game.tick();
 }
 
 public static void main(String[] args) {
	 TheGameMain thegame =  new TheGameMain();
	 thegame.start();
 }
 }

Seems like it wants only to render the last entity declared...
Coder, Mastermind, Friend

Fool Running

I think we need to see the contents of your entity render method. Without seeing that code, my best guess is that you might be clearing the view in the render method. ;)
Programmers will, one day, rule the world... and the world won't notice until its too late.Just testing the marquee option ;D

EB5473

*facepalm* Thank you very much Fool Running, that was the problem >.<
Thanks to all who helped me! Now just to figure out how to serialize that list of entities...
Coder, Mastermind, Friend

frostyfrog

Whoops, how did I miss how that was the problem. Oh well.

A bit off of the OP Topic, but I thought I'd share a bit more info. If I'm understanding you correctly, you want to serialize the list of entities and save them to a file. Then you want to deserialize that data to load it back in. It's not exactly the best way, but I'm currently using JSON-Simple to save and load my map files. I hope that helps you a bit, at least for a little while anyways. If anyone knows of a better way, I'm all ears as I would like to know too.

EB5473

Looks like it would work, may take a little bit of understanding and implementation, but no worries. The only two problems i can see currently are adding entities dynamically (less of a problem, may be able to find a workaround eventually) and killing(removing) entities from the "entities" list (eg.health goes down below zero, remove entity from screen)
Coder, Mastermind, Friend

frostyfrog

Oh, for removing entities from the entity list dynamically, you could do this:
//in game.java
	public void removeEntity(Entity ent) {
		entities.add(ent); // add the entity to a set of entities
	}
	//in, for example, Player.tick()
	public void tick() {
		if(health <= 0) {
			//Some cleanup code
			Game.removeEntity(this);
		}
	}
	//add entity to entity list upon creation (in Entity.java)
	public Entity() {
		Game.addEntity(this);
		//Then when ever ANY entity is created, it is added to the list.
	}

I'm not sure if java will clean up the class once you remove it from the entities list, but it should (I think).

EB5473

I like that idea, I'll try that. In the mean time, JSON-Simple has worked for me, and I've got a (extremely basic) entity saving system. All I have to do is figure out a way to overwrite if needed, and otherwise append, but that's way off topic. thanks for your help!
Coder, Mastermind, Friend

frostyfrog

Just some food for thought on what you were planning...

I thought it would overwrite by default. If that is the case, then to append, just take it's current data string and append to it wherever needed. Then write back to the file.

EB5473

Well, the only thing thats currently happening(using some convoluted append scheme, as overwriting only saves one entity) is the file will keep getting progressively larger, it wont clear, but will instead just append an updated copy to the end of the file...
EDIT:Using the remove code, i now(off and on) get this lovley error:
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
	at java.util.HashMap$KeyIterator.next(Unknown Source)
	at main.Game.tick(Game.java:54)
	at main.TheGameMain.start(TheGameMain.java:36)
	at main.TheGameMain.main(TheGameMain.java:72)


Here be the code in Game:
package main;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Random;
import java.util.Set;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;

import static org.lwjgl.input.Keyboard.*;


public class Game {
	public String name;
	public SaveGame saver = new SaveGame();
	public Set<Entity> entities = new HashSet<Entity>();
	public Random entitynumber;
	public boolean saveReady = true;
	public int saveCounter;
	public boolean spawnReady = true;
	public int spawnCounter;
	public int screenX = 800;
	public int screenY = 600;
	
	
	
	JSONObject ents = new JSONObject();
	
	
	int fps;
	long lastFrame;
	long lastFPS;
	
	public void addEntity(Entity ent) {
		entities.add(ent);
	}
	public void removeEntity(Entity ent) {
		spawnReady = false;
		entities.remove(ent);
		if(spawnCounter >= 5000)
		spawnReady = true;
	}
	
	public void tick() {
		for(Entity ent : entities) {
			ent.tick();
			if(ent.pos.y <= 0) ent.pos.y = 0;
			if(ent.pos.y + 50 >= 600) ent.pos.y = 550;
			if(ent.pos.x <= 0) ent.pos.x = 0;
			if(ent.pos.x + 50 >= 800) ent.pos.x = 750;
		}
		
		if(isKeyDown(KEY_F)) {
			if(saveReady) {
				saveReady=false;
		for(Entity ent : entities) {	
			saver.save(ent, this);
			}
			saveCounter = 0;
		}
		}
		if(isKeyDown(KEY_G)) {
			if(spawnReady) {
				spawnReady =false;
				addEntity(new EntityMob("Mob",this));
				spawnCounter = 0;
			}
		}
		saveCounter++;
		spawnCounter++;
		if(saveCounter >= 5000) {
			saveReady = true;
		}
		if(spawnCounter >= 5000) {
			spawnReady = true;
		}
		System.out.println(spawnReady);
	}
	
	
	
	 public long getTime() {
		    return (Sys.getTime() * 1000) / Sys.getTimerResolution();
		}
	
	public Game(String name) {
		this.name = name;
	}

}
Coder, Mastermind, Friend