Rendering multiple classes of entities by cycling through an ArrayList

So… instead of manually writing a new render line in my render updates method for each new class I make that needs to be rendered, I thought it would be easier to just add all of the objects to be rendered to an ArrayList called entities. This seems like it would work in theory, and I can add the individual objects and cycle through it, but I don’t know how to figure out which one needs to be rendered. If anyone uses a method of rendering like this, some advice on how to do it would be welcome.

-Nathan

I almost understand all except this line

[quote]but I don’t know how to figure out which one needs to be rendered
[/quote]
IMO, you put them into an AL to be rendered. You may want to simplify your code, why not paste your current one?

Sure, here goes:

Main class:

package com.natekramber.main;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

import com.natekramber.entities.player.Player;
import com.natekramber.entities.player.Movement;
import com.natekramber.entities.zombie.Zombie;
import com.natekramber.menus.MenuMain;

public class Main extends Canvas implements Runnable {
	BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
	private static final long serialVersionUID = 1L;
	public static final int HEIGHT = 360;
	public static final int WIDTH = 640;
	public static final int SCALE = 1;
	public static final String title = "Momentum Test";
	
	private boolean applet = false;
	
	public double unprocessed = 0;
	public long lastTime;
	public String setState = "null";
	
	boolean running = false;
	String state = "null";
	
	int ticks = 0;
	
	public Graphics g;
	public ActionListener listener = new ActionListener(this);
	public StateManager statemanager = new StateManager(this);
	public MenuMain menumain = new MenuMain(this);
	public Player player = new Player(this);
	public Movement movement = new Movement(this);
	public SpriteSheet spritesheet = new SpriteSheet(this);
	public Zombie zombie = new Zombie(this);
	
	public synchronized void start() {
		requestFocus();
		running = true;
		new Thread(this).start();
	}
	
	public synchronized void stop() {
		running = false;
	}
	
	public void init() {
		addKeyListener(listener);
		statemanager.setState("menumain");
	}
	
	public void run() {
		lastTime = System.nanoTime();
		unprocessed = 0;
		int frames = 0;
		double nsPerTick = 1000000000.0 / 60.0;
		long lastTimer1 = System.currentTimeMillis();
		
		init();
		
		while (running)
		{
			long now = System.nanoTime();
			unprocessed += (now - lastTime) / nsPerTick;
			lastTime = now;
			
			while (unprocessed >= 1)
			{
				tick();
				frames++;
				render();
				unprocessed -= 1;
			}
			
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
					e.printStackTrace();
			}
			
			if (System.currentTimeMillis() - lastTimer1 > 1000)
			{
				lastTimer1 += 1000;
				System.out.println(ticks + " ticks, " + frames + " fps");
				frames = 0;
				ticks = 0;
			}
		}
		System.exit(0);
	}
	
	public void tick() {
		ticks++;
		
		this.state = statemanager.state;
		if ((!setState.equals(state)) && (!setState.equals("null")))
		{
			if (setState.equals("quitting"))
			{
				if (!isApplet()) stop();
				if (isApplet()) setState = state;
			}
			
			statemanager.setState(setState);
			setState = "null";
		}
		
		logicUpdates();
	}
	
	public void logicUpdates() {
		if (state.equals("menumain")) menumain.update();
		if (state.equals("playing")) {
			player.update();
		}
	}
	
	public void renderUpdates() {
		if (state.equals("menumain")) menumain.render(g);
		if (state.equals("playing")) {
			player.render(g);
		}
	}
	
	public void render() {
		BufferStrategy bs = getBufferStrategy();
		if (bs == null)
		{
			this.createBufferStrategy(3);
			return;
		}
		
		g = image.getGraphics();
		g.setFont(new Font("Arial", 5, 18));
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, WIDTH, HEIGHT);
		g.setColor(Color.WHITE);
		
		renderUpdates();
		
		///
		
		g.drawString(state, 20, 20);
		g.drawString(title, 20, 40);
		
		///
		
		g.dispose();
		
		
		g = bs.getDrawGraphics();
		g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
		g.dispose();
		bs.show();
	}
	
	public static void main(String args[]) {
		Main game = new Main();
		game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		
		JFrame frame = new JFrame(title);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setLayout(new BorderLayout());
		frame.add(game);
		frame.pack();
		frame.setResizable(false);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		game.start();
	}

	public boolean isApplet() {
		return applet;
	}

	public void setApplet(boolean applet) {
		this.applet = applet;
	}
}

Player class:

package com.natekramber.entities.player;

import java.awt.Graphics;

import com.natekramber.main.Interfaces.Screen;
import com.natekramber.main.Main;

public class Player implements Screen {
	Main main;
	private int pHeight = 64;
	private int pWidth = 64;
	
	public Player(Main main) {
		this.main = main;
	}

	public void render(Graphics g) {
		g.drawImage(main.spritesheet.sprites[0], main.movement.getX(), main.movement.getY(), 64, 64, null);
	}

	public void update() {
		main.movement.updateMovement();
	}

	public int getHeight() {
		return pHeight;
	}

	public int getWidth() {
		return pWidth;
	}
}

That’s all that should be needed, I think.

-Nathan

You should let each state to handle the render. For example, your menumain and playing state have different things to render. Your main class should only know which state current is and update it


currentState.render(g);
//when you change the state: currentState = stateManager.getState("playing");

it goes same with entities too. What you need is to let them organized by implementing same interface or extending same superclass.

Maybe have a base class called Entity, which has an update and render methods. All Entities extend this class and implement those methods.

Then in your main class you have an ArrayList so when you update and render:


//update
for(Entity e : entities)
    e.update();

...

//render
for(Entity e : entities)
    e.render(g);

ON Topic:
I’d go the way ra4king said it, it’s plain simple. If you have entities that don’t need to be updated or drawn you could use different ArrayLists or add a boolean field in the Entity class that checks if the entity needs to be updated or drawn, so in the draw loop you simple check for that field. Or if you use different lists, you simple loop the one that needs to be updated.
I hope you don’t run into more troubles with this, but also hope you post them right away so we can handle it together :slight_smile:
ReBirth also said some useful tips, try to implement them!

OFF Topic:
I like your code, very clean, but why this:


if (state.equals("menumain")) menumain.update();

to do right down to it this instead?:


if (state.equals("playing")) {
   player.update();
}

Just found it funny :slight_smile:


if (state.equals("menumain")) menumain.update();
if (state.equals("playing")) player.update();

That way you keep your touch everywhere :slight_smile: