Table of ContentsThe EngineConclusion

Advanced Features

Now that you've covered the basics of a ray-casting engine, you can move on to a few more advanced features you might add to support an actual game. These include collision detection, texture mapping, and sprites.

In the following sections I'll briefly cover ideas for extensions to the engine. I'll leave the development up to you.

Sprites and Actors

The current ray-casting engine is very similar to your basic tile engine before you added any concept of actors. To use ray casting in a game, you need to add these in and move them around the world.

The problem you have to solve is how to draw these actors on the screen. You can't use a normal sprite because the image will always appear the same size. Because you're dealing with a 3D view, you need to scale the sprite images to give the impression that they are far away.The first requirement for this is the ability to scale an image to any size. Because J2ME doesn't support that by default, you have to use a little extra code to do arbitrary scaling. I recommend checking out the ScaleImage class developed as part of the kobject project. You can go to http://sourceforge.net/projects/kobjects for more information. (Browse the source director and look for the class /kobjects/utils4me/src/org/kobjects/ lcdui/ScaleImage.java.)

Once you have a scale image method ready, you need to create different versions of the actor images to display based on their distance from the viewer. The biggest problem with this is you can't scale images on the fly; it's just too slow. That means you need to pre-scale the images and store them for later use. Unfortunately, that chews memory for every copy of the same image. You need to keep your images small and make incremental jumps in the image sizes (say every 8 pixels instead of every 1) to get it all to work within the constraints of most MIDs. Even doing that, you can forget about having many different sprites.

You calculate the image size to use in more or less the same way as you figured the wall size; you just need to play around with the number to get a good balance. You might also find you need more precision up close, but you can increase the size gap as objects move away.

Collision Detection

Collision detection is basically exactly the same as it is in 2D tile games. The player is just another Actor object that moves around the world. You can grab the movement check code from the original Actor class and add it into the ray-casting engine. Of course, to add actors you'll need to add some sprites (see the previous section) to draw them in the game.

Once you detect a collision against a tile, you can react the same way you did in previous games. Try adding a projectile that fires from the player and bounces around the world. It's really cool.

Textures

The walls you currently render in the ray-casting demo use a simple draw line method. This is very fast and easy, but it's also pretty boring. By using textures you can map an image onto the wall to give it a much better look. To do texture mapping with your rendering process, you first need different-sized versions of the wall texture to map onto different-sized walls (based on their distances). You can use the scale image method again to create these.

To render the texture onto the wall, you need to draw a column of the image that matches the point the ray hit along the edge of the wall. To figure that out you can use the offset of the position after you hit it with a ray. For example:

if (gotHorizHit)
{
    // got a hit, let's work out what position along the cell wall the
    // ray struck
    cellPos = MathFP.toInt(destinationXFP) % TILE_HEIGHT;
}

To draw the texture part you need to set clipping so only the part of the image corresponding to that column is drawn. A combination of setClip and drawImage will get you there with a bit of tweaking.

Texture mapping is great, but again the major problem is storing the pre-scaled texture images. Because you need to create so many copies of the image, you'll quickly find that it uses very large amounts of precious memory. If you're using sprites, in most cases you won't have enough memory for textures as well. I'll give you a fair warning (which I know you'll ignore): Do the math on the total memory used by your scaled images before you bother coding. Trust me.

You might also want to consider another cheaper method of adding some texture to your walls: Use different colors when drawing the lines. For example, to draw a vertical edging across the bottom of every wall you can call the drawLine method twiceonce to draw a small border segment, then you change the color, and call it again to draw the rest of the wall. Adding diagonal, checkered, and other patterns is relatively easy using this process. If you really get your stuff together, you can even draw brickwork and doorways.

    Table of ContentsThe EngineConclusion