Let me just drop an interesting alternative of doing this. I also love this way of “composing” my gameobjects. Here is my solution using lua modules as alternative giving you more flexibility, more efficiency, less messages and more access between the “scripts”.
--[[
Instead of hooking a lot of scripts on a gameobject this module helps you use lua modules instead with very similar behaviour.
Script_modules are specific modules for individual gameobjects and will have all script functions called.
Benefit except from optimisation (as several scripts are quite heavy) is also to ensure run order and sharing of properties.
The order the modules are registered in, in that order will all functions (eg update, on_message) run.
The modules will share the same SELF with the go_script
mself is a seperate instance module-context that will not collide with other modules or script self.
All script functions can be passed into the modules. They have the same signature with an additional 'mself' in the end of each function (see example)
Register with full paths:
{
"game.units.tank.turret",
"game.units.tank.movement"
},
Also modules needs to be required somehow to be collected by the defold build (preferable in same module that holds the paths).
EG MAIN GAMEOBJECT SCRIPT:
local script_modules = require "lux.utils.script_modules"
-- these are the modules that will be used "just as scripts"
local m_table = {
"game.units.movement",
"game.units.inventory",
"game.units.health",
"game.units.ai",
}
function init(self)
script_modules.register_modules(self, m_table)
script_modules.init(self)
end
function update(self, dt)
script_modules.update(self, dt)
end
function on_message(self, message_id, message, sender)
script_modules.on_message(self, message_id, message, sender)
end
function final(self)
script_modules.final(self)
end
EG MODULE:
local M = {}
function M.init(self, mself)
mself.offset = vmath.vector4(0,0,0,0) -- private for this module and instance
self.name = "Andy" -- will be shared with all modules and main script.
end
function M.update(self, dt, mself)
mself.offset.x = (mself.offset.x - dt ) -- will only affect this instance and module.
end
function M.on_message(self, message_id, message, sender, mself)
if message_id == hash("bullet_hit") then
-- all modules with a function named "take_damage" will be called (in order) and sent how much damage (hit_points)
script_modules.call_custom(self, "take_damage", nil, message.hit_points)
end
end
function another_custom_fn(self, mself, val_1, val_2)
-- this function can be called from any other module or main script.
end
function M.final(self, mself)
-- end it on module level
end
return M
--]]
local M = {}
-- needs to be done before anything else
-- can be called several times with different modules. This will then erase the last ones.
function M.register_modules(self, module_paths)
self.script_modules = {
mselfs = {},
active = {},
inactive = {},
}
if module_paths then
for i,path in ipairs(module_paths) do
local m = require(path)
table.insert(self.script_modules.active, m)
self.script_modules.mselfs[m]={}
end
end
end
--[[
Call a custom function in all modules containing the function name.
If module_index is provided it will only call function in that specific module
Each custom function must have self and mself as its 2 first arguments and then any argument passed into args (...)
--]]
function M.call_custom(self, fn_name, module_index, ...)
local s = self.script_modules
if module_index then
local mod = s.active[module_index]
if mod and mod[fn_name] then
mod[fn_name](self, s.mselfs[mod], unpack({...}))
end
else
for i,mod in ipairs(s.active) do
if mod[fn_name] then
mod[fn_name](self, s.mselfs[mod], unpack({...}))
end
end
end
end
function M.init(self)
local s = self.script_modules
for i,mod in ipairs(s.active) do
if mod.init then
mod.init(self, s.mselfs[mod])
end
end
end
function M.update(self, dt)
local s = self.script_modules
for i,mod in ipairs(s.active) do
if mod.update then
mod.update(self, dt, s.mselfs[mod])
end
end
end
function M.on_message(self, message_id, message, sender)
local s = self.script_modules
for i,mod in ipairs(s.active) do
if mod.on_message then
mod.on_message(self, message_id, message, sender, s.mselfs[mod])
end
end
end
function M.on_input(self, action_id, action)
local s = self.script_modules
for i,mod in ipairs(s.active) do
if mod.on_input then
mod.on_input(self, action_id, action, s.mselfs[mod])
end
end
end
function M.final(self, mself)
local s = self.script_modules
for i,mod in ipairs(s.active) do
if mod.final then
mod.final(self, s.mselfs[mod])
end
end
end
return M