When I tried to run some functions like terrain generating. Because it need a long time to finish, so I have to face a black screen without any information appear. This is because of the mechanism of Codea
:app
when
setup()
is running, thedraw()
won't run, whensetup()
is finished, it will run thedraw()
. But these functions must run once, so they need to be put intosetup()
.this
When I use these functions in setup()
and I want to see some information drawing on the screen, how can I do?spa
I found coroutine
can deal with this problem, then I began to learn it.code
I am learning and writing a Threads
class. Using it we can solve the problem.orm
The class codeip
--# Threads Threads = class() function Threads:init() self.threads = {} self.taskList = {} self.time = os.clock() self.timeTick = 0.01 self.taskID = 1 self.taskStatus = {} self.taskVT = {} self.img = image(100,100) end -- Add task function into the task list function Threads:addTaskToList(task) local t = function() task() end table.insert(self.taskList, t) end -- Create coroutine for all task, run once function Threads:job() local n = #self.taskList for id = 1, n do -- local f = function () self.taskList[id]() end local f = function () self:taskUnit(id) end local co = coroutine.create(f) table.insert(self.threads, co) -- Record all tasks' status, now should be suspended self.taskStatus[id] = coroutine.status(co) end end -- Task unit function Threads:taskUnit(id) self.taskID = id self.taskList[id]() -- self:switchPoint(id) -- self.taskStatus[id] = "Finished" end -- Switch point, put this function into the task function function Threads:switchPoint(id) -- Switch task,when its timetick is used and task have not finished if (os.clock() - self.time) >= self.timeTick then -- Debug info, do not put the print into the task function print("hello: No."..id.." is "..self.taskStatus[id]) -- self:visual(id) -- reset task time self.time = os.clock() -- Pause the task coroutine.yield() end end -- Put this function into draw() function Threads:dispatch() local n = #self.threads if n == 0 then return end for i = 1, n do -- Record the current task self.taskID = i local status = coroutine.resume(self.threads[i]) -- Record all the tasks' status self.taskStatus[i] = coroutine.status(self.threads[i]) -- If task finished, then remove it from self.threads, and return if not status then self.taskStatus[i] = "Finished" table.remove(self.threads, i) -- table.remove(self.taskList,i) return end end end --[[ function Threads:visual(id) local n = #self.taskList local vt = {} background(18, 16, 16, 255) setContext(self.img) pushStyle() strokeWidth(1) fill(255, 211, 0, 255) -- if self.taskID == 1 then fill(241, 7, 7, 255) else fill(255, 211, 0, 255) end local w,h = self.img.width/n, self.img.height/n local x,y = 0,0 for i = 1, n do vt[i] = function () rect(100+x+(i-1)*w,100+y+(i-1)*h,w,h) end end popStyle() setContext() -- sprite(self.img,300,300) -- vt[self.taskID]() print("id: "..id) vt[id]() end --]]
The test code:rem
--# Main function setup() print("Thread testing ...") myT = Threads() myT.timeTick = 1/60 myT:addTaskToList(tt) myT:addTaskToList(oo) myT:addTaskToList(mf) myT:addTaskToList(pk) --[[ myT.taskList[2]() --]] myT:job() print(unpack(myT.taskList)) end function draw() background(0) myT:dispatch() fill(244, 27, 27, 255) print("2: "..myT.taskStatus[1]) print("length: "..#myT.taskList) sysInfo() end -- The task functions function tt () while true do -- print("tt: "..os.clock()) myT:switchPoint(myT.taskID) end end function oo () while true do -- print("oo: "..os.clock()) myT:switchPoint(myT.taskID) end end function mf () local k = 0 for i=1,10000000 do k = k + i -- print("mf: "..k) -- If the time >= timeTick then pause myT:switchPoint(myT.taskID) end end function pk () local k = 0 for i=1,10000000 do k = k + i -- print("pk: "..k) -- If the time >= timeTick then pause myT:switchPoint(myT.taskID) end end
Assume we have a function createTerrain()
to generate the terrain in setup()
, and it will take a long time, we can do like below:it
function setup() myT = Threads() myT.timeTick = 1/60 myT:addTaskToList(createTerrain) myT:job() end function draw() background(0) myT:dispatch() drawLoadingScreen() end -- This is the loading screen function drawLoadingScreen() ... -- Show some information text("Creating the terrain, please wait...") ... end -- The function will run for a long time function createTerrain() ... for i=1,10000 do for j= 1, 100000 do ... -- Put the switchPoint() here myT:switchPoint(myT.taskID) end end ... for i=1,200000 do for j= 1, 300000 do ... -- Put the switchPoint() here myT:switchPoint(myT.taskID) end end ... end
If the function is a method of a class, like Map:createTerrain()
, we can load it like below:io
myT:addTaskToList(function () Map:createTerrain() end)
OK, it is all.table
BTW. This is for the newbie, if you are experienced, please ignore it. :)