Saturday, September 28, 2013

Shadow and Light Part II




At the end of part I we had the shadows moving with the player, and more or less conforming to the space and shape of the walls, but as the player moved toward the end of the screen, the shadows would completely disappear.





The problem turned out to be with how I was determining the area to select from.  I was using the basic code in the existing pick area method of the composite sprite, and tweaking until I got the correct values.  The basic algorithm is:


1. Convert the AABB of the current scene render state to an OOBB


2. Calculate the inverse of that OOBB using the batch transform of the composite sprite object that is the shadow map.


3. Convert that inverse OOBB to an AABB.


4. Perform a query area on the composite sprite using the AABB, that does NOT target the OOBB of the individual sprites.


The last part, not targeting the OOBB of the individual sprites, was the key to getting the pick are to always return the proper objects based on the position of the player, which the map tracks as the player moves.


Here is a video that shows the shadowing in action.  This video includes the dynamic light radius that is in the game also.  In the video, the light radius is the minimum and would represent going into a dungeon with no torch or other light means.  Equipping a torch gives you a larger view radius and you can use certain objects to change the light color.  this can have an effect, positive or negative, on certain monsters.

video

Friday, September 20, 2013

Shadow and Light!

In the first installment of a two part update we talk about adding dynamic shadows and lighting to 1000 Dungeons.
 Originally Posted  on May 8th, 2013 on our IndieDB game page.
So for the past week or so, most of my development time was spent on getting some lighting and shadowing into the game. I had implemented the same basic functionality in Java using Slick2D, and in a previous version of Torque2D, so I didn't think it was going to be all that tough. Yea, you know what happened. 
Since I was in Torque2D, I decided to start with my previous shadowing code that I used in the older version. It took maybe an hour or so to get the code to the point where it would compile. If only that meant it would work too. After running the initial compiling version, I was treated with absolutely nothing. No shadows, no nothing. Just the dungeon as usual. Time to take a look at what's going on. 
The old code grabbed objects based on the group they were in. Torque2D allows you to assign objects to specific groups and layers, and then you can control various things (like collision or rendering order) on the group and/or layer. It's a very handy feature. The latest version still has this information, but getting it out of the scene is bit more tricky. And that is were the first issue was, the scene is not where my information was. 
When I was initially doing the pick area to grab the objects, I was doing so on the scene and only ever getting 3 things back. No matter what I tried, all I ever got back was 3 items. Why you may ask? Well, because the scene only contained 3 items. It contained a composite sprite, a sprite, and the shadow object. The items I want are on the composite sprite, not in the scene individually. So first update was to change the code to take a composite sprite object and update the selection code to select sprites in group 6 (the group I designated for blocking line of sight). Stepping through the code once more, I now see that I'm getting back 4000+ objects. On the right track! Let's see what what we got, lovely shadows? No.

What we got was a jumbled mess of black things, that somewhat resembled shadows, but were not aligned to the walls of the dungeon. As an added issue, when the player moved, the shape of the shadows did not. Older versions of Torque2D had a mounting feature where you could "attach" one sprite to another with specific offsets, and the two would move as one. The new version no longer has this feature, replacing it instead with Box2D joints. The joints provide a much more powerful way of linking objects together, but it also more complex; especially if you are not familiar with Box2d and its joints, which I'm not. So after playing around for a bit trying to use a joint to get the shadow objects position to move with the players, I just implemented a very basic event system in script that allows objects to subscribe to specific events (like player movement) and have an update method called automatically. So now the shadows are moving with the player, but they have this odd habit of just disappearing as you reach the edges of the map. 

In part two, we will finally track down the final piece to the shadow puzzle and tie it into some actual game play.  As always, you can track the development of the game at our website, and follow us on Twitter.

Inventory: 1000 Dungeons

The work continues!  In the time since the last update, we have completed work on the inventory and equipping screen as well as implemented a mini map to help you navigate the dungeons throughout the worlds.
You can see the inventory screen here:
Inventory
The graphics are all place holder, but it gives the basic UI.  There is a grid of spots where items can be placed.  Items in the game can be stackable or not, and when added to the inventory will automatically stack with existing items of the same type if you have them.  The inventory screen allows drag and drop, something I had to implement from scratch in Torque Script.  The user can drag items from the inventory on the right to the equip slots on the left.  Each item has a set of equip slots that it can be equipped to, and the inventory screen and system will only allow valid items to be equipped.
The other major addition has been a mini map.  You can see an example screen shot here:
minimap
The game offers 3 difficulty levels, which control the size of each of the 1000 dungeons.  In the screen shot above, once fully explored, the entire gray box will be filled with the dungeon.  The mini map automatically updates as you move through the dungeon.  One possible addition I've been thinking about is allowing the user to set a old school mode which would remove the mini map, and you would have to use some graph paper and a pencil if you wanted to track the layout of the dungeon.  Maybe put out a special addition that includes a 1000 page pad of graph paper and a box of #2 pencils.
The battle system for the game has been redesigned and is much simpler and more "rouge like".  I'll save the details on that for the next update, but will say that with the new system you will be tracked by the monsters and creatures in the dungeons via sight, sound, and smell.
Until next time....

Wednesday, September 18, 2013

1000 Dungeons


This blog post is an introduction to the Torque 2D game I'm working on and a highlight of some of the custom features and functions I've built for the game so far.

About 1000 Dungeons

1000 Dungeons is a Rouge like game of exploration, battle, and crafting. The game features 1000 randomly generated dungeons spread out over 10 different randomly generated lands. The goal is to make it through as many of the dungeons as you can. If you die in dungeon 999, you have to start over from number 1. To help you on your journey, there will be weapons to upgrade, items to craft, and varied creatures to hunt and be hunted by.

Features


World Generation

The first element I tackled for the game was creation of the randomly generated worlds. The world generation uses an algorithm based on a seed and grow process. There are 5 basic land types:


  1. Grass
  2. Water
  3. Desert
  4. Mountain
  5. Forest

Each terrain type can have a frequency value set from script before world generation. This allows the game to then control the primary terrain type for each world that gets generated. Terrain types play an important role in the game, as certain monsters will only be in a dungeon that is on a specific terrain type. And since the monsters drop the resources you need to craft items and upgrade weapons, making sure you get all the different creature types is important to being able to make it through all of the dungeons.

Here is an example of what the world might look like after seeding:
www.bloodytongue.com/images/InitialSeed.png
The frequency values across all terrain types totals 100, which is the number of seed tiles that will be placed in the world. Once the seed tiles are placed, the code loops through the map and starts to grow the terrain type it encounters into any empty cells adjacent to the current cell.

Here is what the above image would look like after one growth round:
www.bloodytongue.com/images/Growth1.png
This basic growth routine continues until all of the tiles have been filled. We now have a world.

All of the above is handled by a custom C++ class written and integrated into the T2D engine.

Dungeon Generation

The dungeon generation is another custom C++ class added to the engine. Dungeon generation is room based. It always starts with a random sized room in the middle of the dungeon. Once that initial room is placed, a random location is picked until a we find the wall of an existing room that doesn't already have an opening on either side of our spot. The wall is removed from that location and left as either an empty space, or randomly chosen to be a door. If at any point we loop 10000 times without being able to create a room, then we call the dungeon done.

Example image of a generated dungeon:
www.bloodytongue.com/images/RandomDungeonGG.png

Rendering of Worlds and Dungeons

Example of part of a dungeon being rendered:
www.bloodytongue.com/images/DungeonRender.PNG
Example of part of a world being rendered:
www.bloodytongue.com/images/WorldRender.png
The rendering of the world and the dungeon are both handled with the excellent new CompositeSprite object. When a world is loaded or generated new, the game builds a composite sprite querying the C++ generation code for the tile type and setting the sprite to the correct image. This allows me to easily swap out new graphics for each tile type and only have to make script changes.

Dynamic LOS Shadows

A dungeon crawl wouldn't be a dungeon crawl if you could see everything in the dungeon. To fix that, I added another new class to the engine that builds dynamic shadows based on the players position.

Example:
www.bloodytongue.com/images/JustShadows.png
This was a fun class to write as unlike the dungeon and world generation stuff, this actually renders to the scene, and is a new child of SceneObject. The object takes a copy of the current dungeon map and a radius value and automatically calculates the shadows for the walls of the dungeon based on the position of the player.

This code was based on the resource found on the site for dynamic shadows in TGB. Converting the code from that resource took a bit of time, but in the end, I got it all worked out.

Light

In addition to the dynamic shadows above, light plays an important role in the game. Including the color of the light. Time for another custom scene object. This one renders a gradient circle from the player getting darker as it moves away. This can be dynamically updated with size and color based on what kind of light the player has equipped.

Here is an example with a base yellow torch:
www.bloodytongue.com/images/ShadowLight.png
The color matters, because for some creatures, the color of your torch can weaken or strengthen them. All things the player has to find out as they crawl through the many dungeons.

That covers the custom C++ side of the changes I've made so far for 1000 Dungeons. In the next update I'll go over some of the script side systems I've implemented, including the inventory system (complete with drag and drop), event call backs and all sorts of fun stuff.

If you want to read more about the development of 1000 Dungeons, be sure to follow our page on IndieDB.

If anyone has any specific questions on anything or is interested in more details on anything, just ask here in the comments and I'll give you all the information I can.