CBump Template
From GiderosMobile
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
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!