The wrong way of programming an RPG ?

I'm currently working on an rpg and I have the whole tile system layed out and there are 3 layers. It was fine until i added the 3 layers now it lags. Currently every tile on my map is an object is this the wrong way of going about this ? I'm thinking this is what is lagging my program. Before the layers i had 900 tiles which would be 900 objects now that I have 3 layers though i have 2700 tiles/objects and I know that cant be good =P. Only the images of the tiles which are visible on the screen are being drawn but all the objects are always present

because they are used for positioning purposes. Is there a more efficient way to do this ? Also all my images reference only 1 copy of an image and each tile does not have it's own image so that is not the problem. Please help any suggestions or examples are greatly appreciated, thanks in advance

[870 byte] By [Twistedchaosa] at [2007-10-2 7:59:47]
# 1

One would need a lot more information on what you are doing to give an answer to this, and probably you'd have to post your source code for that to happen. As long as i've no idea what exactly you are doing i can't tell how your algorithms might scale, so there isn't really much i can tell you.

However, I'd strongly recommend using a profiling tool like ej-technologies's JProfiler. It can tell which methods take up the cpu time.

Sarcommanda at 2007-7-16 21:51:20 > top of Java-index,Other Topics,Java Game Development...
# 2

Yeah, I had a feeling you wouldn't be able to do much without my source, but I'm not exactly sure which part to pose .... I guess I can just post it all though.

//Key

/*

*red = trees

*green = grass

*blue = water

*gray = rocks

*white = blank

*

*

*

*Maps

*map00.bmp = Test map that paints a repeating tree, grass, water sequence/ R G B

*map01.bmp = Island map with trees and Rocks

*map02.bmp = 60x60 map

*blank60x60.bmp = blank map

*

*/

//v 1.1.2

import javax.swing.*;

import javax.imageio.*;

import java.awt.*;

import java.io.*;

import java.net.*;

import java.util.*;

import java.awt.Cursor.*;

import java.awt.image.*;

import java.awt.event.*;

interface Movement{

int speed = 30;

int xVel = 0, yVel = 0 ;

public void updatePosition();

public void setPosition(int x, int y);

public void setVelocity(int x, int y);

}

class ImageBank{

static Image images[] = new Image[Tile.numTiles];

public ImageBank(){

for(int i = 0; i<Tile.numTiles ; i++){

try{

images[i] = ImageIO.read(getClass().getResource("Tiles/"+i+".png"));

}catch(IOException exc){

System.out.println("Error Loading Tile Images");

}

catch(IllegalArgumentException exc){

System.out.println("Tile Image Not Found! (check file names and location)");

}

}

System.out.println("Images Loaded");

}

}

class Tile extends Rectangle{

boolean walkable, fishable, destroyable;

int type;

Color pixelColor;

private int defaultWidth, defaultHeight;

String name;

static final int WATER = 0,GRASS = 1,ROCK = 2, TREE = 3, BLANK = 4;

static final int numTiles = 5;

Image image;

static final int TREERGB = Color.red.getRGB(), GRASSRGB = Color.green.getRGB(),

WATERRGB = Color.blue.getRGB(), ROCKRGB = Color.gray.getRGB();

/*

enum Tile{

WATER(0), GRASS(1), ROCK(2), TREE(3), BLANK(4)

int intValue;

Tile(int intValue){

this.intValue = intValue;

}

}

*/

public Tile(){}

public Tile(int type,int xPos,int yPos, int width, int height){

this.type = type;

x = xPos;

y = yPos;

setSize(width,height);

setTileProperties(type);

}

public Tile(int type){

this.type = type;

setTileProperties(type);

setSize(defaultWidth,defaultHeight);

}

public void setTileProperties(int type){

switch(type){

case WATER:

walkable = false;

fishable = true;

destroyable = false;

pixelColor = Color.blue;

name = "Water";

setImage(WATER);

break;

case GRASS:

walkable = true;

fishable = false;

destroyable = false;

pixelColor = Color.green;

name = "Grass";

setImage(GRASS);

break;

case ROCK:

walkable = false;

fishable = false;

destroyable = false;

pixelColor = Color.gray;

name = "Rock";

setImage(ROCK);

break;

case TREE:

walkable = false;

fishable = false;

destroyable = true;

pixelColor = Color.red;

name = "Tree";

setImage(TREE);

break;

case BLANK:

walkable = false;

fishable = false;

destroyable = false;

pixelColor = Color.white;

name = "Blank";

setImage(BLANK);

break;

}

}

public static Tile[] createTileSet(){

Tile t[] = new Tile[numTiles];

for(int i = 0;i<numTiles;i++){

t[i] = new Tile(i);

}

return t;

}

public String getName(){

return name;

}

public int getType(){

return type;

}

private void setImage(int i){

image = ImageBank.images[i];

}

public String toString(){

String string = new String("\n><Tile Info:>\n" +

"Name: " + name+", TypeID: "+type+", Xpos: "+x+", Ypos: "+y+", Width: "+getWidth()+", Height: "+getHeight()

+"\n<Properties:>\n"+

"Walkable = "+walkable+", Fishable = "+fishable+", Destroyable = "+destroyable+

"\nPixel Color Representation = "+pixelColor

);

return string;

}

public void paint(Graphics g, Rectangle c, Camera cam){

g.drawImage(image, x-(int)c.getX(), y-(int)c.getY(), cam);

}

}

class CreateMap{

Tile tiles[][];

Rectangle mapBounds;

int mapWidth, mapHeight, tileWidth, tileHeight;

int pixels[][] = new int[mapWidth][mapHeight];

BufferedImage theMap;

public CreateMap(String nameOfMap){

//Loads BMP/PNG/GIF as map blueprint into theMap

loadMap(nameOfMap);

//initialize a few variables

mapWidth = theMap.getWidth();

mapHeight = theMap.getHeight();

tileWidth = 30;

tileHeight = 30;

mapBounds = new Rectangle(0,0,mapWidth*tileWidth,mapHeight*tileHeight);

//stores blueprint

pixels = getMapPixels(0,0,theMap);

//create storage for tiles

tiles = new Tile[mapWidth][mapHeight];

//switches pixels of theMap out for tiles

switchPixels4Tiles(0,0);

System.out.println("Map created");

}

//used to load BMP map image

private void loadMap(String themapname){

String mapName = themapname;

try{

theMap = ImageIO.read(getClass().getResource("Maps/"+mapName));

}catch(IOException exc){

System.out.println("Error Loading Map Images");

};

}

//returns the pixels of the BMP map

private int[][] getMapPixels(int x, int y, BufferedImage bi){

int tmpPixels[][] = new int[mapWidth][mapHeight];

for(y = 0; y < mapHeight; y++ ){

for(x = 0; x < mapWidth; x++ ){

try{

tmpPixels[x][y] = bi.getRGB(x,y);

}catch(ArrayIndexOutOfBoundsException exc){

System.out.println("Temporary Pixels Array Out Of Bounds");

}

}

}

return tmpPixels;

}

public void switchOutTile(int clickX, int clickY, boolean mousePressed, CreateMap map){

if(mousePressed){

for(int y = 0; y < mapHeight; y++ ){

for(int x = 0; x < mapWidth; x++ ){

/*the mouse is relative to the screen while the tiles are not so

*when you check to see if a tile at 900, 900 contains the mouse

*when on the screen the mouse is at 600, 600, the tile will be

*drawn at 600, 600 because that is the tile that contains those

*points even though you clicked on the tile at 900, 900 that tile

*is located at that position in a virtual space, it's not at

*900, 900 on your screen

*/

if(map.tiles[x][y].contains(clickX,clickY)){

//add a new tile object instead of selection

Tile t = map.tiles[x][y];

map.tiles[x][y] = new Tile(DevTools.selection,

t.x,t.y,(int)t.getWidth(),(int)t.getHeight());

}

}

}

mousePressed = false;

}

}

private void switchPixels4Tiles(int x, int y){

for(y = 0; y < mapHeight; y++ ){

for(x = 0; x < mapWidth; x++ ){

//replaces each pixel with a tile object

if(pixels[x][y] == Tile.TREERGB){

tiles[x][y] = new Tile(Tile.TREE,x*tileWidth,y*tileHeight,

tileWidth,tileHeight);

}

else if(pixels[x][y] == Tile.GRASSRGB){

tiles[x][y] = new Tile(Tile.GRASS,x*tileWidth,y*tileHeight,

tileWidth,tileHeight);

}

else if(pixels[x][y] == Tile.WATERRGB){

tiles[x][y] = new Tile(Tile.WATER,x*tileWidth,y*tileHeight,

tileWidth,tileHeight);

}

else if(pixels[x][y] == Tile.ROCKRGB){

tiles[x][y] = new Tile(Tile.ROCK,x*tileWidth,y*tileHeight,

tileWidth,tileHeight);

}

else if(pixels[x][y] == Color.white.getRGB()){

tiles[x][y] = new Tile(Tile.BLANK);

}

//refers to pixel coords

else System.out.println("Error Switching tiles @ tile "

+ x+","+y);

}

}

}

}

class Camera extends JPanel implements KeyListener, Movement{

protected int camHeight, camWidth;

int xVel, yVel;

CreateMap map;

Rectangle cam;

Tile curTile;

int mrsX, mrsY, mrmX, mrmY;

Image curTileImg;

Point lastTilePos;

CreateMap[] layers = new CreateMap[3];

boolean mouseMovement, mousePressed;

Rectangle screenCoords[][];

int numYTiles = 23, numXTiles = 29;

public Camera(CreateMap layer0, CreateMap layer1, CreateMap layer2,int camWidth, int camHeight){

addKeyListener(this);

setSize(camWidth,camHeight);

cam = new Rectangle(0,0,camWidth,camHeight);

screenCoords = new Rectangle[numXTiles][numYTiles];

for(int y = 0; y<numYTiles; y++){

for(int x = 0; x><numXTiles; x++){

screenCoords[x][y] = new Rectangle(x*30,y*30,30,30);

}

}

layers[0] = layer0;

layers[1] = layer1;

layers[2] = layer2;

this.map = layer0;

this.camWidth = camWidth;

this.camHeight = camHeight;

setFocusable(true);

requestFocus();

repaint();

System.out.println("Cam created");

}

public void setPosition(int x, int y){

cam.x = x;

cam.y = y;

}

public void setVelocity(int x, int y){

xVel = x;

yVel = y;

}

public void updatePosition(){

cam.x += xVel;

cam.y += yVel;

//if cam leaves map then undo cam advance to keep cam still

if(!map.mapBounds.contains(cam)){

cam.x -= xVel;

cam.y -= yVel;

}

repaint();

}

public void keyPressed(KeyEvent e){

if(e.getKeyCode() == e.VK_UP){

setVelocity(0,-speed);

updatePosition();

}

else if(e.getKeyCode() == e.VK_LEFT){

setVelocity(-speed,0);

updatePosition();

}

if(e.getKeyCode() == e.VK_RIGHT){

setVelocity(speed,0);

updatePosition();

}

else if(e.getKeyCode() == e.VK_DOWN){

setVelocity(0,speed);

updatePosition();

}

}

public void keyReleased(KeyEvent e){xVel = 0; yVel = 0;}

public void keyTyped(KeyEvent e){}

public CreateMap getHighestMap(){

CreateMap map = null;

if(DevTools.selectedL0){

map = layers[0];

}

if(DevTools.selectedL1){

map = layers[1];

}

if(DevTools.selectedL2){

map = layers[2];

}

return map;

}

protected void paintTiles(Graphics g, CreateMap layer){

for(int y = 0; y >< map.mapHeight; y++ ){

for(int x = 0; x < map.mapWidth; x++ ){

//sets curTile to the current tile of grid

//set curTileImg to current image for the current tile (curTile)

curTile = layer.tiles[x][y];

//checks for rectangles of the mapOfTiles grid, that the cam Rectangle

//intersects or contains.

if(curTile.image != ImageBank.images[Tile.BLANK]){

if(cam.intersects(curTile) || cam.contains(curTile)){

//paints tiles only in view, paints tiles with offset between cam

//and tiles to allow smooth movement

curTile.paint(g, cam, this);

}

}

}

}

}

public void paintComponent(Graphics g){

paintTiles(g, layers[0]);

paintTiles(g, layers[1]);

paintTiles(g, layers[2]);

}

}

class EditingCamera extends Camera implements MouseMotionListener, MouseListener, Runnable{

int clickX, clickY;

Point Origin;

Thread thread;

Tile t[] = Tile.createTileSet();

int mWidth = (int)map.mapBounds.getWidth();

int mHeight = (int)map.mapBounds.getHeight();

int mapX = map.mapBounds.x, mapY = map.mapBounds.y;

Rectangle outerRectUp, outerRectDown, outerRectLeft, outerRectRight;

public EditingCamera(CreateMap layer0, CreateMap layer1, CreateMap layer2,int w, int h){

super(layer0, layer1,layer2,w,h);

createMouseBounds(70);

Origin = new Point(w/2, h/2);

addMouseMotionListener(this);

addMouseListener(this);

thread = new Thread(this,"thread");

thread.start();

System.out.println("EditingCamera created");

}

public void mouseClicked(MouseEvent e){}

public void mouseEntered(MouseEvent e){}

public void mouseExited(MouseEvent e){setVelocity(0,0);}

public void mousePressed(MouseEvent e){

clickX = e.getX() + (cam.x - map.mapBounds.x);

clickY = e.getY() + (cam.y - map.mapBounds.y);

if(e.isControlDown()){

for(int y = 0; y < map.mapHeight; y++ ){

for(int x = 0; x < map.mapWidth; x++ ){

for(int l = 0; l<layers.length; l++){

if(layers[l].tiles[x][y].contains(clickX,clickY)){

System.out.println(layers[l].tiles[x][y]);

}

}

}

}

}

if(DevTools.isSelected){

mousePressed = true;

map.switchOutTile(clickX,clickY,mousePressed,getHighestMap());

repaint();

}

}

public void mouseReleased(MouseEvent e){mousePressed = false;}

public void mouseDragged(MouseEvent e){}

public void mouseMoved(MouseEvent e){

//relative frame

mrsX = e.getX();

mrsY = e.getY();

//relative map

mrmX = e.getX() + (cam.x - map.mapBounds.x);

mrmY = e.getY() + (cam.y - map.mapBounds.y);

mouseScroll();

}

public void run(){

for(;;){

try{

thread.sleep(50);

updatePosition();

}catch(InterruptedException exc){}

}

}

public void createMouseBounds(int thickness){

outerRectUp = new Rectangle(cam.x,cam.y,camWidth,thickness);

outerRectDown = new Rectangle(cam.x,cam.y+camHeight-thickness,camWidth,thickness);

outerRectLeft = new Rectangle(cam.x,cam.y,thickness,camHeight);

outerRectRight = new Rectangle(cam.x+camWidth-thickness,cam.y,thickness,camHeight);

}

private void mouseScroll(){

Point p = new Point(1,1);

if(outerRectUp.contains(mrsX,mrsY)||outerRectRight.contains(mrsX,mrsY)||

outerRectDown.contains(mrsX,mrsY)||outerRectLeft.contains(mrsX,mrsY)){

p = getSlope(mrsX,mrsY,Origin);

//move up a percentage of the slope !

setVelocity((int)-(p.y*.07),(int)-(p.x*.07));

}else{

setVelocity(0,0);

}

}

private Point getSlope(int x, int y, Point origin){

int newx = origin.y-y;

int newy = origin.x-x;

//arbitrary point x and y values produced though can be considered as y/x

return new Point(newx, newy);

}

public void updatePosition(){

cam.x += xVel;

cam.y += yVel;

//this method is overridden to provide enhanced mouse scrolling movement

//not sure if this long method will slow the program down a lot on slower comps due to length

//Left

if(cam.x ><= mapX){

cam.x -= xVel;

if(!(cam.y <= mapY) && !((cam.y + cam.height)>= mapY+mHeight)){

cam.y += yVel;

}

else cam.y-=yVel;

//Top

}else if(cam.y <= mapY){

if(!(cam.x <= mapX) && !((cam.x + cam.width) >= mapX +mWidth)){

cam.x += xVel;

}

else cam.x -= xVel;

cam.y -= yVel;

//Right

}else if((cam.x + cam.width) >= mapX+mWidth){

cam.x -= xVel;

if(!((cam.y + cam.height)>= mapY+mHeight) && !(cam.y <= mapY) ){

cam.y += yVel;

}

else cam.y -= yVel;

//bottom

}else if ((cam.y + cam.height)>= mapY+mHeight){

if(!((cam.x + cam.width) >= mapX +mWidth)){

cam.x += xVel;

}

else cam.x -= xVel;

cam.y -= yVel;

}

if(DevTools.isSelected){

Image tileImg = ImageBank.images[DevTools.selection];

Toolkit tk = Toolkit.getDefaultToolkit();

Cursor cursor = tk.createCustomCursor(tileImg,new Point(15,15), null);

setCursor(cursor);

}

this.repaint();

}

public void drawMouseBounds(Graphics2D g, Color color){

g.setColor(color);

g.drawRect(outerRectUp.x,outerRectUp.y,(int)outerRectUp.getWidth(),(int)outerRectUp.getHeight());

g.drawRect(outerRectDown.x,outerRectDown.y,(int)outerRectDown.getWidth(),(int)outerRectDown.getHeight());

g.drawRect(outerRectLeft.x,outerRectLeft.y,(int)outerRectLeft.getWidth(),(int)outerRectLeft.getHeight());

g.drawRect(outerRectRight.x,outerRectRight.y,(int)outerRectRight.getWidth(),(int)outerRectRight.getHeight());

}

public void paintComponent(Graphics g){

super.paintComponent(g);

Graphics2D g2d = (Graphics2D)g.create();

drawMouseBounds(g2d,Color.orange);

}

}

class DevTools extends JPanel implements ActionListener, ItemListener{

EditingCamera eCam;

static boolean isSelected, selectedL0= true, selectedL1 = true, selectedL2 = true;

static int selection;

int numTiles = Tile.numTiles;

byte clickCount[] = new byte[numTiles];

JButton button[] = new JButton[numTiles];

Cursor buttonCursor[] = new Cursor[numTiles];

JButton save = new JButton("Save");

static Checkbox layer0 = new Checkbox("L0",true);

static Checkbox layer1 = new Checkbox("L1",true);

static Checkbox layer2 = new Checkbox("L2",true);

public DevTools(EditingCamera eCam, int width, int height){

setSize(width,height);

this.eCam = eCam;

loadDefaultTileSelectors();

setDefaultCursor(Cursor.CROSSHAIR_CURSOR);

layer0.addItemListener(this);

layer1.addItemListener(this);

layer2.addItemListener(this);

add(layer0);

add(layer1);

add(layer2);

save.addActionListener(this);

add(save);

setFocusable(false);

System.out.println("DevGUI created");

}

public void itemStateChanged(ItemEvent e){

Object cbSelected = e.getItemSelectable();

if(cbSelected == layer0) {

selectedL0 = true;

}

else if(cbSelected == layer1){

selectedL1 = true;

}

else if(cbSelected == layer2){

selectedL2 = true;

}

if (e.getStateChange() == ItemEvent.DESELECTED){

if(cbSelected == layer0) {

selectedL0 = false;

}

else if(cbSelected == layer1){

selectedL1 = false;

}

else if(cbSelected == layer2){

selectedL2 = false;

}

}

}

private void loadDefaultTileSelectors(){

// adds a button and cursor to the gui

for(int i = 0; i<Tile.numTiles; i++){

addSelectionButton(i);

buttonCursor[i] = createCursor(i);

}

}

private void setDefaultCursor(int c){

setCursor(Cursor.getPredefinedCursor(c));

eCam.setCursor(Cursor.getPredefinedCursor(c));

}

private void addSelectionButton(int id){

ImageIcon ii = new ImageIcon(new Tile(id).image);

button[id] = new JButton(ii);

button[id].addActionListener(this);

add(button[id]);

}

private Cursor createCursor(int id){

Image i = new Tile(id).image;

Toolkit tk = Toolkit.getDefaultToolkit();

Cursor cursor = tk.createCustomCursor(i,new Point(15,15), null);

return cursor;

}

public void actionPerformed(ActionEvent ae){

saveButton(ae);

//checks buttons for selection of tile

for(int i = 0; i >< numTiles; i++){

buttonActionEvt(i,ae);

}

}

private void buttonActionEvt(int id, ActionEvent e){

if(e.getSource() == button[id]){

//resets other buttons click counts

for(int x = 0; x<clickCount.length; x++){

if(x != id){

clickCount[x] = 0;

}

}

//keeps track of clicks individually

clickCount[id] = checkClicks(clickCount[id]);

}

if(e.getSource() == button[id] && clickCount[id] >< 2 ){

setCursor(buttonCursor[id]);

setSelection(id);

isSelected = true;

}

}

public void setSelection(int selected){

selection = selected;

}

private void saveButton(ActionEvent e){

if(e.getSource() == save){

System.out.println("Saving....");

String nameOfMap = JOptionPane.showInputDialog("Enter map Name: ");

BufferedImage blankImage60x60 = (BufferedImage)createImage(60,60);

Graphics g = blankImage60x60.createGraphics();

pixelateMap(g);

//all this creates an appropriate path name for created map

File f = new File(getClass()+"");

String s = f.getAbsolutePath();

int ls = s.length() - 14, lf = s.length();

StringBuffer pathname = new StringBuffer(s);

pathname = pathname.delete(ls,lf);

String path = pathname.toString()+"Maps/"+nameOfMap+".png";

// create map file

try{

ImageIO.write(blankImage60x60,"png",new File(path));

System.out.println("Map " + nameOfMap + " saved to " + path);

}

catch(IOException exc){System.out.println("unable to save");};

}

}

// ToDo: add saving method for 3 layers

private void pixelateMap(Graphics g){

for(int y = 0 ; y < eCam.map.mapHeight ; y++ ){

for(int x = 0 ; x < eCam.map.mapWidth ; x++ ){

switch(eCam.map.tiles[x][y].getType()){

case Tile.TREE:

g.setColor(eCam.map.tiles[x][y].pixelColor);

g.drawLine(x,y,x,y);

break;

case Tile.WATER:

g.setColor(eCam.map.tiles[x][y].pixelColor);

g.drawLine(x,y,x,y);

break;

case Tile.ROCK:

g.setColor(eCam.map.tiles[x][y].pixelColor);

g.drawLine(x,y,x,y);

break;

case Tile.GRASS:

g.setColor(eCam.map.tiles[x][y].pixelColor);

g.drawLine(x,y,x,y);

break;

}

}

}

}

private byte checkClicks(byte clickCount){

clickCount++;

if(clickCount==2){

setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

eCam.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

isSelected = false;

}

else if(clickCount>=3)clickCount=1;

return clickCount;

}

}

class DevGUI extends JFrame{

public DevGUI(EditingCamera eCam, int width, int height){

setSize(width, height);

DevTools tools = new DevTools(eCam,width,height);

add(tools);

setVisible(true);

}

}

class EditorFrame extends JFrame{

public EditorFrame(int frameWidth, int frameHeight){

setSize(frameWidth,frameHeight);

new ImageBank();

setLocation(105,0);

CreateMap layer0 = new CreateMap("water.bmp");

CreateMap layer1 = new CreateMap("grass.bmp");

CreateMap layer2 = new CreateMap("object.bmp");

EditingCamera eCam = new EditingCamera(layer0, layer1, layer2, frameWidth, frameHeight-30);

new DevGUI(eCam,100,600);

setLayout(new BorderLayout());

getContentPane().add(eCam, BorderLayout.CENTER);

setVisible(true);

}

}

public class LaRPG{

public static void main(String[] args){

new EditorFrame(900,700);

}

}

Twistedchaosa at 2007-7-16 21:51:20 > top of Java-index,Other Topics,Java Game Development...
# 3

I think I know what you're doing and I think I've run into the same problem before.

My suggestion is that you just use 1 panel. Then

1. Add a method to draw an individual tile using a Graphics object.

2. Create an array for the data indicating what's at each square

3. Paint all the tiles to a BufferedImage

4. Draw the bufferedImage in your paintComponent method.

tjacobs01a at 2007-7-16 21:51:20 > top of Java-index,Other Topics,Java Game Development...