Saturday, December 29, 2012

Section Transitions

You may have noticed that all of my demos so far involve a single level (I call them Sections in code). Of course, this won't do for a real game, so I implemented a few things so that we can trigger changes from one level to another.

1. Introduced the Section Registry to the Asset Editor program. The section registry is more or less a list of all the sections in the game. Its primary purpose, for the time being, is to bind file names to section titles, so this screenshot is a bit bare:

So, I've used the level editor to create two maps, and then I've registered them as sections in the asset editor--and titled them Level 1 and Level 2.

2. Added a method hook on the Game Interface that can be called from scripts, called 'ChangeSection.' See the screenshot on how this is used:

What this says is that when the player collides with this entity (seen in the screenshot as one of those invisible triggers, placed right outside the border of the section), a message is sent to the game to change sections, with a few parameters:

  • The level name, seen above as Level 2. The section registry is used to find the corresponding level file to load.
  • The tag of an entity representing the target location (where the player will be standing), seen above as 'right'. Oh, I added a 'tag' field to entities and re-purposed the collision categories screen as a general 'entity properties' screen. See screenshot below to see how an entity on Level 2 was tagged.

  • The last parameter is a description of a well-known transition type. In this case, we're moving left a section, so the parameter value is 'left'
For now, up/down/left/right are supported, as you'll see in the video, but I wanted to make this abstract enough for other transition types. For example, when entering a cave, much of the logic is the same: we need a target section and a place to put the player when they enter, but we'll probably want to fade to black and then fade in from black, or do a screen wipe, or something like that. Same with going up stairs and down stairs, or falling in a pit, or whatever else the game requires.

Anyway, enough writing, on with the video! Here's me running around two very similar levels as a demo. Note that because level transitions can go anywhere, we can even have a level transition into itself, as shown near the end of the video! And because transitions are controlled via script, we can write levels with strange transitions (for example, a door that takes you to a different level or target position depending on whether a switch is hit, or if you have less than 1 heart left, etc).

Saturday, December 22, 2012

Recreating a Familiar Scene... With a Twist

I did a few things which allowed me to produce a cool demo.

Step 1: Introduce a circular model (and a new texture to use it on).

Implementation detail: While collision between polygons and circles is pretty trivial, the ability to stretch and rotate entities freely means that 'circle' is really 'ellipse'. And collision between two arbitrary ellipses, while not unsolvable, is more computationally complex than standard polygon collision. Therefore, circles are approximated as 16-sided polygons ('hexadecagon', the internet suggests, and my spell checker complains of).

Step 2: For entities that act as triggers, spawn points, etc: allow us to set 'no texture' to the entity and exclude it from the rendering process in-game (but not in the editor). This is what they look like:

These triggers are always rendered on the top layer. They have every other property that entities have, namely script events, which enables us to make something happen when the user collides with the trigger zone, or...

Step 3: Introduced an 'update' script event that can be attached to entities. The 'update' script runs every logical frame of the game, or at an interval specified.

Step 4: Added a 'BounceVeloicty' parameter to the arguments passed through to the entity collision event. This could actually be calculated in the script, but it seems that using it will be so common that it's probably more efficient to calculate it from the engine and pass it through every time. The 'bounce velocity' is just what it sounds like--the direction an entity would travel if it were to bounce off of the entity it collide with (like a wall, or another ball).

Step 5: Add a little script, make a video. Enjoy!

Next up: Linking multiple sections (levels) to each other and transitioning between them.

Wednesday, December 19, 2012

When Entities Collide

I implemented an event that fires when an entity collides with another entity. When setting up a demo for this event, I quickly realized I'd need to set up a system of conditional collisions--known as collision categories--to produce the desired behavior.

This is best demonstrated with a simple example. I have a brazier that can spawn moving fireballs. I want the fireballs to be destroyed when they touch a wall. But I also want the fireballs to pass over other braziers (and the brazier that it comes out of, if it spawns directly in the middle!)

Refer to the following video when reading the rest of this post, as it's much easier to explain in video format.

The first brazier has the following script:

local entity = section:CreateEntity(t);
entity:Attach("selfCollideEntity", function() entity:Destroy() end)

This ends up creating an entity that passes through everything. Why? Because the entity has no collision targets. To fix this, we need to add the following script, which is on the second brazier:


(Note that by default, all 'walls' are tagged with the collision category 'wall'. It's a special collision category which has the property of blocking the player's movement when the player collides. Setting a model to 'Solid' in the editor automatically adds the collision category 'wall'.)

Now the entity is destroyed at a wall. Unfortunately, the brazier is also tagged with 'wall', so that's where the fireball disappears. Our third brazier has the same script, but the brazier next to it has its 'wall' tag removed. In the video, you can see that the fireballs pass through the brazier, but so does the player.

The solution: tag the brazier as 'wall' and 'brazier'...

... and add the following to the fireball creation script:


In summary, this means the fireball collides with anything marked 'wall', as long as that thing isn't marked 'brazier'.

That's all for now!

My Sword Does Damage Now

My blade has been infused with an ancient power...

A force found only in ancient legends, striking fear into the hearts of enemies inanimate bushes...

The power of....

section:TriggerAt("playerDamageSelf", { swordPolygon });

Okay, so there's a bunch of stuff that I did recently. I'll get to the damage trigger in a minute. First things first, major changes:

Removed 'Tiles' from the game and replaced them with entities. There was a missing abstraction here--Tiles and Entities were close to identical except for a few properties, so I merged them into one object. Of course, from the editor's point of view, there's still tiles, since it's incredibly convenient to edit layered, axis-aligned stacks of tiles for the level geometry. However, when the game loads these from data, it simply converts them to regular old entities.

Updated the script editor. Here's what it looks like right now:

Most notable is the 'test' button, which compiles and attempts to run the script. Since there's no game context and scripts make use of a number of parameters passed in, the editor uses mock objects that implement those interfaces. At the very least, this ensures two things: that the code compiles, and that the methods called on those interfaces exist, and that the parameters are correct. A limitation of this, however, is that I can't ensure that all branches are executed. In the script above (which turns a light on and off when the player slashes it), the method entity:GetState always returns null, so the 'else' clause won't ever run in the test script.

Also note the label above the method body which tells you the implicit function signature of the event. A future enhancement would be some sort of browser that shows you the available methods on those objects. Intellisense would be great, but that's way out of scope.

Another minor but useful update: added a launch button to the editor. Fourth item on the toolbar (play). Runs the game with whatever section you're editing.

Okay, now for the demos.

This first video illustrates the new trigger 'playerDamageSelf'. This is an event handler for entities, so the 'self' in the name refers to the entity. That is, when you define 'playerDamageSelf', you are declaring what happens to an entity when the player damages it.

There are four sets of bushes, each with a slightly more advanced feature than the last:

1. The leftmost bushes simply change texture when slashed.
2. The script clears the collision model out so the player can walk over it after it's slashed.
3. A single leaf entity is spawned in the middle of the bush.
4. Multiple leaf entities with randomized positions and velocities are spawned.

Next up: Making entities collide. Remember the video with the fireballs coming out of the braziers? Well, let's make them disappear when they hit a wall.

Monday, December 17, 2012

Back from a short hiatus

Was on vacation for the better part of the week, so no updates. Working on getting the scripting architecture exactly where I want it before updating, which is mostly just code restructuring. Look for an update soon!

Monday, December 10, 2012

Enhancing scripts

In my last post, I mentioned a problem I had where I had to specify the 'source texture' when changing a tile's texture from a script. My proposed solution was to move scripts out of the 'TileStack' level and into the 'Tile' level. I went ahead and did this, worked great!

So now, the 'change texture' and 'change animation' script methods are simpler and easier to use.

Having all of this game logic entirely contained in scripts inspired me to move any game-specific logic out of the engine (C#) and into scripts (Lua). So, I migrated the player movement and sword-swinging script (there's barely any code there yet) to a script. There's nothing to show for this, since it's just moving code from one place to another, but it does allow me to change player behavior more dynamically at runtime without rebuilding the code.

Okay, now for the fun stuff. I added a few major things to the scripting infrastructure:

  • A 'CreateEntity' method on the Section interface. This does exactly what you'd expect; it creates an entity based on a number of parameters, such as position, rotation, texture, etc.
  • Added a 'Motion Controller' component to entities. The first implementation of this is a very simple linear motion controller (you give it a velocity vector and the entity will move in that direction every update).
  • Added a ScriptInterface so that scripts can manipulate their own lifetime and other behaviors. This has a few methods, notably the ability to set a 'delay until the script can run again' and 'the number of times a script can run before it is destroyed.'
The following video demonstrates these new features:

Next up: I'll code another event trigger representing 'playerDamage.' That is, the event will be triggered when the player slashes their sword at something.

Saturday, December 8, 2012

Bringer of Light

Minor updates before the demo
  • Updated the editor to allow rotation of textures on tiles and entities. Added shortcut keys (Ctrl+Left, Ctrl+Right) to rotate 90 degrees in either direction.
  • You'll notice a minor change of scenery. Moving indoors for a bit (added a few interior textures).
  • Refactored the scripting engine to pre-compile tile/entity scripts at map load time instead of evaluating them during a trigger.
Okay, a short update. Mostly I just wanted to make a script that did something a bit cooler--so that's what I did. Here's a video of a bunch of lights that change texture from off to on when you touch them:

Now, you might have noticed that there's a bit of awkwardness in the script itself: why should I have to specify the 'source' texture (ground_light_off) in order to change it? The reason, for now, is that the script is actually applied to a collection of tiles (a tile stack). When it executes, it doesn't know which layer of the tile to apply the new texture to. Specifying a source texture lets the method figure out a best guess. Here's the code for that:

I don't really like this method, it's sort of hacky. It seems a more elegant structure would be to allow each layer of the tile stack to have its own scripting triggers. So that's what I'm going to do next.

Wednesday, December 5, 2012

Baby's First Script

I added very basic scripting capabilities! Most of the work here was getting Lua integrated and setting up a data model for saving and executing scripts.

First, set up a tile with some sort of collision model. For now, the engine uses the same collision model to block the player and to detect collision for script execution. This might need to change--what if I want the player to be able to walk over a tile, but have the tile still execute a script?

Okay, as you can see, I've added a new flyout menu for managing scripts. If there's none there already, 'New' is the only option--if scripts exist, there's options for 'Edit' and 'Delete'. Let's create a new script...

Here's the Script Editor form. Pretty basic for now. Pick the function you want to define--I only have two right now, one that's triggered when the player collides with the model ('playerCollide') and one when the player does damage ('playerDamage') to the model. I'm going to choose playerCollide...

...and here's where I write my very first script. As you can guess, this script claims to move the player 80 units right and 30 units down.

Okay, I saved the section and ran the game. It works!

So, there's a very simple script integrated into the game. From here, it'll be easier to add more powerful scripts that act on a variety of game objects. Some examples of the first few things I'll add to the interface to implement the 'player slashes his sword at a shrub'

  • Change the texture of any of the layers of a tile or entity
  • Change the collision model of a tile or entity (you can freely walk over a shrub that's been slashed)
More advanced scripts for the same scenario might involve something like:
  • Spawn several debris entities (leaves)
  • Run scripts on those leaf entities to make them move in a certain way
  • Destroy the leaf entities after they hit the ground
  • Play a sound (I haven't set sound up yet, though)
  • Spawn an object or enemy that was hidden in the shrub.

Sunday, December 2, 2012

Adding collision to entities

As demonstrated in an earlier post, the level editor allows me to edit the collision model of a tile independent of the actual graphic. Before I jumped into scripting, I realized it may be wise to go ahead and implement the same logic for entities (entities are freely positioned, scaled, and rotated objects). This was fairly simple, since the collision detection algorithm was coded to operate on arbitrary polygon models, not just axis-aligned tiles.

Video! Here's some footage of me actually using the editor to show how I set these things up. I create some entities, transform them a bit, set some different collision models on them, and then demonstrate the results in-game.

The prototypes for collision models (you can see them in the video as 'solid', 'half triangle', etc.) are defined as polygons in the unit square space (x and y coordinates between -.5 and .5). I thought it might be neat to show how easy it is for me to add more of them, from start to finish, including the programming. The only thing I've done ahead of time is make the editor graphical representation of the model, which you can see me copying into the texture atlas at the beginning of the video. This one's probably better in highest quality since the code blurry otherwise.

Saturday, December 1, 2012

A bunch of boring stuff

What you'd be missing if you skipped this post: nothing. Game development isn't always exciting, there's definitely boring stuff mixed in with the fun. I'm choosing to tackle some of those issues early, since I'm sure they're not fun near the end of a development cycle when you're all stressed out anyway.

So, a couple of minor technical things:

  • Switched from the manual filling of a dynamic vertex buffer (and subsequently calling DrawPrimitives) to just calling DrawUserPrimitives every frame. I fill my vertex buffer every frame since I don't really have any static content (tiles may seem static, but the programming model allows me to manipulate every sprite for every frame). Performance is about the same, as expected.
  • Since everything is a quad right now, it made sense to use DrawUserIndexedPrimitives instead. Slight performance boost.
  • And here's the stuff I really hate doing: handling 'lost device' exceptions (from ctrl+alt+del, for example), properly releasing and re-allocating all device-dependent resources, etc. Anyway, it works now, so that's out of the way. Full screen alt+tab is still a little messed up, but I'll save that one for another night.
Next up is some really fun stuff that I'm looking forward to implementing: scripting.