Difference between revisions of "Tiled Map Editor"
m (→BUILD YOUR MAP) |
|||
(10 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
− | + | == Description == | |
+ | On this page you will find various classes to help using Tiled Map Editor in Gideros. Enjoy ''':-)''' | ||
+ | |||
'''note:''' you may have to provide your own assets (fonts, gfx, …) | '''note:''' you may have to provide your own assets (fonts, gfx, …) | ||
− | == | + | == Tiled Map Editor == |
− | + | === '''LOAD YOUR MAP''' === | |
+ | Here is a class to load your Tiled map in Gideros. | ||
+ | |||
+ | '''note:''' '''world''' is used for Box2D ('''LiquidFun''') or '''CBUMP''' but you can remove it and adapt to your needs | ||
+ | |||
+ | <syntaxhighlight lang="lua"> | ||
+ | LevelX = Core.class(Sprite) | ||
+ | |||
+ | function LevelX:init() | ||
+ | application:setBackgroundColor(0x0) | ||
+ | -- the tiled world | ||
+ | self.tiled_level = Tiled_Levels.new(self.world, "tiled/levels/level01.lua") | ||
+ | self:addChild(self.tiled_level) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Below is the '''Tiled_Levels''' class. | ||
+ | |||
+ | === '''BUILD YOUR MAP''' === | ||
+ | Here is the class to import the levels you created in Tiled and draw them in Gideros using the '''RECTANGLES''', '''POLYGONS''', '''ELLIPSES''' classes you will find further below on this page. | ||
+ | |||
+ | '''note:''' don't forget to exclude from execution your level lua file (right click -> exclude from execution) | ||
+ | |||
+ | '''note2:''' those are placeholder names ('''bg''' = background layer, '''mg''' = middle ground layer...) adapt to your needs | ||
+ | |||
+ | <syntaxhighlight lang="lua"> | ||
+ | Tiled_Levels = Core.class(Sprite) | ||
+ | |||
+ | function Tiled_Levels:init(xworld, xtiledlevelpath) | ||
+ | self.world = xworld | ||
+ | self.camera = Sprite.new() | ||
+ | self.bg = Sprite.new() -- bg deco | ||
+ | self.mg = Sprite.new() -- mg level + sensors | ||
+ | self.fg = Sprite.new() -- fg deco | ||
+ | self.camera:addChild(self.bg) | ||
+ | self.camera:addChild(self.mg) | ||
+ | self.camera:addChild(self.fg) | ||
+ | self:addChild(self.camera) | ||
+ | -- load the tiled level | ||
+ | local tiledlevel = loadfile(xtiledlevelpath)() | ||
+ | -- the tiled map size | ||
+ | local mapwidth, mapheight = tiledlevel.width * tiledlevel.tilewidth, tiledlevel.height * tiledlevel.tileheight | ||
+ | print("map size "..mapwidth..", "..mapheight, "app size "..myappwidth..", "..myappheight, "all in pixels.") | ||
+ | -- parse the tiled level | ||
+ | local layers = tiledlevel.layers | ||
+ | local myshape -- shapes from Tiled | ||
+ | local mytable -- intermediate table for shapes params | ||
+ | for i = 1, #layers do | ||
+ | local layer = layers[i] | ||
+ | -- ************* | ||
+ | -- MG GAME LAYER | ||
+ | -- ************* | ||
+ | if layer.name == "mg" then -- your Tiled layer name here! | ||
+ | local levelsetup = {} | ||
+ | local objects = layer.objects | ||
+ | for i = 1, #objects do | ||
+ | local object = objects[i] | ||
+ | local objectName = object.name | ||
+ | myshape, mytable = nil, nil | ||
+ | -- *************** | ||
+ | -- SHAPES | ||
+ | -- *************** | ||
+ | if objectName == "groundA" then | ||
+ | mytable = {color=0x00ff00, isshape=true} | ||
+ | else | ||
+ | mytable = {color=0xff0000, isshape=true} | ||
+ | end | ||
+ | levelsetup = {} | ||
+ | for k, v in pairs(mytable) do levelsetup[k] = v end | ||
+ | self:buildShapes(layer.name, object, levelsetup) | ||
+ | -- *************** | ||
+ | -- OBJECTS (players, npc, nmes...) | ||
+ | -- *************** | ||
+ | if objectName == "player1" then | ||
+ | self.world.player = LF_Dynamic_Player.new( | ||
+ | self.world, | ||
+ | "gfx/playable/caverman.png", "player", 6, 7, | ||
+ | 1, 1, | ||
+ | nil, nil, 0, -5, | ||
+ | 1, 0, 1, | ||
+ | G_BITPLAYER, playercollisions, G_PLAYER) | ||
+ | self.world.player.body:setPosition(object.x, object.y) -- position the body, the bitmap will follow in enterframe | ||
+ | self.mg:addChild(self.world.player) | ||
+ | end | ||
+ | end | ||
+ | -- ************* | ||
+ | -- WHAT?! | ||
+ | -- ************* | ||
+ | else | ||
+ | print("WHAT?!", layer.name) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | function Tiled_Levels:buildShapes(xlayer, xobject, xlevelsetup) | ||
+ | local myshape = nil | ||
+ | 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(self.world, 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(self.world, 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(self.world, tablebase) | ||
+ | else | ||
+ | print("*** CANNOT PROCESS THIS SHAPE! ***", xobject.shape, xobject.name) | ||
+ | return | ||
+ | end | ||
+ | myshape:setPosition(xobject.x, xobject.y) | ||
+ | if xlayer == "bg" then -- your Tiled layer name here | ||
+ | self.bg:addChild(myshape) | ||
+ | else -- mg = player layer + sensors layer... | ||
+ | self.mg:addChild(myshape) | ||
+ | end | ||
+ | end | ||
+ | </syntaxhighlight> | ||
== Tiled Map Editor: OBJECT LAYER == | == Tiled Map Editor: OBJECT LAYER == | ||
− | Here are | + | Here are the classes to import the shapes your created in Tiled and draw them in Gideros. |
− | '''note:''' don't forget to export your Tiled map to a lua file | + | |
+ | '''note:''' don't forget to export your Tiled map to a lua file and exclude it from execution (in Gideros Studio) | ||
=== '''RECTANGLES''' === | === '''RECTANGLES''' === | ||
− | < | + | <syntaxhighlight lang="lua"> |
Tiled_Shape_Rectangle = Core.class(Sprite) | Tiled_Shape_Rectangle = Core.class(Sprite) | ||
Line 146: | Line 278: | ||
end | end | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
=== '''POLYGONS''' === | === '''POLYGONS''' === | ||
− | < | + | <syntaxhighlight lang="lua"> |
Tiled_Shape_Polygon = Core.class(Sprite) | Tiled_Shape_Polygon = Core.class(Sprite) | ||
Line 316: | Line 448: | ||
end | end | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
=== '''ELLIPSES''' === | === '''ELLIPSES''' === | ||
− | < | + | <syntaxhighlight lang="lua"> |
Tiled_Shape_Ellipse = Core.class(Sprite) | Tiled_Shape_Ellipse = Core.class(Sprite) | ||
Line 341: | Line 473: | ||
params.type = xparams.type or nil -- default = b2.STATIC_BODY | params.type = xparams.type or nil -- default = b2.STATIC_BODY | ||
params.fixedrotation = xparams.fixedrotation or nil | params.fixedrotation = xparams.fixedrotation or nil | ||
− | params.density = xparams.density or | + | params.density = xparams.density or 0 |
params.restitution = xparams.restitution or nil | params.restitution = xparams.restitution or nil | ||
params.friction = xparams.friction or nil | params.friction = xparams.friction or nil | ||
Line 418: | Line 550: | ||
if params.w < params.h then -- capsule up | if params.w < params.h then -- capsule up | ||
-- print("capsule up") | -- print("capsule up") | ||
− | local shape = b2.CircleShape.new(params.w / 2, params. | + | local shape = b2.CircleShape.new(params.w / 2, 0 + params.w / 2, params.w / 2) -- (centerx, centery, radius) |
local shape2 = b2.CircleShape.new(params.w / 2, params.h - params.w / 2, params.w / 2) -- (centerx, centery, radius) | local shape2 = b2.CircleShape.new(params.w / 2, params.h - params.w / 2, params.w / 2) -- (centerx, centery, radius) | ||
local shape3 = b2.EdgeShape.new(0, params.w / 2, 0, params.h - (params.w / 2)) -- (x1,y1,x2,y2) | local shape3 = b2.EdgeShape.new(0, params.w / 2, 0, params.h - (params.w / 2)) -- (x1,y1,x2,y2) | ||
Line 449: | Line 581: | ||
elseif params.w > params.h then -- capsule laid | elseif params.w > params.h then -- capsule laid | ||
-- print("capsule laid") | -- print("capsule laid") | ||
− | local shape = b2.CircleShape.new(params. | + | local shape = b2.CircleShape.new(0 + params.h / 2, params.h/2, params.h/2) -- (centerx, centery, radius) |
− | local shape2 = b2.CircleShape.new(params.w - params. | + | local shape2 = b2.CircleShape.new(params.w - params.h / 2, params.h/2, params.h/2) -- (centerx, centery, radius) |
local shape3 = b2.EdgeShape.new(params.h / 2, 0, params.w - (params.h / 2), 0) -- (x1,y1,x2,y2) | local shape3 = b2.EdgeShape.new(params.h / 2, 0, params.w - (params.h / 2), 0) -- (x1,y1,x2,y2) | ||
local shape4 = b2.EdgeShape.new(params.h / 2, params.h, params.w - (params.h / 2), params.h) -- (x1,y1,x2,y2) | local shape4 = b2.EdgeShape.new(params.h / 2, params.h, params.w - (params.h / 2), params.h) -- (x1,y1,x2,y2) | ||
Line 514: | Line 646: | ||
end | end | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
+ | |||
+ | == Tiled Map Editor: IMAGE LAYER == | ||
+ | It is the same as above: | ||
+ | *we load the Tiled level in the scene (our level) | ||
+ | *we have a class which will load our images from Tiled to our level | ||
+ | |||
+ | === '''Our Level''' === | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | LevelX = Core.class(Sprite) | ||
+ | |||
+ | function LevelX:init() | ||
+ | -- b2d world | ||
+ | if g_currentlevel == 1 then self.world = b2.World.new(0, 20, true) -- XXX | ||
+ | elseif g_currentlevel == 2 then self.world = b2.World.new(0, 20, true) -- XXX | ||
+ | else self.world = b2.World.new(0, 20, true) -- XXX | ||
+ | end | ||
+ | -- lists | ||
+ | self.world.grounds = {} | ||
+ | self.world.groundnmes = {} | ||
+ | self.world.airnmes = {} | ||
+ | -- ... | ||
+ | -- the tiled level | ||
+ | tiled_levels = {} | ||
+ | tiled_levels[1] = loadfile("tiled/levels/forest01.lua")() | ||
+ | tiled_levels[2] = loadfile("tiled/levels/mountain_village.lua")() | ||
+ | -- ... | ||
+ | g_currentlevel = 1 | ||
+ | self.tiled_level = Tiled_Levels.new(self.world, tiled_levels[g_currentlevel]) | ||
+ | -- ... | ||
+ | -- order | ||
+ | self:addChild(self.tiled_level ) | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === '''IMAGES''' === | ||
+ | Here is the class to import images from Tiled and draw them in your game. | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | Tiled_Levels = Core.class(Sprite) | ||
+ | |||
+ | function Tiled_Levels:init(xworld, xtiledlevel) | ||
+ | self.world = xworld | ||
+ | -- the Tiled map size, I need the Tiled map dimensions for the parallax | ||
+ | self.mapwidth, self.mapheight = xtiledlevel.width * xtiledlevel.tilewidth, | ||
+ | xtiledlevel.height * xtiledlevel.tileheight | ||
+ | print("map size "..self.mapwidth..", "..self.mapheight, | ||
+ | "app size "..myappwidth..", "..myappheight, "all in pixels.") | ||
+ | -- camera | ||
+ | self.camera = Sprite.new() | ||
+ | -- game layers | ||
+ | self.bg2_backB = Sprite.new() -- game bg: nmes, deco, ... | ||
+ | self.bg2_backA = Sprite.new() -- game bg: nmes, deco, ... | ||
+ | self.bg2_base = Sprite.new() -- game bg: nmes, deco, ... | ||
+ | self.bg2_frontA = Sprite.new() -- game bg: nmes, deco, ... | ||
+ | -- ... | ||
+ | -- order | ||
+ | self.camera:addChild(self.bg2_backB) | ||
+ | self.camera:addChild(self.bg2_backA) | ||
+ | self.camera:addChild(self.bg2_base) | ||
+ | self.camera:addChild(self.bg2_frontA) | ||
+ | self.camera:addChild(self.bg2_frontB) | ||
+ | -- ... | ||
+ | -- final | ||
+ | self:addChild(self.camera) | ||
+ | |||
+ | -- put all the Tiled Tileset images in a table | ||
+ | local tilesetimages = {} | ||
+ | local tilesets = xtiledlevel.tilesets | ||
+ | for i = 1, #tilesets do | ||
+ | local tileset = tilesets[i] | ||
+ | -- TILESETS | ||
+ | -- ******** | ||
+ | if tileset.name == "images" then -- your Tiled Tileset! name here | ||
+ | local tiles = tileset.tiles | ||
+ | for i = 1, #tiles do | ||
+ | tilesetimages[tiles[i].id+1] = { | ||
+ | path=tiles[i].image, | ||
+ | width=tiles[i].width, | ||
+ | height=tiles[i].height, | ||
+ | } | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | |||
+ | -- parse the Tiled level | ||
+ | local layers = xtiledlevel.layers | ||
+ | local myshape -- shapes from Tiled | ||
+ | local mytable -- intermediate table for shapes params | ||
+ | for i = 1, #layers do | ||
+ | local layer = layers[i] | ||
+ | |||
+ | -- PLAYABLE | ||
+ | -- ******** | ||
+ | -- ... | ||
+ | |||
+ | -- GROUPED IMAGES | ||
+ | -- ************** | ||
+ | local path = "tiled/levels/" | ||
+ | if layer.name == "IMAGES" then -- group | ||
+ | local layers2 = layer.layers | ||
+ | for i = 1, #layers2 do | ||
+ | local layer2 = layers2[i] | ||
+ | if layer2.name == "bg_images_deco" then | ||
+ | local objects = layer2.objects | ||
+ | for i = 1, #objects do | ||
+ | local object = objects[i] | ||
+ | local tex = Texture.new(path..tilesetimages[object.gid].path, false) | ||
+ | local bitmap = Bitmap.new(tex) | ||
+ | bitmap:setAnchorPoint(0, 1) -- because I always forget to set Tiled object alignment | ||
+ | local scalex, scaley = object.width / tex:getWidth(), object.height / tex:getHeight() | ||
+ | bitmap:setScale(scalex, scaley) | ||
+ | bitmap:setPosition(object.x, object.y) | ||
+ | bitmap:setRotation(object.rotation) | ||
+ | self.bg2_base:addChild(bitmap) | ||
+ | end | ||
+ | elseif layer2.name == "fg_images_deco" then | ||
+ | local objects = layer2.objects | ||
+ | for i = 1, #objects do | ||
+ | local object = objects[i] | ||
+ | local tex = Texture.new(path..tilesetimages[object.gid].path, false) | ||
+ | local bitmap = Bitmap.new(tex) | ||
+ | bitmap:setAnchorPoint(0, 1) -- because I always forget to set Tiled object alignment | ||
+ | local scalex, scaley = object.width / tex:getWidth(), object.height / tex:getHeight() | ||
+ | bitmap:setScale(scalex, scaley) | ||
+ | bitmap:setPosition(object.x, object.y) | ||
+ | bitmap:setRotation(object.rotation) | ||
+ | self.fg2_base:addChild(bitmap) | ||
+ | end | ||
+ | elseif layer2.name == "static_images" then | ||
+ | -- ... | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 20:09, 26 November 2024
Description
On this page you will find various classes to help using Tiled Map Editor in Gideros. Enjoy :-)
note: you may have to provide your own assets (fonts, gfx, …)
Tiled Map Editor
LOAD YOUR MAP
Here is a class to load your Tiled map in Gideros.
note: world is used for Box2D (LiquidFun) or CBUMP but you can remove it and adapt to your needs
LevelX = Core.class(Sprite)
function LevelX:init()
application:setBackgroundColor(0x0)
-- the tiled world
self.tiled_level = Tiled_Levels.new(self.world, "tiled/levels/level01.lua")
self:addChild(self.tiled_level)
end
Below is the Tiled_Levels class.
BUILD YOUR MAP
Here is the class to import the levels you created in Tiled and draw them in Gideros using the RECTANGLES, POLYGONS, ELLIPSES classes you will find further below on this page.
note: don't forget to exclude from execution your level lua file (right click -> exclude from execution)
note2: those are placeholder names (bg = background layer, mg = middle ground layer...) adapt to your needs
Tiled_Levels = Core.class(Sprite)
function Tiled_Levels:init(xworld, xtiledlevelpath)
self.world = xworld
self.camera = Sprite.new()
self.bg = Sprite.new() -- bg deco
self.mg = Sprite.new() -- mg level + sensors
self.fg = Sprite.new() -- fg deco
self.camera:addChild(self.bg)
self.camera:addChild(self.mg)
self.camera:addChild(self.fg)
self:addChild(self.camera)
-- load the tiled level
local tiledlevel = loadfile(xtiledlevelpath)()
-- the tiled map size
local mapwidth, mapheight = tiledlevel.width * tiledlevel.tilewidth, tiledlevel.height * tiledlevel.tileheight
print("map size "..mapwidth..", "..mapheight, "app size "..myappwidth..", "..myappheight, "all in pixels.")
-- parse the tiled level
local layers = tiledlevel.layers
local myshape -- shapes from Tiled
local mytable -- intermediate table for shapes params
for i = 1, #layers do
local layer = layers[i]
-- *************
-- MG GAME LAYER
-- *************
if layer.name == "mg" then -- your Tiled layer name here!
local levelsetup = {}
local objects = layer.objects
for i = 1, #objects do
local object = objects[i]
local objectName = object.name
myshape, mytable = nil, nil
-- ***************
-- SHAPES
-- ***************
if objectName == "groundA" then
mytable = {color=0x00ff00, isshape=true}
else
mytable = {color=0xff0000, isshape=true}
end
levelsetup = {}
for k, v in pairs(mytable) do levelsetup[k] = v end
self:buildShapes(layer.name, object, levelsetup)
-- ***************
-- OBJECTS (players, npc, nmes...)
-- ***************
if objectName == "player1" then
self.world.player = LF_Dynamic_Player.new(
self.world,
"gfx/playable/caverman.png", "player", 6, 7,
1, 1,
nil, nil, 0, -5,
1, 0, 1,
G_BITPLAYER, playercollisions, G_PLAYER)
self.world.player.body:setPosition(object.x, object.y) -- position the body, the bitmap will follow in enterframe
self.mg:addChild(self.world.player)
end
end
-- *************
-- WHAT?!
-- *************
else
print("WHAT?!", layer.name)
end
end
end
function Tiled_Levels:buildShapes(xlayer, xobject, xlevelsetup)
local myshape = nil
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(self.world, 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(self.world, 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(self.world, tablebase)
else
print("*** CANNOT PROCESS THIS SHAPE! ***", xobject.shape, xobject.name)
return
end
myshape:setPosition(xobject.x, xobject.y)
if xlayer == "bg" then -- your Tiled layer name here
self.bg:addChild(myshape)
else -- mg = player layer + sensors layer...
self.mg:addChild(myshape)
end
end
Tiled Map Editor: OBJECT LAYER
Here are the classes to import the shapes your created in Tiled and draw them in Gideros.
note: don't forget to export your Tiled map to a lua file and exclude it from execution (in Gideros Studio)
RECTANGLES
Tiled_Shape_Rectangle = Core.class(Sprite)
function Tiled_Shape_Rectangle:init(xworld, xparams)
-- params
local params = xparams or {}
params.x = xparams.x or nil
params.y = xparams.y or nil
params.w = xparams.w or 32
params.h = xparams.h or 32
params.color = xparams.color or nil
params.alpha = xparams.alpha or 1
params.tex = xparams.tex or nil
params.isdeco = xparams.isdeco or nil
params.isshape = xparams.isshape or nil
params.isbmp = xparams.isbmp or nil
params.ispixel = xparams.ispixel or nil
params.scalex = xparams.scalex or 1
params.scaley = xparams.scaley or params.scalex
params.rotation = xparams.rotation or 0
params.type = xparams.type or nil -- default = b2.STATIC_BODY
params.fixedrotation = xparams.fixedrotation or nil
params.density = xparams.density or nil
params.restitution = xparams.restitution or nil
params.friction = xparams.friction or nil
params.issensor = xparams.issensor or nil
params.gravityscale = xparams.gravityscale or 1
params.BIT = xparams.BIT or nil
params.COLBIT = xparams.COLBIT or nil
params.NAME = xparams.NAME or nil
params.addenterframe = xparams.addenterframe or nil
-- image
if params.issensor then
-- no image for sensors
else
if params.isshape then
self.img = Shape.new()
self.img:setLineStyle(4, 0x0000ff, 1) -- change here (width, color, alpha)
if params.tex then
local tex = Texture.new(params.tex, false, {wrap = Texture.REPEAT})
local matrix = Matrix.new(params.scalex, 0, 0, params.scaley, 0, 0)
self.img:setFillStyle(Shape.TEXTURE, tex, matrix)
tex = nil
else
self.img:setFillStyle(Shape.SOLID, params.color)
end
self.img:beginPath()
self.img:moveTo(0, 0)
self.img:lineTo(params.w, 0)
self.img:lineTo(params.w, params.h)
self.img:lineTo(0, params.h)
self.img:lineTo(0, 0)
self.img:endPath()
if params.rotation < 0 then self.img:setAnchorPoint(0, -0.5) end
if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
self.img:setRotation(params.rotation)
self.img:setAlpha(params.alpha)
end
if params.isbmp then
if not params.tex then print("!!!YOU MUST PROVIDE A TEXTURE FOR THE BITMAP!!!") return end
local tex = Texture.new(params.tex, false)
self.img = Bitmap.new(tex)
self.img.isbmp = true
self.img.w, self.img.h = params.w, params.h
if params.rotation < 0 then self.img:setAnchorPoint(0, 0.5) end
-- if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
self.img:setAlpha(params.alpha)
tex = nil
end
if params.ispixel then
if params.tex then
local tex = Texture.new(params.tex, false, {wrap = TextureBase.REPEAT})
self.img = Pixel.new(tex, params.w, params.h)
self.img.ispixel = true
self.img.w, self.img.h = params.w, params.h
self.img:setScale(params.scalex, params.scaley)
if params.rotation < 0 then self.img:setAnchorPoint(0, -0.5) end
if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
self.img:setRotation(params.rotation)
self.img:setAlpha(params.alpha)
tex = nil
else
self.img = Pixel.new(params.color, 1, params.w, params.h)
self.img.ispixel = true
self.img.w, self.img.h = params.w, params.h
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
self.img:setAlpha(params.alpha)
end
end
-- debug
if self.img then
if xworld.isdebug then self.img:setAlpha(0.5) end
self:addChild(self.img)
end
end
-- body
self.body = xworld:createBody { type = params.type } -- b2.STATIC_BODY, b2.KINEMATIC_BODY, b2.DYNAMIC_BODY
self.body:setGravityScale(params.gravityscale)
self.body.name = params.NAME
self.body.isdirty = false
self.body:setFixedRotation(params.fixedrotation)
local shape = b2.PolygonShape.new()
shape:setAsBox(params.w/2, params.h/2, params.w/2, params.h/2, ^<params.rotation) -- (half w, half h, centerx, centery, rotation)
local fixture = self.body:createFixture {
shape = shape,
density = params.density, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local filterData = { categoryBits = params.BIT, maskBits = params.COLBIT, groupIndex = 0 }
fixture:setFilterData(filterData)
-- clean up?
filterData = nil
fixture = nil
shape = nil
-- listeners?
if params.addenterframe then
self.world = xworld
self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
end
function Tiled_Shape_Rectangle:onEnterFrame()
-- your logic here
end
function Tiled_Shape_Rectangle:setPosition(xposx, xposy)
if self.body then
self.body:setPosition(xposx, xposy)
if self.img then self.img:setPosition(self.body:getPosition()) end
else
if self.img then self.img:setPosition(xposx, xposy) end
end
end
POLYGONS
Tiled_Shape_Polygon = Core.class(Sprite)
function Tiled_Shape_Polygon:init(xworld, xparams)
-- params
local params = xparams or {}
params.x = xparams.x or nil
params.y = xparams.y or nil
params.coords = xparams.coords or nil
params.color = xparams.color or nil
params.tex = xparams.tex or nil
params.isshape = xparams.isshape or nil
params.isbmp = xparams.isbmp or nil
params.ispixel = xparams.ispixel or nil
params.isdeco = xparams.isdeco or nil
params.scalex = xparams.scalex or 1
params.scaley = xparams.scaley or params.scalex
params.rotation = xparams.rotation or 0
params.type = xparams.type or nil -- default = b2.STATIC_BODY
params.fixedrotation = xparams.fixedrotation or true
params.density = xparams.density or nil
params.restitution = xparams.restitution or nil
params.friction = xparams.friction or nil
params.gravityscale = xparams.gravityscale or 1
params.issensor = xparams.issensor or false
params.BIT = xparams.BIT or nil
params.COLBIT = xparams.COLBIT or nil
params.NAME = xparams.NAME or nil
params.addenterframe = xparams.addenterframe or nil
-- image
if params.issensor then
-- no image for sensors
else
if params.isshape then
self.img = Shape.new()
self.img:setLineStyle(4, 0x0000ff, 1) -- change here (width, color, alpha)
if params.tex then
local tex = Texture.new(params.tex, false, {wrap = TextureBase.REPEAT})
local matrix = Matrix.new(params.scalex, 0, 0, params.scaley, 0, 0)
self.img:setFillStyle(Shape.TEXTURE, tex, matrix)
tex = nil
else
self.img:setFillStyle(Shape.SOLID, params.color)
end
self.img:beginPath()
self.img:moveTo(params.coords[1].x, params.coords[1].y)
for p = 2, #params.coords do
self.img:lineTo(params.coords[p].x, params.coords[p].y)
end
self.img:closePath()
self.img:endPath()
self.img:setRotation(params.rotation)
self.w, self.h = self.img:getWidth(), self.img:getHeight()
end
if params.isbmp then
if not params.tex then print("!!!YOU MUST PROVIDE A TEXTURE FOR THE BITMAP!!!") return end
-- calculate polygon width and height
local minx, maxx, miny, maxy = 0, 0, 0, 0
for k, v in pairs(params.coords) do
--print("polygon coords", k, v.x, v.y)
if v.x < minx then minx = v.x end
if v.y < miny then miny = v.y end
if v.x > maxx then maxx = v.x end
if v.y > maxy then maxy = v.y end
end
local pw, ph = maxx - minx, maxy - miny -- the polygon dimensions
local tex = Texture.new(params.tex, false)
self.img = Bitmap.new(tex)
self.img.isbmp = true
self.img.w, self.img.h = pw, ph
self.img:setAnchorPoint(0.5, 0.5)
if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
if params.rotation < 0 then self.img:setAnchorPoint(0.5, 1) end
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
tex = nil
end
if params.ispixel then
if params.tex then
-- calculate polygon width and height
local minx, maxx, miny, maxy = 0, 0, 0, 0
for k, v in pairs(params.coords) do
--print("polygon coords", k, v.x, v.y)
if v.x < minx then minx = v.x end
if v.y < miny then miny = v.y end
if v.x > maxx then maxx = v.x end
if v.y > maxy then maxy = v.y end
end
local pw, ph = maxx - minx, maxy - miny -- the polygon dimensions
local tex = Texture.new(params.tex, false, {wrap = TextureBase.REPEAT})
self.img = Pixel.new(tex, pw, ph)
self.img.ispixel = true
self.img.w, self.img.h = pw, ph
self.img:setAnchorPoint(0, -0.5) -- 0.5, 0.5
if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
if params.rotation < 0 then self.img:setAnchorPoint(0.5, 1) end
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
self.img:setTexturePosition(0, 0)
tex = nil
else
-- calculate polygon width and height
local minx, maxx, miny, maxy = 0, 0, 0, 0
for k, v in pairs(params.coords) do
--print("polygon coords", k, v.x, v.y)
if v.x < minx then minx = v.x end
if v.y < miny then miny = v.y end
if v.x > maxx then maxx = v.x end
if v.y > maxy then maxy = v.y end
end
local pw, ph = maxx - minx, maxy - miny -- the polygon dimensions
self.img = Pixel.new(params.color, 1, pw, ph)
self.img.ispixel = true
self.img.w, self.img.h = pw, ph
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
end
end
-- debug
if self.img then
if xworld.isdebug then self.img:setAlpha(0.5) end
self:addChild(self.img)
end
end
-- body
self.body = xworld:createBody {type = params.type} -- b2.STATIC_BODY, b2.KINEMATIC_BODY, b2.DYNAMIC_BODY
self.body:setGravityScale(params.gravityscale)
self.body.name = params.NAME
self.body.isdirty = false
self.body:setFixedRotation(params.fixedrotation)
local shape = b2.ChainShape.new()
local cs = {}
for c = 1, #params.coords do
cs[#cs+1] = params.coords[c].x
cs[#cs+1] = params.coords[c].y
end
shape:createLoop(unpack(cs)) -- XXX
local fixture = self.body:createFixture {
shape = shape,
density = params.density, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local filterData = { categoryBits = params.BIT, maskBits = params.COLBIT, groupIndex = 0 }
fixture:setFilterData(filterData)
-- clean up?
filterData = nil
fixture = nil
cs = nil
shape = nil
-- listeners?
if params.addenterframe then
self.world = xworld
self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
end
function Tiled_Shape_Polygon:onEnterFrame()
-- your logic here
end
function Tiled_Shape_Polygon:setPosition(xposx, xposy)
if self.body then
self.body:setPosition(xposx, xposy)
if self.img then self.img:setPosition(self.body:getPosition()) end
else
if self.img then self.img:setPosition(xposx, xposy) end
end
end
ELLIPSES
Tiled_Shape_Ellipse = Core.class(Sprite)
function Tiled_Shape_Ellipse:init(xworld, xparams)
-- params
local params = xparams or {}
params.x = xparams.x or nil
params.y = xparams.y or nil
params.w = xparams.w or 32
params.h = xparams.h or 32
params.steps = xparams.steps or 16 -- 24
params.color = xparams.color or nil
params.tex = xparams.tex or nil
params.isdeco = xparams.isdeco or nil
params.isshape = xparams.isshape or nil
params.isbmp = xparams.isbmp or nil
params.ispixel = xparams.ispixel or nil
params.scalex = xparams.scalex or 1
params.scaley = xparams.scaley or params.scalex
params.rotation = xparams.rotation or 0
params.type = xparams.type or nil -- default = b2.STATIC_BODY
params.fixedrotation = xparams.fixedrotation or nil
params.density = xparams.density or 0
params.restitution = xparams.restitution or nil
params.friction = xparams.friction or nil
params.gravityscale = xparams.gravityscale or 1
params.issensor = xparams.issensor or nil
params.BIT = xparams.BIT or nil
params.COLBIT = xparams.COLBIT or nil
params.NAME = xparams.NAME or nil
params.addenterframe = xparams.addenterframe or nil
-- image
if params.issensor then
-- no image for sensors
else
local sin, cos, d2r = math.sin, math.cos, math.pi / 180
if params.isshape then
self.img = Shape.new()
self.img:setLineStyle(4, 0x0000ff, 1) -- change here (width, color, alpha)
if params.tex then
local tex = Texture.new(params.tex, false, {wrap = Texture.REPEAT})
local matrix = Matrix.new(params.scalex, 0, 0, params.scaley, 0, 0)
self.img:setFillStyle(Shape.TEXTURE, tex, matrix)
tex = nil
else
self.img:setFillStyle(Shape.SOLID, params.color)
end
self.img:beginPath()
for i = 0, 360, 360 / params.steps do
self.img:lineTo((params.w / 2) + params.w / 2 * sin(i * d2r), (params.h / 2) + params.h / 2 * cos(i * d2r))
end
self.img:endPath()
self.img:setRotation(params.rotation)
end
if params.isbmp then
if not params.tex then print("!!!YOU MUST PROVIDE A TEXTURE FOR THE BITMAP!!!") return end
local tex = Texture.new(params.tex, false)
self.img = Bitmap.new(tex)
self.img.w, self.img.h = params.w, params.h
if params.rotation > 0 then self.img:setAnchorPoint(0, 0.5) end
if params.rotation < 0 then self.img:setAnchorPoint(0.5, 1) end
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
-- print("bmp", params.w, params.h)
tex = nil
end
if params.ispixel then
if params.tex then
local tex = Texture.new(params.tex, false, {wrap = TextureBase.REPEAT})
self.img = Pixel.new(tex, params.w, params.h)
self.img.ispixel = true
self.img.w, self.img.h = params.w, params.h
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
-- print("pixel", params.w, params.h, params.rotation)
tex = nil
else
self.img = Pixel.new(0xff0000, 1, params.w, params.h)
self.img.ispixel = true
self.img.w, self.img.h = params.w, params.h
self.img:setScale(params.scalex, params.scaley)
self.img:setRotation(params.rotation)
-- print("pixel", params.w, params.h, params.rotation)
end
end
-- debug
if self.img then
if xworld.isdebug then self.img:setAlpha(0.5) end
self:addChild(self.img)
end
end
-- body
self.body = xworld:createBody { type = params.type } -- b2.STATIC_BODY, b2.KINEMATIC_BODY, b2.DYNAMIC_BODY
self.body:setGravityScale(params.gravityscale)
self.body.name = params.NAME
self.body.isdirty = false
self.body:setFixedRotation(params.fixedrotation)
if params.w < params.h then -- capsule up
-- print("capsule up")
local shape = b2.CircleShape.new(params.w / 2, 0 + params.w / 2, params.w / 2) -- (centerx, centery, radius)
local shape2 = b2.CircleShape.new(params.w / 2, params.h - params.w / 2, params.w / 2) -- (centerx, centery, radius)
local shape3 = b2.EdgeShape.new(0, params.w / 2, 0, params.h - (params.w / 2)) -- (x1,y1,x2,y2)
local shape4 = b2.EdgeShape.new(params.w, params.w / 2, params.w, params.h - (params.w / 2)) -- (x1,y1,x2,y2)
local fixture = self.body:createFixture {
shape = shape,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture2 = self.body:createFixture {
shape = shape2,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture3 = self.body:createFixture {
shape = shape3,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture4 = self.body:createFixture {
shape = shape4,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local filterData = { categoryBits = params.BIT, maskBits = params.COLBIT, groupIndex = 0 }
fixture:setFilterData(filterData)
fixture2:setFilterData(filterData)
fixture3:setFilterData(filterData)
fixture4:setFilterData(filterData)
-- clean up?
filterData = nil
fixture, fixture2, fixture3, fixture4 = nil, nil,nil, nil
shape, shape2, shape3, shape4 = nil, nil, nil, nil
elseif params.w > params.h then -- capsule laid
-- print("capsule laid")
local shape = b2.CircleShape.new(0 + params.h / 2, params.h/2, params.h/2) -- (centerx, centery, radius)
local shape2 = b2.CircleShape.new(params.w - params.h / 2, params.h/2, params.h/2) -- (centerx, centery, radius)
local shape3 = b2.EdgeShape.new(params.h / 2, 0, params.w - (params.h / 2), 0) -- (x1,y1,x2,y2)
local shape4 = b2.EdgeShape.new(params.h / 2, params.h, params.w - (params.h / 2), params.h) -- (x1,y1,x2,y2)
local fixture = self.body:createFixture {
shape = shape,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture2 = self.body:createFixture {
shape = shape2,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture3 = self.body:createFixture {
shape = shape3,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local fixture4 = self.body:createFixture {
shape = shape4,
density = params.density * 0.25, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local filterData = { categoryBits = params.BIT, maskBits = params.COLBIT, groupIndex = 0 }
fixture:setFilterData(filterData)
fixture2:setFilterData(filterData)
fixture3:setFilterData(filterData)
fixture4:setFilterData(filterData)
-- clean up?
filterData = nil
fixture, fixture2, fixture3, fixture4 = nil, nil,nil, nil
shape, shape2, shape3, shape4 = nil, nil, nil, nil
else -- circle
-- print("circle")
local shape = b2.CircleShape.new(params.w / 2, params.h / 2, params.w / 2) -- (centerx, centery, radius)
local fixture = self.body:createFixture {
shape = shape,
density = params.density, restitution = params.restitution, friction = params.friction, isSensor = params.issensor
}
local filterData = { categoryBits = params.BIT, maskBits = params.COLBIT, groupIndex = 0 }
fixture:setFilterData(filterData)
-- clean up?
filterData = nil
fixture = nil
shape = nil
end
-- rotation
local rot = math.rad(params.rotation)
self.body:setAngle(rot)
-- listeners?
if params.addenterframe then
self.world = xworld
self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
end
function Tiled_Shape_Ellipse:onEnterFrame()
-- your logic here
end
function Tiled_Shape_Ellipse:setPosition(xposx, xposy)
if self.body then
self.body:setPosition(xposx, xposy)
if self.img then self.img:setPosition(self.body:getPosition()) end
else
if self.img then self.img:setPosition(xposx, xposy) end
end
end
Tiled Map Editor: IMAGE LAYER
It is the same as above:
- we load the Tiled level in the scene (our level)
- we have a class which will load our images from Tiled to our level
Our Level
LevelX = Core.class(Sprite)
function LevelX:init()
-- b2d world
if g_currentlevel == 1 then self.world = b2.World.new(0, 20, true) -- XXX
elseif g_currentlevel == 2 then self.world = b2.World.new(0, 20, true) -- XXX
else self.world = b2.World.new(0, 20, true) -- XXX
end
-- lists
self.world.grounds = {}
self.world.groundnmes = {}
self.world.airnmes = {}
-- ...
-- the tiled level
tiled_levels = {}
tiled_levels[1] = loadfile("tiled/levels/forest01.lua")()
tiled_levels[2] = loadfile("tiled/levels/mountain_village.lua")()
-- ...
g_currentlevel = 1
self.tiled_level = Tiled_Levels.new(self.world, tiled_levels[g_currentlevel])
-- ...
-- order
self:addChild(self.tiled_level )
end
IMAGES
Here is the class to import images from Tiled and draw them in your game.
Tiled_Levels = Core.class(Sprite)
function Tiled_Levels:init(xworld, xtiledlevel)
self.world = xworld
-- the Tiled map size, I need the Tiled map dimensions for the parallax
self.mapwidth, self.mapheight = xtiledlevel.width * xtiledlevel.tilewidth,
xtiledlevel.height * xtiledlevel.tileheight
print("map size "..self.mapwidth..", "..self.mapheight,
"app size "..myappwidth..", "..myappheight, "all in pixels.")
-- camera
self.camera = Sprite.new()
-- game layers
self.bg2_backB = Sprite.new() -- game bg: nmes, deco, ...
self.bg2_backA = Sprite.new() -- game bg: nmes, deco, ...
self.bg2_base = Sprite.new() -- game bg: nmes, deco, ...
self.bg2_frontA = Sprite.new() -- game bg: nmes, deco, ...
-- ...
-- order
self.camera:addChild(self.bg2_backB)
self.camera:addChild(self.bg2_backA)
self.camera:addChild(self.bg2_base)
self.camera:addChild(self.bg2_frontA)
self.camera:addChild(self.bg2_frontB)
-- ...
-- final
self:addChild(self.camera)
-- put all the Tiled Tileset images in a table
local tilesetimages = {}
local tilesets = xtiledlevel.tilesets
for i = 1, #tilesets do
local tileset = tilesets[i]
-- TILESETS
-- ********
if tileset.name == "images" then -- your Tiled Tileset! name here
local tiles = tileset.tiles
for i = 1, #tiles do
tilesetimages[tiles[i].id+1] = {
path=tiles[i].image,
width=tiles[i].width,
height=tiles[i].height,
}
end
end
end
-- parse the Tiled level
local layers = xtiledlevel.layers
local myshape -- shapes from Tiled
local mytable -- intermediate table for shapes params
for i = 1, #layers do
local layer = layers[i]
-- PLAYABLE
-- ********
-- ...
-- GROUPED IMAGES
-- **************
local path = "tiled/levels/"
if layer.name == "IMAGES" then -- group
local layers2 = layer.layers
for i = 1, #layers2 do
local layer2 = layers2[i]
if layer2.name == "bg_images_deco" then
local objects = layer2.objects
for i = 1, #objects do
local object = objects[i]
local tex = Texture.new(path..tilesetimages[object.gid].path, false)
local bitmap = Bitmap.new(tex)
bitmap:setAnchorPoint(0, 1) -- because I always forget to set Tiled object alignment
local scalex, scaley = object.width / tex:getWidth(), object.height / tex:getHeight()
bitmap:setScale(scalex, scaley)
bitmap:setPosition(object.x, object.y)
bitmap:setRotation(object.rotation)
self.bg2_base:addChild(bitmap)
end
elseif layer2.name == "fg_images_deco" then
local objects = layer2.objects
for i = 1, #objects do
local object = objects[i]
local tex = Texture.new(path..tilesetimages[object.gid].path, false)
local bitmap = Bitmap.new(tex)
bitmap:setAnchorPoint(0, 1) -- because I always forget to set Tiled object alignment
local scalex, scaley = object.width / tex:getWidth(), object.height / tex:getHeight()
bitmap:setScale(scalex, scaley)
bitmap:setPosition(object.x, object.y)
bitmap:setRotation(object.rotation)
self.fg2_base:addChild(bitmap)
end
elseif layer2.name == "static_images" then
-- ...
end
end
end
end
end