The Medium is the Message 50¢ Contact
 

House of Shadows: Tech Demo

Adobe FlashHouse of Shadows is a Flash-based web game I’ve been working on for quite some time. It is set on Halloween night. You are a young boy trick-or-treating with your little sister. As you inch past a creepy old house, she decides to run up and knock on the door. It opens and suddenly she is pulled inside. You have to enter the house, find your sister and rescue her. Along the way you encounter ghosts and ghouls, stalking through the house and looking for little kids. You have to sneak past them or they’ll notice you, chase you, and gobble you up. So in terms of gameplay it’s a “sneaker” like Metal Gear Solid or Thief. But it also has some pretty cool tech.

I’ve written a couple of prototypes for the game, and the people who have played it are begging me to finish it. So far I haven’t been able to find the time. The game not only has fun, spooky gameplay, but some pretty nifty shadowing technology. Well, pretty cool as far as 2D Flash games go. Specifically, it has a shadow casting system that works like a top-down version of Doom 3. Allow me to demonstrate.


House of Shadows Tech Demo



Here, for the technically minded, is an overview of how these shadows work.

The game world is a grid, and each grid cell is marked as “blocking” or “unblocking.” Blocking cells block the light and therefore cast shadows; unblocking cells, naturally, do not.

To generate the shadows, I start by identifying which grid cell the light source is in. I then visit every other cell in the world that lies within the light’s maximum radius. For every blocked cell I add the one or two edges that “face toward” the light source to a list of blocking edges. (Actually, this step is done once when the map loads and cached for later access.)

Now that I have a list of blocking edges, each consisting of two vertices, I measure the angle from the light to each vertex and then sort all vertices by their angle. This gives me a 360-degree “panoramic” list of the vertices.

Now I walk this list in order, generating the “render vertices” as I go. This step is complicated, but basically, for each vertex visited, I check for:

  • Coterminal vertices. Two vertices that are at the same location can be treated as one vertex.
  • Silhouette vertices. A vertex at the “edge” of a blocking cell (or group of cells) casts a shadow. I create a new “shadow edge” by casting a ray back through each silhouette vertex. I then have to check if this shadow edge intersects another blocking edge, in which case the blocking edge is bisected and a new vertex created. Otherwise—if the shadow edge doesn’t intersect anything, but goes past the light’s radius—then I create a new “far edge”—an arc representing the far limit of the light’s area.

Once I’ve visited all the blocking vertices, I now have a list of “render edges.” These consist of line segments and curves that I then pass into Flash (using graphics.drawPaths()) in order to render a shape that represents the area illuminated by the light.

I do this for all light sources, rendering one shape per light source.

This collection of rendered shapes is then used as a mask onto the actual world—the floor boards, characters, etc. Flash uses masks to decide what to render, so in effect, my light shapes cut away parts of the world that no light illuminates.

I also use the collection of rendered shapes, along with a glow filter, to create the fuzzy darkness that haunts the edges of the shadows. Without this step, shadow edges would be absolutely sharp (as in Doom 3).

Finally, each light source is also given its own MovieClip so that it shows up as a glowing ball with a colored alpha gradient. This gives the illusion of colored light sources. Up to this point, lights have no color.

That, in a nutshell, is how it works. If you’re interested and want more detail, let me know.

As you can see, the shadowing technology is done. I have another prototype of the actual gameplay. Hopefully I’ll get some time this summer to go beyond prototyping, to finish the game and make it available.

share:
  • Twitter
  • Facebook
  • MySpace
  • Reddit
  • Digg
  • StumbleUpon
  • del.icio.us
  • Google Bookmarks
  • Slashdot
  • Technorati
  • Yahoo! Buzz
This entry was posted in games, programming, technology. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

11 Comments

  1. Patrick
    Posted May 27, 2009 at 11:08 am | Permalink

    Impressive, but your shadow technology is hardly “done.” There are still several dozen artifacts and odd shadow shapes that occur very easily and totally spoil the effect. Lots of unrealistic merging of shadows and sharp pointy things where there shouldn’t be any. Also, when there are two blocks corner to corner then a shadow is generated in the corner when there should be none. There’s an article on these 2D shadows like this on GameDev.net you should check out. Here’s the link: http://www.gamedev.net/reference/programming/features/2dsoftshadow/

    • Posted May 27, 2009 at 11:18 am | Permalink

      Cool link. Thanks for that. But those techniques are for Direct3D/OpenGL, and I’m implementing in Flash. For Flash, this is far as I’m taking it, and I doubt it could be taken much farther due to performance considerations. The only serious artifacts remaining are those that occur when you move the light source out of the map or inside a blocker—which can’t happen in the real game because of collision detection.

  2. PapaJ
    Posted May 27, 2009 at 1:50 pm | Permalink

    Looks cool, however now and then the debugger spits out

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at CShadowCalculator/GetRayRayIntersection()
    at CShadowCalculator/GetIntersection()
    at CShadowCalculator/CalculateLightPolygonBasedOnBlockages()
    at CShadowCalculator/CalculateLightPolygon()
    at CShadowCastGame/NotifyEnterFrame()

    This happens pretty much every time I move the light into the obstacle in the middle of the left hand side.

    • Posted May 27, 2009 at 2:22 pm | Permalink

      Thanks for the bug report! I will look into that. As I mentioned in a previous comment, the ability to move lights inside things that block lights is a temporary, developer feature, so I’m not surprised there’s a bug there. But I’d still like to kill it.

  3. Jeramy
    Posted May 27, 2009 at 8:44 pm | Permalink

    Oh man this is cool! I would love to chat with you about how this is done as I’m looking for a lighting solution for a shockwave tile engine I am working on!

    So cool!

    - ook

    • Posted May 28, 2009 at 8:13 am | Permalink

      Hi Jeramy! Let’s talk—I’d be happy to share more about it. Also, check out this solution that someone else pointed out. I think it’s basically the same approach but with some interesting differences.

  4. Stringycustard
    Posted August 7, 2009 at 6:40 am | Permalink

    Cool demo. I found it after knocking up my own shadow/light casting system for a game I’m doing (also in Flash). In order to better blend the shadows between multiple lights I’m creating new shadow layers per light. This way, you can blend more easily between the shadow layers to create a smoother transition. I’ve gotten it working nicely with any shape (including irregular shapes with multiple vertices) – the algorithm behind this is very simple to keep flash running smoothly. Also I’ve worked out a nice (fast/cpu-unintensive) method of doing hard-soft shadow shaping if you’re interested…

    • Posted August 7, 2009 at 9:20 am | Permalink

      That’s sounds awesome. I’d love to see more. Incidentally, my system has developed a bit over the summer but I haven’t had time to post the new demo. The new system is pretty ideal in terms of features and still runs fast, though it doesn’t support soft shadows. Check back in a week or so if you’re interested.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>