--# Main function setup() displayMode(FULLSCREEN) s = Scene() e = Editor(64) s:add(Background()) s:add(e) s:add(DPad(e)) s:add(SelectTile(e)) end function draw() background(40, 40, 50) s:draw() end function touched(touch) s:touched(touch) end --# Scene Scene = class() function Scene:init() self.layers = {} end function Scene:add(l) table.insert(self.layers, l) end function Scene:draw() for i,l in ipairs(self.layers) do l:draw() end end function Scene:touched(touch) -- call touch function from top layer and down until one returns true for i = #self.layers, 1,-1 do local l = self.layers[i] if l.touched ~= nil then if l:touched(touch) == true then return end end end end --# Editor Editor = class() function Editor:init(tilesize) self.w = tilesize self.tile = 1 -- tile to draw -- camera position self.c = vec2(0,0) self.grid = Grid(self.w) self.level = Level("level",self.w) end function Editor:draw() pushMatrix() translate(self.c.x, self.c.y) self.grid:draw(self.c) self.level:draw() popMatrix() end function Editor:move(delta) self.c = self.c - delta end function Editor:selectTile(i) self.tile = i end function Editor:touched(touch) if touch.state == ENDED then local t = vec2(touch.x, touch.y) - self.c local tx,ty = math.floor(t.x/self.w), math.floor(t.y/self.w) self.level:toggle(tx,ty,self.tile) end end --# Grid -- Draw a dashed grid that follows the camera Grid = class() function Grid:init(tilesize) local w = tilesize local mx = math.ceil(math.max(WIDTH, HEIGHT)/w) * w -- create dashed grid image self.dash = image(w,w) setContext(self.dash) stroke(0, 0, 0, 255) strokeWidth(3) for d = 10,w,10 do line(d-5,0,d,0) line(0,d-5,0,d) end setContext() self.m = mesh() self.m.texture = self.dash for y = -mx,mx,w do for x = -mx,mx,w do self.m:addRect(x-w/2,y-w/2,w,w) end end self.mx = mx end function Grid:draw(camera) local dx = math.floor(camera.x /self.mx) local dy = math.floor(camera.y /self.mx) pushMatrix() -- move dashed grid so its always in view translate(-dx*self.mx, -dy*self.mx) self.m:draw() popMatrix() end --# Background -- animated background Background = class() function Background:init(x) local m = mesh() m.shader = makePattern() m.texture = readImage(asset.builtin.Blocks.Lava) m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT) m:setRectTex(1,0,0,5,5) self.m = m end function Background:draw() self.m.shader.time = ElapsedTime; self.m:draw() end function makePattern() pattern = shader() pattern.vertexProgram = [[ uniform mat4 modelViewProjection; attribute vec4 position; attribute vec2 texCoord; varying highp vec2 vTexCoord; void main() { vTexCoord = texCoord; gl_Position = modelViewProjection * position; } ]] pattern.fragmentProgram = [[ uniform lowp sampler2D texture; uniform highp float time; varying highp vec2 vTexCoord; void main() { lowp vec4 col = texture2D(texture, mod(vTexCoord+vec2(time,0), vec2(1.,1.))); gl_FragColor = col; } ]] return pattern end --# DPad -- joystick to control the camera DPad = class() function DPad:init(listener) local w, hw = 128, 64 self.pad = image(w,w) setContext(self.pad) fill(219, 183, 183, 185) stroke(0, 0, 0, 194) strokeWidth(3) ellipse(w/2,w/2,w) setContext() self.m = mesh() self.m.texture = self.pad self.m:addRect(hw,hw,w,w) self.mc = mesh() self.mc.texture = self.pad self.mc:addRect(0,0,hw,hw) self.w, self.hw = w, hw self.c = vec2(hw,hw) self.pressed = false self.cb = listener end function DPad:draw() self.m:draw() pushMatrix() translate(self.c.x, self.c.y) self.mc:draw() popMatrix() if self.pressed and self.cb then self.cb:move(self.dv*DeltaTime*15) end end function DPad:touched(touch) local hw = self.hw if touch.state == ENDED and self.pressed then self.pressed = false tween(.1,self.c,{x=hw,y=hw}) return true end local t = vec2(touch.x, touch.y) local c = vec2(hw,hw) local dist = t:dist(c) if (dist < hw and touch.state == BEGAN) or self.pressed then local v = (t - c):normalize() * math.min(hw/2, dist) self.c = c + v self.dv = v self.pressed = true return true end end --# Level -- Model for a tilemap level -- the level is stored in a Table -- where the coordinate x,y is -- encoded as x + y*mapsize and -- x,y=0,0 is in the center of the map -- The level is drawn with one mesh for every available tile, could be replaced with a sprite atlas Level = class() function Level:init(name,tilesize) self.name=name self.size=1024 self.w=tilesize self.data = {} self:load(name) self:update() end function Level:toggle(x,y,tile) local key=self:encode(x,y) local d = self.data if d[key] ~= tile then d[key]=tile else d[key]=nil end self:update() self:save() end function Level:encode(x,y) -- x,y as a key in the array local s=self.size return (x+s/2) + (y+s/2)*s end function Level:decode(index) local s=self.size local x = index%s local y = (index - x)/s return x-s/2,y-s/2 end function Level:draw() for i,v in ipairs(self.ts) do v:draw() end end -- recreate the meshes on update function Level:update() local w = self.w local hw = w/2 self.ts = {} for i,v in ipairs(atlas) do local m = mesh() m.texture = v table.insert(self.ts, m) end for index,v in pairs(self.data) do local x,y=self:decode(index) self.ts[v]:addRect(x*w+hw, y*w+hw,w,w) end end function Level:save() local js = json.encode(self.data) saveProjectData(self.name,js) end function Level:load(name) local js=readProjectData(name) if js ~= nil then self.data ={} -- need to transform keys to int for i,v in pairs(json.decode(js)) do self.data[tonumber(i)]=v end end end -- The available tiles atlas = { readImage(asset.builtin.Blocks.Redstone), readImage(asset.builtin.Blocks.Dirt_Grass), readImage(asset.builtin.Blocks.Wood), readImage(asset.builtin.Blocks.Greystone_Sand), readImage(asset.builtin.Blocks.Leaves) } --# SelectTile -- ui to select tile to draw with SelectTile = class() function SelectTile:init(cb) self.curr = 1 self.cb = cb end function SelectTile:draw() local w=64*#atlas rectMode(CORNER) pushMatrix() translate(WIDTH/2-w/2,HEIGHT-64) fill(61, 124) rect(0,0,w,64) fill(255, 150) rect(self.curr*64-64,0,64,64) for i,v in ipairs(atlas) do sprite(v,-32+i*64,32,50,50) end popMatrix() end function SelectTile:touched(touch) local w = 64*#atlas local t = vec2(touch.x,touch.y) if t.y > HEIGHT- 64 then for i,v in ipairs(atlas) do local x,y = WIDTH/2-w/2,HEIGHT-32 x=x-32+i*64 if vec2(x,y):dist(t)<32 then self.curr = i self.cb:selectTile(i) return true end end end end