8 Replies - 380 Views - Last Post: 28 March 2012 - 07:18 AM Rate Topic: -----

#1 NantucketSleighride  Icon User is offline

  • D.I.C Head

Reputation: 22
  • View blog
  • Posts: 106
  • Joined: 13-February 11

Sharing the main game's data with its classes

Posted 26 March 2012 - 07:13 PM

Major derp moment. Forgive me - but I do have a question to ask, still.

I've created a frame and a panel.

The frame holds the panel - and the panel paints the game on itself.

The thing is - the frame is what holds all the information relating to the game - like where things are and what things exist. Since the panel paints on itself, I need to pass it this information, however, you can't really alter the paintComponent method by adding parameters that it accepts (or so - when I've tried it told me to get lost).

So I've resorted to passing it either each object when the panel is created - so it has its own pointer to say "player", or I've passed it the entire frame so that it has access to all of its public variables, arraylists, objects, etc.

I'm not really sure I should be doing these things - but I'm not entirely sure what else to do.

The Swing Timer is held in the JFrame, and then it calls the panel with .repaint() - then the panel just runs its paintComponent() code. That's how I've got it set up right now.

I'm not quite sure the best way to go about this - or if perhaps I'm doing it just fine.

Edit: also - on the same note - what's the best way to go about holding all the game data? Am I right in creating something that holds all of the information (like the JFrame) and then generally it passes it to other instances of classes when they need to alter that information?

For instance - the JFrame holds "Player player", "ArrayList<Zombie> zombies", "ArrayList<Human> humans", "Tile[][] tiles" - and then just tosses those to methods in other classes when they need to look at it.

This post has been edited by NantucketSleighride: 26 March 2012 - 07:23 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Sharing the main game's data with its classes

#2 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8334
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Sharing the main game's data with its classes

Posted 26 March 2012 - 07:23 PM

How do you think we can comment if we don't see the code ?
Was This Post Helpful? 0
  • +
  • -

#3 NantucketSleighride  Icon User is offline

  • D.I.C Head

Reputation: 22
  • View blog
  • Posts: 106
  • Joined: 13-February 11

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 04:05 AM

View Postpbl, on 26 March 2012 - 07:23 PM, said:

How do you think we can comment if we don't see the code ?


It's not a code question - it's a question on how to design the code.

I'm not asking how to create anything, I'm asking for design tips - where to create things, and the best ways to pass them around.
Was This Post Helpful? 0
  • +
  • -

#4 farrell2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 848
  • View blog
  • Posts: 2,585
  • Joined: 29-July 11

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 05:02 AM

I'd put the common info in your JPanel, in which I would also put the timer. Your JPanel's main job should be to listen to key events and paint the game world. You could create a game world object that represents your 2d tile array and simply have it paint itself to the JPanel on request. It's up to you. The reference to the player and other game object need to be known by the JPanel.

Your game objects such as the player, enemy, etc, should manage their own locations, update their own state, and paint themselves.
Was This Post Helpful? 2
  • +
  • -

#5 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5846
  • View blog
  • Posts: 12,703
  • Joined: 16-October 07

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 06:21 AM

Just so you know, this is a pretty classic problem.

You're familar with swing components? How does the JButton let the rest rest of the program know it's been pressed? It fires off an action to all registered listeners. This can be thought of as an "event model" or "observer pattern." I'm going with event model, more info here: http://docs.oracle.c...ents/intro.html

The parent can tell the child object anything it likes by just calling methods. The child can't tell the parent anything directly unless it has a parent reference, which is generally considered bad design. The solution is to allow the child to shout to anyone listening. The listener is often the parent, but needn't be. It's any object that's added itself to the listener list.

You want to know about a child's paint event? Since that is a standard event, you can hook into it directly. If there's custom changes you want to fire yourself, you might need to write some code.

Note, I wouldn't hold everything in your frame. Rather, I would have some kind of state object that holds everything. Your frame would pass that object to it's children. You can write change notification in that object and all your listeners will be notified.

Yes it's complex. There is no one solution. If you offer a solid example, in code, we can offer more help.
Was This Post Helpful? 2
  • +
  • -

#6 nunc  Icon User is offline

  • D.I.C Head

Reputation: 36
  • View blog
  • Posts: 131
  • Joined: 20-November 11

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 09:21 AM

This is what I use

Frame (get width and height of screen)
Panel(has game loop, and list of states, Loops through current state)
States (Title, Level, GameOver, ect)

Those states call/create whatever classes they need to. All states are children of an abstract GameState class.

I wouldn't put any game info in your JFrame, it doesn't need it. If you want to put game info somewhere together, at least put it in your JPanel, where listeners should also be.

http://www.dreaminco...state-machines/

For more examples on States
Was This Post Helpful? 0
  • +
  • -

#7 NantucketSleighride  Icon User is offline

  • D.I.C Head

Reputation: 22
  • View blog
  • Posts: 106
  • Joined: 13-February 11

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 12:08 PM

Alright - thanks for the replies.

I just got home - so I quickly whipped up something to give an example of how I often code. I took the advice to move my listeners and what not to the JPanel instead of the JFrame. I then pass things from it to other classes to be altered.

Please note the question comment code in the keyPressed() method on the JPanel.

This is how I often work and perhaps it's really bad. I'd love to get some input on it. I'm self taught, and while it's easy to find help with getting code to work and run, it's often harder to find help on design: where to place code, how to pass it around, etc.

All this does is allow the player to move around in one direction and plant a tree or drop 1 piece of zombie bait. The zombie will chase the bait and eat it, then attack the player. Trees do nothing. Players can go off screen - and things aren't really meant to be exhilarating or anything - it's just an example. I want to work on these pixel type simulations, though - I find them fun.

Everything is drawn using a Line2D shape that starts and ends on the same point. It's the only way I know how to draw just a single pixel to the screen.

I didn't include the tree and zombieBait classes because they do nothing - they just hold where and what color.

My Frame and entry point
import java.awt.*;
import javax.swing.*;

public class PixelStory extends JFrame{

	GamePanel gamePanel = new GamePanel();
	Container pane;
	
	
	public PixelStory(){
		this.setSize(500,500);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
				
		pane = this.getContentPane();
		pane.add(gamePanel);
		
		this.setVisible(true);
		
	}
	
	public static void main(String[] args){
		new PixelStory();
	}
}





My Custom JPanel
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import java.util.*;

public class GamePanel extends JPanel implements ActionListener, KeyListener{

	Timer gameTimer = new Timer(20, this); 
	Player player = new Player("Player", 250, 250);
	ArrayList<Tree> trees = new ArrayList<Tree>(); //holds all the trees in the game
	ArrayList<Zombie> zombies = new ArrayList<Zombie>(); //holds all the zombies in the game
	ArrayList<ZombieBait> zombieBaits = new ArrayList<ZombieBait>(); //holds all the bait in the game
	
	public GamePanel(){
		this.setBackground(Color.BLACK);
		this.setFocusable(true);
		this.requestFocus();
		this.addKeyListener(this);
		
		gameTimer.start();
	}
	
	public void actionPerformed(ActionEvent e){
		
		//moves the player
		player.move();
		
		//randomly add a zombie. Currently only allowing 2 at a time on screen
		if(zombies.size() < 2){
			Random ranNum = new Random();
			if(ranNum.nextInt(500) + 1 > 490){
				 zombies.add(new Zombie(0,0));
			}
		}
		
		//if there's bait on the screen, chase it - otherwise, chase the player
		for(Zombie zombie : zombies){
			if(zombieBaits.size() > 0){
				zombie.chaseZombieBait(zombieBaits.get(0), zombieBaits);
			}
			else{
				zombie.chasePlayer(player);
			}
			
		}
		
		repaint();
	}
	
	public void keyPressed(KeyEvent e){
		
		//sets direction - player can only go one direction at a time, currently
		if(e.getKeyChar() == 'd'){
			player.setDirection(false, false, true, false);
		}
		else if(e.getKeyChar() == 'a'){
			player.setDirection(false, false, false, true);
		}
		else if(e.getKeyChar() == 'w'){
			player.setDirection(true, false, false, false);
		}
		else if(e.getKeyChar() == 's'){
			player.setDirection(false, true, false, false);
		}
		else if(e.getKeyChar() == 'x'){
			player.setDirection(false, false, false, false);
		}
		
		//plants a tree
		if(e.getKeyChar() == 'p'){
			player.plantTree(trees);
		}
		
		//drops zombie bait - currently only allows one piece at a time
		if(e.getKeyChar() == 'b'){
			if(zombieBaits.size() < 1){
				player.dropZombieBait(zombieBaits);
			}
			
		}
		
		/*QUESTION - would I be better off here taking an object back and adding it here, instead of passing my lists and letting
		 *the other object add to it? For example:
		 *
		 *trees.add(player.plantTree());
		 * 
		 *That's the only other way I can think to do it. Perhaps it makes more sense, too - I've just never really thought
		 *of doing it that way until I made this topic.
		*/
	}
	
	public void keyReleased(KeyEvent e){
		
	}
	
	public void keyTyped(KeyEvent e){
		
	}
	
	public void paintComponent(Graphics g){
		Graphics2D g2 = (Graphics2D)g;
		
		super.paintComponent(g2);		
		
		Font font = new Font("Times New Roman", Font.PLAIN, 10);
		
		//draw all the trees
		for(Tree t : trees){
			g2.setColor(t.getColor());
			g2.draw(t.getShape());
		}
		
		//draw all the zombies
		for(Zombie zombie : zombies){
			g2.setColor(zombie.getColor());
			g2.draw(zombie.getShape());
		}
		
		//draw all the zombie bait
		for(ZombieBait zombieBait : zombieBaits){
			g2.setColor(zombieBait.getColor());
			g2.draw(zombieBait.getShape());
		}
		
		//draw the player
		g2.setColor(player.getColor());
		g2.draw(player.getShape());
		g2.setFont(font);
		g2.drawString(player.getName(), player.getLocX() - 15, player.getLocY() - 5);
		
	}
}




Zombie Class
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;

public class Zombie{

	private Color zombieColor = Color.GRAY; //defines color of zombie
	
	private int locX; //defines X location of Zombie
	private int locY; //defines Y location of Zombie
	
		
	public Zombie(int locX, int locY){
		this.locX = locX;
		this.locY = locY;
	}
	
	public Color getColor(){
		return zombieColor;
	}
	
	public int getlocX(){
		return locX;
	}
	
	public int getlocY(){
		return locY;
	}
	
	//chases zombie bait
	public void chaseZombieBait(ZombieBait zombieBait, ArrayList<ZombieBait> zombieBaits){
		
		if(zombieBait.getLocX() > locX){
			locX++;
		}
		else if(zombieBait.getLocX() < locX){
			locX--;
		}
		
		if(zombieBait.getLocY() > locY){
			locY++;
		}
		else if(zombieBait.getLocY() < locY){
			locY--;
		}
		
		//remove the bait from the ArrayList passed to it, once it touches it.
		if(zombieBait.getLocX() == locX && zombieBait.getLocY() == locY){
			System.out.println("Bait Eaten");
			zombieBaits.remove(zombieBait);
		}
	}
			
	//chases the player
	public void chasePlayer(Player player){
		
			if(player.getLocX() > locX){
				locX++;
			}
			else if(player.getLocX() < locX){
				locX--;
			}
			
			if(player.getLocY() > locY){
				locY++;
			}
			else if(player.getLocY() < locY){
				locY--;
			}
			
			//displays a message once a zombie 'catches' a player
			if(player.getLocX() == locX && player.getLocY() == locY){
				System.out.println("Player Caught");
			}
					
	}
	
	//returns the zombie shape
	public Shape getShape(){
		Shape line = new Line2D.Double(locX, locY, locX, locY);
		return line;
	}
	
}




Player Class:
import java.awt.*;
import java.awt.geom.*;
import java.util.*;

public class Player {
	
	private String name;
	private int locX;
	private int locY;
	Color color = Color.RED;
	
	private boolean movingNorth = false;
	private boolean movingSouth = false;
	private boolean movingEast = false;
	private boolean movingWest = false;
	
	public Player(String name, int locX, int locY){
		this.name = name;
		this.locX = locX;
		this.locY = locY;
		
	}
	
	public Color getColor(){
		return color;
	}
	
	public String getName(){
		return name;
	}
	
	public Shape getShape(){
		Shape s = new Line2D.Float(locX, locY, locX, locY);
		return s;
	}
	
	public int getLocX(){
		return locX;
	}
	
	public int getLocY(){
		return locY;
	}
	
	//only allows one direction at a time, currently.
	public void setDirection(boolean north, boolean south, boolean east, boolean west){
		this.movingNorth = north;
		this.movingSouth = south;
		this.movingEast = east;
		this.movingWest = west;
	}
	
	public void move(){
		
		if(movingNorth){
			locY--;
		}
		else if(movingSouth){
			locY++;
		}
		
		if(movingEast){
			locX++;
		}
		else if(movingWest){
			locX--;
		}
	}
	
	//plants a tree - takes the ArrayList and adds a new one
	public void plantTree(ArrayList<Tree> trees){
		trees.add(new Tree(locX, locY));
	}
	
	//drops bait - takes the arrayList and adds a new one
	public void dropZombieBait(ArrayList<ZombieBait> zombieBait){
		zombieBait.add(new ZombieBait(locX, locY));
	}
}



Was This Post Helpful? 0
  • +
  • -

#8 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8334
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Re: Sharing the main game's data with its classes

Posted 27 March 2012 - 02:16 PM

or ... if you make the JPanel and inner class of the JFrame it will see all the JFrame variables. It is a solution until the sum of the two classes lines of code exceed a reasonnable amount.
Was This Post Helpful? 0
  • +
  • -

#9 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon

Reputation: 5846
  • View blog
  • Posts: 12,703
  • Joined: 16-October 07

Re: Sharing the main game's data with its classes

Posted 28 March 2012 - 07:18 AM

Hope you don't mind if I fiddle with this; it's fun.

Forget about events, you don't need them yet. Here you just need to organize stuff. A basic shared "sprite" class, for sure. I like location(x,y) to be it's own object.

GameState actually became more of a controller class, but that's ok. Ultimately none of the sprites really needed to talk to the controller, so shared state wasn't actually an issue. Notice how I keep all those collections together in their own class and offer the methods you want everyone else to use. If nothing else, do that.

The sprites needn't be too responsible. They just need to be able to draw themselves and tell you how they're doing.

Maybe this will give you some ideas:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import java.util.*;

class Location {
	private int x, y;
	public Location(int x, int y) { set(x,y); }
	public Location(Location loc) { set(loc); }
	public void set(int x, int y) { this.x = x; this.y = y; }
	public void set(Location loc) { set(loc.x, loc.y); }
	public int getX() { return x; }
	public int getY() { return y; }
	public void setX(int x) { this.x = x; }
	public void setY(int y) { this.y = y; }
	public boolean equals(Object obj) {
		if (this==obj) { return true; }
		if (obj instanceof Location) {
			Location loc  = (Location)obj;
			return loc.x == x && loc.y == y;
		}
		return false;
	}
	public String toString() { return "(" + x + "," + y + ")"; }
}

class Delta {
	public enum MoveDirection {
		North, South, East, West, None;
	};
	public final int magX, magY;
	public int dX, dY;
	
	public Delta(int x, int y) { this.magX = Math.abs(x); this.magY = Math.abs(y); }
	public Delta() { this(1,1); }
	public Delta(Delta d) { 
		this(d.magX, d.magY);
		this.dX = d.dX;
		this.dY = d.dY;
	}
	
	public void clear() { dX = dY = 0; }
	
	public void add(MoveDirection dir) {
		if (dir==MoveDirection.North) { dY = -magY;
		} else if (dir==MoveDirection.South) { dY = magY;
		} else if (dir==MoveDirection.East) { dX = magX;
		} else if (dir==MoveDirection.West) { dX = -magX;
		}
	}
	
	public void set(MoveDirection dir) {
		clear();
		add(dir);
	}
	
	public void apply(Location loc) { loc.set(loc.getX() + dX, loc.getY() + dY); }
	
	public String toString() { return "<" + dX + "," + dY + ">"; }
}

abstract class Mob {
	protected Location loc;
	protected Delta movement;
	public Mob(Location loc) { 
		this.loc = new Location(loc);
		movement = new Delta();
	}
	
	public String getName() { return this.getClass().getName(); }
	
	public void setLocation(Location loc) { this.loc.set(loc); }
	public Location getLocation() { return new Location(loc); }
	public void move() { movement.apply(loc); }
	public Delta getMovement() { return new Delta(movement); }
	
	public boolean moveToward(Location target) {
		movement.clear();
		int dX = target.getX() - loc.getX();
		int dY = target.getY() - loc.getY();
		if (dX==0 && dY==0) { return false; }
		if(dY!=0) { movement.add((dY<0) ? Delta.MoveDirection.North : Delta.MoveDirection.South); }
		if(dX!=0) { movement.add((dX<0) ? Delta.MoveDirection.West : Delta.MoveDirection.East ); }
		return true;
	}
	
	public Shape getShape() {
		//return new Line2D.Double(loc.getX(), loc.getY(),loc.getX(), loc.getY());
		return new Rectangle(loc.getX()-5, loc.getY()-5,10, 10);
	}
	
	public Color getColor() { return Color.CYAN; }
	
	public void draw(Graphics2D g2) {
		// System.out.println(this);
		g2.setColor(getColor());
		g2.draw(getShape());
	}
	
	public String toString() { return getName() + loc; }
	
}

class Tree extends Mob { 
	public Tree(Location loc) { super(loc); } 
} 

class ZombieBait extends Mob {
	public ZombieBait(Location loc) { super(loc); }
	public String getName() { return "Bait"; }
} 

class Zombie extends Mob {
	public Zombie(Location loc) { super(loc); }
	public Zombie() { super(new Location(0,0)); }
	public Color getColor(){ return Color.GRAY; }
	
}

class Player extends Mob {
	private final String name;
	private boolean alive;
	
	public Player(String name, Location loc) {
		super(loc);
		this.name = name;
		alive = true;
	}
	public String getName() { return name; }
	
	public boolean isAlive() { return alive; }
	
	// in the future, a zombie may no kill Player outright
	public void zombieAttack(Zombie zombie) { alive = false; }
	
	public Color getColor() { return Color.RED; }
	
	public boolean processMoveKey(char ch) {
		if(ch == 'd'){ movement.set(Delta.MoveDirection.East);
		} else if(ch == 'a'){ movement.set(Delta.MoveDirection.West);
		} else if(ch == 'w'){ movement.set(Delta.MoveDirection.North);
		} else if(ch == 's'){ movement.set(Delta.MoveDirection.South);
		} else if(ch == 'x'){ movement.set(Delta.MoveDirection.None);
		} else { return false;		
		}
		return true;
	}
	
}




class GameState {
	// you may want some of these public, but start private
	private Random ranNum = new Random();
	private Player player;
	private java.util.List<Tree> trees;
	private java.util.List<Zombie> zombies;
	private java.util.List<ZombieBait> zombieBaits;
	
	
	public GameState() {
		ranNum = new Random();
		player = new Player("Player", new Location(250, 250));
		trees = new ArrayList<Tree>(); //holds all the trees in the game
		zombies = new ArrayList<Zombie>(); //holds all the zombies in the game
		zombieBaits = new ArrayList<ZombieBait>(); //holds all the bait in the game
	}
	
	public boolean isDone() { return !player.isAlive(); }

	public void dropZombieBait() { 
		if(zombieBaits.size() > 0) { return; }
		zombieBaits.add(new ZombieBait(player.getLocation()));
	}

	private boolean catchMob(Zombie zombie, Mob target) {
		if (zombie.moveToward(target.getLocation())) {
			// System.out.println(zombie + " attacks " + target + " : " + zombie.getMovement());
			zombie.move();
			return false;
		}
		return true;
	}
	
	private void chaseBait(Zombie zombie, ZombieBait zombieBait) {
		if (catchMob(zombie, zombieBait)) {
			System.out.println("Bait Eaten");
			zombieBaits.remove(zombieBait);
		}
	}
	private void chasePlayer(Zombie zombie){
		if (catchMob(zombie, player)) {
			player.zombieAttack(zombie);
			System.out.println("Player Caught");
		}
	}
	
	public void moveZombies() {
		for(Zombie zombie : zombies){
			if(zombieBaits.size() > 0) {
				chaseBait(zombie, zombieBaits.get(0));
			}
			else{
				chasePlayer(zombie);
			}
		}
	}
	
	public void movePlayer() {
		player.move();
	}
	
	public void growZombies() {
		if(zombies.size() < 2){
			// if(ranNum.nextInt(500) + 1 > 490){
				 zombies.add(new Zombie());
			// }
		}
	}
	
	
	
	public void processKeyPressed(char ch) {
		if (player.processMoveKey(ch)) { return; }
		if(ch == 'p') { trees.add(new Tree(player.getLocation()));
		} else if (ch=='b') {
			dropZombieBait();
		}
	}

	
	private <T extends Mob> void paintMobList(Graphics2D g2, Collection<T> mobs) {
		Iterator<T> it = mobs.iterator(); 
		while(it.hasNext()) { it.next().draw(g2); }
	}
	
	public void paintMobs(Graphics2D g2) {
		paintMobList(g2, trees);
		paintMobList(g2, zombies);
		paintMobList(g2, zombieBaits);
		player.draw(g2);
	}
	
	public void paintPlayer(Graphics2D g2, Font font) {
		player.draw(g2);
		g2.setFont(font);
		Location loc = player.getLocation();
		g2.drawString(player.getName(), loc.getX() - 15, loc.getY() - 5);
	}
}


class GamePanel extends JPanel implements ActionListener, KeyListener{
	Timer gameTimer = new Timer(20, this); 
	GameState state = new GameState();
	Font font = new Font("Times New Roman", Font.PLAIN, 10);
	
	public GamePanel(){
		this.setBackground(Color.BLACK);
		this.setFocusable(true);
		this.requestFocus();
		this.addKeyListener(this);
		gameTimer.start();
	}
	
	public void actionPerformed(ActionEvent e){
		state.movePlayer();
		state.growZombies();
		state.moveZombies();
		repaint();
		if (state.isDone()) {
			gameTimer.stop();
		}
	}
	
	public void keyPressed(KeyEvent e) { state.processKeyPressed(e.getKeyChar()); }
	
	public void keyReleased(KeyEvent e){ }
	
	public void keyTyped(KeyEvent e){ }
	
	public void paintComponent(Graphics g){
		super.paintComponent(g);
		Graphics2D g2 = (Graphics2D)g;
		state.paintMobs(g2);
		state.paintPlayer(g2, font);
	}
}


public class PixelStory extends JFrame{
	public PixelStory(){
		this.setSize(500,500);
		this.setLocationRelativeTo(null);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setResizable(false);
		add(new GamePanel());
		this.setVisible(true);
	}
	
	public static void main(String[] args){
		new PixelStory();
	}
}


Was This Post Helpful? 1
  • +
  • -

Page 1 of 1