Jump to content
  • Advertisement
Sign in to follow this  
Nicholas Kong

Need feedback on code design for maps of an RPG

This topic is 2070 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Code is in Java. Screen0 class updates and renders a quest giver npc. Screen1 class updates and renders 9 pigs. Only one instance of each class is created when the user enters the main menu state of the game. <-Let me know if this is actually allowed or if I need to change it somehow. Only one map is update and drawn on screen therefore only things on that one map also gets update and render which is performed in the Game class. The logic of update and render itself are in written in each map since each map is derived as a Sprite class, a class that can update and render.

 

The game is 2D top-down RPG. The wall collision code is in each map class (the code that needs to set the camera coordinates).

 

It seems I have more control over the maps doing it this way, but it might just be me.

 

Camera speed controls how fast everything around the main character moves since everything around the main character is drawn relative to the camera. The main character is locked at the centered of the screen even if its action state is running. The set on the camera coordinate in the wall collision code is to make the camera not move on the character collides with the wall so it would make "appear" that link is "actually" colliding with the wall. 

 

Screen1 class handles drawing the loot dropped by the pigs and erases all the loot when the pigs are all killed. When pigs all get killed and when the main character goes to a different screen, the pigs are back to full health. 

 

The two maps uses the same map texture so they share the same directory for that texture. 

package com.nicholaskong.Game.MapScreen;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import com.nicholaskong.Game.Animation;
import com.nicholaskong.Game.Camera;
import com.nicholaskong.Game.MapTracker;
import com.nicholaskong.Game.Sprite;
import com.nicholaskong.Game.Dialogue.Dialogue;
import com.nicholaskong.Game.DialogueSystem.NPC;
import com.nicholaskong.Game.GameCharacters.Soldier;
import com.nicholaskong.Game.GameCharacters.Link;
 
public class Screen0 extends Sprite
{
private Animation anim;
private NPC soldier;
private static String[] animations = {"src/screen/screen0.png"};
private Rectangle leftRectangle;
private ArrayList<NPC> npcList;
private Rectangle rightRectangle;
private Rectangle topRectangle;
private Rectangle downRectangle;
 
private static final int frameInterval = 0;
 
 
public Screen0(double x, double y) {
              super(x, y);
              anim = new Animation(animations,frameInterval);
              Dialogue dialogue = new Dialogue();
              soldier = new NPC(new Soldier(getWidth() - (getWidth() * .66)  , (getHeight() - (getHeight() * 1.2))  ),dialogue);
              npcList = new ArrayList<>();
 
              MapTracker.SCREEN = 0;
              npcList.add(soldier);
}
 
public int getX()
{
return super.getX();
}
 
public int getY()
{
return super.getY();
}
 
public int getWidth()
{
return anim.getWidth();
}
 
public int getHeight()
{
return anim.getHeight();
}
 
public Rectangle getLeftRectangle()
{
return leftRectangle;
}
 
public Rectangle getRightRectangle()
{
return rightRectangle;
}
 
public Rectangle getTopRectangle()
{
return topRectangle;
}
 
public Rectangle getDownRectangle()
{
return downRectangle;
}
 
public void update(Camera camera, Link link) {
 
             int rectWidth = 50;
             int rectHeight = 50;
             // left
             leftRectangle = new Rectangle(((int)(getWidth() * -.04)) - camera.getX(),getY() - camera.getY(),rectWidth,getHeight());
             // right
             rightRectangle = new Rectangle(((int)(getWidth())) - camera.getX(),getY() - camera.getY(),rectWidth,getHeight());
             // top
             topRectangle = new Rectangle((int)(getWidth() *  0)- camera.getX(),(int) ((getHeight() * -0.06) - camera.getY()) ,getWidth(),rectHeight);
             // down
             downRectangle = new Rectangle((int)(getWidth() * 0)- camera.getX(),getHeight()  - camera.getY() ,getWidth(),rectHeight);
 
 
 
             if(link.getRectangle().intersects(leftRectangle))
             {
                       MapTracker.SCREEN = 1;
             }
             // right wall collision
             else if(link.getX() + link.getIdleRightWidth() > ((int)(getWidth())) - camera.getX())
             {
                       camera.setX(camera.getX() - camera.getSpeed());
             }
             // top wall collision
             else if(link.getY() < (int) ((getHeight() * -0.06) - camera.getY()) + topRectangle.getHeight() - 10 )
             { 
                       camera.setY(camera.getY() + camera.getSpeed());
             }
             // down wall collision
             else if( (link.getY() + link.getIdleDownHeight() > downRectangle.getY()))
             { 
                       camera.setY(camera.getY() - camera.getSpeed());
             }
 
 
 
}
 
public ArrayList<NPC> getNPCsInMap()
{
return npcList;
}
 
public void draw(Graphics2D g2D, Camera camera)
{
                    int rectWidth = 50;
                    g2D.setColor(Color.BLUE);
 
 
                    g2D.drawImage(anim.getImage(),super.getX() - camera.getX(),super.getY()- camera.getY(),null);
                    //left
                    g2D.fillRect(((int)(getWidth() * -.04))- camera.getX(),getY() - camera.getY(),rectWidth , getHeight());
 
 
                    // draw all npcs on the map
                    for(int i = 0; i < npcList.size();i++)
                    {
                               npcList.get(i).draw(g2D, camera);
                    }
 
 
}
 
@Override
public void update(Camera camera) {
// TODO Auto-generated method stub
 
}
 
 
 
}

package com.nicholaskong.Game.MapScreen;
 
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.util.ArrayList;
import com.nicholaskong.Game.ActionState;
import com.nicholaskong.Game.Animation;
import com.nicholaskong.Game.Camera;
import com.nicholaskong.Game.Collidable;
import com.nicholaskong.Game.MapTracker;
import com.nicholaskong.Game.Sprite;
import com.nicholaskong.Game.DialogueSystem.NPC;
import com.nicholaskong.Game.GameCharacters.Link;
import com.nicholaskong.Game.Monsters.Pig;
 
public class Screen1 extends Sprite
{
private Animation anim;
private static String[] animations = {"src/screen/screen0.png"};
private Rectangle rightRectangle;
private ArrayList<Collidable> monsterList;
private ArrayList<Collidable> projectileList;
private static final int frameInterval = 0;
private int numOfMonsters = 9;
private double[] widthData = {1.3,1.31,1.32,1.33,1.34,1.35,1.36,1.37,1.38 };
private double[] heightData = {0.90,0.83,0.76,0.69,0.62,0.55,0.48,0.41,0.34 };
private int[] behaviorData = {10,280,190,370,100,460,90,210,137 };
private ArrayList<Collidable> lootList;
private Rectangle leftRectangle;
private Rectangle topRectangle;
private Rectangle bottomRectangle;
 
public Screen1(double x, double y) {
super(x, y);
anim = new Animation(animations,frameInterval); 
monsterList = new ArrayList<>();
lootList = new ArrayList<>();
 
 
for(int i = 0; i < numOfMonsters;i++)
{
Pig pig = new Pig(getWidth() - (getWidth() * widthData[i]),getHeight() - (getHeight() * heightData[i]));
pig.setBehaviorCounter(behaviorData[i]);
monsterList.add(pig);
}
 
 
 
 
projectileList = new ArrayList<>();
 
 
rightRectangle = new Rectangle(0,0,(int)( .50 * getWidth()),getHeight());
 
}
 
public int getX()
{
return super.getX();
}
 
public int getY()
{
return super.getY();
}
 
public int getWidth()
{
return anim.getWidth();
}
 
public int getHeight()
{
return anim.getHeight();
}
 
public Rectangle getRightRectangle()
{
return rightRectangle;
}
 
public Rectangle getTopRectangle()
{
return topRectangle;
 
}
 
public ArrayList<NPC> getNPCsInMap()
{
return npcList;
}
 
public void update(Camera camera, Link link) {
 
int rectWidth = 50;
int rectHeight = 50;
 
rightRectangle = new Rectangle((int)(getWidth() * .16 ) - camera.getX(),getY() - camera.getY(),50,getHeight());
leftRectangle = new Rectangle((int)((getX())- camera.getX() - rectWidth),getY() - camera.getY(),rectWidth , getHeight());
topRectangle = new Rectangle((int)((getX())- camera.getX()),getY() - camera.getY() - rectHeight,getWidth() , rectHeight);
bottomRectangle = new Rectangle((int)((getX())- camera.getX()),getY() - camera.getY() + getHeight(),getWidth() , rectHeight);
 
 
if(link.getRectangle().intersects(rightRectangle))
{
MapTracker.SCREEN = 0;
 
}
else if(link.getX() <= leftRectangle.getX() + leftRectangle.getWidth())
{
camera.setX(camera.getX() + camera.getSpeed());
}
else if(link.getY() <= topRectangle.getY() + topRectangle.getHeight() - 5)
{
camera.setY(camera.getY() + camera.getSpeed());
}
else if(link.getY() + link.getIdleDownHeight() > bottomRectangle.getY())
{
camera.setY(camera.getY() - camera.getSpeed());
 
}
 
if(numOfMonsters == 0 && MapTracker.SCREEN == 0)
{
lootList.removeAll(lootList);
numOfMonsters = 9;
 
for(int i = 0; i < numOfMonsters;i++)
{
Collidable mob = monsterList.get(i);
mob.setLife(mob.getMaxLife());
mob.setState(ActionState.RUNNING);
 
}
}
 
for(int i = 0; i < monsterList.size();i++)
{
Collidable mob = monsterList.get(i);
 
if(mob.getLife() == 0)
{
lootList.add(mob.getGold());
 
 
link.addExp(link.getExp() + mob.getExp());
numOfMonsters--;
 
}
 
if(mob.getLife() >= 0)
{
mob.update(camera);
}
 
 
}
 
 
}
 
public ArrayList<Collidable> getMonstersOnMap()
{
return monsterList;
}
 
public void draw(Graphics2D g2D, Camera camera)
{
int rectWidth = 50;
 
g2D.setColor(Color.BLUE);
g2D.drawImage(anim.getImage(),getX() - camera.getX(),getY()- camera.getY(),null);
// right
g2D.fillRect((int)((getWidth() * .16 )- camera.getX()),getY() - camera.getY(),rectWidth , getHeight());


 
 
 
// draw and update all monsters on the map
for(int i = 0; i < monsterList.size();i++)
{
Collidable mob  = monsterList.get(i);
mob.draw(g2D, camera);
 
}
 
// draw all the loots
for(int i = 0; i < lootList.size();i++)
{
Collidable loot = lootList.get(i);
 
loot.update(camera);
 
loot.draw(g2D, camera);
 
}
 
 

 
 
 
 
}
 
 
public void update(Camera camera) {
// TODO Auto-generated method stub
 
}
 
public ArrayList<Collidable> getLootsOnMap()
{
return lootList;
}
 
 
 
public ArrayList<Collidable> getProjectilesInMap()
{
return projectileList;
}
 
 
}
Edited by warnexus

Share this post


Link to post
Share on other sites
Advertisement

It seems these Screen# classes are some kind of God Classes... lot of coupling here. They know pretty evertything of everyone, which is simpler at an early stage of the development, but becomes a mess fast.

Anyway, if you are happy with this design, go on, but you could at least make a ScreenBase class from which every Screen inherits. A lot more Javaish, isn't it?

Share this post


Link to post
Share on other sites

It seems these Screen# classes are some kind of God Classes... lot of coupling here. They know pretty evertything of everyone, which is simpler at an early stage of the development, but becomes a mess fast.
Anyway, if you are happy with this design, go on, but you could at least make a ScreenBase class from which every Screen inherits. A lot more Javaish, isn't it?

It is written in Java. Thanks for the response. Looks like I need to separate the systems so they don't know about the other systems.

I hate dealing with a mess. I will be sure to restructure this code base to prevent the mess.

Code is written in Java which explains it being Javaish.

Share this post


Link to post
Share on other sites

Yes, I recognized Java syntax. My "Javaish" was referred to a more structured code rather than the one you have now. It was a joke: Java (imo) tends to over-use the oop paradigm, with a proliferation of classes even for a simple task like a button click handler, while your code is totally in the opposite direction ;)

Share this post


Link to post
Share on other sites
public int getX()
{
return super.getX();
}

This is neither necessary nor proper style. The geX() method inherited will be visible as long as it's public.

int rectWidth = 50;
             int rectHeight = 50;
             // left
             leftRectangle = new Rectangle(((int)(getWidth() * -.04)) - camera.getX(),getY() - camera.getY(),rectWidth,getHeight());
             // right
             rightRectangle = new Rectangle(((int)(getWidth())) - camera.getX(),getY() - camera.getY(),rectWidth,getHeight());
             // top
             topRectangle = new Rectangle((int)(getWidth() *  0)- camera.getX(),(int) ((getHeight() * -0.06) - camera.getY()) ,getWidth(),rectHeight);
             // down
             downRectangle = new Rectangle((int)(getWidth() * 0)- camera.getX(),getHeight()  - camera.getY() ,getWidth(),rectHeight);
 
 
 
             if(link.getRectangle().intersects(leftRectangle))
             {
                       MapTracker.SCREEN = 1;
             }
             // right wall collision
             else if(link.getX() + link.getIdleRightWidth() > ((int)(getWidth())) - camera.getX())
             {
                       camera.setX(camera.getX() - camera.getSpeed());
             }
             // top wall collision
             else if(link.getY() < (int) ((getHeight() * -0.06) - camera.getY()) + topRectangle.getHeight() - 10 )
             { 
                       camera.setY(camera.getY() + camera.getSpeed());
             }
             // down wall collision
             else if( (link.getY() + link.getIdleDownHeight() > downRectangle.getY()))
             { 
                       camera.setY(camera.getY() - camera.getSpeed());
             }

Try not to create objects when it's not necessary. Why not just check if the player's x, y coordinates are within the camera frustum, if not shift the Scene.

 

Try using a node based scene heiarchy it will be much more flexible

/**
 *
 * @author William Gervasio
 *
 */

import java.util.ArrayList;
import java.util.List;

import core.util.Node;
import core.util.math.Vector2;


public abstract class SceneNode extends Node<SceneNode> {

    private Vector2 translation;
    private List<IBehavior> behaviors = new ArrayList<IBehavior>();
    private float sceneTime;

    public SceneNode() {
        this(new Vector2());
    }

    public SceneNode(Vector2 translation) {
        this.translation = translation;
    }

    public void render(int delta) {
        for (Node<SceneNode> node : this.getSubnodes()) {
            ((SceneNode) node).render(delta);
        }
    }

    public void renderDebug(int delta) {
        for (Node<SceneNode> node : this.getSubnodes()) {
            ((SceneNode) node).renderDebug(delta);
        }
    }

    public void update(int delta) {
        this.sceneTime += delta;

        for (IBehavior behavior : behaviors) {
            behavior.act(this);
        }

        for (Node<SceneNode> node : this.getSubnodes()) {
            ((SceneNode) node).update(delta);
        }
    }

    public SceneNode setTranslation(Vector2 translation) {
        return setTranslation(translation.x, translation.y);
    }

    public SceneNode setTranslation(float x, float y) {
        for (Node<SceneNode> node : this.getSubnodes()) {
            Vector2 offset = ((SceneNode) node).getTranslation().sub(x, y);
            ((SceneNode) node).setTranslation(offset.add(x, y));
        }
        translation.set(x, y);

        return this;
    }

    public SceneNode translate(Vector2 translation) {
        return translate(translation.x, translation.y);
    }

    public SceneNode translate(float x, float y) {
        for (Node<SceneNode> node : this.getSubnodes()) {
            ((SceneNode) node).getTranslation().add(x, y);
        }
        translation.add(x, y);

        return this;
    }

    public Vector2 getTranslation() {
        return translation;
    }

    public float getX() {
        return translation.x;
    }

    public float getY() {
        return translation.y;
    }

    public float getSceneTime() {
        return sceneTime;
    }

/**
 *
 * @author William Gervasio
 *
 */

import java.util.ArrayList;
import java.util.List;

public class Node<T> implements Cloneable {

    private List<Node<T>> subnodes;

    public Node() {
        this(new ArrayList<Node<T>>());
    }

    public Node(List<Node<T>> subnodes) {
        this.subnodes = subnodes;
    }

    public void attach(Node<T> node) {
        subnodes.add(node);
    }

    public void clearNodes() {
        subnodes.clear();
    }

    public void detach(Node<T> node) {
        subnodes.remove(node);
    }

    public void deepDetach(T node) {
        subnodes.remove(node);
        for (Node<T> i : subnodes) {
            i.deepDetach(node);
        }
    }

    public boolean hasNode(Node<T> node) {
        return subnodes.contains(node);
    }

    public List<Node<T>> getSubnodes() {
        return subnodes;
    }

    public void setSubnodes(List<Node<T>> subnodes) {
        this.subnodes = subnodes;
    }


Edited by GameGeazer

Share this post


Link to post
Share on other sites


Try not to create objects when it's not necessary. Why not just check if the player's x, y coordinates are within the camera frustum, if not shift the Scene.

 

I need the rectangles which are the bounding box for the walls in the map. Without those, I cannot do collision. 

Share this post


Link to post
Share on other sites

 


Try not to create objects when it's not necessary. Why not just check if the player's x, y coordinates are within the camera frustum, if not shift the Scene.
 

 

I need the rectangles which are the bounding box for the walls in the map. Without those, I cannot do collision. 

 

Try translating the boxes instead of creating new objects.

Edited by GameGeazer

Share this post


Link to post
Share on other sites

Try translating the boxes instead of creating new objects.

 

Would that not be optimizing? I don't think I should optimize if the solution works.

Edited by warnexus

Share this post


Link to post
Share on other sites


This is neither necessary nor proper style. The geX() method inherited will be visible as long as it's public.

 

Ah true. I see where you are getting at. It sure looks redundant too.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!