Difference between revisions of "Tuto tiny-ecs 2d platformer Part 4 LevelX"
| (One intermediate revision by the same user not shown) | |||
| Line 6: | Line 6: | ||
The LevelX scene holds the game loop and controls the flow of each levels. | The LevelX scene holds the game loop and controls the flow of each levels. | ||
| − | When the scene loads, it constructs the level | + | When the scene loads, it constructs the level into layers, more on that in the code comments below ;-) |
The code is not that long given it takes care of all levels of the game: level 1, 2 and 3. To make it easy to follow and debug, I use '''FIGlet''' (https://en.wikipedia.org/wiki/FIGlet). | The code is not that long given it takes care of all levels of the game: level 1, 2 and 3. To make it easy to follow and debug, I use '''FIGlet''' (https://en.wikipedia.org/wiki/FIGlet). | ||
| Line 241: | Line 241: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
| − | === LevelX:init | + | == LevelX Code comments == |
| + | === LevelX:init === | ||
==== plugins ==== | ==== plugins ==== | ||
Here we initialize our plugins. tiny-ecs is declared as a Class variable (''self.tiny'') because we use it outside the ''init'' function. Bump can be local as we only use it in the ''init'' function. | Here we initialize our plugins. tiny-ecs is declared as a Class variable (''self.tiny'') because we use it outside the ''init'' function. Bump can be local as we only use it in the ''init'' function. | ||
| − | It is worth noting that ''tiny.tworld'' is attached to ''self.tiny'' variable. This makes it | + | It is worth noting that ''tiny.tworld'' is attached to ''self.tiny'' variable. This makes it easier to access through the rest of the code in our project. |
==== layers ==== | ==== layers ==== | ||
| Line 257: | Line 258: | ||
We use '''Tiled''' to build our levels (https://www.mapeditor.org/) and we call the ''TiledLevels Class'' to manage the parsing of the Tiled elements in our game. | We use '''Tiled''' to build our levels (https://www.mapeditor.org/) and we call the ''TiledLevels Class'' to manage the parsing of the Tiled elements in our game. | ||
| − | '''We dedicate an entire folder for Tiled in our project and we | + | '''We dedicate an entire folder for Tiled in our project and we talk more about it down below''' |
The ''mapdef'' is a table which has the map definition (dimensions), so we can pass it to functions that will require the size of the map for calculations. | The ''mapdef'' is a table which has the map definition (dimensions), so we can pass it to functions that will require the size of the map for calculations. | ||
| Line 272: | Line 273: | ||
We use a camera in our game and ''camfollowoffsety'' will offset the camera following the player. It is easier to change it being a variable. | We use a camera in our game and ''camfollowoffsety'' will offset the camera following the player. It is easier to change it being a variable. | ||
| − | We use a slightly modified version of '''MultiPain''''s (aka rrraptor on Gideros forum) '''GCam''' Class for our camera. | + | We use a slightly modified version of '''MultiPain''''s (aka rrraptor on Gideros forum) '''GCam''' Class for our camera. We pass it the ''mainlayer'' as the content and the player1 to follow. |
| − | |||
| − | |||
| − | |||
| − | We pass the ''mainlayer'' as the content and the player1 to follow. | ||
Using the map definition we set the camera bounds. We also set the soft and the dead size parameters and we tell it to follow the player. | Using the map definition we set the camera bounds. We also set the soft and the dead size parameters and we tell it to follow the player. | ||
| Line 305: | Line 302: | ||
== TiledLevels Class == | == TiledLevels Class == | ||
| + | To better organise the game, I put all files related to '''Tiled''' in a '''tiled''' folder. | ||
| + | |||
Let's have a look at how we construct our levels. | Let's have a look at how we construct our levels. | ||
| − | + | In Tiled we add many layers, in our case '''Object Layers'''. Each layers have a type and a name (eg. ''physics_ptpfs'', ''physics_coins'', ''bg_deco_images'', ...). In each layers we draw the level using shapes (rectangles, ellipses, ...). Shapes are also used to position the actors in the level, like the player, the enemies, moving platforms, ... | |
| + | |||
| + | Once the level is done in Tiled, we export it as a '''lua''' file Gideros will use (read the layers name, the shapes, the color, the position, ...). | ||
| + | |||
| + | === TiledLevels Class Code comments === | ||
| + | The very first thing we do is include the Tiled lua file. I also set up some variables to check if we are testing a prototype level and if we are in development mode. | ||
| + | |||
| + | ==== Tileset ==== | ||
| + | This is the classic: a tileset with a tilemap image ('''Tile Layer''' in Tiled). We don't use tilsets in this game but feel free to do so. The code extracts the tilemap info: number of columns, rows, tile id (gid) and texture. | ||
| + | |||
| + | ==== Tileset images ==== | ||
| + | Instead of tilesets we use single images to make our levels. Here we read the gid and the image path and store them in tables. The tables are then turned into '''[[TexturePack]]''' so the game runs faster. | ||
| + | |||
| + | ==== Build Level ==== | ||
| + | To build a level, we iterate all the tilemap layers according to their '''type''' and their '''name'''. We either build shapes the '''physics world''' can use or we add images to decorate the level using the ''parseImage'' function. | ||
| + | |||
| + | '''note''': we don't use Tiled ''Tile Layer'' in this game but you can, Build Level takes care of it for you in ''layer.type == "tilelayer"'' | ||
| + | |||
| + | And this is the code that reads the Tiled lua file to build our levels: | ||
| + | <syntaxhighlight lang="lua"> | ||
| + | TiledLevels = Core.class(Sprite) | ||
| + | |||
| + | function TiledLevels:init(tmpath, xtiny, xbworld, xlayers) | ||
| + | local tm = require(tmpath) -- eg.: "tiled/test" without ".lua" extension + exclude from execution! | ||
| + | local proto = false | ||
| + | if tmpath:find("proto") then | ||
| + | proto = true | ||
| + | end | ||
| + | -- some functions | ||
| + | local function rgb2hex(r, g, b) | ||
| + | return (r << 16) + (g << 8) + b | ||
| + | end | ||
| + | local dev = false -- true, false | ||
| + | local tsimgpath -- "tiled/", root path to tileset tilemap images | ||
| + | if dev then -- in development | ||
| + | tsimgpath = -- path to external folder | ||
| + | "C:/X_MOKA/PROJECTS/_gideros/WIKI/_assets/tuto_pf_cbump_tecs/" | ||
| + | else -- prod | ||
| + | if g_currlevel == 1 then tsimgpath = "tiled/lvl001/" | ||
| + | elseif g_currlevel == 2 then tsimgpath = "tiled/lvl001/" | ||
| + | elseif g_currlevel == 3 then tsimgpath = "tiled/lvl002/" | ||
| + | end | ||
| + | end | ||
| + | -- _______ _____ _ ______ _____ ______ _______ | ||
| + | --|__ __|_ _| | | ____|/ ____| ____|__ __| | ||
| + | -- | | | | | | | |__ | (___ | |__ | | | ||
| + | -- | | | | | | | __| \___ \| __| | | | ||
| + | -- | | _| |_| |____| |____ ____) | |____ | | | ||
| + | -- |_| |_____|______|______|_____/|______| |_| | ||
| + | -- this is the classic: a tileset with a tilemap image | ||
| + | for i = 1, #tm.tilesets do -- important | ||
| + | local tileset = tm.tilesets[i] | ||
| + | -- add extra values (variables) to a tm.tilesets[i] table | ||
| + | if tileset.image then -- only tileset tilemap layers | ||
| + | tileset.numcols = math.floor( | ||
| + | (tileset.imagewidth-tileset.margin+tileset.spacing)/ | ||
| + | (tileset.tilewidth+tileset.spacing) | ||
| + | ) | ||
| + | tileset.numrows = math.floor( | ||
| + | (tileset.imageheight-tileset.margin+tileset.spacing)/ | ||
| + | (tileset.tileheight+tileset.spacing) | ||
| + | ) | ||
| + | tileset.lastgid = tileset.firstgid+(tileset.numcols*tileset.numrows)-1 | ||
| + | tileset.texture = Texture.new( | ||
| + | tsimgpath..tileset.image, false, | ||
| + | { transparentColor=tonumber(tileset.transparentcolor), } | ||
| + | ) | ||
| + | end | ||
| + | end | ||
| + | -- tileset function | ||
| + | local function gid2tileset(tm, gid) | ||
| + | for i = 1, #tm.tilesets do | ||
| + | local tileset = tm.tilesets[i] | ||
| + | if tileset.image then -- only valid tileset layers | ||
| + | if tileset.firstgid <= gid and gid <= tileset.lastgid then | ||
| + | return tileset | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | -- _______ _____ _ ______ _____ ______ _______ | ||
| + | --|__ __|_ _| | | ____|/ ____| ____|__ __| | ||
| + | -- | | | | | | | |__ | (___ | |__ | | | ||
| + | -- | | | | | | | __| \___ \| __| | | | ||
| + | -- | | _| |_| |____| |____ ____) | |____ | | | ||
| + | -- |_| |_____|______|______|_____/|______| |_| | ||
| + | -- _____ __ __ _____ ______ _____ | ||
| + | --|_ _| \/ | /\ / ____| ____|/ ____| | ||
| + | -- | | | \ / | / \ | | __| |__ | (___ | ||
| + | -- | | | |\/| | / /\ \| | |_ | __| \___ \ | ||
| + | -- _| |_| | | |/ ____ \ |__| | |____ ____) | | ||
| + | --|_____|_| |_/_/ \_\_____|______|_____/ | ||
| + | -- this one parses individual images stored in a tileset | ||
| + | local tilesetimages = {} -- table holding all the tileset images info (path, width, height) | ||
| + | for i = 1, #tm.tilesets do | ||
| + | local tileset = tm.tilesets[i] | ||
| + | if not tileset.image then -- filter out tileset tilemap layers, only tileset images | ||
| + | local tiles = tileset.tiles | ||
| + | for j = 1, #tiles do | ||
| + | -- populate the tilesetimages table based on the tile gid and id | ||
| + | -- note: you may have to adjust the path to point to the image folder | ||
| + | tilesetimages[tileset.firstgid + tiles[j].id] = { | ||
| + | path=tsimgpath..tiles[j].image, | ||
| + | width=tiles[j].width, | ||
| + | height=tiles[j].height, | ||
| + | } | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | -- pack all the deco images (TexturePack), perfs? XXX | ||
| + | local bgtpt = {} -- background texture pack table | ||
| + | local bgtptx = {} -- background texture pack table x | ||
| + | local fgtpt = {} -- foreground texture pack table | ||
| + | local fgtptx = {} -- foreground texture pack table x | ||
| + | for i = 1, #tm.layers do | ||
| + | local layer = tm.layers[i] | ||
| + | if layer.name:match("bg_deco_images") then | ||
| + | if layer.name:match("x$") then -- x$ = string ends with x | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | -- print("bg", tilesetimages[layer.objects[i].gid].path) | ||
| + | bgtptx[#bgtptx+1] = tilesetimages[layer.objects[i].gid].path | ||
| + | end | ||
| + | end | ||
| + | else | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | -- print("bg", tilesetimages[layer.objects[i].gid].path) | ||
| + | bgtpt[#bgtpt+1] = tilesetimages[layer.objects[i].gid].path | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | elseif layer.name:match("fg_deco_images") then | ||
| + | if layer.name:match("x$") then -- x$ = string ends with x | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | -- print("fg", tilesetimages[layer.objects[i].gid].path) | ||
| + | fgtptx[#fgtptx+1] = tilesetimages[layer.objects[i].gid].path | ||
| + | end | ||
| + | end | ||
| + | else | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | -- print("fg", tilesetimages[layer.objects[i].gid].path) | ||
| + | fgtpt[#fgtpt+1] = tilesetimages[layer.objects[i].gid].path | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | local bgtp = TexturePack.new(bgtpt, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs | ||
| + | local bgtpx = TexturePack.new(bgtptx, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs | ||
| + | local fgtp = TexturePack.new(fgtpt, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs | ||
| + | local fgtpx = TexturePack.new(fgtptx, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs | ||
| + | if #bgtp:getRegionsNames() > 0 then print("bgtp", bgtp:getSize()) end | ||
| + | if #bgtpx:getRegionsNames() > 0 then print("bgtpx", bgtpx:getSize()) end | ||
| + | if #fgtp:getRegionsNames() > 0 then print("fgtp", fgtp:getSize()) end | ||
| + | if #fgtpx:getRegionsNames() > 0 then print("fgtpx", fgtpx:getSize()) end | ||
| + | -- ____ _ _ _____ _ _____ _ ________ ________ _ | ||
| + | --| _ \| | | |_ _| | | __ \ | | | ____\ \ / / ____| | | ||
| + | --| |_) | | | | | | | | | | | | | | | |__ \ \ / /| |__ | | | ||
| + | --| _ <| | | | | | | | | | | | | | | __| \ \/ / | __| | | | ||
| + | --| |_) | |__| |_| |_| |____| |__| | | |____| |____ \ / | |____| |____ | ||
| + | --|____/ \____/|_____|______|_____/ |______|______| \/ |______|______| | ||
| + | -- tileset images function | ||
| + | local function parseImage(xobject, xlayer, xtplayer, xtexpack) | ||
| + | local bitmap | ||
| + | if xtplayer == "bg" then | ||
| + | if xtexpack == "x" then | ||
| + | bitmap = Bitmap.new(bgtpx:getTextureRegion(tilesetimages[xobject.gid].path)) | ||
| + | else | ||
| + | bitmap = Bitmap.new(bgtp:getTextureRegion(tilesetimages[xobject.gid].path)) | ||
| + | end | ||
| + | else -- fg | ||
| + | if xtexpack == "x" then | ||
| + | bitmap = Bitmap.new(fgtpx:getTextureRegion(tilesetimages[xobject.gid].path)) | ||
| + | else | ||
| + | bitmap = Bitmap.new(fgtp:getTextureRegion(tilesetimages[xobject.gid].path)) | ||
| + | end | ||
| + | end | ||
| + | bitmap:setAnchorPoint(0, 1) -- because I always forget to modify Tiled objects alignment | ||
| + | -- supports Tiled image scaling | ||
| + | local scalex, scaley = xobject.width/bitmap:getWidth(), xobject.height/bitmap:getHeight() | ||
| + | bitmap:setScale(scalex, scaley) | ||
| + | bitmap:setRotation(xobject.rotation) | ||
| + | bitmap:setPosition(xobject.x, xobject.y) | ||
| + | xlayer:addChild(bitmap) | ||
| + | end | ||
| + | for i = 1, #tm.layers do | ||
| + | local layer = tm.layers[i] | ||
| + | local tilemaps = {} | ||
| + | local group -- group = Sprite.new() | ||
| + | -- _______ _____ _ ______ _ __ ________ _____ | ||
| + | --|__ __|_ _| | | ____| | /\\ \ / / ____| __ \ | ||
| + | -- | | | | | | | |__ | | / \\ \_/ /| |__ | |__) | | ||
| + | -- | | | | | | | __| | | / /\ \\ / | __| | _ / | ||
| + | -- | | _| |_| |____| |____| |____ / ____ \| | | |____| | \ \ | ||
| + | -- |_| |_____|______|______|______/_/ \_\_| |______|_| \_\ | ||
| + | if layer.type == "tilelayer" and (layer.name:match("bg") or layer.name:match("fg")) then | ||
| + | if layer.name:match("bg") then group = xlayers["bg"] | ||
| + | else group = xlayers["fg"] | ||
| + | end | ||
| + | for y = 1, layer.height do | ||
| + | for x = 1, layer.width do | ||
| + | local index = x + (y - 1) * layer.width | ||
| + | local gid = layer.data[index] | ||
| + | local gidtileset = gid2tileset(tm, gid) | ||
| + | if gidtileset then | ||
| + | local tilemap | ||
| + | if tilemaps[gidtileset] then | ||
| + | tilemap = tilemaps[gidtileset] | ||
| + | else | ||
| + | tilemap = TileMap.new( | ||
| + | layer.width, layer.height, | ||
| + | gidtileset.texture, gidtileset.tilewidth, gidtileset.tileheight, | ||
| + | gidtileset.spacing, gidtileset.spacing, | ||
| + | gidtileset.margin, gidtileset.margin, | ||
| + | tm.tilewidth, tm.tileheight | ||
| + | ) | ||
| + | tilemaps[gidtileset] = tilemap | ||
| + | group:addChild(tilemap) | ||
| + | end | ||
| + | local tx = (gid - gidtileset.firstgid) % gidtileset.numcols + 1 | ||
| + | local ty = math.floor((gid - gidtileset.firstgid) / gidtileset.numcols) + 1 | ||
| + | -- set the tile with flip info | ||
| + | tilemap:setTile(x, y, tx, ty) | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | group:setAlpha(layer.opacity) | ||
| + | -- ____ ____ _ ______ _____ _______ _ __ ________ _____ | ||
| + | -- / __ \| _ \ | | ____/ ____|__ __| | /\\ \ / / ____| __ \ | ||
| + | --| | | | |_) | | | |__ | | | | | | / \\ \_/ /| |__ | |__) | | ||
| + | --| | | | _ < _ | | __|| | | | | | / /\ \\ / | __| | _ / | ||
| + | --| |__| | |_) | |__| | |___| |____ | | | |____ / ____ \| | | |____| | \ \ | ||
| + | -- \____/|____/ \____/|______\_____| |_| |______/_/ \_\_| |______|_| \_\ | ||
| + | elseif layer.type == "objectgroup" then | ||
| + | local o | ||
| + | local myshape, mytable | ||
| + | -- _ __ _ _ _ _ | ||
| + | -- | | / _(_) (_) | (_) | ||
| + | -- _ __ ___ __ _ _ __ __| | ___| |_ _ _ __ _| |_ _ ___ _ __ | ||
| + | --| '_ ` _ \ / _` | '_ \ / _` |/ _ \ _| | '_ \| | __| |/ _ \| '_ \ | ||
| + | --| | | | | | (_| | |_) | | (_| | __/ | | | | | | | |_| | (_) | | | | | ||
| + | --|_| |_| |_|\__,_| .__/ \__,_|\___|_| |_|_| |_|_|\__|_|\___/|_| |_| | ||
| + | -- | | | ||
| + | -- |_| | ||
| + | if layer.name == "physics_map_def" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | self.mapdef = {} | ||
| + | self.mapdef.t = o.y | ||
| + | self.mapdef.l = o.x | ||
| + | self.mapdef.r = o.width | ||
| + | self.mapdef.b = o.height | ||
| + | end | ||
| + | -- _ _ | ||
| + | -- | | | | | ||
| + | -- __| | ___ ___ ___ | | __ _ _ _ ___ _ __ ___ | ||
| + | -- / _` |/ _ \/ __/ _ \ | |/ _` | | | |/ _ \ '__/ __| | ||
| + | --| (_| | __/ (_| (_) | | | (_| | |_| | __/ | \__ \ | ||
| + | -- \__,_|\___|\___\___/ |_|\__,_|\__, |\___|_| |___/ | ||
| + | -- __/ | | ||
| + | -- |___/ | ||
| + | elseif layer.name == "bg_deco_texts" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(o.color or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | elseif layer.name:match("bg_deco_shapes") then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local texpath | ||
| + | local color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) -- math.random(0xffffff) | ||
| + | local r, g, b | ||
| + | if o.name == "hint" then | ||
| + | color = 0xffffff | ||
| + | elseif o.name == "lava" then | ||
| + | texpath = "gfx/textures/Water_01_Grey_1.png" | ||
| + | color = nil | ||
| + | r, g, b = 124, 205, 70 | ||
| + | elseif o.name == "spikes" then | ||
| + | color = 0xff5500 | ||
| + | end | ||
| + | mytable = { | ||
| + | texpath=texpath, istexpot=true, scalex=0.4, | ||
| + | color=color, r=r, g=g, b=b, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | elseif layer.name:match("bg_deco_images") then | ||
| + | --parseImage(xobject, xlayer, xtplayer, xtexpack) | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.name:match("x$") then | ||
| + | parseImage(layer.objects[i], xlayers["bg"], "bg", "x") | ||
| + | else | ||
| + | parseImage(layer.objects[i], xlayers["bg"], "bg", nil) | ||
| + | end | ||
| + | end | ||
| + | elseif layer.name == "fg_deco_texts" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(o.color or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["fg"]:addChild(myshape) | ||
| + | end | ||
| + | elseif layer.name:match("fg_deco_shapes") then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["fg"]:addChild(myshape) | ||
| + | end | ||
| + | elseif layer.name:match("fg_deco_images") then | ||
| + | for i = 1, #layer.objects do | ||
| + | if layer.name:match("x$") then | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | parseImage(layer.objects[i], xlayers["fg"], "fg", "x") | ||
| + | end | ||
| + | else | ||
| + | if layer.objects[i].gid then -- if no gid then this is not an image, skip it! | ||
| + | parseImage(layer.objects[i], xlayers["fg"], "fg", nil) | ||
| + | end | ||
| + | end | ||
| + | end | ||
| + | -- _ _ _ | ||
| + | -- | | (_) | | | ||
| + | -- _ __ | |__ _ _ ___ _ ___ ___ | | __ _ _ _ ___ _ __ ___ | ||
| + | --| '_ \| '_ \| | | / __| |/ __/ __| | |/ _` | | | |/ _ \ '__/ __| | ||
| + | --| |_) | | | | |_| \__ \ | (__\__ \ | | (_| | |_| | __/ | \__ \ | ||
| + | --| .__/|_| |_|\__, |___/_|\___|___/ |_|\__,_|\__, |\___|_| |___/ | ||
| + | --| | __/ | __/ | | ||
| + | --|_| |___/ |___/ | ||
| + | -- _ _ | ||
| + | -- | | | | | ||
| + | --__ _____ _ __| | __| | | ||
| + | --\ \ /\ / / _ \| '__| |/ _` | | ||
| + | -- \ V V / (_) | | | | (_| | | ||
| + | -- \_/\_/ \___/|_| |_|\__,_| | ||
| + | elseif layer.name == "physics_exits" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isexit = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_voids" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isvoid = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_spikes" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isspike = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_springs" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local vx, vy | ||
| + | local index | ||
| + | index = o.name:find("vx") -- index vx | ||
| + | vx = tonumber(o.name:sub(index):match("%d+")) -- find vxNNN | ||
| + | index = o.name:find("vy") -- index vy | ||
| + | vy = tonumber(o.name:sub(index):match("%d+")) -- find vyNNN | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isspring = true | ||
| + | o.vx = vx*64 -- some multiplier, magik XXX | ||
| + | o.vy = vy*64 -- some multiplier, magik XXX | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_sensors" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local xid = o.name -- examples: "mvgrAA", ... | ||
| + | local opos = vector(o.x, o.y) | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | alpha=0.25, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | --ESensor:init(xid, xspritelayer, xpos, w, h, xlayers["bgfx"]) | ||
| + | local e = ESensor.new( | ||
| + | xid, xlayers["actors"], opos, o.width, o.height, xlayers["bgfx"] | ||
| + | ) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | -- some cleaning? | ||
| + | e = nil | ||
| + | end | ||
| + | elseif layer.name:match("physics_doors") then -- doors = doorAAdx0dy8vel10dirU | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local xpos = vector(o.x, o.y) | ||
| + | local dx, dy, vel, xdir | ||
| + | local index | ||
| + | index = o.name:find("dx") -- index dx | ||
| + | local xid = o.name:sub(1, index-1) -- examples: "doorAA", "doorBB", ... | ||
| + | dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN | ||
| + | index = o.name:find("dy") -- index dy | ||
| + | dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN | ||
| + | index = o.name:find("vel") -- index vel | ||
| + | vel = tonumber(o.name:sub(index):match("%d+")) -- find velNN | ||
| + | index = o.name:find("dir") -- index dir | ||
| + | xdir = o.name:sub(index+3) -- initial heading direction, examples: "U", "DR", "UL", ... | ||
| + | local angle = math.atan2(dy, dx) | ||
| + | local xspeed = vector(vel*math.cos(angle), vel*math.sin(angle))*vector(8, 8) | ||
| + | local xtexpath | ||
| + | local xcolor | ||
| + | if proto then | ||
| + | xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | xtexpath = "gfx/textures/Concrete_01_Grey_1.png" | ||
| + | xcolor = 0xa76f53 -- plus a tint color | ||
| + | end | ||
| + | --EDoor:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer) | ||
| + | local e = EDoor.new( | ||
| + | xid, xlayers["actors"], xpos, xtexpath, xcolor, o.width, o.height, | ||
| + | dx, dy, xdir, xspeed, xlayers["bgfx"] | ||
| + | ) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h)) | ||
| + | -- some cleaning? | ||
| + | e = nil | ||
| + | end | ||
| + | elseif layer.name:match("physics_ptmvpfs") then -- passthrough moving platform | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local vpos = vector(o.x, o.y) | ||
| + | local dx, dy, vel, xdir | ||
| + | local index | ||
| + | index = o.name:find("dx") -- index dx | ||
| + | local xid = o.name:sub(1, index-1) -- examples: "idAA", "idBB", ... | ||
| + | dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN | ||
| + | index = o.name:find("dy") -- index dy | ||
| + | dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN | ||
| + | index = o.name:find("vel") -- index vel | ||
| + | vel = tonumber(o.name:sub(index):match("%d+"))*8 -- find velNNN | ||
| + | index = o.name:find("dir") -- index dir | ||
| + | xdir = o.name:sub(index+3) -- initial heading direction, examples: "U", "DR", "UL", ... | ||
| + | local angle = math.atan2(dy, dx) | ||
| + | local vspeed = vector(vel*math.cos(angle), vel*math.sin(angle)) | ||
| + | local xtexpath | ||
| + | local xcolor | ||
| + | if proto then | ||
| + | xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | xtexpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png" | ||
| + | xcolor = 0xffaa7f -- plus a tint color | ||
| + | end | ||
| + | --EMvpf:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer) | ||
| + | local e = EMvpf.new( | ||
| + | xid, xlayers["actors"], vpos, xtexpath, xcolor, o.width, o.height, | ||
| + | dx, dy, xdir, vspeed, true, xlayers["bgfx"] | ||
| + | ) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h)) | ||
| + | -- some cleaning? | ||
| + | e = nil | ||
| + | end | ||
| + | elseif layer.name:match("physics_mvpfs") then -- non passthrough moving platform | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local vpos = vector(o.x, o.y) | ||
| + | local dx, dy, vel, xdir | ||
| + | local index | ||
| + | index = o.name:find("dx") -- index dx | ||
| + | local xid = o.name:sub(1, index-1) -- examples: "idAA", "idBB", ... | ||
| + | dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN | ||
| + | index = o.name:find("dy") -- index dy | ||
| + | dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN | ||
| + | index = o.name:find("vel") -- index vel | ||
| + | vel = tonumber(o.name:sub(index):match("%d+"))*8 -- find velNNN | ||
| + | index = o.name:find("dir") -- index dir | ||
| + | xdir = o.name:sub(index+3) -- initial heading direction, examples: "N", "SE", "NW", ... | ||
| + | local angle = math.atan2(dy, dx) | ||
| + | local vspeed = vector(vel*math.cos(angle), vel*math.sin(angle)) | ||
| + | local xtexpath | ||
| + | local xcolor | ||
| + | if proto then | ||
| + | xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | xtexpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png" | ||
| + | xcolor = 0xffaa7f -- plus a tint color | ||
| + | end | ||
| + | --EMvpf:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer) | ||
| + | local e = EMvpf.new( | ||
| + | xid, xlayers["actors"], vpos, xtexpath, xcolor, o.width, o.height, | ||
| + | dx, dy, xdir, vspeed, nil, xlayers["bgfx"] | ||
| + | ) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h)) | ||
| + | -- some cleaning? | ||
| + | e = nil | ||
| + | end | ||
| + | elseif layer.name == "physics_ladders" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | alpha=0.5, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isladder = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_walls" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local texpath | ||
| + | local color | ||
| + | local r, g, b | ||
| + | if proto then | ||
| + | color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | texpath = "gfx/textures/Rock_Grey_02.png" | ||
| + | r, g, b =139, 56, 28 | ||
| + | end | ||
| + | mytable = { | ||
| + | texpath=texpath, istexpot=true, scalex=0.5, | ||
| + | color=color, | ||
| + | r=r, g=g, b=b, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | o.iswall = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_steps" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | if proto then | ||
| + | mytable = { | ||
| + | color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )), | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | end | ||
| + | o.isstep = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_ptpfs" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | o.pos = vector(o.x, o.y) | ||
| + | local texpath | ||
| + | local color | ||
| + | local r, g, b | ||
| + | if proto then | ||
| + | color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | if o.name == "t" then | ||
| + | -- use "t" as the object name to make it "transparent" | ||
| + | else | ||
| + | texpath = "gfx/textures/1K-brick_wall_17_baseColor.jpg_0001.png" | ||
| + | r, g, b = 139, 56, 28 -- plus a tint color | ||
| + | end | ||
| + | end | ||
| + | mytable = { | ||
| + | texpath=texpath, istexpot=true, scalex=0.5, | ||
| + | color=color, | ||
| + | r=r, g=g, b=b, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | o.isptpf = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | elseif layer.name == "physics_grounds" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local texpath | ||
| + | local color | ||
| + | local r, g, b | ||
| + | if proto then | ||
| + | color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) | ||
| + | else | ||
| + | if o.name == "t" then -- transparent | ||
| + | -- use "t" as the object name to make it "transparent" | ||
| + | else | ||
| + | texpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png" | ||
| + | r, g, b = 255, 192, 160 -- plus a tint color | ||
| + | end | ||
| + | end | ||
| + | mytable = { | ||
| + | texpath=texpath, istexpot=true, scalex=1, skewx=3, | ||
| + | color=color, | ||
| + | r=r, g=g, b=b, | ||
| + | } | ||
| + | myshape = self:buildShapes(o, mytable) | ||
| + | myshape:setPosition(o.x, o.y) | ||
| + | xlayers["bg"]:addChild(myshape) | ||
| + | o.isfloor = true | ||
| + | xbworld:add(o, o.x, o.y, o.width, o.height) | ||
| + | end | ||
| + | -- _ _ _ _ _ _ | ||
| + | -- | | | | | (_) | | | | ||
| + | -- ___ ___ | | | ___ ___| |_ _| |__ | | ___ ___ | ||
| + | -- / __/ _ \| | |/ _ \/ __| __| | '_ \| |/ _ \/ __| | ||
| + | --| (_| (_) | | | __/ (__| |_| | |_) | | __/\__ \ | ||
| + | -- \___\___/|_|_|\___|\___|\__|_|_.__/|_|\___||___/ | ||
| + | elseif layer.name == "physics_keys" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local xid = o.name -- example "doorAA", "mvpfBB", ... | ||
| + | xid = "key"..tostring(xid) -- we append the keyword key to tell it is a key XXX | ||
| + | local opos = vector(o.x, o.y) | ||
| + | --ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy) | ||
| + | local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 5*8, 4*8) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | elseif layer.name == "physics_coins" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | local xid = layer.name:gsub("physics_", "") | ||
| + | --ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy) | ||
| + | local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 4*8, 4*8) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | elseif layer.name == "physics_lives" then | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | local xid = layer.name:gsub("physics_", "") | ||
| + | --ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy) | ||
| + | local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 5*8, 4*8) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | -- _ | ||
| + | -- | | | ||
| + | -- __ _ ___| |_ ___ _ __ ___ | ||
| + | -- / _` |/ __| __/ _ \| '__/ __| | ||
| + | --| (_| | (__| || (_) | | \__ \ | ||
| + | -- \__,_|\___|\__\___/|_| |___/ | ||
| + | elseif layer.name == "physics_ground_nmes03" then -- nmes | ||
| + | -- 300: no move, no jump, shoot all angles | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | local collectible = o.name | ||
| + | if collectible == "" then collectible = "coins" -- default to coins | ||
| + | elseif collectible == "x" then collectible = nil | ||
| + | end | ||
| + | --EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible) | ||
| + | local e = EGroundNmes.new(300, xlayers["actors"], opos, xlayers["bgfx"], collectible) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | elseif layer.name == "physics_ground_nmes02" then -- nmes | ||
| + | -- 200: move, jump, no shoot | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | local collectible = o.name | ||
| + | if collectible == "" then collectible = "coins" -- default to coins | ||
| + | elseif collectible == "x" then collectible = nil | ||
| + | end | ||
| + | --EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible) | ||
| + | local e = EGroundNmes.new(200, xlayers["actors"], opos, xlayers["bgfx"], collectible) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | elseif layer.name == "physics_ground_nmes" then | ||
| + | -- 100: no move, no jump, no shoot | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | local collectible = o.name | ||
| + | if collectible == "" then collectible = nil -- default to coins | ||
| + | elseif collectible == "x" then collectible = nil | ||
| + | end | ||
| + | --EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible) | ||
| + | local e = EGroundNmes.new(100, xlayers["actors"], opos, xlayers["bgfx"], collectible) | ||
| + | xtiny.tworld:addEntity(e) | ||
| + | xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h) | ||
| + | end | ||
| + | elseif layer.name == "physics_players" then -- player1 | ||
| + | for i = 1, #layer.objects do | ||
| + | o = layer.objects[i] | ||
| + | local opos = vector(o.x, o.y) | ||
| + | -- EPlayer1:init(xspritelayer, xpos, xbgfxlayer) | ||
| + | self.player1 = EPlayer1.new(xlayers["actors"], opos, xlayers["bgfx"]) | ||
| + | xtiny.tworld:addEntity(self.player1) | ||
| + | xbworld:add( | ||
| + | self.player1, | ||
| + | self.player1.pos.x, self.player1.pos.y, | ||
| + | self.player1.collbox.w, self.player1.collbox.h | ||
| + | ) | ||
| + | end | ||
| + | end | ||
| + | -- some cleaning? | ||
| + | o = nil | ||
| + | myshape, mytable = nil, nil | ||
| + | end | ||
| + | end | ||
| + | end | ||
| − | + | function TiledLevels:buildShapes(xobject, xlevelsetup) | |
| + | local myshape -- Tiled shapes: ellipse, point, polygon, polyline, rectangle, text | ||
| + | local tablebase = {} | ||
| + | if xobject.shape == "ellipse" then | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | w=xobject.width, h=xobject.height, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Ellipse.new(tablebase) | ||
| + | elseif xobject.shape == "point" then | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Point.new(tablebase) | ||
| + | elseif xobject.shape == "polygon" then | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | coords=xobject.polygon, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Polygon.new(tablebase) | ||
| + | elseif xobject.shape == "polyline" then -- lines | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | coords=xobject.polyline, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Polyline.new(tablebase) | ||
| + | elseif xobject.shape == "rectangle" then | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | w=xobject.width, h=xobject.height, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Rectangle.new(tablebase) | ||
| + | elseif xobject.shape == "text" then | ||
| + | tablebase = { | ||
| + | x=xobject.x, y=xobject.y, | ||
| + | text=xobject.text, | ||
| + | w=xobject.width, h=xobject.height, | ||
| + | rotation=xobject.rotation, | ||
| + | } | ||
| + | for k, v in pairs(xlevelsetup) do tablebase[k] = v end | ||
| + | myshape = Tiled_Shape_Text.new(tablebase) | ||
| + | else | ||
| + | print("*** CANNOT PROCESS THIS SHAPE! ***", xobject.shape, xobject.name) | ||
| + | return | ||
| + | end | ||
| − | + | return myshape | |
| + | end | ||
| + | </syntaxhighlight> | ||
| − | + | To build the shapes in the game we use the Gideros '''[[Shape]]''' class. I have written several classes for each one of them: ''Tiled_Shape_Ellipse'', ''Tiled_Shape_Point'', ''Tiled_Shape_Polygon'', ... . When we read the Tiled file we look for the layer name, if the layer contains shapes, we call the appropriate shape class and voilà :-). The ''buildShapes'' function is at the very bottom of the class. | |
| − | + | Please add the Tiled Deco Shapes classes to your project: '''[[Tiled Deco Shapes]]''' | |
| − | You can read more about Tiled and Gideros here: [[Tiled_Bump]]. | + | That's basically how we build our levels using Tiled. You can read more about Tiled and Gideros here: [[Tiled_Bump]]. |
== Next? == | == Next? == | ||
Latest revision as of 01:43, 26 October 2025
the levelX.lua file
This is where all the fun begins!
The LevelX scene holds the game loop and controls the flow of each levels.
When the scene loads, it constructs the level into layers, more on that in the code comments below ;-)
The code is not that long given it takes care of all levels of the game: level 1, 2 and 3. To make it easy to follow and debug, I use FIGlet (https://en.wikipedia.org/wiki/FIGlet).
I use this one https://sourceforge.net/projects/figletgenerator/
The code:
LevelX = Core.class(Sprite)
local ispaused = false
function LevelX:init()
-- move the cursor out of the way
if not application:isPlayerMode() then
local sw, sh = application:get("screenSize") -- the user's screen size!
application:set("cursorPosition", sw-10, sh-10) -- 0, 0
end
-- _____ _ _ _ _____ _____ _ _ _____
--| __ \| | | | | |/ ____|_ _| \ | |/ ____|
--| |__) | | | | | | | __ | | | \| | (___
--| ___/| | | | | | | |_ | | | | . ` |\___ \
--| | | |___| |__| | |__| |_| |_| |\ |____) |
--|_| |______\____/ \_____|_____|_| \_|_____/
-- tiny-ecs
if not self.tiny then self.tiny = require "classes/tiny-ecs" end
self.tiny.tworld = self.tiny.world()
-- cbump (bworld)
local bump = require "cbump"
local bworld = bump.newWorld() -- 16, grid cell size, default = 64
-- _ __ ________ _____ _____
--| | /\\ \ / / ____| __ \ / ____|
--| | / \\ \_/ /| |__ | |__) | (___
--| | / /\ \\ / | __| | _ / \___ \
--| |____ / ____ \| | | |____| | \ \ ____) |
--|______/_/ \_\_| |______|_| \_\_____/
local layers = {}
layers["main"] = Sprite.new() -- one Sprite to hold them all
layers["bg"] = Sprite.new() -- bg layer
layers["bgfx"] = Sprite.new() -- bg fx layer
layers["actors"] = Sprite.new() -- actors layer
layers["fgfx"] = Sprite.new() -- fg fx layer
layers["fg"] = Sprite.new() -- fg layer
layers["player1input"] = Sprite.new() -- player1 input layer
-- _ ________ ________ _ _____
--| | | ____\ \ / / ____| | / ____|
--| | | |__ \ \ / /| |__ | | | (___
--| | | __| \ \/ / | __| | | \___ \
--| |____| |____ \ / | |____| |____ ____) |
--|______|______| \/ |______|______|_____/
self.tiledlevels = {}
self.tiledlevels[1] = "tiled/lvl001/_level1_proto" -- lua file without extension
self.tiledlevels[2] = "tiled/lvl001/level1" -- lua file without extension
-- self.tiledlevels[3] = "tiled/lvl002/level1_proto" -- lua file without extension
local mapdef = {} -- game area (rect: top, left, right, bottom)
local zoom = 1 -- 1.3, 1.5, 1.7
local camfollowoffsety = 28 -- 32, 36, 48, camera follow player1 y offset, magik XXX
self.tiny.player1inventory = {} -- player1 inventory
self.tiny.numberofcoins = 0
local currlevel = TiledLevels.new(
self.tiledlevels[g_currlevel], self.tiny, bworld, layers
)
-- currlevel map definition (top, left, right, bottom) for the camera
mapdef.t, mapdef.l =
currlevel.mapdef.t, currlevel.mapdef.l
mapdef.r, mapdef.b =
currlevel.mapdef.l+currlevel.mapdef.r, currlevel.mapdef.t+currlevel.mapdef.b
-- _____ _ __ ________ _____ __
--| __ \| | /\\ \ / / ____| __ \/_ |
--| |__) | | / \\ \_/ /| |__ | |__) || |
--| ___/| | / /\ \\ / | __| | _ / | |
--| | | |____ / ____ \| | | |____| | \ \ | |
--|_| |______/_/ \_\_| |______|_| \_\|_|
self.player1 = currlevel.player1
-- _ _ _ _ _____
--| | | | | | | __ \
--| |__| | | | | | | |
--| __ | | | | | | |
--| | | | |__| | |__| |
--|_| |_|\____/|_____/
-- function
local function clamp(v, mn, mx) return (v><mx)<>mn end
local function map(v, minSrc, maxSrc, minDst, maxDst, clampValue)
local newV = (v-minSrc)/(maxSrc-minSrc)*(maxDst-minDst)+minDst
return not clampValue and newV or clamp(newV, minDst><maxDst, minDst<>maxDst)
end
self.tiny.hud = Sprite.new()
-- hud lives
self.tiny.hudlives = {}
local pixellife
for i = 1, self.player1.currlives do
pixellife = Pixel.new(0xffff00, 0.8, 16, 16)
pixellife:setPosition(8+(i-1)*(16+8), 8)
self.tiny.hud:addChild(pixellife)
self.tiny.hudlives[i] = pixellife
end
-- hud health
local hudhealthwidth = map(self.player1.currhealth, 0, self.player1.totalhealth, 0, 100, true)
self.tiny.hudhealth = Pixel.new(0x00ff00, 2, hudhealthwidth, 8)
self.tiny.hudhealth:setPosition(8*1, 8*3.5)
self.tiny.hud:addChild(self.tiny.hudhealth)
-- hud coins
self.tiny.hudcoins = TextField.new(cf2, "COINS: "..self.tiny.numberofcoins)
self.tiny.hudcoins:setTextColor(0xff5500)
self.tiny.hudcoins:setPosition(8*20, 8*2.3)
self.tiny.hud:addChild(self.tiny.hudcoins)
-- _____ __ __ ______ _____
-- / ____| /\ | \/ | ____| __ \ /\
--| | / \ | \ / | |__ | |__) | / \
--| | / /\ \ | |\/| | __| | _ / / /\ \
--| |____ / ____ \| | | | |____| | \ \ / ____ \
-- \_____/_/ \_\_| |_|______|_| \_\/_/ \_\
-- camera: 'content' is a Sprite which holds all your graphics
self.camera = GCam.new(layers["main"], nil, nil, self.player1) -- (content [, anchorX, anchorY]) -- anchor default 0.5, 0.5
self.camera:setAutoSize(true)
self.camera:setZoom(zoom)
self.camera:setBounds(
mapdef.l+myappwidth/2/zoom, -- left
mapdef.t+myappheight/2/zoom, -- top
mapdef.r-myappwidth/2/zoom, -- right
mapdef.b-myappheight/2/zoom -- bottom
)
self.camera:setSoftSize(1.5*32, 2.2*32) -- 1.5*32, 3*32, w, h
self.camera:setDeadSize(3*32, 4.4*32) -- 3*32, 5*32, w, h
self.camera:setFollow(self.player1.sprite)
self.camera:setFollowOffset(0, camfollowoffsety)
self.camera:setPredictMode(true)
self.camera:setPrediction(0.9) -- 0.8, 0.75, 0.5, number betwwen 0 and 1
self.camera:lockPredictionY() -- no prediction on Y axis
self.camera:setPredictionSmoothing(4) -- 8, 4, smooth prediction
-- self.camera:setDebug(true) -- uncomment for camera debug mode
-- ____ _____ _____ ______ _____
-- / __ \| __ \| __ \| ____| __ \
--| | | | |__) | | | | |__ | |__) |
--| | | | _ /| | | | __| | _ /
--| |__| | | \ \| |__| | |____| | \ \
-- \____/|_| \_\_____/|______|_| \_\
layers["main"]:addChild(layers["bg"])
layers["main"]:addChild(layers["bgfx"])
layers["main"]:addChild(layers["actors"])
layers["main"]:addChild(layers["fgfx"])
layers["main"]:addChild(layers["fg"])
self:addChild(self.camera)
self:addChild(self.tiny.hud)
self:addChild(layers["player1input"])
-- _______ _______ _______ ______ __ __ _____
-- / ____\ \ / / ____|__ __| ____| \/ |/ ____|
--| (___ \ \_/ / (___ | | | |__ | \ / | (___
-- \___ \ \ / \___ \ | | | __| | |\/| |\___ \
-- ____) | | | ____) | | | | |____| | | |____) |
--|_____/ |_| |_____/ |_| |______|_| |_|_____/
self.tiny.tworld:add(
-- debug
-- SDebugPosition.new(self.tiny),
-- SDebugCollision.new(self.tiny),
-- SDebugShield.new(self.tiny),
-- systems
SDrawable.new(self.tiny),
SAnimation.new(self.tiny),
SPlayer1.new(self.tiny, bworld, self.camera),
SPlayer1Control.new(self.tiny, self.camera, mapdef, player1inputlayer),
SNmes.new(self.tiny, bworld, self.player1),
SAI.new(self.tiny, self.player1),
SSensor.new(self.tiny, bworld, self.player1),
SDoor.new(self.tiny, bworld, self.player1),
SMvpf.new(self.tiny, bworld),
SCollectibles.new(self.tiny, bworld, self.player1),
SProjectiles.new(self.tiny, bworld),
SOscillation.new(self.tiny, bworld, self.player1),
SCollision.new(self.tiny, bworld, self.player1)
)
-- _ ______ _______ _ _____ _____ ____ _
--| | | ____|__ __( )/ ____| / ____|/ __ \| |
--| | | |__ | | |/| (___ | | __| | | | |
--| | | __| | | \___ \ | | |_ | | | | |
--| |____| |____ | | ____) | | |__| | |__| |_|
--|______|______| |_| |_____/ \_____|\____/(_)
self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self) -- the game loop
self:myKeysPressed() -- keys handler
end
-- game loop
local timer = 40*8 -- magik XXX
function LevelX:onEnterFrame(e)
if not ispaused then
local dt = e.deltaTime
if self.player1.restart then
if g_currlevel > #self.tiledlevels then
timer -= 1
if self.camera:getZoom() < 2 then
self.camera:setZoom(self.camera:getZoom()+0.4*dt)
end
if timer <= 0 then
self:gotoScene(Win.new())
timer = 40*8
end
else
self:gotoScene(LevelX.new())
end
self.camera:setPredictionSmoothing(1) -- 4, smooth prediction
self.camera:update(dt) -- e.deltaTime, 1/60
else
self.camera:update(dt) -- e.deltaTime, 1/60
self.tiny.tworld:update(dt) -- tiny world (last)
end
end
end
-- keys handler
function LevelX:myKeysPressed()
self:addEventListener(Event.KEY_DOWN, function(e) -- KEY_UP
if e.keyCode == KeyCode.ESC or e.keyCode == KeyCode.BACK then -- MENU
self:gotoScene(Menu.new())
elseif e.keyCode == KeyCode.P then -- PAUSE
ispaused = not ispaused
elseif e.keyCode == KeyCode.R then -- RESTART
self.player1.restart = true
end
-- modifier
local modifier = application:getKeyboardModifiers()
local alt = (modifier & KeyCode.MODIFIER_ALT) > 0
if (not alt and e.keyCode == KeyCode.ENTER) then -- nothing here!
elseif alt and e.keyCode == KeyCode.ENTER then -- SWITCH FULLSCREEN
ismyappfullscreen = not ismyappfullscreen
application:setFullScreen(ismyappfullscreen)
end
end)
end
-- scenes navigation
function LevelX:gotoScene(xscene)
switchToScene(xscene) -- next scene
end
LevelX Code comments
LevelX:init
plugins
Here we initialize our plugins. tiny-ecs is declared as a Class variable (self.tiny) because we use it outside the init function. Bump can be local as we only use it in the init function.
It is worth noting that tiny.tworld is attached to self.tiny variable. This makes it easier to access through the rest of the code in our project.
layers
This one is easy :-)
We create several layers which will be laid out on top of each other. The background layer will have all the graphics for the background, etc...
There is a player1inputlayer which will capture the user input to control the player, it won't hold any graphics.
Tiled levels
We use Tiled to build our levels (https://www.mapeditor.org/) and we call the TiledLevels Class to manage the parsing of the Tiled elements in our game.
We dedicate an entire folder for Tiled in our project and we talk more about it down below
The mapdef is a table which has the map definition (dimensions), so we can pass it to functions that will require the size of the map for calculations.
player1
The player is an ECS entity we create in the TiledLevels Class, more on that in the next chapters. We need it here because the HUD and the camera depend on it.
hud
I added a simple head up display to the game, so we can see the player current health, number of lives, ...
The same way we attached tiny.tworld to the self.tiny variable, we attach some more variables to it. This makes it easier to access those variables.
the camera
We use a camera in our game and camfollowoffsety will offset the camera following the player. It is easier to change it being a variable.
We use a slightly modified version of MultiPain's (aka rrraptor on Gideros forum) GCam Class for our camera. We pass it the mainlayer as the content and the player1 to follow.
Using the map definition we set the camera bounds. We also set the soft and the dead size parameters and we tell it to follow the player.
order
This is the order of the layers, from bottom to top. We first add the background layer and the other layers on top of it.
systems
Once all entities are done, we add the ECS systems. We will see those ECS systems in the coming parts of the tutorial.
let's go
Finally we are ready to run the game loop!
We also listen to some key events to pause the game, go fullscreen, ...
LevelX:onEnterFrame THE GAME LOOP
Before the game loop, I declare a local variable timer which is the time before transitioning to the next level.
If we beat the level then we go to the next level or the Win scene.
Otherwise, if the game is not paused we update the camera and the tiny-ecs world. tiny-ecs world runs all the systems (animations, movements, AI, ...). Some systems will be called only once, others every frame per second. More on Systems later on in this tutorial.
LevelX:myKeysPressed
Here we simply listen to some KEY_DOWN Events, that is keys pressed on the keyboard. The key ESC will go back to the menu, the letter P will pause the game and when you press ALT+ENTER you switch the game to fullscreen.
LevelX:gotoScene
This is where we transition to the next scene calling the global function switchToScene.
TiledLevels Class
To better organise the game, I put all files related to Tiled in a tiled folder.
Let's have a look at how we construct our levels.
In Tiled we add many layers, in our case Object Layers. Each layers have a type and a name (eg. physics_ptpfs, physics_coins, bg_deco_images, ...). In each layers we draw the level using shapes (rectangles, ellipses, ...). Shapes are also used to position the actors in the level, like the player, the enemies, moving platforms, ...
Once the level is done in Tiled, we export it as a lua file Gideros will use (read the layers name, the shapes, the color, the position, ...).
TiledLevels Class Code comments
The very first thing we do is include the Tiled lua file. I also set up some variables to check if we are testing a prototype level and if we are in development mode.
Tileset
This is the classic: a tileset with a tilemap image (Tile Layer in Tiled). We don't use tilsets in this game but feel free to do so. The code extracts the tilemap info: number of columns, rows, tile id (gid) and texture.
Tileset images
Instead of tilesets we use single images to make our levels. Here we read the gid and the image path and store them in tables. The tables are then turned into TexturePack so the game runs faster.
Build Level
To build a level, we iterate all the tilemap layers according to their type and their name. We either build shapes the physics world can use or we add images to decorate the level using the parseImage function.
note: we don't use Tiled Tile Layer in this game but you can, Build Level takes care of it for you in layer.type == "tilelayer"
And this is the code that reads the Tiled lua file to build our levels:
TiledLevels = Core.class(Sprite)
function TiledLevels:init(tmpath, xtiny, xbworld, xlayers)
local tm = require(tmpath) -- eg.: "tiled/test" without ".lua" extension + exclude from execution!
local proto = false
if tmpath:find("proto") then
proto = true
end
-- some functions
local function rgb2hex(r, g, b)
return (r << 16) + (g << 8) + b
end
local dev = false -- true, false
local tsimgpath -- "tiled/", root path to tileset tilemap images
if dev then -- in development
tsimgpath = -- path to external folder
"C:/X_MOKA/PROJECTS/_gideros/WIKI/_assets/tuto_pf_cbump_tecs/"
else -- prod
if g_currlevel == 1 then tsimgpath = "tiled/lvl001/"
elseif g_currlevel == 2 then tsimgpath = "tiled/lvl001/"
elseif g_currlevel == 3 then tsimgpath = "tiled/lvl002/"
end
end
-- _______ _____ _ ______ _____ ______ _______
--|__ __|_ _| | | ____|/ ____| ____|__ __|
-- | | | | | | | |__ | (___ | |__ | |
-- | | | | | | | __| \___ \| __| | |
-- | | _| |_| |____| |____ ____) | |____ | |
-- |_| |_____|______|______|_____/|______| |_|
-- this is the classic: a tileset with a tilemap image
for i = 1, #tm.tilesets do -- important
local tileset = tm.tilesets[i]
-- add extra values (variables) to a tm.tilesets[i] table
if tileset.image then -- only tileset tilemap layers
tileset.numcols = math.floor(
(tileset.imagewidth-tileset.margin+tileset.spacing)/
(tileset.tilewidth+tileset.spacing)
)
tileset.numrows = math.floor(
(tileset.imageheight-tileset.margin+tileset.spacing)/
(tileset.tileheight+tileset.spacing)
)
tileset.lastgid = tileset.firstgid+(tileset.numcols*tileset.numrows)-1
tileset.texture = Texture.new(
tsimgpath..tileset.image, false,
{ transparentColor=tonumber(tileset.transparentcolor), }
)
end
end
-- tileset function
local function gid2tileset(tm, gid)
for i = 1, #tm.tilesets do
local tileset = tm.tilesets[i]
if tileset.image then -- only valid tileset layers
if tileset.firstgid <= gid and gid <= tileset.lastgid then
return tileset
end
end
end
end
-- _______ _____ _ ______ _____ ______ _______
--|__ __|_ _| | | ____|/ ____| ____|__ __|
-- | | | | | | | |__ | (___ | |__ | |
-- | | | | | | | __| \___ \| __| | |
-- | | _| |_| |____| |____ ____) | |____ | |
-- |_| |_____|______|______|_____/|______| |_|
-- _____ __ __ _____ ______ _____
--|_ _| \/ | /\ / ____| ____|/ ____|
-- | | | \ / | / \ | | __| |__ | (___
-- | | | |\/| | / /\ \| | |_ | __| \___ \
-- _| |_| | | |/ ____ \ |__| | |____ ____) |
--|_____|_| |_/_/ \_\_____|______|_____/
-- this one parses individual images stored in a tileset
local tilesetimages = {} -- table holding all the tileset images info (path, width, height)
for i = 1, #tm.tilesets do
local tileset = tm.tilesets[i]
if not tileset.image then -- filter out tileset tilemap layers, only tileset images
local tiles = tileset.tiles
for j = 1, #tiles do
-- populate the tilesetimages table based on the tile gid and id
-- note: you may have to adjust the path to point to the image folder
tilesetimages[tileset.firstgid + tiles[j].id] = {
path=tsimgpath..tiles[j].image,
width=tiles[j].width,
height=tiles[j].height,
}
end
end
end
-- pack all the deco images (TexturePack), perfs? XXX
local bgtpt = {} -- background texture pack table
local bgtptx = {} -- background texture pack table x
local fgtpt = {} -- foreground texture pack table
local fgtptx = {} -- foreground texture pack table x
for i = 1, #tm.layers do
local layer = tm.layers[i]
if layer.name:match("bg_deco_images") then
if layer.name:match("x$") then -- x$ = string ends with x
for i = 1, #layer.objects do
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
-- print("bg", tilesetimages[layer.objects[i].gid].path)
bgtptx[#bgtptx+1] = tilesetimages[layer.objects[i].gid].path
end
end
else
for i = 1, #layer.objects do
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
-- print("bg", tilesetimages[layer.objects[i].gid].path)
bgtpt[#bgtpt+1] = tilesetimages[layer.objects[i].gid].path
end
end
end
elseif layer.name:match("fg_deco_images") then
if layer.name:match("x$") then -- x$ = string ends with x
for i = 1, #layer.objects do
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
-- print("fg", tilesetimages[layer.objects[i].gid].path)
fgtptx[#fgtptx+1] = tilesetimages[layer.objects[i].gid].path
end
end
else
for i = 1, #layer.objects do
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
-- print("fg", tilesetimages[layer.objects[i].gid].path)
fgtpt[#fgtpt+1] = tilesetimages[layer.objects[i].gid].path
end
end
end
end
end
local bgtp = TexturePack.new(bgtpt, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs
local bgtpx = TexturePack.new(bgtptx, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs
local fgtp = TexturePack.new(fgtpt, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs
local fgtpx = TexturePack.new(fgtptx, nil, nil, { format=TextureBase.RGBA4444, } ) -- perfs
if #bgtp:getRegionsNames() > 0 then print("bgtp", bgtp:getSize()) end
if #bgtpx:getRegionsNames() > 0 then print("bgtpx", bgtpx:getSize()) end
if #fgtp:getRegionsNames() > 0 then print("fgtp", fgtp:getSize()) end
if #fgtpx:getRegionsNames() > 0 then print("fgtpx", fgtpx:getSize()) end
-- ____ _ _ _____ _ _____ _ ________ ________ _
--| _ \| | | |_ _| | | __ \ | | | ____\ \ / / ____| |
--| |_) | | | | | | | | | | | | | | | |__ \ \ / /| |__ | |
--| _ <| | | | | | | | | | | | | | | __| \ \/ / | __| | |
--| |_) | |__| |_| |_| |____| |__| | | |____| |____ \ / | |____| |____
--|____/ \____/|_____|______|_____/ |______|______| \/ |______|______|
-- tileset images function
local function parseImage(xobject, xlayer, xtplayer, xtexpack)
local bitmap
if xtplayer == "bg" then
if xtexpack == "x" then
bitmap = Bitmap.new(bgtpx:getTextureRegion(tilesetimages[xobject.gid].path))
else
bitmap = Bitmap.new(bgtp:getTextureRegion(tilesetimages[xobject.gid].path))
end
else -- fg
if xtexpack == "x" then
bitmap = Bitmap.new(fgtpx:getTextureRegion(tilesetimages[xobject.gid].path))
else
bitmap = Bitmap.new(fgtp:getTextureRegion(tilesetimages[xobject.gid].path))
end
end
bitmap:setAnchorPoint(0, 1) -- because I always forget to modify Tiled objects alignment
-- supports Tiled image scaling
local scalex, scaley = xobject.width/bitmap:getWidth(), xobject.height/bitmap:getHeight()
bitmap:setScale(scalex, scaley)
bitmap:setRotation(xobject.rotation)
bitmap:setPosition(xobject.x, xobject.y)
xlayer:addChild(bitmap)
end
for i = 1, #tm.layers do
local layer = tm.layers[i]
local tilemaps = {}
local group -- group = Sprite.new()
-- _______ _____ _ ______ _ __ ________ _____
--|__ __|_ _| | | ____| | /\\ \ / / ____| __ \
-- | | | | | | | |__ | | / \\ \_/ /| |__ | |__) |
-- | | | | | | | __| | | / /\ \\ / | __| | _ /
-- | | _| |_| |____| |____| |____ / ____ \| | | |____| | \ \
-- |_| |_____|______|______|______/_/ \_\_| |______|_| \_\
if layer.type == "tilelayer" and (layer.name:match("bg") or layer.name:match("fg")) then
if layer.name:match("bg") then group = xlayers["bg"]
else group = xlayers["fg"]
end
for y = 1, layer.height do
for x = 1, layer.width do
local index = x + (y - 1) * layer.width
local gid = layer.data[index]
local gidtileset = gid2tileset(tm, gid)
if gidtileset then
local tilemap
if tilemaps[gidtileset] then
tilemap = tilemaps[gidtileset]
else
tilemap = TileMap.new(
layer.width, layer.height,
gidtileset.texture, gidtileset.tilewidth, gidtileset.tileheight,
gidtileset.spacing, gidtileset.spacing,
gidtileset.margin, gidtileset.margin,
tm.tilewidth, tm.tileheight
)
tilemaps[gidtileset] = tilemap
group:addChild(tilemap)
end
local tx = (gid - gidtileset.firstgid) % gidtileset.numcols + 1
local ty = math.floor((gid - gidtileset.firstgid) / gidtileset.numcols) + 1
-- set the tile with flip info
tilemap:setTile(x, y, tx, ty)
end
end
end
group:setAlpha(layer.opacity)
-- ____ ____ _ ______ _____ _______ _ __ ________ _____
-- / __ \| _ \ | | ____/ ____|__ __| | /\\ \ / / ____| __ \
--| | | | |_) | | | |__ | | | | | | / \\ \_/ /| |__ | |__) |
--| | | | _ < _ | | __|| | | | | | / /\ \\ / | __| | _ /
--| |__| | |_) | |__| | |___| |____ | | | |____ / ____ \| | | |____| | \ \
-- \____/|____/ \____/|______\_____| |_| |______/_/ \_\_| |______|_| \_\
elseif layer.type == "objectgroup" then
local o
local myshape, mytable
-- _ __ _ _ _ _
-- | | / _(_) (_) | (_)
-- _ __ ___ __ _ _ __ __| | ___| |_ _ _ __ _| |_ _ ___ _ __
--| '_ ` _ \ / _` | '_ \ / _` |/ _ \ _| | '_ \| | __| |/ _ \| '_ \
--| | | | | | (_| | |_) | | (_| | __/ | | | | | | | |_| | (_) | | | |
--|_| |_| |_|\__,_| .__/ \__,_|\___|_| |_|_| |_|_|\__|_|\___/|_| |_|
-- | |
-- |_|
if layer.name == "physics_map_def" then
for i = 1, #layer.objects do
o = layer.objects[i]
self.mapdef = {}
self.mapdef.t = o.y
self.mapdef.l = o.x
self.mapdef.r = o.width
self.mapdef.b = o.height
end
-- _ _
-- | | | |
-- __| | ___ ___ ___ | | __ _ _ _ ___ _ __ ___
-- / _` |/ _ \/ __/ _ \ | |/ _` | | | |/ _ \ '__/ __|
--| (_| | __/ (_| (_) | | | (_| | |_| | __/ | \__ \
-- \__,_|\___|\___\___/ |_|\__,_|\__, |\___|_| |___/
-- __/ |
-- |___/
elseif layer.name == "bg_deco_texts" then
for i = 1, #layer.objects do
o = layer.objects[i]
mytable = {
color=rgb2hex(table.unpack(o.color or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
elseif layer.name:match("bg_deco_shapes") then
for i = 1, #layer.objects do
o = layer.objects[i]
local texpath
local color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )) -- math.random(0xffffff)
local r, g, b
if o.name == "hint" then
color = 0xffffff
elseif o.name == "lava" then
texpath = "gfx/textures/Water_01_Grey_1.png"
color = nil
r, g, b = 124, 205, 70
elseif o.name == "spikes" then
color = 0xff5500
end
mytable = {
texpath=texpath, istexpot=true, scalex=0.4,
color=color, r=r, g=g, b=b,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
elseif layer.name:match("bg_deco_images") then
--parseImage(xobject, xlayer, xtplayer, xtexpack)
for i = 1, #layer.objects do
if layer.name:match("x$") then
parseImage(layer.objects[i], xlayers["bg"], "bg", "x")
else
parseImage(layer.objects[i], xlayers["bg"], "bg", nil)
end
end
elseif layer.name == "fg_deco_texts" then
for i = 1, #layer.objects do
o = layer.objects[i]
mytable = {
color=rgb2hex(table.unpack(o.color or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["fg"]:addChild(myshape)
end
elseif layer.name:match("fg_deco_shapes") then
for i = 1, #layer.objects do
o = layer.objects[i]
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["fg"]:addChild(myshape)
end
elseif layer.name:match("fg_deco_images") then
for i = 1, #layer.objects do
if layer.name:match("x$") then
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
parseImage(layer.objects[i], xlayers["fg"], "fg", "x")
end
else
if layer.objects[i].gid then -- if no gid then this is not an image, skip it!
parseImage(layer.objects[i], xlayers["fg"], "fg", nil)
end
end
end
-- _ _ _
-- | | (_) | |
-- _ __ | |__ _ _ ___ _ ___ ___ | | __ _ _ _ ___ _ __ ___
--| '_ \| '_ \| | | / __| |/ __/ __| | |/ _` | | | |/ _ \ '__/ __|
--| |_) | | | | |_| \__ \ | (__\__ \ | | (_| | |_| | __/ | \__ \
--| .__/|_| |_|\__, |___/_|\___|___/ |_|\__,_|\__, |\___|_| |___/
--| | __/ | __/ |
--|_| |___/ |___/
-- _ _
-- | | | |
--__ _____ _ __| | __| |
--\ \ /\ / / _ \| '__| |/ _` |
-- \ V V / (_) | | | | (_| |
-- \_/\_/ \___/|_| |_|\__,_|
elseif layer.name == "physics_exits" then
for i = 1, #layer.objects do
o = layer.objects[i]
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isexit = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_voids" then
for i = 1, #layer.objects do
o = layer.objects[i]
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isvoid = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_spikes" then
for i = 1, #layer.objects do
o = layer.objects[i]
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isspike = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_springs" then
for i = 1, #layer.objects do
o = layer.objects[i]
local vx, vy
local index
index = o.name:find("vx") -- index vx
vx = tonumber(o.name:sub(index):match("%d+")) -- find vxNNN
index = o.name:find("vy") -- index vy
vy = tonumber(o.name:sub(index):match("%d+")) -- find vyNNN
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isspring = true
o.vx = vx*64 -- some multiplier, magik XXX
o.vy = vy*64 -- some multiplier, magik XXX
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_sensors" then
for i = 1, #layer.objects do
o = layer.objects[i]
local xid = o.name -- examples: "mvgrAA", ...
local opos = vector(o.x, o.y)
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
alpha=0.25,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
--ESensor:init(xid, xspritelayer, xpos, w, h, xlayers["bgfx"])
local e = ESensor.new(
xid, xlayers["actors"], opos, o.width, o.height, xlayers["bgfx"]
)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
-- some cleaning?
e = nil
end
elseif layer.name:match("physics_doors") then -- doors = doorAAdx0dy8vel10dirU
for i = 1, #layer.objects do
o = layer.objects[i]
local xpos = vector(o.x, o.y)
local dx, dy, vel, xdir
local index
index = o.name:find("dx") -- index dx
local xid = o.name:sub(1, index-1) -- examples: "doorAA", "doorBB", ...
dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN
index = o.name:find("dy") -- index dy
dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN
index = o.name:find("vel") -- index vel
vel = tonumber(o.name:sub(index):match("%d+")) -- find velNN
index = o.name:find("dir") -- index dir
xdir = o.name:sub(index+3) -- initial heading direction, examples: "U", "DR", "UL", ...
local angle = math.atan2(dy, dx)
local xspeed = vector(vel*math.cos(angle), vel*math.sin(angle))*vector(8, 8)
local xtexpath
local xcolor
if proto then
xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
xtexpath = "gfx/textures/Concrete_01_Grey_1.png"
xcolor = 0xa76f53 -- plus a tint color
end
--EDoor:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer)
local e = EDoor.new(
xid, xlayers["actors"], xpos, xtexpath, xcolor, o.width, o.height,
dx, dy, xdir, xspeed, xlayers["bgfx"]
)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h))
-- some cleaning?
e = nil
end
elseif layer.name:match("physics_ptmvpfs") then -- passthrough moving platform
for i = 1, #layer.objects do
o = layer.objects[i]
local vpos = vector(o.x, o.y)
local dx, dy, vel, xdir
local index
index = o.name:find("dx") -- index dx
local xid = o.name:sub(1, index-1) -- examples: "idAA", "idBB", ...
dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN
index = o.name:find("dy") -- index dy
dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN
index = o.name:find("vel") -- index vel
vel = tonumber(o.name:sub(index):match("%d+"))*8 -- find velNNN
index = o.name:find("dir") -- index dir
xdir = o.name:sub(index+3) -- initial heading direction, examples: "U", "DR", "UL", ...
local angle = math.atan2(dy, dx)
local vspeed = vector(vel*math.cos(angle), vel*math.sin(angle))
local xtexpath
local xcolor
if proto then
xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
xtexpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png"
xcolor = 0xffaa7f -- plus a tint color
end
--EMvpf:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer)
local e = EMvpf.new(
xid, xlayers["actors"], vpos, xtexpath, xcolor, o.width, o.height,
dx, dy, xdir, vspeed, true, xlayers["bgfx"]
)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h))
-- some cleaning?
e = nil
end
elseif layer.name:match("physics_mvpfs") then -- non passthrough moving platform
for i = 1, #layer.objects do
o = layer.objects[i]
local vpos = vector(o.x, o.y)
local dx, dy, vel, xdir
local index
index = o.name:find("dx") -- index dx
local xid = o.name:sub(1, index-1) -- examples: "idAA", "idBB", ...
dx = tonumber(o.name:sub(index):match("%d+")) -- find dxNNN
index = o.name:find("dy") -- index dy
dy = tonumber(o.name:sub(index):match("%d+")) -- find dyNNN
index = o.name:find("vel") -- index vel
vel = tonumber(o.name:sub(index):match("%d+"))*8 -- find velNNN
index = o.name:find("dir") -- index dir
xdir = o.name:sub(index+3) -- initial heading direction, examples: "N", "SE", "NW", ...
local angle = math.atan2(dy, dx)
local vspeed = vector(vel*math.cos(angle), vel*math.sin(angle))
local xtexpath
local xcolor
if proto then
xcolor = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
xtexpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png"
xcolor = 0xffaa7f -- plus a tint color
end
--EMvpf:init(xid, xspritelayer, xpos, xtexpath, xcolor, w, h, dx, dy, xdir, xspeed, xbgfxlayer)
local e = EMvpf.new(
xid, xlayers["actors"], vpos, xtexpath, xcolor, o.width, o.height,
dx, dy, xdir, vspeed, nil, xlayers["bgfx"]
)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
e.sprite:setPosition(e.pos + vector(e.collbox.w/2, -e.h/2+e.collbox.h))
-- some cleaning?
e = nil
end
elseif layer.name == "physics_ladders" then
for i = 1, #layer.objects do
o = layer.objects[i]
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
alpha=0.5,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isladder = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_walls" then
for i = 1, #layer.objects do
o = layer.objects[i]
local texpath
local color
local r, g, b
if proto then
color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
texpath = "gfx/textures/Rock_Grey_02.png"
r, g, b =139, 56, 28
end
mytable = {
texpath=texpath, istexpot=true, scalex=0.5,
color=color,
r=r, g=g, b=b,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
o.iswall = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_steps" then
for i = 1, #layer.objects do
o = layer.objects[i]
if proto then
mytable = {
color=rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } )),
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
end
o.isstep = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_ptpfs" then
for i = 1, #layer.objects do
o = layer.objects[i]
o.pos = vector(o.x, o.y)
local texpath
local color
local r, g, b
if proto then
color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
if o.name == "t" then
-- use "t" as the object name to make it "transparent"
else
texpath = "gfx/textures/1K-brick_wall_17_baseColor.jpg_0001.png"
r, g, b = 139, 56, 28 -- plus a tint color
end
end
mytable = {
texpath=texpath, istexpot=true, scalex=0.5,
color=color,
r=r, g=g, b=b,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
o.isptpf = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
elseif layer.name == "physics_grounds" then
for i = 1, #layer.objects do
o = layer.objects[i]
local texpath
local color
local r, g, b
if proto then
color = rgb2hex(table.unpack(layer.tintcolor or { 255, 255, 255 } ))
else
if o.name == "t" then -- transparent
-- use "t" as the object name to make it "transparent"
else
texpath = "gfx/textures/wdipagu_2K_Albedo.jpg_0008.png"
r, g, b = 255, 192, 160 -- plus a tint color
end
end
mytable = {
texpath=texpath, istexpot=true, scalex=1, skewx=3,
color=color,
r=r, g=g, b=b,
}
myshape = self:buildShapes(o, mytable)
myshape:setPosition(o.x, o.y)
xlayers["bg"]:addChild(myshape)
o.isfloor = true
xbworld:add(o, o.x, o.y, o.width, o.height)
end
-- _ _ _ _ _ _
-- | | | | | (_) | | |
-- ___ ___ | | | ___ ___| |_ _| |__ | | ___ ___
-- / __/ _ \| | |/ _ \/ __| __| | '_ \| |/ _ \/ __|
--| (_| (_) | | | __/ (__| |_| | |_) | | __/\__ \
-- \___\___/|_|_|\___|\___|\__|_|_.__/|_|\___||___/
elseif layer.name == "physics_keys" then
for i = 1, #layer.objects do
o = layer.objects[i]
local xid = o.name -- example "doorAA", "mvpfBB", ...
xid = "key"..tostring(xid) -- we append the keyword key to tell it is a key XXX
local opos = vector(o.x, o.y)
--ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy)
local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 5*8, 4*8)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
elseif layer.name == "physics_coins" then
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
local xid = layer.name:gsub("physics_", "")
--ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy)
local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 4*8, 4*8)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
elseif layer.name == "physics_lives" then
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
local xid = layer.name:gsub("physics_", "")
--ECollectibles:init(xid, xspritelayer, xpos, xspeed, xdx, xdy)
local e = ECollectibles.new(xid, xlayers["actors"], opos, 1*8, 5*8, 4*8)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
-- _
-- | |
-- __ _ ___| |_ ___ _ __ ___
-- / _` |/ __| __/ _ \| '__/ __|
--| (_| | (__| || (_) | | \__ \
-- \__,_|\___|\__\___/|_| |___/
elseif layer.name == "physics_ground_nmes03" then -- nmes
-- 300: no move, no jump, shoot all angles
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
local collectible = o.name
if collectible == "" then collectible = "coins" -- default to coins
elseif collectible == "x" then collectible = nil
end
--EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible)
local e = EGroundNmes.new(300, xlayers["actors"], opos, xlayers["bgfx"], collectible)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
elseif layer.name == "physics_ground_nmes02" then -- nmes
-- 200: move, jump, no shoot
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
local collectible = o.name
if collectible == "" then collectible = "coins" -- default to coins
elseif collectible == "x" then collectible = nil
end
--EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible)
local e = EGroundNmes.new(200, xlayers["actors"], opos, xlayers["bgfx"], collectible)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
elseif layer.name == "physics_ground_nmes" then
-- 100: no move, no jump, no shoot
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
local collectible = o.name
if collectible == "" then collectible = nil -- default to coins
elseif collectible == "x" then collectible = nil
end
--EGroundNmes:init(xid, xspritelayer, xpos, xlayers, xcollectible)
local e = EGroundNmes.new(100, xlayers["actors"], opos, xlayers["bgfx"], collectible)
xtiny.tworld:addEntity(e)
xbworld:add(e, e.pos.x, e.pos.y, e.collbox.w, e.collbox.h)
end
elseif layer.name == "physics_players" then -- player1
for i = 1, #layer.objects do
o = layer.objects[i]
local opos = vector(o.x, o.y)
-- EPlayer1:init(xspritelayer, xpos, xbgfxlayer)
self.player1 = EPlayer1.new(xlayers["actors"], opos, xlayers["bgfx"])
xtiny.tworld:addEntity(self.player1)
xbworld:add(
self.player1,
self.player1.pos.x, self.player1.pos.y,
self.player1.collbox.w, self.player1.collbox.h
)
end
end
-- some cleaning?
o = nil
myshape, mytable = nil, nil
end
end
end
function TiledLevels:buildShapes(xobject, xlevelsetup)
local myshape -- Tiled shapes: ellipse, point, polygon, polyline, rectangle, text
local tablebase = {}
if xobject.shape == "ellipse" then
tablebase = {
x=xobject.x, y=xobject.y,
w=xobject.width, h=xobject.height,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Ellipse.new(tablebase)
elseif xobject.shape == "point" then
tablebase = {
x=xobject.x, y=xobject.y,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Point.new(tablebase)
elseif xobject.shape == "polygon" then
tablebase = {
x=xobject.x, y=xobject.y,
coords=xobject.polygon,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Polygon.new(tablebase)
elseif xobject.shape == "polyline" then -- lines
tablebase = {
x=xobject.x, y=xobject.y,
coords=xobject.polyline,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Polyline.new(tablebase)
elseif xobject.shape == "rectangle" then
tablebase = {
x=xobject.x, y=xobject.y,
w=xobject.width, h=xobject.height,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Rectangle.new(tablebase)
elseif xobject.shape == "text" then
tablebase = {
x=xobject.x, y=xobject.y,
text=xobject.text,
w=xobject.width, h=xobject.height,
rotation=xobject.rotation,
}
for k, v in pairs(xlevelsetup) do tablebase[k] = v end
myshape = Tiled_Shape_Text.new(tablebase)
else
print("*** CANNOT PROCESS THIS SHAPE! ***", xobject.shape, xobject.name)
return
end
return myshape
end
To build the shapes in the game we use the Gideros Shape class. I have written several classes for each one of them: Tiled_Shape_Ellipse, Tiled_Shape_Point, Tiled_Shape_Polygon, ... . When we read the Tiled file we look for the layer name, if the layer contains shapes, we call the appropriate shape class and voilà :-). The buildShapes function is at the very bottom of the class.
Please add the Tiled Deco Shapes classes to your project: Tiled Deco Shapes
That's basically how we build our levels using Tiled. You can read more about Tiled and Gideros here: Tiled_Bump.
Next?
That was quite a lot of work but we coded the heart of our game and we are already nearly done!!
All is left to do is add the actors. Actors will be ECS entites, those entities will have components and systems will control them.
In the next part we deal with our player1 entity.
Prev.: Tuto tiny-ecs 2d platformer Part 3 transitions menu options
Next: Tuto tiny-ecs 2d platformer Part 5 ePlayer1
Tutorial - tiny-ecs 2d platformer