This is Bananattack.

Minty chewing gum.

Posted by Overkill on January 25, 2009 at 2:10 am under Uncategorized

This week, I poured over some more scripting language stuff in LuaVerge. Monotonous work really, but it needed to be done!

As a result, LuaVerge is nearly done. I’m convinced it’s at about the point where it’s usable again.
 
 
 
I added a way to load from .vpk packfiles with Lua scripts. So now require statements will also examine packfiles for source code, meaning that things like vx, and peoples’ games can be packed away in a single file.

I might add override loadfile and dofile too, if it’s actually required, but I figure those are mainly lowlevel things, and most work can USUALLY be done by require. I guess if someone were using Lua as a file format for game data (like a database of items or something) as well, then you’d need to worry about this. I’ll take peoples’ input here, for those Lua users out there.
 
 
 
One of the biger things I spent time scratching my head over was binding Verge’s builtin variables. You see, Lua shoves in getters and setters for variables as functions. So at the lower level, to get the backend variable VC-style entity.x[ent], it calls v3.get_entity_x(ent).

However forcing people to call raw functions rather than assign variables, I’m told is annoying. So, I added support, through Lua’s crazy voodoo magic with metatables.

Since ‘variable’-style accesses to builtins are more expensive than their raw function calls, a bit of optimization had to be considered, to at least lower these costs. After all, the older LuaVerge had issues with speedy access.

This was a bit of a surprise to me at first, but also really neat, local variables have a much faster access time than global ones. Storing local copies of global variables gives a significant efficiency boost. Some report about a 10% lookup for globals, so imagine if it were a nested table of some sort. Caching variable lookups as much as possible would very much improve performance.

So using this knowledge and a few tricks, I think I’ve constructed a way to make fast lookups to LuaVerge builtins. I won’t bore or scare you with the details.

But if you insist, and are curious, all the work is in the source repository for Verge at http://www.verge-rpg.com/svn/verge3/ with username “anonymous” and password “anonymous”.

Particular files to look at are:

 
 
 
So after all this, I started to repair the vx library so it’d work after the giant rewrite. Surprisingly, it wasn’t too hard to repair.

Why? Because vx enclosed all its source in a custom class system. Object oriented code made things compartmentalized and simple enough to refactor.

I made a simple vergeclass function that declares a class, and basically allows you to declare objects of that class after it’s been made.

You declare classes like this:

vergeclass "Thing"

(Whoa! no brackets around the function call? Lua lets you omit them for literals like quoted strings or tables). Anyway, after that point, there’s a globally named class called “Thing” which can be instantiated.

Constructors are declared like

function Thing:__init(a, b, c)
    self.a = a
    self.b = b
    self.c = c
end

And methods inside the class are sort like

function Thing:method()
    print "Hody2!"
end

Objects can be instantiated like this:

mything = Thing(5, 3, "tacos")

Their methods can be invoked fairly simply:

mything:method()

The downside, was the original system I had (before LuaVerge was rewritten) was horrendously inefficient, but it worked somehow. Lua is magical and allows you to horrendously hack things that just work.

Anyway, I revisited this code not because of this but for another reason. None of classes could be ‘extended’ or ‘inherited’ from, making the OOP limited. Granted, for vx, inheritance wasn’t necessary, and I didn’t really desire people extending the core objects because I’d much prefer composition.

Basically objects should as much as possible use “has-a” relations instead, and avoid “is-a” whenever possible, however tempting it is. It decreases coupling somewhat that way.
 
 
 
But I had other code, with Resonance’s sidescroller engine that I wanted to salvage. It used a moderate degree of inheritance, which given for free by luabind. Luabind being the thing that glued LuaVerge behind the scenes before we tossed it, and it came with a class system with inheritance… Anyway.

So I added a single-inheritance system to vx’s vergeclass so I could eliminate this old dependency and have working code again. This involved entirely rewriting the old system now that I understand the Lua library, Lua-based optimizations and metatables a lot better.

It was actually pretty enjoyable to overhaul the whole thing, and… weird. Metatables have some godly power to them. Anyway I finished, and even gave two methods of setting up inheritance.

I supply a temporary global function super(...) which must be invoked in a child class’s initializer to ensure the object inherits all the attributes that its parent class has. Later, I might enforce the call be made on every subclass just to ensure undefined behaviour won’t happen.

Now any time a method is called, it’ll look in its own class first, and failing that it’ll look in it’s parent’s class. If a class needs to explicitly call a method of its parent, it can index self.parent and use self.parent.method(self, ...).

So using the Thing example from earlier, the calls to create a child class are either this…

vergeclass("SuperThing", Thing) do
    SuperThing:__init(a, b, c, x, y, z)
        super(a, b, c)
        self.x = x
        self.y = y
        self.z = z
    end
end

…or this, which I prefer because it looks like Python kind of:

vergeclass "SuperThing"(Thing) do
    SuperThing:__init(a, b, c, x, y, z)
        super(a, b, c)
        self.x = x
        self.y = y
        self.z = z
    end
end

Either one of them reads “The class SuperThing is a child of Thing”. And there you go! Inheritance! Nifty! Sexy!

I even added one other thing! Properties. Other languages provide properties to declare pseudovariables with functions that are called when their values are accessed or mutated. I do similarly, through a bit of metatable magic.

Let’s take an example of a typical RPG player for a second, who has an HP attribute. To declare a property, you can go something like this:

Player._property('alive', function(self) return self.hp ~= 0 end)

You can then use the ‘alive property like a readonly variable, for instance on a player object named bob:

if bob.alive then print("We're still kicking!") end

I might expand this class system to allow multiple inheritance or mixins at some point. Not a high priority, but it’d be nifty!
 
 
 
So yeah, this has been my week. I’d love to hear about yours too! :D

Tags: , , , , ,


« Older: McSchooldorf      Newer: It is never too late to be great. »

[...] For one, I used the vx library I had written. It came with a nifty object-oriented class system. [...]


Trackback

Add your comment!

Name (required)
Email (this will not be published, but it is required.)
Website

You can use the following HTML code:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>