Wednesday, October 29, 2008

Understanding frameworks by example...2


Step 3: Check your code so far, revise and optimize


All might seem rosy at the moment but when you try out this code…you’ll notice that

1) Frame rates vary widely, some systems show FPS of up to 150 and some about 30
To ensure frame-rate consistency we’ll add ensureMinFPS() and ensureMaxFPS() functions. This can be done by skipping calls too render() function, also known as frame skipping.

That means we update status like 3 times but only draw it once onto the screen. The result is that the sprite may seem to be moving with large steps and may damage smoothness. But that’s the PC's problem nothing to do with us. Also we could add a convenience method getCurrentFPS(), just to check the performance.

2) For animating a moving object like a man, we’ll have to cycle through a set of images…
This requires some handling which is again redundant. So we’ll add AnimatedSprite class. This class may contain methods such as addFrame(Image img, long persistenceTime);

We’ll extend this class with Renderable.class so that its update and render methods can be utilized by the Game.class when registered with it.
@Override
public void update(long time)
{
Image curFrame = frames.get(curFrameIndex);
animTime += elapsedTime;

if(animTime >= curFrameduration)
{
curFrameIndex++;

//provides roll-over for animTime accurately...
animTime = animTime % curFrame.duration;

if(curFrameIndex == frames.size())
curFrameIndex = 0;
}
}
But sprites can move to...So we could use xSpeed, ySpeed and update them using:
xPos += xSpeed * (elapsedTime);
yPos += ySpeed * (elapsedTime);
this xPos and yPos could be utilized in render()
drawImage(getCurFrame() ,xPos,yPos);

Now re-run your code again. This time when you add sufficient renderable objects to your game, you’ll notice Overlapping.

i.e., the object added first is drawn first…
how will it seem if the graphics of a tree is being drawn after drawing man??


Forgive me for my crappy drawing…that’s the best I can come up with!
The problem occurs because in run() method of Game.class, we use:
for(int i=0 to numRenderable)
{
arrRenderable.get(i).update(curTime - prevTime);
arrRenderable.get(i).render();
}
avoid this we could modify addRenderableObject as
//image with smallest index is drawn 1st…
addRenderableObject (Renderable rend, int index)
{
arrRenderable.add(rend, index);
}
Now that the drawing order can be determined, overlapping won’t be a problem.

So far we have managed:
- Screen management, ensure min frame rate, a generic Renderable class that can be used with Game class followed by easier Animation handling.

Now imagine that you’re coding the game using THIS framework, suppose the game works on 500 images…you probably will have to load all the images @ startup and show a progress bar like loading or something…Now we’ll integrate this feature into this framework

Lets design a class called ResourceBox, that holds all the images.
public abstract class ResourceBox
{
Private HashMap hm = new HashMap();

//this is where you’ll load all the images…or ne other stuff
Public abstract void init();

Public void add(String id, Image img)
{
hm.put(id, img);
}

//methods such as remove, replace and so on…
//Include a method getImage(String Id)
}
We first create a ResourceBox object as:
ResourceBox rb = new ResourceBox()
{
@Override
Public void init()
{
//Load whatever resources you want…
//Ex – add(“man”, imgMan);
}
}
Remember the Game class…?
How do we register this object with the game class??
Game(ResourceBox rb)
{
//Copy this ref into a private variable within game class
}
This is how the game would run previously:
run()
{
Init();
Game loop
{

}
}
Here we plug in a bit of code:
run()
{
Init();
If(rb != null)
rb.init();
Game loop
{

}
}
Also we’ll add a utility method to game class as:
public ResourceBox getResourceBox();
This method is needed as we could use:
Image man = gameObj.getResourceBox().get(“man”);
Cool eh?

What about the progress bar??
We can’t provide an implementation as it would restrict the user from customizing/ making his own style of progress bar. More-over the user defined Loading screen or whatever must be shown when rb.init() method is called.

We’re gonna try some sort of an event listener kinda thingy. Here goes:

Include a method in ResourceBox.class
public void registerListener(Listener l);

//This is how Listener’s defined
Public class Listener
{
Public abstract void onInit(Graphics g);
}
First of all user’s gotta create an obj of Listener class for which you HAVE TO override the onInit() method
Listener l = new Listener()
{
@Override
Public void onInit(Graphics g)
{
//Draw your stuff on this graphics context…
//Ex = ((Graphics2D)g).do any thing();
}
}
Now we’ll also include a method getRegisteredListener() in ResourceBox.class
We now modify the game class run method as follows:
run()
{
Init();
if(rb != null)
{
Listener l = rb.getRegisteredListener();
If(l != null)
{
// Assuming we have some hypothetical screen management class
Graphics g = ScreenManager.getGraphics();
l.onInit(g);
}
rb.init();
g.dispose();// or clear…
}

Game loop
{

}
}
Now user can show a progress bar or…loading screen with his/her own graphics style…
So here’s a short summary,

- Abstract methods and stuff is absolutely integral in designing a good framework
(Primarily to give user the control of things, just as swing lets us control the shape of a button)

- You might need event listeners in case where your internal handling code needs to call something…like the onInit() we just discussed (swing provides action listeners etc…to a button which are automatically called on mouse click on that component, hope you can associate this with the onInit())

- Let your imagination run wild…experiment, try, you’ll eventually be able to write the top notch code. Okay, enough philosophy I suppose lets move further.

Go back to step 2:

The best way to add new feature(s) to your framework is like I said can be done by imagining the usage. So let’s start with some analysis...how do we integrate sounds? After all any game would suck without sounds…

In my next blog I’ll put up a nice UML diagram of the concepts so far and also discuss sound integration. As always, do comment and help me improve my posts.

No comments:

Post a Comment