Difference between revisions of "Tiled Map Editor"

From GiderosMobile
 
(3 intermediate revisions by 2 users not shown)
Line 11: Line 11:
 
'''note:''' '''world''' is used for Box2D ('''LiquidFun''') or '''CBUMP''' but you can remove it and adapt to your needs
 
'''note:''' '''world''' is used for Box2D ('''LiquidFun''') or '''CBUMP''' but you can remove it and adapt to your needs
  
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
LevelX = Core.class(Sprite)
 
LevelX = Core.class(Sprite)
  
Line 20: Line 20:
 
self:addChild(self.tiled_level)
 
self:addChild(self.tiled_level)
 
end
 
end
</source>
+
</syntaxhighlight>
  
 
Below is the '''Tiled_Levels''' class.
 
Below is the '''Tiled_Levels''' class.
  
 
=== '''BUILD YOUR MAP''' ===
 
=== '''BUILD YOUR MAP''' ===
Here is the class to import the levels your created in Tiled and draw them in Gideros using the '''RECTANGLES''', '''POLYGONS''', '''ELLIPSES''' classes you will find further below on this page.<br/>
+
Here is the class to import the levels your 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)<br/>
+
 
 +
'''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
 
'''note2:''' those are placeholder names ('''bg''' = background layer, '''mg''' = middle ground layer...) adapt to your needs
  
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
Tiled_Levels = Core.class(Sprite)
 
Tiled_Levels = Core.class(Sprite)
  
Line 133: Line 135:
 
end
 
end
 
end
 
end
</source>
+
</syntaxhighlight>
  
 
== Tiled Map Editor: OBJECT LAYER ==
 
== Tiled Map Editor: OBJECT LAYER ==
Line 141: Line 143:
  
 
=== '''RECTANGLES''' ===
 
=== '''RECTANGLES''' ===
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
Tiled_Shape_Rectangle = Core.class(Sprite)
 
Tiled_Shape_Rectangle = Core.class(Sprite)
  
Line 276: Line 278:
 
end
 
end
 
end
 
end
</source>
+
</syntaxhighlight>
  
 
=== '''POLYGONS''' ===
 
=== '''POLYGONS''' ===
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
Tiled_Shape_Polygon = Core.class(Sprite)
 
Tiled_Shape_Polygon = Core.class(Sprite)
  
Line 446: Line 448:
 
end
 
end
 
end
 
end
</source>
+
</syntaxhighlight>
  
 
=== '''ELLIPSES''' ===
 
=== '''ELLIPSES''' ===
<source lang="lua">
+
<syntaxhighlight lang="lua">
 
Tiled_Shape_Ellipse = Core.class(Sprite)
 
Tiled_Shape_Ellipse = Core.class(Sprite)
  
Line 644: Line 646:
 
end
 
end
 
end
 
end
</source>
+
</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 22:54, 18 November 2023

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 your 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