hplus is still correct, though: ultimately the easiest design choice that will benefit you down the road is to maintain a seperation of functions and data. (however, sometimes in adventure games, it seems, it is useful to just throw everything together since there are so many unique and individual actions) But maybe I can just show you some snippets of my (early) code right now that would make things more clear.
In my game, "Rooms" are the individual areas the player runs around in. Rooms are connected to other Rooms through doors, and when a player goes through a door, the new Room starts being "processed" (AI, physics) and the old room is serialized.
Here is the lua file that defines the functions for a Room.
---------------------------------------------------------------------------------- Room.lua--------------------------------------------------------------------------------lightclass 'Room'function Room:draw(camRect) for i,layer in ipairs(self.map) do local pCamRect = ZRect(camRect:x() * layer.p,camRect:y() * layer.p,camRect:width(),camRect:height()) gl.PushMatrix() gl.Translate(-pCamRect:x(),-pCamRect:y(),0) RoomDrawTileLayer(layer.data, self.map.width, pCamRect, self.tileset.tileVector) gl.PopMatrix() end endfunction Room:setTile( l, x, y, t ) self.map[l].data:set(y * self.map.width + x, t)endfunction Room:getTile( l, x, y ) return self.map[l].data:get(y * self.map.width + x)endfunction Room:__init() print "Room constructor!"end
The function "lightclass" is my OO system, with it's workings inspired by the lua book. So a call to "lightclass('thing')" will make it so that you can create new things by writing
t = thing()
Then functions defined for thing...
function thing:doSomething(param)
print "thing is doing something! " .. param
end
...will then operate on all things. It seems kind of complicated at first, but it ends up really being worth it, and you can inherit and do all the fun polymorphism stuff too.
---------------------------------------------------------------------------------- lightclass.lua--------------------------------------------------------------------------------function lightclass(name) _G[name] = _G[name] or {} _G[name].new = function(self,o) o = o or {} setmetatable(o, self) self.__index = self self.__lightclass = name if self.__init then o:__init() end return o end setmetatable(_G[name],{__call = _G[name].new}) end
So I have a set of operations for Room defined. Now when I want to load a room, I call a Lua file that was automagically created from a Room table before (I'll get to that). Here's an example of a file that returns a Room object...
local room = Room()room["map"] = {}room["map"][1] = {}room["map"][1]["p"] = 0.7room["map"][1]["data"] = IntVector{10,10,10, -- ...goes on for each tile in the layerroom["map"][2] = {}room["map"][2]["p"] = 1room["map"][2]["data"] = IntVector{1,1,1, --... goes onroom["map"]["height"] = 40room["map"]["width"] = 40room["name"] = "mountainpath"room["tileset"] = Tileset{filename='mountain.bmp'}return room
Right now there are no entities stored in the Room files...I'm working on that. IntVector is a class just like Room, so Lua knows how to construct them. To create this file, I just used a nifty function that serializes any Lua table. The table can have cycles (it can refer to itself or objects already in the table). The bulk of this function is also from the Lua book (which, if you can't tell by now, is amazingly helpful)--they just released it online, btw, at www.lua.org. save looks like this:
---------------------------------------------------------------------------------- Serialization.lua---------------------------------------------------------------------------------- Allows for serialization of tables WITH cycles. This quite clever code was -- stolen (with a few modifications) from Programming in Lua, the programming -- guide book for the Lua language. You can find it online at -- http://www.lua.org/pil/---------------------------------------------------------------------------------- note on lightclass functionality:-- the rule is, if the object has a tostring method defined then the save-- method will not delve any deeper into its table; it is assumed that all of-- the object's state is contained in the result of the tostring. if there is no-- tostring then the name of the class is pulled form the __lightclass element,-- an "empty" constructor is written, and the table is recursively saved.-- the examples are: Room is depth-written, and Tileset is surface-level-written-- the reasoning behind this is that some data, like Tilesets, cannot be written-- to text, because they have image data. instead the image data filename is-- written with a conststructor and serialization transparency is maintained.--local basicSerialize = function(o) if type(o) == "number" then return tostring(o) elseif type(o) == "string" then return string.format("%q", o) elseif type(o) == "boolean" then if o == true then return "true" else return "false" end elseif type(o) == "userdata" then if not getmetatable(o).__tostring then return 'nil -- error: no __tostring method defined for this userdata! KTW' end return tostring(o) end endfunction save (name, value, saved, chunkReturn) saved = saved or {} -- initial value local cReturnText = "" if chunkReturn then cReturnText = "local " end io.write(cReturnText, name, " = ") if type(value) == "number" or type(value) == "string" or type(value) == "userdata" or type(value) == "boolean" then io.write(basicSerialize(value), "\n") elseif type(value) == "table" then if saved[value] then -- value already saved? io.write(saved[value], "\n") -- use its previous name else saved[value] = name -- save name for next time local endHere = false if value.__lightclass then if ( value.__tostring ) then io.write( tostring(value) .. '\n' ) endHere = true else io.write( value.__lightclass .. "()\n" ) end else io.write("{}\n") -- create a new table end if not endHere then for k,v in pairs(value) do -- save its fields local fieldname = string.format("%s[%s]", name, basicSerialize(k)) save(fieldname, v, saved) end end end else io.write("nil -- cannot save a " .. type(value),"\n") end if chunkReturn then io.write("\nreturn ",name) endend -- function save
So maybe that will give you something to chew on as far as the whole object-data-state-serialization mess is concerned--it's never very easy, there's always gotchas, but Lua can make it simple.