Difference between revisions of "CBump Template"
From GiderosMobile
|  (added content) | |||
| (16 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
| __TOC__ | __TOC__ | ||
| + | Here you will find various resources to help you create games and apps in Gideros Studio. | ||
| − | + | '''note''': You may have to provide your own assets (fonts, gfx, …). | |
| − | + | === Description === | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | ===  | ||
| − | |||
| − | |||
| This section deals with various ways to use CBump. | This section deals with various ways to use CBump. | ||
| − | |||
| + | === Using CBump and TILED (handles collisions) === | ||
| + | It's easy: you draw your map with Tile then you export your map to a lua file.<br/> | ||
| + | The class handles tile collisions automatically. | ||
| − | + | <syntaxhighlight lang="lua"> | |
| − | |||
| − | |||
| -- usage | -- usage | ||
| --local map = CBumpTiled.new("tiled/test.lua", world, bg, true) | --local map = CBumpTiled.new("tiled/test.lua", world, bg, true) | ||
| -- where world is the cbump world | -- where world is the cbump world | ||
| − | -- bg is a sprite layer (here a background sprite) | + | -- bg is a sprite layer (here a background sprite layer) | 
| − | -- true  | + | -- true if only foreground is added to cbump collision world | 
| CBumpTiled = Core.class(Sprite) | CBumpTiled = Core.class(Sprite) | ||
| Line 37: | Line 30: | ||
| function CBumpTiled:init(filename, xworld, xlayer, xtop) | function CBumpTiled:init(filename, xworld, xlayer, xtop) | ||
| − | |||
| 	-- Bits on the far end of the 32-bit global tile ID are used for tile flags (flip, rotate) | 	-- Bits on the far end of the 32-bit global tile ID are used for tile flags (flip, rotate) | ||
| 	local FLIPPED_HORIZONTALLY_FLAG = 0x80000000; | 	local FLIPPED_HORIZONTALLY_FLAG = 0x80000000; | ||
| Line 60: | Line 52: | ||
| 					if gid ~= 0 then | 					if gid ~= 0 then | ||
| 						-- Read flipping flags | 						-- Read flipping flags | ||
| − | 						flipHor =  | + | 						flipHor = gid & FLIPPED_HORIZONTALLY_FLAG | 
| − | 						flipVer =  | + | 						flipVer = gid & FLIPPED_VERTICALLY_FLAG | 
| − | 						flipDia =  | + | 						flipDia = gid & FLIPPED_DIAGONALLY_FLAG | 
| 						-- Convert flags to gideros style | 						-- Convert flags to gideros style | ||
| 						if(flipHor ~= 0) then flipHor = TileMap.FLIP_HORIZONTAL end | 						if(flipHor ~= 0) then flipHor = TileMap.FLIP_HORIZONTAL end | ||
| Line 68: | Line 60: | ||
| 						if(flipDia ~= 0) then flipDia = TileMap.FLIP_DIAGONAL end | 						if(flipDia ~= 0) then flipDia = TileMap.FLIP_DIAGONAL end | ||
| 						-- Clear the flags from gid so other information is healthy | 						-- Clear the flags from gid so other information is healthy | ||
| − | 						gid =  | + | 						gid = gid & ~ ( | 
| − | + | 							FLIPPED_HORIZONTALLY_FLAG | | |
| − | + | 							FLIPPED_VERTICALLY_FLAG | | |
| − | + | 							FLIPPED_DIAGONALLY_FLAG | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| 						) | 						) | ||
| 					end | 					end | ||
| Line 100: | Line 87: | ||
| 						local ty = math.floor((gid - tileset.firstgid) / tileset.sizex) + 1 | 						local ty = math.floor((gid - tileset.firstgid) / tileset.sizex) + 1 | ||
| 						-- Set the tile with flip info | 						-- Set the tile with flip info | ||
| − | 						tilemap:setTile(x, y, tx, ty,  | + | 						tilemap:setTile(x, y, tx, ty, (flipHor | flipVer | flipDia)) | 
| 						xlayer:addChild(tilemap) | 						xlayer:addChild(tilemap) | ||
| 						xlayer:setAlpha(layer.opacity) | 						xlayer:setAlpha(layer.opacity) | ||
| Line 128: | Line 115: | ||
| 	end | 	end | ||
| end | end | ||
| − | </ | + | </syntaxhighlight> | 
| − | + | '''Usage''' | |
| − | |||
| This is an example of how to set up CBump and TILED. | This is an example of how to set up CBump and TILED. | ||
| − | + | <syntaxhighlight lang="lua"> | |
| − | |||
| − | < | ||
| -- cbump | -- cbump | ||
| local cbump = require "cbump" | local cbump = require "cbump" | ||
| − | local world =  | + | -- Constructor: BumpWorld.new([cellSize = 64]) | 
| + | local world = BumpWorld.new() | ||
| -- camera | -- camera | ||
| local camera = Sprite.new() | local camera = Sprite.new() | ||
| local bg = Sprite.new() | local bg = Sprite.new() | ||
| − | |||
| local fg = Sprite.new() | local fg = Sprite.new() | ||
| − | |||
| camera:addChild(bg) | camera:addChild(bg) | ||
| camera:addChild(fg) | camera:addChild(fg) | ||
| Line 153: | Line 136: | ||
| -- tiled | -- tiled | ||
| local map = CBumpTiled.new("tiled/test.lua", world, bg, true) | local map = CBumpTiled.new("tiled/test.lua", world, bg, true) | ||
| − | </ | + | </syntaxhighlight> | 
| − | < | + | |
| − | < | + | === A Full CBump Platformer Level with enemies, collectibles, exit,... === | 
| + | This class is an example of a full CBump platformer level using TILED as a map editor. Please note that you need to modify the code depending on your assets path. Enjoy! | ||
| + | <syntaxhighlight lang="lua"> | ||
| + | LevelX = Core.class(Sprite) | ||
| + | |||
| + | function LevelX:init() | ||
| + | 	-- BG | ||
| + | 	application:setBackgroundColor(0x403C38) | ||
| + | |||
| + | 	-- cbump | ||
| + | 	local cbump = require "cbump" | ||
| + | 	self.world = cbump.newWorld() -- default is 64*64 | ||
| + | |||
| + | 	-- camera | ||
| + | 	self.camera = Sprite.new() | ||
| + | 	self.bg = Sprite.new() | ||
| + | 	self.fg = Sprite.new() | ||
| + | 	self.camera:setScale(1.75) | ||
| + | 	self.camera:addChild(self.bg) | ||
| + | 	self.camera:addChild(self.fg) | ||
| + | 	self:addChild(self.camera) | ||
| + | |||
| + | 	-- class lists | ||
| + | 	self.enemies = {} | ||
| + | 	self.coins = {} | ||
| + | 	self.bullets = {} | ||
| + | |||
| + | 	-- TILED: the level map | ||
| + | 	CBumpTiled.new("tiled/level01.lua", self.world, self.bg, true) | ||
| + | |||
| + | 	-- TILED: process the tiled layers | ||
| + | 	local level = loadfile("tiled/level01.lua")() | ||
| + | 	local layers = level.layers | ||
| + | 	for i = 1, #layers do | ||
| + | 		local layer = layers[i] | ||
| + | 		local layerType = layer.type | ||
| + | 		local layerName = layer.name | ||
| + | |||
| + | 		if layerType == "imagelayer" then | ||
| + | 			-- process image layer here | ||
| + | |||
| + | 		elseif layerType == "objectgroup" then | ||
| + | 			local objects = layer.objects | ||
| + | 			for i = 1, #objects do | ||
| + | 				local object = objects[i] | ||
| + | 				local objectName = object.name -- type of object | ||
| + | |||
| + | 				if objectName == "player" then | ||
| + | 					-- process player start location | ||
| + | 					self.player1 = Sprite_Maker.new("player1", "gfx/player/HQ_Trooper_all.png", 6, 13, object.x, object.y) | ||
| + | 					self.player1:setScale(0.9) | ||
| + | 					self.player1:setAlpha(0.75) | ||
| + | 					self.player1.lives = 3 | ||
| + | 					self.player1.filter = function(item, other) | ||
| + | 						if other.name == "coin01" then | ||
| + | 							return "cross" | ||
| + | 						elseif other.name == "bullet01" then | ||
| + | 							return "cross" | ||
| + | 						elseif other.name == "exit" then | ||
| + | 							return "cross" | ||
| + | 						else | ||
| + | 							return "slide" | ||
| + | 						end | ||
| + | 					end | ||
| + | 					self.fg:addChild(self.player1) | ||
| + | 					self.world:add(self.player1, self.player1.x, self.player1.y, self.player1.w * self.player1:getScaleX(), self.player1.h * self.player1:getScaleY()) | ||
| + | |||
| + | 				elseif objectName == "e01" then | ||
| + | 					local enemy = Sprite_Maker.new("enemy01", "gfx/nmes/snake.png", 5, 4, object.x, object.y) | ||
| + | 					enemy:setScale(0.85) | ||
| + | 					enemy.lives = math.random(2, 4) | ||
| + | 					enemy.vx = 32 | ||
| + | 					enemy.filter = function(item, other) | ||
| + | 						if other.name == "enemy01" or other.name == "enemy02" then | ||
| + | 							--return "cross" | ||
| + | 						elseif other.name == "exit" then | ||
| + | 							--return "cross" | ||
| + | 						else | ||
| + | 							return "slide" | ||
| + | 						end | ||
| + | 					end | ||
| + | 					self.enemies[#self.enemies + 1] = enemy | ||
| + | 					self.bg:addChild(enemy) | ||
| + | 					self.world:add(enemy, enemy.x, enemy.y, enemy.w * enemy:getScaleX(), enemy.h * enemy:getScaleY()) | ||
| + | |||
| + | 				elseif objectName == "e02" then | ||
| + | 					local enemy = Sprite_Maker.new("enemy02", "gfx/nmes/enemy01.png", 23, 4, object.x, object.y) | ||
| + | 					enemy:setScale(1.25) | ||
| + | 					enemy.lives = math.random(3, 5) | ||
| + | 					enemy.vx = 18 | ||
| + | 					enemy.filter = function(item, other) | ||
| + | 						if other.name == "enemy01" or other.name == "enemy02" then | ||
| + | 							--return "cross" | ||
| + | 						elseif other.name == "exit" then | ||
| + | 							--return "cross" | ||
| + | 						else | ||
| + | 							return "slide" | ||
| + | 						end | ||
| + | 					end | ||
| + | 					self.enemies[#self.enemies + 1] = enemy | ||
| + | 					self.bg:addChild(enemy) | ||
| + | 					self.world:add(enemy, enemy.x, enemy.y, enemy.w * enemy:getScaleX(), enemy.h * enemy:getScaleY()) | ||
| + | |||
| + | 				elseif objectName == "c01" then | ||
| + | 					local coin01 = Sprite_Maker.new("coin01", "gfx/coins/coin_20_x01.png", 6, 1, object.x, object.y + 48) | ||
| + | 					coin01:setAnchorPoint(0.5, 0.5) | ||
| + | 					self.coins[#self.coins + 1] = coin01 | ||
| + | 					self.bg:addChild(coin01) | ||
| + | 					self.world:add(coin01, coin01.x, coin01.y, coin01.w * coin01:getScaleX(), coin01.h * coin01:getScaleY()) | ||
| + | |||
| + | 				elseif objectName == "exit" then | ||
| + | 					local texture = Texture.new("gfx/doors/door.png") | ||
| + | 					local tile = Bitmap.new(texture) | ||
| + | 					tile.name = "exit" | ||
| + | 					tile:setPosition(object.x, object.y) | ||
| + | 					self.bg:addChild(tile) | ||
| + | 					self.world:add(tile, object.x, object.y, tile:getWidth(), tile:getHeight()) | ||
| + | |||
| + | 				else | ||
| + | 					print("unknown object type") | ||
| + | 				end | ||
| + | 			end | ||
| + | |||
| + | 		else | ||
| + | 			print("unknown layer type") | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- AUDIO | ||
| + | 	self.bulletsnd = Sound.new("audio/shoot.wav") | ||
| + | 	self.coinsnd = Sound.new("audio/coin.wav") | ||
| + | |||
| + | 	-- LISTENERS | ||
| + | 	self:addEventListener("enterBegin", self.onTransitionInBegin, self) | ||
| + | 	self:addEventListener("enterEnd", self.onTransitionInEnd, self) | ||
| + | 	self:addEventListener("exitBegin", self.onTransitionOutBegin, self) | ||
| + | 	self:addEventListener("exitEnd", self.onTransitionOutEnd, self) | ||
| + | end | ||
| + | |||
| + | -- GAME LOOP | ||
| + | function LevelX:onEnterFrame(e) | ||
| + | 	-- camera follows player | ||
| + | 	local playerposx, playerposy = self.player1:getPosition() | ||
| + | 	playerposx, playerposy = playerposx * self.camera:getScale(), playerposy * self.camera:getScale() | ||
| + | 	local offsetX = -(playerposx - application:getContentWidth() / 2 + self.player1:getWidth() / 4 * self.player1:getScaleX()) | ||
| + | 	local offsetY = -(playerposy - (4.5 * 64) * self.camera:getScale() / self.player1:getScale()) | ||
| + | 	self.camera:setPosition(offsetX, offsetY) | ||
| + | |||
| + | 	-- player1 | ||
| + | 	if self.player1.lives > 0 then | ||
| + | 		self:updatePlayer1(self.player1, e.deltaTime) | ||
| + | 	else | ||
| + | 		print("you lose!") | ||
| + | --		scenemanager:changeScene("gameover", 3, transitions[6], easing.outBack) | ||
| + | 	end | ||
| + | 	-- bullets | ||
| + | 	for b = #self.bullets, 1, -1 do | ||
| + | 		self:updateBullets(self.bullets[b], e.deltaTime) | ||
| + | 	end | ||
| + | 	-- bullets hit | ||
| + | 	for b = #self.bullets, 1, -1 do | ||
| + | 		local sprite = self.bullets[b] | ||
| + | 		if sprite.lives <= 0 then | ||
| + | 			self.camera:removeChild(sprite) | ||
| + | 			self.world:remove(sprite) | ||
| + | 			table.remove(self.bullets, b) | ||
| + | 		end | ||
| + | 	end | ||
| + | 	-- enemies | ||
| + | 	for n = #self.enemies, 1, -1 do | ||
| + | 		self:updateEnemies(self.enemies[n], e.deltaTime) | ||
| + | 	end | ||
| + | 	-- enemy is dead | ||
| + | 	for n = #self.enemies, 1, -1 do | ||
| + | 		local sprite = self.enemies[n] | ||
| + | 		if sprite.lives <= 0 then | ||
| + | 			self.bg:removeChild(sprite) | ||
| + | 			self.world:remove(sprite) | ||
| + | 			table.remove(self.enemies, n) | ||
| + | 		end | ||
| + | 	end | ||
| + | 	-- coins | ||
| + | 	for c = #self.coins, 1, -1 do | ||
| + | 		self:updateCollectibles(self.coins[c], e.deltaTime) | ||
| + | 	end | ||
| + | 	-- collectibles collected | ||
| + | 	for c = #self.coins, 1, -1 do | ||
| + | 		local sprite = self.coins[c] | ||
| + | 		if sprite.lives <= 0 then | ||
| + | 			self.bg:removeChild(sprite) | ||
| + | 			self.world:remove(sprite) | ||
| + | 			table.remove(self.coins, c) | ||
| + | 		end | ||
| + | 	end | ||
| + | end | ||
| + | |||
| + | -- FUNCTIONS | ||
| + | function LevelX:updatePlayer1(xsprite, dt) | ||
| + | 	-- player1 pushed by gravity | ||
| + | 	xsprite.vy += GRAVITY * dt | ||
| + | |||
| + | 	-- cbump | ||
| + | 	local goalx = xsprite.x + xsprite.vx * dt | ||
| + | 	local goaly = xsprite.y + xsprite.vy * dt | ||
| + | 	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter) | ||
| + | 	-- collisions | ||
| + | 	for i = 1, len do | ||
| + | 		local col = collisions[i] | ||
| + | 		-- collectibles collisions | ||
| + | 		if col.other.name == "coin01" then | ||
| + | 			local sound = self.coinsnd:play() | ||
| + | 			sound:setVolume(0.1) | ||
| + | 			col.other.lives -= 1 | ||
| + | 		end | ||
| + | 		-- enemy collision | ||
| + | 		if col.other.name == "enemy01" or col.other.name == "enemy02" then | ||
| + | 			if col.normal.y == -1 then | ||
| + | 				col.other.lives -= 1 | ||
| + | 				col.item.isattacking = true | ||
| + | 			end | ||
| + | 		end | ||
| + | 		-- sprite collides top or bottom | ||
| + | 		if col.normal.y == 1 or col.normal.y == -1 then | ||
| + | 			if col.other.name == "coin01" or col.other.name == "exit" then | ||
| + | 				-- nothing here | ||
| + | 			else | ||
| + | 				col.item.vy = 0 | ||
| + | 			end | ||
| + | 		end | ||
| + | 		-- sprite is on floor | ||
| + | 		if col.normal.y == -1 then | ||
| + | 			if col.other.name == "coin01" then | ||
| + | 				col.item.isonfloor = false | ||
| + | 			elseif col.item.isattacking then | ||
| + | 				col.item.vy = -col.item.jumpspeed / 1.5 | ||
| + | 				col.item.isonfloor = false | ||
| + | 				col.item.isattacking = false | ||
| + | 			else | ||
| + | 				col.item.isonfloor = true | ||
| + | 			end | ||
| + | 		end | ||
| + | 		-- EXIT | ||
| + | 		if col.other.name == "exit" then | ||
| + | 			if #self.coins == 0 then | ||
| + | 				g_level += 1 | ||
| + | 				if g_level == 4 then | ||
| + | --					scenemanager:changeScene("youwin", 5, transitions[21], easing.outBack) | ||
| + | 					print("you win!") | ||
| + | 					g_level = 1 | ||
| + | 				end | ||
| + | 				scenemanager:changeScene("levelx", 2, transitions[2], easing.outBack) | ||
| + | 			end | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- anim state | ||
| + | 	if xsprite.isonfloor then | ||
| + | 		if xsprite.isshooting then | ||
| + | 			xsprite.currentanim = "shoot" | ||
| + | 		else | ||
| + | 			if xsprite.vx == 0 then | ||
| + | 				xsprite.currentanim = "idle" | ||
| + | 			elseif xsprite.vx ~= 0 then | ||
| + | 				xsprite.currentanim = "walk" | ||
| + | 			end | ||
| + | 		end | ||
| + | 	else | ||
| + | 		if xsprite.vy < 0 then | ||
| + | 			xsprite.currentanim = "jump_up" | ||
| + | 		elseif xsprite.vy >= 0 then | ||
| + | 			xsprite.currentanim = "jump_down" | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- keyboard handling | ||
| + | 	if xsprite.iskeyright then | ||
| + | 		xsprite.vx += xsprite.accel * dt | ||
| + | 		if xsprite.vx > xsprite.maxspeed then xsprite.vx = xsprite.maxspeed end | ||
| + | 		xsprite.flip = 1 | ||
| + | 	elseif xsprite.iskeyleft then | ||
| + | 		xsprite.vx -= xsprite.accel * dt | ||
| + | 		if xsprite.vx < -xsprite.maxspeed then xsprite.vx = -xsprite.maxspeed end | ||
| + | 		xsprite.flip = -1 | ||
| + | 	else | ||
| + | 		xsprite.vx = 0 | ||
| + | 	end | ||
| + | 	if xsprite.iskeyup then | ||
| + | 		if xsprite.isonfloor then | ||
| + | 			xsprite.vy = -xsprite.jumpspeed | ||
| + | 			xsprite.isonfloor = false | ||
| + | 		end | ||
| + | 	end | ||
| + | 	if xsprite.iskeyspace then | ||
| + | 		self:shoot() | ||
| + | 		xsprite.vx = -96 * xsprite.flip | ||
| + | 		xsprite.isshooting = true | ||
| + | 		xsprite.iskeyspace = false | ||
| + | 	end | ||
| + | |||
| + | 	-- anim loop | ||
| + | 	if xsprite.currentanim ~= "" then | ||
| + | 		xsprite.animtimer = xsprite.animtimer - dt | ||
| + | 		if xsprite.animtimer <= 0 then | ||
| + | 			xsprite.frame += 1 | ||
| + | 			xsprite.animtimer = xsprite.animspeed | ||
| + | 			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then | ||
| + | 				xsprite.frame = 1 | ||
| + | 				xsprite.isshooting = false | ||
| + | 			end | ||
| + | 			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame]) | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- move & flip | ||
| + | 	xsprite.x = nextx | ||
| + | 	xsprite.y = nexty | ||
| + | 	xsprite:setPosition(xsprite.x, xsprite.y) | ||
| + | 	xsprite.bmp:setScale(xsprite.flip, 1) | ||
| + | end | ||
| + | |||
| + | function LevelX:shoot() | ||
| + | 	local sound = self.bulletsnd:play() | ||
| + | 	sound:setVolume(0.1) | ||
| + | 	local bullet01 = Sprite_Maker.new("bullet01", "gfx/bullets/laser_01.png", 1, 1, | ||
| + | 			self.player1.x + (self.player1.w * self.player1.flip), self.player1.y + 16) | ||
| + | 	bullet01:setScale(1.1) | ||
| + | 	bullet01.vx = 2 * self.player1.accel * self.player1.flip | ||
| + | 	bullet01.flip = self.player1.flip | ||
| + | 	bullet01.filter = function(item, other) | ||
| + | 		if other.name == "player1" then | ||
| + | 			-- nothing here | ||
| + | 		elseif other.name == "coin01" then | ||
| + | 			-- nothing here | ||
| + | 		elseif other.name == "enemy01" or other.name == "enemy02" then | ||
| + | 			if math.abs(other.x - self.player1.x) < application:getContentWidth() / 2 - other.w then | ||
| + | 				return "slide" | ||
| + | 			else | ||
| + | 				return "slide" | ||
| + |  			end | ||
| + | 		else | ||
| + | 			return "slide" | ||
| + | 		end | ||
| + | 	end | ||
| + | 	self.bullets[#self.bullets + 1] = bullet01 | ||
| + | 	self.camera:addChild(bullet01) | ||
| + | 	self.world:add(bullet01, bullet01.x, bullet01.y, | ||
| + | 		bullet01.w * bullet01:getScaleX(), bullet01.h * bullet01:getScaleY()) | ||
| + | end | ||
| + | |||
| + | function LevelX:updateBullets(xsprite, dt) | ||
| + | 	-- mouvement | ||
| + | 	xsprite.vx += dt | ||
| + | --	xsprite.vy += 50 | ||
| + | |||
| + | 	-- cbump | ||
| + | 	local goalx = xsprite.x + xsprite.vx * dt | ||
| + | 	local goaly = xsprite.y + xsprite.vy * dt | ||
| + | 	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter) | ||
| + | 	for i = 1, len do | ||
| + | 		local col = collisions[i] | ||
| + | 		if col.other.name == "enemy01" or col.other.name == "enemy02" then | ||
| + | 			if math.abs(col.other.x - self.player1.x) < application:getContentWidth() / 2 - col.other.w then | ||
| + | 				col.other:setColorTransform(0, 1, 1, 0.5) | ||
| + | 				col.other.lives -= 1 | ||
| + | 				col.item.lives -= 1 | ||
| + | 				col.other.vx = 96 * col.other.flip | ||
| + | 				for t = 1, 1000000 do -- tip from pro | ||
| + | 					t += 1 | ||
| + | 				end | ||
| + | 			end | ||
| + | 		else | ||
| + | 			-- nothing here! | ||
| + | 		end | ||
| + | 		col.item.lives -= 1 | ||
| + | 	end | ||
| + | |||
| + | 	-- move & flip | ||
| + | 	xsprite.x = nextx | ||
| + | 	xsprite.y = nexty | ||
| + | 	xsprite:setPosition(xsprite.x, xsprite.y) | ||
| + | 	xsprite.bmp:setScale(xsprite.flip, 1) | ||
| + | end | ||
| + | |||
| + | function LevelX:updateEnemies(xsprite, dt) | ||
| + | 	-- pushed by gravity | ||
| + | 	xsprite.vy += GRAVITY * dt | ||
| + | |||
| + | 	-- cbump | ||
| + | 	local goalx = xsprite.x + xsprite.vx * dt | ||
| + | 	local goaly = xsprite.y + xsprite.vy * dt | ||
| + | 	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter) | ||
| + | 	-- collisions | ||
| + | 	for i = 1, len do | ||
| + | 		local col = collisions[i] | ||
| + | 		-- sprite collides top or bottom | ||
| + | 		if col.other.name == "player1" then | ||
| + | 			col.other.lives -= 1 | ||
| + | 			print("player lives: "..col.other.lives) | ||
| + | 		end | ||
| + | 		if col.normal.y == 1 or col.normal.y == -1 then | ||
| + | 			col.item.vy = 0 | ||
| + | 		end | ||
| + | 		if col.normal.x == 1 or col.normal.x == -1 then | ||
| + | 			col.item.vx = -col.item.vx | ||
| + | 			col.item.flip = -col.item.flip | ||
| + | 		end | ||
| + | 		-- sprite is on floor | ||
| + | 		if col.normal.y == -1 then | ||
| + | 			col.item.isonfloor = true | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- anim state | ||
| + | 	if xsprite.isonfloor then | ||
| + | 		if xsprite.vx == 0 then | ||
| + | 			xsprite.currentanim = "walk" | ||
| + | 		elseif xsprite.vx ~= 0 then | ||
| + | 			xsprite.currentanim = "walk" | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- anim loop | ||
| + | 	if xsprite.currentanim ~= "" then | ||
| + | 		xsprite.animtimer = xsprite.animtimer - dt | ||
| + | 		if xsprite.animtimer <= 0 then | ||
| + | 			xsprite.frame += 1 | ||
| + | 			xsprite.animtimer = xsprite.animspeed | ||
| + | 			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then | ||
| + | 				xsprite.frame = 1 | ||
| + | 			end | ||
| + | 			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame]) | ||
| + | 			xsprite:setColorTransform(1, 1, 1, 1) | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- move & flip | ||
| + | 	xsprite.x = nextx | ||
| + | 	xsprite.y = nexty | ||
| + | 	xsprite:setPosition(xsprite.x, xsprite.y) | ||
| + | 	xsprite.bmp:setScale(xsprite.flip, 1) | ||
| + | end | ||
| + | |||
| + | function LevelX:updateCollectibles(xsprite, dt) | ||
| + | 	-- anim state | ||
| + | 	xsprite.currentanim = "walk" | ||
| + | |||
| + | 	-- anim loop | ||
| + | 	if xsprite.currentanim ~= "" then | ||
| + | 		xsprite.animtimer = xsprite.animtimer - dt | ||
| + | 		if xsprite.animtimer <= 0 then | ||
| + | 			xsprite.frame += 1 | ||
| + | 			xsprite.animtimer = xsprite.animspeed | ||
| + | 			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then | ||
| + | 				xsprite.frame = 1 | ||
| + | 			end | ||
| + | 			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame]) | ||
| + | 		end | ||
| + | 	end | ||
| + | |||
| + | 	-- show | ||
| + | 	xsprite:setPosition(xsprite.x, xsprite.y) | ||
| + | --	xsprite.bmp:setScale(xsprite.flip, 1) | ||
| + | end | ||
| + | |||
| + | -- EVENT LISTENERS | ||
| + | function LevelX:onTransitionInBegin() | ||
| + | 	self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self) | ||
| + | end | ||
| + | |||
| + | function LevelX:onTransitionInEnd() | ||
| + | 	self:myKeysPressed() | ||
| + | 	-- mobile controls | ||
| + | --	local mymobile = MobileX.new(self.player1) | ||
| + | --	self:addChild(mymobile) | ||
| + | end | ||
| + | |||
| + | function LevelX:onTransitionOutBegin() | ||
| + | 	self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self) | ||
| + | end | ||
| + | |||
| + | function LevelX:onTransitionOutEnd() | ||
| + | end | ||
| + | |||
| + | -- KEYS HANDLER | ||
| + | function LevelX:myKeysPressed() | ||
| + | 	self:addEventListener(Event.KEY_DOWN, function(e) | ||
| + | 		-- for mobiles and desktops | ||
| + | 		if e.keyCode == KeyCode.BACK or e.keyCode == KeyCode.ESC then | ||
| + | --			scenemanager:changeScene("menu", 1, transitions[2], easing.outBack) | ||
| + | 		end | ||
| + | 	end) | ||
| + | end | ||
| + | </syntaxhighlight> | ||
| + | |||
| + | === A YouTube Video === | ||
| + | A step by step video on how to use this platformer template. Enjoy! | ||
| + | |||
| + | <youtube>https://www.youtube.com/watch?v=WPcbsgOCnLU</youtube> | ||
| + | |||
| + | |||
| + | {{GIDEROS IMPORTANT LINKS}} | ||
Latest revision as of 22:54, 18 November 2023
Here you will find various resources to help you create games and apps in Gideros Studio.
note: You may have to provide your own assets (fonts, gfx, …).
Description
This section deals with various ways to use CBump.
Using CBump and TILED (handles collisions)
It's easy: you draw your map with Tile then you export your map to a lua file.
The class handles tile collisions automatically.
-- usage
--local map = CBumpTiled.new("tiled/test.lua", world, bg, true)
-- where world is the cbump world
-- bg is a sprite layer (here a background sprite layer)
-- true if only foreground is added to cbump collision world
CBumpTiled = Core.class(Sprite)
local function gid2tileset(map, gid)
	for i = 1, #map.tilesets do
		local tileset = map.tilesets[i]
		if tileset.firstgid <= gid and gid <= tileset.lastgid then
			return tileset
		end
	end
end
function CBumpTiled:init(filename, xworld, xlayer, xtop)
	-- Bits on the far end of the 32-bit global tile ID are used for tile flags (flip, rotate)
	local FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
	local FLIPPED_VERTICALLY_FLAG   = 0x40000000;
	local FLIPPED_DIAGONALLY_FLAG   = 0x20000000;
	local map = loadfile(filename)()
	for i = 1, #map.tilesets do
		local tileset = map.tilesets[i]
		tileset.sizex = math.floor((tileset.imagewidth - tileset.margin + tileset.spacing) / (tileset.tilewidth + tileset.spacing))
		tileset.sizey = math.floor((tileset.imageheight - tileset.margin + tileset.spacing) / (tileset.tileheight + tileset.spacing))
		tileset.lastgid = tileset.firstgid + (tileset.sizex * tileset.sizey) - 1
		tileset.texture = Texture.new(tileset.image, false, {transparentColor = tonumber(tileset.transparentcolor)})
	end
	for i = 1, #map.layers do
		if map.layers[i].type == "tilelayer" then
			local layer = map.layers[i]
			local tilemaps = {}
			for y = 1, layer.height do
				for x = 1, layer.width do
					local i = x + (y - 1) * layer.width
					local gid = layer.data[i]
					if gid ~= 0 then
						-- Read flipping flags
						flipHor = gid & FLIPPED_HORIZONTALLY_FLAG
						flipVer = gid & FLIPPED_VERTICALLY_FLAG
						flipDia = gid & FLIPPED_DIAGONALLY_FLAG
						-- Convert flags to gideros style
						if(flipHor ~= 0) then flipHor = TileMap.FLIP_HORIZONTAL end
						if(flipVer ~= 0) then flipVer = TileMap.FLIP_VERTICAL end
						if(flipDia ~= 0) then flipDia = TileMap.FLIP_DIAGONAL end
						-- Clear the flags from gid so other information is healthy
						gid = gid & ~ (
							FLIPPED_HORIZONTALLY_FLAG |
							FLIPPED_VERTICALLY_FLAG |
							FLIPPED_DIAGONALLY_FLAG
						)
					end
					local tileset = gid2tileset(map, gid)
					if tileset then
						local tilemap = nil
						tilemap = TileMap.new(
							layer.width, 
							layer.height,
							tileset.texture,
							tileset.tilewidth,
							tileset.tileheight,
							tileset.spacing,
							tileset.spacing,
							tileset.margin,
							tileset.margin,
							map.tilewidth,
							map.tileheight
						)
						tilemaps[tileset] = tilemap
						local tx = (gid - tileset.firstgid) % tileset.sizex + 1
						local ty = math.floor((gid - tileset.firstgid) / tileset.sizex) + 1
						-- Set the tile with flip info
						tilemap:setTile(x, y, tx, ty, (flipHor | flipVer | flipDia))
						xlayer:addChild(tilemap)
						xlayer:setAlpha(layer.opacity)
						if xtop then
							if layer.id == 1 then
								xworld:add(
									tilemap,
									(x - 1) * tileset.tilewidth * xlayer:getScaleX(),
									(y - 1) * tileset.tileheight * xlayer:getScaleY(),
									tileset.tilewidth * xlayer:getScaleX(),
									tileset.tileheight * xlayer:getScaleY()
								)
							end
						else
							xworld:add(
								tilemap,
								(x - 1) * tileset.tilewidth * xlayer:getScaleX(),
								(y - 1) * tileset.tileheight * xlayer:getScaleY(),
								tileset.tilewidth * xlayer:getScaleX(),
								tileset.tileheight * xlayer:getScaleY()
							)
						end
					end
				end
			end
		end
	end
end
Usage
This is an example of how to set up CBump and TILED.
-- cbump
local cbump = require "cbump"
-- Constructor: BumpWorld.new([cellSize = 64])
local world = BumpWorld.new()
-- camera
local camera = Sprite.new()
local bg = Sprite.new()
local fg = Sprite.new()
camera:addChild(bg)
camera:addChild(fg)
stage:addChild(camera)
-- tiled
local map = CBumpTiled.new("tiled/test.lua", world, bg, true)
A Full CBump Platformer Level with enemies, collectibles, exit,...
This class is an example of a full CBump platformer level using TILED as a map editor. Please note that you need to modify the code depending on your assets path. Enjoy!
LevelX = Core.class(Sprite)
function LevelX:init()
	-- BG
	application:setBackgroundColor(0x403C38)
	-- cbump
	local cbump = require "cbump"
	self.world = cbump.newWorld() -- default is 64*64
	-- camera
	self.camera = Sprite.new()
	self.bg = Sprite.new()
	self.fg = Sprite.new()
	self.camera:setScale(1.75)
	self.camera:addChild(self.bg)
	self.camera:addChild(self.fg)
	self:addChild(self.camera)
	-- class lists
	self.enemies = {}
	self.coins = {}
	self.bullets = {}
	-- TILED: the level map
	CBumpTiled.new("tiled/level01.lua", self.world, self.bg, true)
	
	-- TILED: process the tiled layers
	local level = loadfile("tiled/level01.lua")()
	local layers = level.layers
	for i = 1, #layers do
		local layer = layers[i]
		local layerType = layer.type
		local layerName = layer.name
		if layerType == "imagelayer" then
			-- process image layer here
		elseif layerType == "objectgroup" then
			local objects = layer.objects
			for i = 1, #objects do
				local object = objects[i]
				local objectName = object.name -- type of object
				if objectName == "player" then
					-- process player start location
					self.player1 = Sprite_Maker.new("player1", "gfx/player/HQ_Trooper_all.png", 6, 13, object.x, object.y)
					self.player1:setScale(0.9)
					self.player1:setAlpha(0.75)
					self.player1.lives = 3
					self.player1.filter = function(item, other)
						if other.name == "coin01" then
							return "cross"
						elseif other.name == "bullet01" then
							return "cross"
						elseif other.name == "exit" then
							return "cross"
						else
							return "slide"
						end
					end
					self.fg:addChild(self.player1)
					self.world:add(self.player1, self.player1.x, self.player1.y, self.player1.w * self.player1:getScaleX(), self.player1.h * self.player1:getScaleY())
				elseif objectName == "e01" then
					local enemy = Sprite_Maker.new("enemy01", "gfx/nmes/snake.png", 5, 4, object.x, object.y)
					enemy:setScale(0.85)
					enemy.lives = math.random(2, 4)
					enemy.vx = 32
					enemy.filter = function(item, other)
						if other.name == "enemy01" or other.name == "enemy02" then
							--return "cross"
						elseif other.name == "exit" then
							--return "cross"
						else
							return "slide"
						end
					end
					self.enemies[#self.enemies + 1] = enemy
					self.bg:addChild(enemy)
					self.world:add(enemy, enemy.x, enemy.y, enemy.w * enemy:getScaleX(), enemy.h * enemy:getScaleY())
				elseif objectName == "e02" then
					local enemy = Sprite_Maker.new("enemy02", "gfx/nmes/enemy01.png", 23, 4, object.x, object.y)
					enemy:setScale(1.25)
					enemy.lives = math.random(3, 5)
					enemy.vx = 18
					enemy.filter = function(item, other)
						if other.name == "enemy01" or other.name == "enemy02" then
							--return "cross"
						elseif other.name == "exit" then
							--return "cross"
						else
							return "slide"
						end
					end
					self.enemies[#self.enemies + 1] = enemy
					self.bg:addChild(enemy)
					self.world:add(enemy, enemy.x, enemy.y, enemy.w * enemy:getScaleX(), enemy.h * enemy:getScaleY())
				elseif objectName == "c01" then
					local coin01 = Sprite_Maker.new("coin01", "gfx/coins/coin_20_x01.png", 6, 1, object.x, object.y + 48)
					coin01:setAnchorPoint(0.5, 0.5)
					self.coins[#self.coins + 1] = coin01
					self.bg:addChild(coin01)
					self.world:add(coin01, coin01.x, coin01.y, coin01.w * coin01:getScaleX(), coin01.h * coin01:getScaleY())
				elseif objectName == "exit" then
					local texture = Texture.new("gfx/doors/door.png")
					local tile = Bitmap.new(texture)
					tile.name = "exit"
					tile:setPosition(object.x, object.y)
					self.bg:addChild(tile)
					self.world:add(tile, object.x, object.y, tile:getWidth(), tile:getHeight())
				else
					print("unknown object type")
				end
			end
		else
			print("unknown layer type")
		end
	end
	-- AUDIO
	self.bulletsnd = Sound.new("audio/shoot.wav")
	self.coinsnd = Sound.new("audio/coin.wav")
	-- LISTENERS
	self:addEventListener("enterBegin", self.onTransitionInBegin, self)
	self:addEventListener("enterEnd", self.onTransitionInEnd, self)
	self:addEventListener("exitBegin", self.onTransitionOutBegin, self)
	self:addEventListener("exitEnd", self.onTransitionOutEnd, self)
end
-- GAME LOOP
function LevelX:onEnterFrame(e)
	-- camera follows player
	local playerposx, playerposy = self.player1:getPosition()
	playerposx, playerposy = playerposx * self.camera:getScale(), playerposy * self.camera:getScale()
	local offsetX = -(playerposx - application:getContentWidth() / 2 + self.player1:getWidth() / 4 * self.player1:getScaleX())
	local offsetY = -(playerposy - (4.5 * 64) * self.camera:getScale() / self.player1:getScale())
	self.camera:setPosition(offsetX, offsetY)
	-- player1
	if self.player1.lives > 0 then
		self:updatePlayer1(self.player1, e.deltaTime)
	else
		print("you lose!")
--		scenemanager:changeScene("gameover", 3, transitions[6], easing.outBack)
	end
	-- bullets
	for b = #self.bullets, 1, -1 do
		self:updateBullets(self.bullets[b], e.deltaTime)
	end
	-- bullets hit
	for b = #self.bullets, 1, -1 do
		local sprite = self.bullets[b]
		if sprite.lives <= 0 then
			self.camera:removeChild(sprite)
			self.world:remove(sprite)
			table.remove(self.bullets, b)
		end
	end
	-- enemies
	for n = #self.enemies, 1, -1 do
		self:updateEnemies(self.enemies[n], e.deltaTime)
	end
	-- enemy is dead
	for n = #self.enemies, 1, -1 do
		local sprite = self.enemies[n]
		if sprite.lives <= 0 then
			self.bg:removeChild(sprite)
			self.world:remove(sprite)
			table.remove(self.enemies, n)
		end
	end
	-- coins
	for c = #self.coins, 1, -1 do
		self:updateCollectibles(self.coins[c], e.deltaTime)
	end
	-- collectibles collected
	for c = #self.coins, 1, -1 do
		local sprite = self.coins[c]
		if sprite.lives <= 0 then
			self.bg:removeChild(sprite)
			self.world:remove(sprite)
			table.remove(self.coins, c)
		end
	end
end
-- FUNCTIONS
function LevelX:updatePlayer1(xsprite, dt)
	-- player1 pushed by gravity
	xsprite.vy += GRAVITY * dt
	-- cbump
	local goalx = xsprite.x + xsprite.vx * dt
	local goaly = xsprite.y + xsprite.vy * dt
	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter)
	-- collisions
	for i = 1, len do
		local col = collisions[i]
		-- collectibles collisions
		if col.other.name == "coin01" then
			local sound = self.coinsnd:play()
			sound:setVolume(0.1)
			col.other.lives -= 1
		end
		-- enemy collision
		if col.other.name == "enemy01" or col.other.name == "enemy02" then
			if col.normal.y == -1 then
				col.other.lives -= 1
				col.item.isattacking = true
			end
		end
		-- sprite collides top or bottom
		if col.normal.y == 1 or col.normal.y == -1 then
			if col.other.name == "coin01" or col.other.name == "exit" then
				-- nothing here
			else
				col.item.vy = 0
			end
		end
		-- sprite is on floor
		if col.normal.y == -1 then
			if col.other.name == "coin01" then
				col.item.isonfloor = false
			elseif col.item.isattacking then
				col.item.vy = -col.item.jumpspeed / 1.5
				col.item.isonfloor = false
				col.item.isattacking = false
			else
				col.item.isonfloor = true
			end
		end
		-- EXIT
		if col.other.name == "exit" then
			if #self.coins == 0 then
				g_level += 1
				if g_level == 4 then
--					scenemanager:changeScene("youwin", 5, transitions[21], easing.outBack)
					print("you win!")
					g_level = 1
				end
				scenemanager:changeScene("levelx", 2, transitions[2], easing.outBack)
			end
		end
	end
	-- anim state
	if xsprite.isonfloor then
		if xsprite.isshooting then
			xsprite.currentanim = "shoot"
		else
			if xsprite.vx == 0 then
				xsprite.currentanim = "idle"
			elseif xsprite.vx ~= 0 then
				xsprite.currentanim = "walk"
			end
		end
	else
		if xsprite.vy < 0 then
			xsprite.currentanim = "jump_up"
		elseif xsprite.vy >= 0 then
			xsprite.currentanim = "jump_down"
		end
	end
	-- keyboard handling
	if xsprite.iskeyright then
		xsprite.vx += xsprite.accel * dt
		if xsprite.vx > xsprite.maxspeed then xsprite.vx = xsprite.maxspeed end
		xsprite.flip = 1
	elseif xsprite.iskeyleft then
		xsprite.vx -= xsprite.accel * dt
		if xsprite.vx < -xsprite.maxspeed then xsprite.vx = -xsprite.maxspeed end
		xsprite.flip = -1
	else
		xsprite.vx = 0
	end
	if xsprite.iskeyup then
		if xsprite.isonfloor then
			xsprite.vy = -xsprite.jumpspeed
			xsprite.isonfloor = false
		end
	end
	if xsprite.iskeyspace then
		self:shoot()
		xsprite.vx = -96 * xsprite.flip
		xsprite.isshooting = true
		xsprite.iskeyspace = false
	end
	-- anim loop
	if xsprite.currentanim ~= "" then
		xsprite.animtimer = xsprite.animtimer - dt
		if xsprite.animtimer <= 0 then
			xsprite.frame += 1
			xsprite.animtimer = xsprite.animspeed
			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then
				xsprite.frame = 1
				xsprite.isshooting = false
			end
			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame])
		end
	end
	-- move & flip
	xsprite.x = nextx
	xsprite.y = nexty
	xsprite:setPosition(xsprite.x, xsprite.y)
	xsprite.bmp:setScale(xsprite.flip, 1)
end
function LevelX:shoot()
	local sound = self.bulletsnd:play()
	sound:setVolume(0.1)
	local bullet01 = Sprite_Maker.new("bullet01", "gfx/bullets/laser_01.png", 1, 1,
			self.player1.x + (self.player1.w * self.player1.flip), self.player1.y + 16)
	bullet01:setScale(1.1)
	bullet01.vx = 2 * self.player1.accel * self.player1.flip
	bullet01.flip = self.player1.flip
	bullet01.filter = function(item, other)
		if other.name == "player1" then
			-- nothing here
		elseif other.name == "coin01" then
			-- nothing here
		elseif other.name == "enemy01" or other.name == "enemy02" then
			if math.abs(other.x - self.player1.x) < application:getContentWidth() / 2 - other.w then
				return "slide"
			else
				return "slide"
 			end
		else
			return "slide"
		end
	end
	self.bullets[#self.bullets + 1] = bullet01
	self.camera:addChild(bullet01)
	self.world:add(bullet01, bullet01.x, bullet01.y,
		bullet01.w * bullet01:getScaleX(), bullet01.h * bullet01:getScaleY())
end
function LevelX:updateBullets(xsprite, dt)
	-- mouvement
	xsprite.vx += dt
--	xsprite.vy += 50
	-- cbump
	local goalx = xsprite.x + xsprite.vx * dt
	local goaly = xsprite.y + xsprite.vy * dt
	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter)
	for i = 1, len do
		local col = collisions[i]
		if col.other.name == "enemy01" or col.other.name == "enemy02" then
			if math.abs(col.other.x - self.player1.x) < application:getContentWidth() / 2 - col.other.w then
				col.other:setColorTransform(0, 1, 1, 0.5)
				col.other.lives -= 1
				col.item.lives -= 1
				col.other.vx = 96 * col.other.flip
				for t = 1, 1000000 do -- tip from pro
					t += 1
				end
			end
		else
			-- nothing here!
		end
		col.item.lives -= 1
	end
	-- move & flip
	xsprite.x = nextx
	xsprite.y = nexty
	xsprite:setPosition(xsprite.x, xsprite.y)
	xsprite.bmp:setScale(xsprite.flip, 1)
end
function LevelX:updateEnemies(xsprite, dt)
	-- pushed by gravity
	xsprite.vy += GRAVITY * dt
	-- cbump
	local goalx = xsprite.x + xsprite.vx * dt
	local goaly = xsprite.y + xsprite.vy * dt
	local nextx, nexty, collisions, len = self.world:move(xsprite, goalx, goaly, xsprite.filter)
	-- collisions
	for i = 1, len do
		local col = collisions[i]
		-- sprite collides top or bottom
		if col.other.name == "player1" then
			col.other.lives -= 1
			print("player lives: "..col.other.lives)
		end
		if col.normal.y == 1 or col.normal.y == -1 then
			col.item.vy = 0
		end
		if col.normal.x == 1 or col.normal.x == -1 then
			col.item.vx = -col.item.vx
			col.item.flip = -col.item.flip
		end
		-- sprite is on floor
		if col.normal.y == -1 then
			col.item.isonfloor = true
		end
	end
	-- anim state
	if xsprite.isonfloor then
		if xsprite.vx == 0 then
			xsprite.currentanim = "walk"
		elseif xsprite.vx ~= 0 then
			xsprite.currentanim = "walk"
		end
	end
	-- anim loop
	if xsprite.currentanim ~= "" then
		xsprite.animtimer = xsprite.animtimer - dt
		if xsprite.animtimer <= 0 then
			xsprite.frame += 1
			xsprite.animtimer = xsprite.animspeed
			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then
				xsprite.frame = 1
			end
			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame])
			xsprite:setColorTransform(1, 1, 1, 1)
		end
	end
	-- move & flip
	xsprite.x = nextx
	xsprite.y = nexty
	xsprite:setPosition(xsprite.x, xsprite.y)
	xsprite.bmp:setScale(xsprite.flip, 1)
end
function LevelX:updateCollectibles(xsprite, dt)
	-- anim state
	xsprite.currentanim = "walk"
	-- anim loop
	if xsprite.currentanim ~= "" then
		xsprite.animtimer = xsprite.animtimer - dt
		if xsprite.animtimer <= 0 then
			xsprite.frame += 1
			xsprite.animtimer = xsprite.animspeed
			if xsprite.frame > #xsprite.anims[xsprite.currentanim] then
				xsprite.frame = 1
			end
			xsprite.bmp:setTextureRegion(xsprite.anims[xsprite.currentanim][xsprite.frame])
		end
	end
	-- show
	xsprite:setPosition(xsprite.x, xsprite.y)
--	xsprite.bmp:setScale(xsprite.flip, 1)
end
-- EVENT LISTENERS
function LevelX:onTransitionInBegin()
	self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
function LevelX:onTransitionInEnd()
	self:myKeysPressed()
	-- mobile controls
--	local mymobile = MobileX.new(self.player1)
--	self:addChild(mymobile)
end
function LevelX:onTransitionOutBegin()
	self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
end
function LevelX:onTransitionOutEnd()
end
-- KEYS HANDLER
function LevelX:myKeysPressed()
	self:addEventListener(Event.KEY_DOWN, function(e)
		-- for mobiles and desktops
		if e.keyCode == KeyCode.BACK or e.keyCode == KeyCode.ESC then
--			scenemanager:changeScene("menu", 1, transitions[2], easing.outBack)
		end
	end)
end
A YouTube Video
A step by step video on how to use this platformer template. Enjoy!
