Grid map

7 views
Skip to first unread message

Rodrigo Moraes

unread,
Dec 15, 2009, 5:59:14 PM12/15/09
to The Render Engine
Hello,
A couple of newbie questions (still lost with all the classes). Just
need some hints.

What is the object I should look at to render a tile based map? Think
on Civilization: single or multi-layered tiles for the terrains
(grassland, hills, forest, grassland + tree, grassland + tree + ...).

I was doing this on my own but things started to grow and become a
mess. For the sprites, I have a probability logic to not render always
the "same" terrain - there are several tiles for each terrain so the
map doesn't look too "tiled". Also in terrain intersections, I render
a tile to make a smooth transition from one terrain type to other.

I created all the logic to generate a hexagon-based map (and pixel-to-
hexagon, hexagon-to-pixel coordinates, this kind of stuff) and fill it
using an array of terrain definitions. Same terrains types are reused,
to not add overhead for a big map.

Now, I am trying to make all this things more modular. I was playing
with spriteLoader, but not sure if I can incorporate probability to
use different tiles for the "same" terrain, or should I simply define
each one separately, or extend it, or what.

Hey, I'm not asking for a solution, just for a few directions, since
all examples are more arcade stuff.

thanks,
rodrigo

bfat...@gmail.com

unread,
Dec 15, 2009, 7:01:21 PM12/15/09
to The Render Engine
Well, first things first. The SpriteLoader is simply a way to load
sprites based on a definition file which describes the sprite (or
sprites) located in a single bitmap. The descriptor says what the
base image file is, the overall height and width of the image, and
then an object which describes each sprite (either a single static
frame, or an animation). If you look at the documentation for the
SpriteLoader class, you can see an example of the descriptor. So,
you'd have two files for each sprite image loaded. You could either
have two files per tile, or two files in total: the bitmap with all of
the tiles in it, and multiple descriptions for each tile. However you
do it, you'll be loading each sprite "sheet" with a name that the
SpriteLoader will cache it with.

Once you have loaded the sprites you need loaded, you'll be able to
access those sprites by requesting them from the SpriteLoader by the
name of the sprite "sheet". Then you can request a sprite by name
from that sprite "sheet". For example, if you look in the resource
folder for the SMB demo, you'll see "smbtiles.js" and "smbtiles.png".
The JS file describes the sprites in the PNG file. It should give you
an idea of what you load and how you access the sprites.

Next, you could render the tile map in one of two ways. The first way
would be to create a tile object which extends Object2D, and then add
SpriteComponents for each layer of the sprite you want rendered. The
order in which they are rendered in the tile object would be their
priority. So, if you wanted a 3 layer tile with layers A on top, B in
the middle, and C on the bottom, you'd assign the highest priority to
A and the lowest to C. Then assign a sprite to each SpriteComponent.
When the tile object is rendered, the individual layers will render in
the order you specify.

The second way would be to use only one tile object (as above) that
renders all of the tiles to a static context. Static contexts are
something I added after beta 1.4, so the only way to get them is by
downloading the API from Subversion. Setting a context to static
means that you can control how a context renders the world. Normal
operation is to clear the render context, render all of the objects,
and repeat. I'm making a small change, as I write this, so that if a
context is static it will not reset and render the context at the
desired framerate. Instead, you can direct the context to reset and
render as you want. Rendering the world updates all of the objects
and causes them to render to the context. This will facilitate the
second method I'm describing.

So, with a static context, you could reset the context once, then use
a single tile renderer to draw all of the tiles. You'd end up moving
the single renderer for each tile and calling the render() method of
the context to cause the tile object to "stamp" itself onto the
context. This second method would only draw your context as needed
using one tile renderer for all of the tiles. In essence, you'd be
using it to create a tiled image for your context. So, the process
would be:

1) Mark the context static
2) Clear the context with the reset() method
3) Randomize tile sprites
4) Position tile object
5) Call render() on the context to "stamp" the tile
6) Repeat steps 3, 4, and 5 to generate each tile until complete
7) Snapshot the context with the getImage() method of the context to
store the generated map image

Then you would be able to set the context to "non-static" and add an
object to render the stored background image, along with your player
images. The cycle would then be:

1) Render background (tile map)
2) Render player objects

I hope this makes some sense to you. While the engine has many
capabilities, it's going to be up to you to utilize what's there to
build functionality that isn't. At some point in v2.0, I hope to add
a tilemap generator and some components for creating RTS or RPG style
games.

- Brett

Rodrigo Moraes

unread,
Dec 16, 2009, 12:33:58 PM12/16/09
to The Render Engine
Hi Brett,
Thanks for the quick and detailed answer. Yes, everything makes sense.
My lack of familiarity with the API is what makes things take a bit
more time than they should. After a bit of code diving and trials and
errors, I got the tiled map rendered.

Thanks for the update for static contexts. I was using the development
version already (btw, is it generally working or sometimes it is left
in broken state? just to know, I commonly use development versions,
unless it is not advisable).

Two things that were a bit confusing for me:

1) When a dependency needs to be loaded (do I need to load any file to
use Point2D? or Object2D? or ...?
2) How the program flows. Of course this is because I still don't see
how everything works as a whole etc, and I'm not sure what could be
done to help people to understand all the engine layers.

Thanks for the help, and let me see if I get into another problem. ;)
-- rodrigo

bfat...@gmail.com

unread,
Dec 16, 2009, 1:46:10 PM12/16/09
to The Render Engine
I try to keep the development version in a working state, mainly
because it runs the demos on the main site and on the code page. I
typically don't commit broken code, but being human I sometimes make a
mistake and either forget a new file, or I unintentionally check in
something that I overlooked and was broken.

To address your two points:

1) The linker will attempt to resolve all dependencies it can locate
in an object. It uses regular expressions to parse the objects and
looks for known patterns which indicate a dependency. It can resolve
most, but sometimes the patterns that one developer uses don't parse
properly and a dependency is either overlooked or it is improperly
identified as a dependency. The page at http://code.google.com/p/renderengine/wiki/EngineCodingStyle
attempts to explain what the engine is looking for to be able to parse
the cleanest. Since Javascript has so many ways to do one thing, I
chose (what I thought was) the cleanest syntax.

When you define an object and initialize it using Engine.initObject
("CLASSNAME", "PRIMARY_DEPENDENCY", function() {...}) it will
determine which objects your object depends on. The primary
dependency should be the class from which your class extends. Other
dependencies are typically resolved through included files. You need
to include the files before you call Engine.initObject() using the
Engine.include("path/filename.ext") method calls. It is up to you,
the developer, to make sure you include the needed files. Some
objects are included just by including a different file. If you look
at the source for "engine.object2d.js", you'll see that it includes:
"engine.hostobject.js". That file then includes:
"engine.container.js", "component.base.js", and "component.host.js".
Each of those has includes as well.

So when you're constructing your objects, you have to think about what
your object primarily depends on and what files those objects are
included in. Some files include multiple objects, such as
"engine.math2d.js" (which includes Math2D, Point2D, Vector2D,
Rectangle2D, and so forth. So you only need to make sure to include
"engine.math2d.js" for all of those objects to be resolvable.
Including the same file multiple times will not have a negative
impact, as the engine keeps track of which files have already been
included.

Basically, the linker will see code like:

var x = Point2D.create(3, 10);
x.mul(Vector2D.create(5, 0));

It will see "x" is a variable and store it as "already resolved". It
then sees that you're calling the create() method of Point2D and say
that your file depends on Point2D. Next it sees that the mul() method
is called on "x". Since "x" was already resolved, the linker will
assume that the mul() method is available. Finally, the create()
method of Vector2D is found, and the Vector2D class will be added as a
dependency of the class. In the end, we have the following knowns and
unknowns:

resolved: "x"
unresolved: "Point2D, Vector2D"

When the classes which Point2D and Vector2D depend on are resolved,
then the object with the code from above will be resolved as well.
That's the linker in a nutshell. Use the API docs to determine which
Javascript files contain the objects your class is using, and make
sure to include those before your class initializes.

More information can be found here: http://code.google.com/p/renderengine/wiki/DependencyProcessor


2) The Engine class contains a method called worldTimer() which
executes each frame. When a frame is executed, the Engine will call
the update() method of its default context (the document body). Your
rendering context(s) should have been added to the default context
before your game started running. Anything that is added to your
context(s) will then be added to the chain of objects which must be
processed. So lets assume the following scenario:

* Your context is called "gameContext"
* You have 3 objects (extending from HostObject) which need to be
updated when the frame is rendered, so you add them (A,B,C) to
"gameContext"
* Each of those objects has 3 components (extending from
BaseComponent) which were added to the object (x,y,z)

The execution flow would be:

[START FRAME]

gameContext ->
A -> Ax -> Ay -> Az
B -> Bx -> By -> Bz
C -> Cx -> Cy -> Cz

[END FRAME]

Objects have the update() method, and components have the execute()
method. Contexts and HostObjects extend from Container, whose update
() method will call update() on each object contained within the
container. As you can surmise, this proceeds until all objects which
are within a container linked back to the default context have been
updated. Then a new frame is generated. This goes on until the
engine is shut down.

This is something I should probably add to the Wiki at some point.

- Brett

Rodrigo Moraes

unread,
Dec 20, 2009, 7:41:23 AM12/20/09
to The Render Engine
On Dec 15, 10:01 pm, wrote:
> Well, first things first.  The SpriteLoader is simply a way to load
> sprites based on a definition file which describes the sprite (or
> sprites) located in a single bitmap.  The descriptor says what the
> base image file is, the overall height and width of the image, and
> then an object which describes each sprite (either a single static
> frame, or an animation).  If you look at the documentation for the
> SpriteLoader class, you can see an example of the descriptor.  So,
> you'd have two files for each sprite image loaded.  You could either
> have two files per tile, or two files in total: the bitmap with all of
> the tiles in it, and multiple descriptions for each tile.  However you
> do it, you'll be loading each sprite "sheet" with a name that the
> SpriteLoader will cache it with.

Just a note: the sprite loader is awesome.

-- rodrigo

Reply all
Reply to author
Forward
0 new messages