Lua, Servers, Nav Meshes and Editors

Published April 14, 2007
Advertisement
Some progress, and some more thinking.

I had to get the source for Lua, because I was suffering a crashing bug deep in the Lua DLL, and after trying for a while, I still couldn't figure out why by just stepping through my code. I got the Lua source, compiled it with debug assertions on, and immediately were told what the problem was (turned out lua_next() returns two items on the stack everytime except the last, so I was popping one too many items). I'm torn between the satisfaction that quality open source eases development (because you can get source), or annoyance that I had to break my "libraries only" approach. At least I haven't been tempted to modify the Lua interepreter, so I think I'm doing OK so far.

I have a few components written so far (components go together to make an object):

  • posori -- Keeps the "position" and "orientation" properties. Typically initialized from script. Provides a known rendez-vous point for this infomation.

  • rendermesh -- Renders a given mesh at a specified location/orientation. The default for location/orientation is to bind to the posori properties.

  • generic -- Whatever properties are specified with values in the config script, are added as properties to this component. Convenient for generic tagging.

  • spawner -- Can instantiate another object type depending on some simple rules. Can also track the lifetime of the object it has spawned.


The Lua "MakeObject" function takes a table of component names mapping to tables of named properties with values. I also have a templating system, which allows the object to only specify the components/properties that vary from the default -- written entirely in Lua. And, to tell the truth, because of the power of Lua, that templating is just a single function!

--  Merge a template with an object specification, in place.--  This can be used to template templates, if you want.function ExpandTemplate(tm,obj)  for k,v in pairs(tm) do    ev = obj[k];    if ev == nil then      obj[k] = v; -- take the template value if not already specified    else      if type(v) == "table" and type(ev) == "table" then        ExpandTemplate(v, ev); -- recurse if it's a component      end    end  end  return obj;end


Behold the Awesome Power of Scripting! :-)

So now what? Some previous hobby projects have stopped at about this point, because I haven't had enough art to make it worthwhile, or becaue I've started climbing down a hole of toolsmithing, or some other such reason. I'll need some help to get around it this time. I stand at a crossroads:

  • I can start adding an animation component and a locomotion component, to start building the players and other characters.

  • I can start adding a simple editor interface, which allows me to open a level script, move around/add/delete static entities, and save the script back.

  • I can start on the physics/AI/navigation part of world representation.

  • I can start building stand-in art, so that I can populate a world with "stuff" that I can use for demo purposes. I kind-of like poking around in 3D creation programs, but I'm not an artist, so it takes too long, and looks too crappy for it to be a useful use of time.



Nav meshes vs physics is interesting. I will not be needing physics gameplay, and I will be having an online component. Synchronizing a physics engine over a network is an interesting challenge, but it's hard, and it's something I don't need to accomplish my game goals. Thus, I won't use a physics engine (even though I downloaded the latest Ageia SDK, version 2.7 -- I hadn't used it since 2.2!)

My game idea does involve moving over land, and it will be moderately important to not let players walk through walls by hacking the client. The "normal" way of solving this is to send continual position updates from the client to the server, and have the server cast rays or sweep spheres through a copy of the level, sending a correction back to the client if it finds that the movement intersects the world. However, I'm not sure I want the server to load the level geometry and run collision. An alternative, cheaper idea is in my mind.

I could cover the entire level by a grid of 1x1 or 2x2 meter squares. The game involves humanoids and slow moving ground vehicles in an outdoors environment, for scale purposes. I can shoot rays from the center of each square, to the center of each neighbor (4 or 8 neighbors -- not sure which, yet). I can record the result in the bits of one byte per square. I can also measure ground slope, and record "impassable" if the ground is too steep in any one direction. Then, when moving, I just have to trace the path through a sequence of cells, making sure they are connected with passability. If not, I snap the player to the center of the last good cell. A 2000x2000 meter level at 2x2 meter resolution costs just a meg of RAM. Heck, on a modern CPU, it might even stay in L2 cache on the server!

This method has the benefit that it is REALLY CHEAP (tm). It has the draw-back of spatial quantization. For example, if a wall passes through a cell close to the center, and the wall is thin, then you can actually move through that wall a little bit before you get snapped back. See drawing:


The green arrow shows how you can pass through a wall without leaving the square to detect the direction is not passable. Once you move further than the arrow indicates into another square, you would snap back to outside the wall. Similarly, this method also disallows moving very close to the wall from the inside (in the above example) because of the quantization at the cell borders.

Work-arounds might include using the other 4 bits of the byte to store passability within the cell (say, from the center to the midpoint between center and edge in each direction), which reduces the impact of quantization a little bit, but really isn't a "true" solution.

Another option would be to store simple passable/blocked information per grid cell, in true old-skool fashion. That way, any cell that the wall enters into would be non-passable. This has the effect of "fattening up" collision meshes, and makes it impossible to create thin walls within the world, but is more robust. Also, with the same memory requirements, you could have 0.7x0.7 squares instead of 2x2 squares, if you packed each cell into a single bit.


Or maybe I'll just go to TurboSquid and buy some trees and houses, and work on building a sample level for now. After I sleep.
Previous Entry Level! Objects!
Next Entry Turbosquid sadness
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement