CBump Template
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.
<syntaxhighlight lang="lua"> -- 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 </source>
Usage
This is an example of how to set up CBump and TILED. <syntaxhighlight lang="lua"> -- 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) </source>
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 </source>
A YouTube Video
A step by step video on how to use this platformer template. Enjoy!