Hi here!
I would need suggestions about clean code and refactoring, can’t find a proper way.
I have an enemy.go along with its enemy.script which holds the basic common stuff (update, movement, etc.) along with 3 different types of AI. I find it a bit messy that the script is already 250+ lines long so I decided to extract the different AI logics into seperate scripts to make it more clear. However if I call out to another script by msg.post I will only get it done by the next frame.
I was looking for something similar like in Java to call out and continue the flow after the job is done.
Could you please suggest?
Below is the code snippet:
function update(self, dt)
-- choose destination depending on AI type
if not self.isMoving and not self.isTurnFinished then
if self.aiType == hash("random_walker") then
randomWalkerAi(self)
elseif self.aiType == hash("tracker") then
usingTrackerAi(self)
elseif self.aiType == hash("artifact_guarder") then
usingArtifactGuarderAi(self)
end
end
-- actual movement update
if self.isMoving then
...
end
end
Thank you for the prompt reply.
I’m trying to grasp the idea but still not clear
As I understand it returns a function but how can I get back for example a table?
I would have the following in the module:
return function()
local pool = {}
local x = go.get_position().x
local y = go.get_position().y
if wallPositions[x + (y + TILE_SIZE) * TILE_SIZE] == nil and y < (LAST_INNER_ROW - 1) * TILE_SIZE then
table.insert(pool, "UP")
end
if wallPositions[x + (y - TILE_SIZE) * TILE_SIZE] == nil and y > FIRST_INNER_ROW * TILE_SIZE then
table.insert(pool, "DOWN")
end
if wallPositions[(x - TILE_SIZE) + y * TILE_SIZE] == nil and x > FIRST_INNER_COL * TILE_SIZE then
table.insert(pool, "LEFT")
end
if wallPositions[(x + TILE_SIZE) + y * TILE_SIZE] == nil and x < (LAST_INNER_COL - 1) * TILE_SIZE then
table.insert(pool, "RIGHT")
end
if #pool == 0 then
table.insert(pool, "PASS")
end
return pool
end
What I would like to get back is the pool.
if self.aiType == hash("random_walker") then
self.selectionPool = require("main.enemies.ai.random_walker")
My two cents. First off, 250 lines is not a lot. Dividing your code into a lot of little chunks can just as easily be “messy” and hard to sort through. For larger files, use the search function to navigate rather than trying to scroll with the mouse or page-up/down.
If you’re going to use these AI functions in multiple enemy scripts, then a module is the way to go. If you’re only going to have one enemy script for all your enemies, then there’s no real point to separate them…but if things feel tidier for you that way, go for it.
Don’t forget that Lua treats functions like any other value, so you can put them in tables. This is how I would do it:
-- Pre-hashing names is good for performance,
-- especially if you use them in update or on_message.
local RANDOM_WALKER = hash("random_walker")
local TRACKER = hash("tracker")
local ARTIFACT_GUARDER = hash("artifact_guarder")
local AI_UPDATE = {
[RANDOM_WALKER] = randomWalkerAi,
[TRACKER] = usingTrackerAi,
[ARTIFACT_GUARDER] = usingArtifactGuarderAi,
}
local function test_update(self, dt)
-- choose destination depending on AI type
if not self.isMoving and not self.isTurnFinished then
AI_UPDATE[self.aiType](self)
end
-- actual movement update
if self.isMoving then
-- ...
end
end