Skip to content

Instantly share code, notes, and snippets.

@royletron
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save royletron/b4381e108e6834283d8f to your computer and use it in GitHub Desktop.
Save royletron/b4381e108e6834283d8f to your computer and use it in GitHub Desktop.
Flibble Test Build: v0.0.1 Build: 55
Flibble Tab Order Version: 0.0.1
------------------------------
This file should not be included in the Codea project.
#FlbAnim
#FlbButton
#FlbCamera
#FlbController
#FlbRect
#FlbSliderController
#FlbSplitController
#FlbSprite
#FlbStickController
#FlbText
#FlbTilemap
#FlbTouch
#Main
#FlbBasic
#FPSCounter
#FlbGame
#FlbGroup
#FlbObject
#FlbState
#FlbG
#FlbU
#TestChar
#TestMenuState
#TestState
--- @type FlbAnim
-- A basic class to house animations
FlbAnim = class()
--- Constructor function for the animation
-- @param Name the name of the animation
-- @param Frames a table containing the frame numbers for this animation
-- @param[opt] FrameRate the framerate per second for this animation (default is 1)
-- @param[opt] Looped a boolean as to whether this animation loops (default is true)
function FlbAnim:init(Name, Frames, FrameRate, Looped)
self.name, self.frames = Name, Frames
self.framerate, self.looped = FrameRate or 1, Looped or true
end
--- @type FlbBasic
-- This is a useful generic "Flibbel" object.
-- Both FlbObject and FlbGroup extend this.
-- It has now graphical coords or size
-- @author Royletron
FlbBasic = class()
--- Constructor
function FlbBasic:init()
-- Whether the object is being updated/drawn (TODO)
self.active = true
self.visible = true
self.alive = true
-- A name for debugging purposes, can be overriden
self.name = "Basic"
end
--- The update function should be filled in by the child class
function FlbBasic:update()
-- fallback
end
--- The draw function should be filled in by the child class
function FlbBasic:draw()
-- fallback
end
--- A destroy method fallback
function FlbBasic:destroy()
end
--- @type FlbButton
-- A simple button class for quickly mocking up UI (probably want something better full time)
FlbButton = class(FlbSprite)
--- The constructor function for the button
-- @param X the x coord to place the button
-- @param Y the y coord to place the button
-- @param Label the text to write on the button
-- @param OnClick the function to trigger when the button is clicked
function FlbButton:init(X, Y, Label, OnClick)
FlbSprite.init(self, X, Y)
self.width, self.height = 110, 35
self.label = Label
self.onclick = OnClick
self.text = FlbText(X, Y, 0, Label)
self.text:setFormat("Menlo", 18, color(255,255,255,255))
self.metrics = fontMetrics()
self.mesh = mesh()
self.mesh:addRect(self.x, self.y, self.width, self.height)
self.mesh:setRectColor(1,255,255,255,255)
self.mesh:addRect(self.x, self.y, self.width-4, self.height-4)
self.mesh:setRectColor(2,100,100,100,255)
self.triggered = false
end
function FlbButton:update()
FlbSprite.update(self)
self.DOWN = false
if FlbG.touch.DOWN then
if FlbU:pointOverlaps(self.camera:getCameraPoint(vec2(FlbG.touch.screenX, FlbG.touch.screenY)), self) then
self.DOWN = true
end
end
if self.triggered == false and self.DOWN then
if self.onclick ~= nil then self.onclick(self) end
self.triggered = true
elseif self.triggered and self.DOWN == false then
self.triggered = false
end
end
function FlbButton:getBoundingBox()
return FlbObject.getBoundingBox(self)
end
function FlbButton:draw()
if self.DOWN then
self.mesh:setRectColor(2,100,130,130,255)
else
self.mesh:setRectColor(2,100,100,100,255)
end
self.mesh:draw()
self.text:draw()
end
function FlbButton:setCameras(Cameras)
FlbSprite.setCameras(self, Cameras)
end
function FlbButton:onAdd()
FlbSprite.onAdd(self)
end
--- @type FlbCamera
-- Defines a basica camera type that can be used to follow a target around.
-- Can also be bound to a certain rectangular area, and have a dead zone in the middle.
FlbCamera = class(FlbBasic)
--- The constructor function for the camera
-- @param[opt] X the x coord to place the camera to begin with (defaults to 0)
-- @param[opt] Y the y coord to place the camera to begin with (default to 0)
-- @param[opt] Width the width of the visible area (defaults to stage width)
-- @param[opt] Height the height of the visible area (defaults to stage height)
-- @param[opt] Zoom the starting zoom of the camera (defaults to the generic FlbG.zoom)
function FlbCamera:init(X, Y, Width, Height, Zoom)
FlbBasic.init(self)
self.x, self.y = X or 0,Y or 0
self.width, self.height = Width or WIDTH, Height or HEIGHT
self.zoom = Zoom or FlbG.zoom
self.bounds = nil
self.bgColor = FlbG.bgColor
self.members = {}
self.deadzone = vec2(100,20)
end
--- Adds an object to the drawlist for this camera
-- @param object the object to add
function FlbCamera:add(object)
table.insert(self.members, object)
end
--- Removes an object from the drawlist for this camera
-- @param object the object to remove
function FlbCamera:remove(object)
FlbU:removeFromTable(self.members, object)
end
--- The update function for the camera
function FlbCamera:update()
if self.target then
local targetpos = vec2(-self.target.x + (self.width/2)/self.zoom, -self.target.y + (self.height/2)/self.zoom)
local deadzone = vec2(self.deadzone.x / self.zoom, self.deadzone.y / self.zoom)
if (targetpos.x - self.x) > deadzone.x then
self.x = targetpos.x - deadzone.x
elseif (targetpos.x - self.x) < -deadzone.x then
self.x = targetpos.x + deadzone.x
end
if (targetpos.y - self.y) > deadzone.y then
self.y = targetpos.y - deadzone.y
elseif (targetpos.y - self.y) < -deadzone.y then
self.y = targetpos.y + deadzone.y
end
end
--[[
local pos = vec2(0,0)
if self.target then
pos.x = -self.target.x + (self.width/2)/self.zoom
pos.y = -self.target.y + (self.height/2)/self.zoom
end
]]
--print(self.x)
if self.bounds then
if -self.x < self.bounds.left then self.x = -self.bounds.left end
if self.x + (self.width/self.zoom) > self.bounds.right then self.x = self.bounds.right - (self.width/self.zoom) end
if -self.y < self.bounds.bottom then self.y = -self.bounds.bottom end
if self.y + self.height/self.zoom > self.bounds.top then self.y = self.bounds.top - self.height/self.zoom end
end
end
--- Makes the camera flash!
-- @param Time the time in seconds to flash for
function FlbCamera:flash(Time, Color)
self._flashTime = Time
self._flashCounter = 0
self._flashColor = Color or color(255,255,255,255)
self._flashMesh = mesh()
self._flashMesh:addRect(self.x + self.width/2, self.y + self.height/2, self.width, self.height)
self._flashMesh:setColors(self._flashColor.r, self._flashColor.g, self._flashColor.b,255)
end
function FlbCamera:getCameraPoint(Point)
return vec2((Point.x - self.x)/self.zoom, (Point.y - self.y)/self.zoom)
end
--- Types
STYLE_LOCKON = 0
STYLE_PLATFORMER = 1
STYLE_TOPDOWN = 2
STYLE_TOPDOWN_TIGHT = 3
--- @type FlbController
-- The base class for all types of controller. Taken from the excellent
-- controllers github project (stick a link in yeah).
FlbController = class()
--- Starts the controller off and receiving events.
function FlbController:activate()
self.analog = true
touched = function(t)
self:touched(t)
end
end
--- The draw function for the controller, will be overridden on the lower level.
function FlbController:draw()
-- nothing
end
--- Utility functions
function touchPos(t)
return vec2(t.x, t.y)
end
function clamp(x, min, max)
return math.max(min, math.min(max, x))
end
function clampAbs(x, maxAbs)
return clamp(x, -maxAbs, maxAbs)
end
function clampLen(vec, maxLen)
if vec == vec2(0,0) then
return vec
else
return vec:normalize() * math.min(vec:len(), maxLen)
end
end
-- projects v onto the direction represented by the given unit vector
function project(v, unit)
return v:dot(unit)
end
function sign(x)
if x == 0 then
return 0
elseif x < 0 then
return -1
elseif x > 0 then
return 1
else
return x -- x is NaN
end
end
function doNothing()
end
--- @module FlbG
-- A jolly big helper singleton that can do all sorts of stuff that is game specific.
local flbg = class()
local _cache = {}
function flbg:init()
self.level, self.score = nil
self.levels, self.scores = {}
self.LIBRARY_NAME = "flibble"
self.LIBRARY_MAJOR_VERSION = 0
self.LIBRARY_MINOR_VERSION = 1
self.state = nil
self.debug = false
self.showbounding = false
self.fpscounter = FPSCounter()
self.bgColor = color(62, 62, 62, 255)
self.cameras = {}
self.joystickpos = vec2(0,0)
self.touch = FlbTouch{}
self.touch:activate()
self.shader = shader("Basic:Invert")
end
function flbg:usejoystick(Type, Moved, Release)
if Type == nil or Type == STICK_CONTROLLER then
self.joystick = FlbStickController {
moved = Moved or function(v) self.joystickpos = v end,
released = Release or function(v) self.joystickpos = vec2(0,0) end
}
elseif Type == SLIDER_CONTROLLER then
self.joystick = FlbSliderController {
moved = Moved or function(v) self.joystickpos = v end,
released = Release or function(v) self.joystickpos = vec2(0,0) end
}
end
self.joystick:activate()
end
function flbg:setup(game, zoom)
self._game = game or nil
self.zoom = zoom or 1
self:setCamera(FlbCamera())
end
function flbg:setShader(Mesh)
if self.shader then
Mesh.shader = self.shader
end
end
function flbg:setCamera(cam)
self.camera = cam
local cs = {cam}
for k,v in ipairs(self.cameras) do
if v ~= cam then table.insert(cs, v) end
end
self.cameras = cs
--print(table.getn(self.cameras))
end
--- Destroys all cameras
function flbg:destroyCameras()
for k,v in ipairs(self.cameras) do
--v:destroy()
v = nil
end
self.cameras = {}
end
--- Destroys all controllers
function flbg:destroyControllers()
self.joystick = nil
end
--- Switches the game from one state to another
-- @param State the state to change to.
function flbg:switchState(State)
self._game._requestedState = State
end
--- Starts a flash on all of the current cameras
-- @param Time The time in seconds to show the flash
-- @param Color the color of the flash
function flbg:flash(Time, Color)
for k,v in ipairs(self.cameras) do
v:flash(Time, Color)
end
end
function flbg:setGravity(X,Y)
physics.gravity(X,Y)
end
function flbg:addCamera(cam)
for k,v in ipairs(self.cameras) do
if v == cam then
print("Camera already added")
return
end
end
table.insert(self.cameras, cam)
end
function flbg:getLibraryName()
return self.LIBRARY_NAME .. " v" .. self.LIBRARY_MAJOR_VERSION .. "." .. self.LIBRARY_MINOR_VERSION;
end
function flbg:addBitmap(Graphic, Reverse, Unique, Key)
end
function flbg:log(Message)
print(Message)
end
STICK_CONTROLLER = 1
SLIDER_CONTROLLER = 2
FlbG = flbg()
--- @type FlbGame
-- FlbGame is the heart of all Flibble games, it runs all of the loops and is the
-- starting point for all of the state management.
FlbGame = class()
--- The constructor function for the Game.
-- @param state The initial FlbState to run inside the game.
-- @param zoom The initial zoom level to start at.
function FlbGame:init(state, zoom)
FlbG:setup(self, zoom)
self.name = "Game"
-- Constructs the initial state
self.state = state()
end
--- The update function for the game
function FlbGame:update()
if self._requestedState == nil then
self.state:update()
for k,v in ipairs(FlbG.cameras) do
v:update()
end
else
-- Switching state
FlbG:destroyCameras()
FlbG:destroyControllers()
self.state.destroy()
self.state = nil
FlbG:setCamera(FlbCamera())
self.state = self._requestedState()
self._requestedState = nil
end
end
--- The draw function for the game
function FlbGame:draw()
background(0, 0, 0, 255)
noSmooth()
for k,v in ipairs(FlbG.cameras) do
if v.active then
pushMatrix()
scale(v.zoom)
translate(v.x, v.y)
if v.tint then
tint(v.tint)
end
for l,b in ipairs(v.members) do
b:draw()
end
popMatrix()
if v._flashTime then
--background(255, 255, 255, 255 * ((v._flashTime - v._flashCurrent)/v._flashTime))
v._flashMesh:setColors(v._flashColor.r, v._flashColor.g, v._flashColor.b, v._flashColor.a * ((v._flashTime - v._flashCounter)/v._flashTime))
pushStyle()
blendMode(ADDITIVE)
v._flashMesh:draw()
popStyle()
v._flashCounter = v._flashCounter + DeltaTime
--print((v._flashTime - v._flashCounter)/v._flashTime)
if v._flashCounter > v._flashTime then
v._flashCounter, v._flashTime, v._flashMesh = nil, nil, nil
end
end
end
end
if FlbG.debug then
FlbG.fpscounter:draw()
end
if FlbG.joystick then
FlbG.joystick:draw()
end
end
--- @type FlbGroup
-- An organisational class for grouping FlbBasics, updating and drawing them together.
-- @author Royletron
FlbGroup = class(FlbBasic)
--- Constructor function for the group
function FlbGroup:init()
FlbBasic.init(self)
self.name = "Group"
-- Sets an empty table for its members
self.members = {}
end
--- The update function for the group
function FlbGroup:update()
--FlbBasic.update(self)
for k,v in ipairs(self.members) do
if v then v:update() end
end
end
--- The draw function for the group
function FlbGroup:draw()
-- FlbBasic.draw(self)
for k,v in ipairs(self.members) do
v:draw()
end
end
--- Adds a given object to this group
-- @param object the object to add to this group
-- @return Sends the object back
function FlbGroup:add(object)
-- Ensures the object isn't already in the table.
if not FlbU:inTable(self.members) then
table.insert(self.members, object)
end
-- Runs the onAdd() function for the added object.
object:onAdd()
return(object)
end
--- @type FlbObject
-- This is the base class for most of the display objects (FlbSprite, FlbText etc).
-- Contains information regarding position and size and any physics attributes
-- @author Royletron
FlbObject = class(FlbBasic)
--- Constructor function
-- @param[opt] x The x coord for the object.
-- @param[opt] y The y coord for the object.
-- @param[opt] width The width of the object.
-- @param[opt] height The height of the object.
function FlbObject:init(x,y,width,height)
FlbBasic.init(self)
-- Override the name for debugging
self.name = "Object"
-- Set position and size based on constructor attributes or default all to 0
self.x, self.y, self.width, self.height = x or 0, y or 0, width or 0, height or 0
-- Set whether this object is static in the physics world
self.immovable = false
-- I think this is now defunct...
self.velocity = vec2(0,0)
-- When the camera scrolls this is a scaling factor to the scroll (allows slow parallax effects)
self.scrollfactor = vec2(1,1)
-- Similar to the scroll controller but scales the zoom of the camera (doesn't work)
self.zoomfactor = 1
-- A list of the cameras that draw this object, defaults to the main camera
self.cameras = {FlbG.camera}
-- Accesses the first camera in the cameras list
self.camera = FlbG.camera
-- Whether or not this object will work in the physics world defaulting to false
self.physical = false
-- A value to keep track of where the object was (think this is defunct now)
self._prevPos = vec2(self.x, self.y)
-- A value to keep track of the previous frames velocity (think this is now defunct)
end
--- All objects will call this when added to a FlbGroup or or child class adding the object to the groups camera(s)
function FlbObject:onAdd()
for k,v in ipairs(self.cameras) do
v:add(self)
end
end
--- Used for finding out the rectangular area of the object (should replace with FlbRect)
-- @returns four numbers representing the x coord, y coord, width and height in that order
function FlbObject:getBoundingBox()
return self.x - self.width/2, self.y - self.height/2, self.width, self.height
end
--- The update function of the object
function FlbObject:update()
-- If the object has been set up as a physical entity and hasn't yet had its body constructed.
if self.physical and self._body == nil then
-- Constructs the physical body of the object (need to watch for updates)
self._body = physics.body(POLYGON,
vec2(-self.width/2, self.height/2),
vec2(-self.width/2, -self.height/2),
vec2(self.width/2, -self.height/2),
vec2(self.width/2, self.height/2))
self._body.x = self.x
self._body.y = self.y
end
-- If the object has been set up as a physical entity.
if self.physical then
-- This is a fairly poor way at overriding the X and Y as set by the physics if required.
if self.x == self._prevPos.x and self.y == self._prevPos.y then
self.x, self.y = self._body.x, self._body.y
else
self._body.x, self._body.y = self.x, self.y
end
end
-- Keeps track of the previous position
self._prevPos.x, self._prevPos.y = self.x, self.y
end
--- Sets the velocity of the objects body
-- @param VelocityX The velocity in the x plane to set the physical body to
-- @param VelocityY The velocitt in the y plane to set the physical body to
function FlbObject:setVelocity(VelocityX, VelocityY)
self._body.linearVelocity = vec2(VelocityX or self._body.linearVelocity.x, VelocityY or self._body.linearVelocity.y)
end
--- Gets the current velocity of the objects body
-- @treturn vec2 Representing the bodies velocity
function FlbObject:getVelocity()
return self._body.linearVelocity
end
--- The draw function for the FlbObject
function FlbObject:draw()
if FlbG.showbounding then
pushStyle()
noFill()
stroke(103, 255, 0, 255)
strokeWidth(2)
rect(self.x, self.y, self.width, self.height)
popStyle()
end
end
--- Sets the cameras that this sprite belongs to
-- @param Cameras A table containing all of the cameras
function FlbObject:setCameras(Cameras)
for k,v in ipairs(self.cameras) do
v:remove(self)
table.remove(self.cameras, k)
end
for k,v in ipairs(Cameras) do
v:add(self)
table.insert(self.cameras, v)
end
end
--- @type FlbRect
-- Stores a rectangle
-- @author Royletron
FlbRect = class()
--- Constructor method
-- @param X The x coord of the rectangle
-- @param Y the y coord of the rectangle
-- @param Width the width of the rectangle
-- @param Height the height of the rectangle
function FlbRect:init(X, Y, Width, Height)
-- you can accept and set parameters here
self.x, self.y = X or 0, Y or 0
self.width, self.height = Width or 0, Height or 0
end
--- Setting an index with four 'get' functions.
function FlbRect:__index( index )
if index == "left" then
return self.x
elseif index == "right" then
return self.x + self.width
elseif index == "top" then
return self.y + self.height
elseif index == "bottom" then
return self.y
else
return rawget( self, index )
end
end
function FlbRect:overlaps(Target)
-- todo
end
FlbSliderController = class(FlbController)
-- A virtual analogue slider with a dead-zone at the center,
-- which activates wherever the user touches their finger
--
-- Arguments:
-- orientation - A unit vector that defines the orientation of the slider.
-- For example orientation=vec2(1,0) creates a horizontal slider,
-- orientation=vec2(0,1) creates a vertical slider. The slider
-- can be given an arbitrary orientation; it does not have to be
-- aligned with the x or y axis. For example, setting
-- orientation=vec2(1,1):normalize() creates a diagonal slider.
-- radius - Distance from the center to the end of the slider (default = 100)
-- deadZoneRadius - Distance from the center to the end of the dead zone (default = 25)
-- moved(x) - Called when the slider is moved
-- x : float - in the range -1 to 1
-- pressed() - Called when the user starts using the slider (optional)
-- released() - Called when the user releases the slider (optional)
function FlbSliderController:init(args)
self.orientation = args.orientation or vec2(1,0)
self.radius = args.radius or 100
self.deadZoneRadius = args.deadZoneRadius or 25
self.releasedCallback = args.released or doNothing
self.movedCallback = args.moved or doNothing
self.pressedCallback = args.pressed or doNothing
end
function FlbSliderController:touched(t)
local pos = touchPos(t)
if t.state == BEGAN and self.touchId == nil then
self.touchId = t.id
self.touchStart = pos
self.sliderOffset = 0
self.pressedCallback()
elseif t.id == self.touchId then
if t.state == MOVING then
local v = pos - self.touchStart
self.sliderOffset = clampAbs(project(v, self.orientation), self.radius)
self.movedCallback(self:value())
elseif t.state == ENDED or t.state == CANCELLED then
self:reset()
self.releasedCallback()
end
end
end
function FlbSliderController:reset()
self.touchId = nil
self.touchStart = nil
self.sliderOffset = nil
end
function FlbSliderController:value()
local range = self.radius - self.deadZoneRadius
local amount = sign(self.sliderOffset) * math.max(math.abs(self.sliderOffset) - self.deadZoneRadius, 0)
return amount/range
end
function FlbSliderController:draw()
if self.touchId ~= nil then
pushStyle()
ellipseMode(RADIUS)
strokeWidth(3)
stroke(255, 255, 255, 255)
lineCapMode(SQUARE)
noFill()
local function polarLine(orientation, fromRadius, toRadius)
local from = orientation * fromRadius
local to = orientation * toRadius
line(from.x, from.y, to.x, to.y)
end
pushMatrix()
translate(self.touchStart.x, self.touchStart.y)
polarLine(self.orientation, self.deadZoneRadius, self.radius)
polarLine(self.orientation, -self.deadZoneRadius, -self.radius)
local sliderPos = self.orientation * self.sliderOffset
translate(sliderPos.x, sliderPos.y)
strokeWidth(1)
ellipse(0, 0, 25, 25)
popMatrix()
popStyle()
end
end
--- @type FlbSplitController
-- Doesn't currently work.
FlbSplitController = class(FlbController)
function FlbSplitController:init(split)
if split.top ~= nil then
self.split = {split.bottom, split.top}
self.orientation = function(v) return v.y end
else
self.split = {split.left, split.right}
self.orientation = function(v) return v.x end
end
self.touches = {}
end
function FlbSplitController:touched(t)
local controller
if t.state == BEGAN then
local extent = self.orientation(vec2(WIDTH,HEIGHT))
local coord = self.orientation(t)
if coord < extent/2 then
controller = self.split[1]
else
controller = self.split[2]
end
self.touches[t.id] = controller
else
controller = self.touches[t.id]
if t.state == ENDED or t.state == CANCELLED then
self.touches[t.id] = nil
end
end
controller:touched(t)
end
function FlbSplitController:draw()
self.split[1]:draw()
self.split[2]:draw()
end
--- @type FlbSprite
-- The main "game object" class, a sprite is a FlbObject
-- with graphics options and abilities like animation.
-- @author Royletron
FlbSprite = class(FlbObject)
--- Constructor function
-- @param[opt] x The x coord of the sprite
-- @param[opt] y The y coord of the sprite
-- @param[opt] simpleGraphic A simple image location to display (optional)
function FlbSprite:init(x, y, simpleGraphic)
FlbObject.init(self, x, y)
self.name = "Sprite"
self.facing = RIGHT
self._curFacing = RIGHT
self._animations = {}
self._mesh = mesh()
self._curFrame, self.frame = -1, -1
self._curTime = 0
self._curPos = 0
if simpleGraphic then self:loadGraphic(simpleGraphic) end
end
--- Sets the velocity of the objects body
-- @param VelocityX The velocity in the x plane to set the physical body to
-- @param VelocityY The velocitt in the y plane to set the physical body to
function FlbSprite:setVelocity(VelocityX, VelocityY)
FlbObject.setVelocity(self, VelocityX, VelocityY)
end
--- Gets the current velocity of the objects body
-- @treturn vec2 Representing the bodies velocity
function FlbSprite:getVelocity()
return self._body.linearVelocity
end
--- Sets the cameras that this sprite belongs to
-- @param Cameras A table containing all of the cameras
function FlbSprite:setCameras(Cameras)
FlbObject.setCameras(self, Cameras)
end
--- The sprites update function
function FlbSprite:update()
FlbObject.update(self)
if self._curAnim ~= nil then
self._curTime = self._curTime + DeltaTime
if self._curTime > (1/self._curAnim.framerate) then
self._curTime = self._curTime - (1/self._curAnim.framerate)
self._curPos = self._curPos + 1
if self._curPos > table.getn(self._curAnim.frames) then
self._curPos = 1
end
self.frame = self._curAnim.frames[self._curPos]
--self:setFrame(current.frames[self.currentFrame])
end
end
if self._curFrame ~= self.frame then
self._curFrame = self.frame
local offx = self.frame%self._cols
local offy = math.floor(self.frame/self._cols)
if value == 0 then offy = 0 end
self._mesh:setRectTex(self._index, offx / self._cols, self._rows - ((offy + 1) / self._rows), 1 / self._cols, 1 / self._rows)
end
end
--- The draw function
function FlbSprite:draw()
--[[
if self.scrollfactor.x ~= 1 or self.scrollfactor.y ~= 1 then
pushMatrix()
local mat = modelMatrix()
local x = - mat[13] + mat[13]*self.scrollfactor.x
local y = - mat[14] + mat[14]*self.scrollfactor.y
translate(x,y)
--resetMatrix()
--print(modelMatrix())
--print(camera)
-- print(viewMatrix())
--applyMatrix()
end
]]
if self.frame ~= -1 then
local r = 1
if self.reverse then
if self.facing == LEFT then r = -1 end
end
self._mesh:setRect(self._index, self.x + self.width/2, self.y + self.height/2, self.width*r, self.height)
end
self._mesh:draw()
FlbObject.draw(self)
--[[
if self.scrollfactor.x ~= 1 or self.scrollfactor.y ~= 1 then
popMatrix()
end
]]
end
--- Loads the graphic into this sprite.
-- @param Graphic The location of the texture.
-- @param Animated
function FlbSprite:loadGraphic(Graphic, Animated, Reverse, Width, Height)
self.reverse = Reverse
local texture = readImage(Graphic)
self._mesh.texture = texture
self._rows, self._cols = 1,1
if Animated then
self.width, self.height = Width, Height
self._rows, self._cols = math.floor(texture.height/Height), math.floor(texture.width/Width)
else
self.width, self.height = texture.width, texture.height
end
self._index = self._mesh:addRect(self.x, self.y, self.width, self.height)
self.frame = 0
end
function FlbSprite:addAnimation(Name, Frames, FrameRate, Looped)
if self._animations[Name] ~= nil then
FlbG.log("Animation '"..Name.."' already exists")
return
end
self._animations[Name] = FlbAnim(Name, Frames, FrameRate, Looped)
end
function FlbSprite:play(Name, Forced)
if self._animations[Name] == nil then
FlbG.log("Animation doesn't exist")
elseif self._curAnim == nil or (self._curAnim.name ~= Name) then
self._curAnim = self._animations[Name]
self.frame = 0
end
end
function FlbSprite:onAdd()
FlbObject.onAdd(self)
end
--[[
function Sprite:setTexture(texture, spriteWidth, spriteHeight, pos)
self.texture = readImage(texture)
self.spriteCols = math.floor(self.texture.width/spriteWidth) or 1
self.spriteRows = math.floor(self.texture.height/spriteHeight) or 1
self.pos = pos or 0
if self.texture ~= nil then
self.width = spriteWidth or self.texture.width
self.height = spriteHeight or self.texture.height
self.mesh.texture = self.texture
self.index = self.mesh:addRect(self.x, self.y, self.width, self.height)
self:setFrame(self.pos)
end
end
function Sprite:addAnimation(name, frames, frameRate, looped)
if self.animations[name] == nil then
local anim = {}
anim.frames = frames
anim.frameRate = frameRate
anim.looped = looped or true
self.animations[name] = anim
else
print("animation "..name.." already exists.")
end
end
function Sprite:play(name)
if self.animations[name] == nil then
print("animation "..name.." doesn't exist")
elseif self.current ~= name then
self.current = name
self.currentFrame, self.currentTime = 1, 0
self:setFrame(self.animations[name].frames[self.currentFrame])
end
end
function Sprite:setFrame(pos)
if self.frame ~= pos then
self.frame = pos
self.spriteOffset.x = self.frame%self.spriteCols
self.spriteOffset.y = math.floor(self.frame/self.spriteCols)
if pos == 0 then self.spriteOffset.y = 0 end
self.mesh:setRectTex(self.index, self.spriteOffset.x / self.spriteCols, self.spriteRows - ((self.spriteOffset.y + 1) / self.spriteRows), 1 / self.spriteCols, 1 / self.spriteRows)
end
end
function Sprite:update()
if self.current ~= nil then
self.currentTime = self.currentTime + DeltaTime
local current = self.animations[self.current]
if self.currentTime > (1/current.frameRate) then
self.currentTime = self.currentTime - (1/current.frameRate)
self.currentFrame = self.currentFrame + 1
if self.currentFrame > table.getn(current.frames) then
self.currentFrame = 1
end
self:setFrame(current.frames[self.currentFrame])
end
end
end
function Sprite:draw()
--self:update()
if self.texture ~= nil then
self.mesh:setRect(self.index, self.x, self.y, self.width, self.height)
self.mesh:draw()
end
if self.showBounding then
--local box = self:getBoundingBox()
pushStyle()
noFill()
stroke(26, 255, 0, 255)
if self.active==true then stroke(255, 11, 0, 255) end
strokeWidth(1)
--rect(box.left, box.top, box.right-box.left, box.bottom-box.top)
--rect(box.x, box.y, box.width, box.height)
rect(self:getBoundingBox())
ellipse(self.x,self.y, 5)
popStyle()
end
self.prev_x, self.prev_y = self.x, self.y
end
]]
--- @type FlbState
-- Represents a specific state within the game play. States can be used for moving between
-- different types of screen, for example from a menu state into a gameplay state. The state
-- becomes responsible for calling the update and draw functions for the objects that make the
-- state up. Extends a group to inherit the basic member management.
FlbState = class(FlbGroup)
--- The constructor function for the state.
function FlbState:init()
FlbGroup.init(self)
self.name = "State"
end
-- A virtual analogue joystick with a dead-zone at the center,
-- which activates wherever the user touches their finger
--
-- Arguments:
-- radius - radius of the stick (default = 100)
-- deadZoneRadius - radius of the stick's dead zone (default = 25)
-- moved(v) - Called when the stick is moved
-- v : vec2 - in the range vec2(-1,-1) and vec2(1,1)
-- pressed() - Called when the user starts using the stick (optional)
-- released() - Called when the user releases the stick (optional)
FlbStickController = class(FlbController)
function FlbStickController:init(args)
--FlbController.init(self)
self.radius = args.radius or 100
self.deadZoneRadius = args.deadZoneRadius or 25
self.releasedCallback = args.released or doNothing
self.steerCallback = args.moved or doNothing
self.pressedCallback = args.pressed or doNothing
end
function FlbStickController:touched(t)
local pos = touchPos(t)
if t.state == BEGAN and self.touchId == nil then
self.touchId = t.id
self.touchStart = pos
self.stickOffset = vec2(0, 0)
self.pressedCallback()
elseif t.id == self.touchId then
if t.state == MOVING then
self.stickOffset = clampLen(pos - self.touchStart, self.radius)
self.steerCallback(self:vector())
elseif t.state == ENDED or t.state == CANCELLED then
self:reset()
self.releasedCallback()
end
end
end
function FlbStickController:vector()
local stickRange = self.radius - self.deadZoneRadius
local stickAmount = math.max(self.stickOffset:len() - self.deadZoneRadius, 0)
local stickDirection = self.stickOffset:normalize()
return stickDirection * (stickAmount/stickRange)
end
function FlbStickController:reset()
self.touchId = nil
self.touchStart = nil
self.stickOffset = nil
end
function FlbStickController:draw()
if self.touchId ~= nil then
pushStyle()
ellipseMode(RADIUS)
strokeWidth(1)
stroke(255, 255, 255, 255)
fill(68, 68, 68, 81)
pushMatrix()
translate(self.touchStart.x, self.touchStart.y)
ellipse(0, 0, self.radius, self.radius)
ellipse(0, 0, self.deadZoneRadius, self.deadZoneRadius)
translate(self.stickOffset.x, self.stickOffset.y)
ellipse(0, 0, 25, 25)
popMatrix()
popStyle()
end
end
--- @type FlbText
-- Creates a text field that can have its text to
-- display anything required
-- @author Royletron
FlbText = class(FlbSprite)
--- accepts the width (currently unused) and the text to display
-- @param x the x coordinate to place the text, bottom left convention
-- @param y the y coordinate to place the text, bottom left convention
-- @param Width the width of the text field, to be used for text wrapping (todo)
-- @param Text the text to fill into the text field
function FlbText:init(x, y, Width, Text)
FlbSprite.init(self, x, y)
-- the width of the textfield, currently unused
self.width = Width or 0
-- the text to display in the text field, can be updated anytime
self.text = Text
-- the name of the font to use, as per ios fonts see iosfonts.com (defaults to menlo bold)
self.font = "Menlo-Bold"
-- the color of the displayed text, defaults to white
self.color = color(255, 255, 255, 255)
-- the alignment of the text, currently only accepts left and center, defaults to left
self.alignment = CENTER
-- the size of the displayed text, defaults to 8
self.size = 8
end
--- Called on every frame if added to a camera
function FlbText:draw()
pushStyle()
-- set the font style
font(self.font)
fontSize(self.size)
-- set the alignment
textAlign(self.alignment or CENTER)
-- displays s shadow if a shadow color is set, default is non set
if self.shadow then
fill(self.shadow)
text(self.text, self.x + 1, self.y - 1)
end
-- sets the font color
fill(self.color)
-- prints the text
text(self.text, self.x, self.y)
popStyle()
end
--- You can use this if you have a lot of text parameters
-- to set instead of the individual properties
-- @param Font The name of the font face for the text display.
-- @param Size The size of the font (in pixels essentially).
-- @param Color The color of the text in traditional flash 0xRRGGBB format.
-- @param Alignment A string representing the desired alignment ("left,"right" or "center").
-- @param ShadowColor A uint representing the desired text shadow color in flash 0xRRGGBB format.
--
-- @return This FlbText instance (nice for chaining stuff together, if you're into that).
function FlbText:setFormat(Font, Size, Color, Alignment, ShadowColor)
self.font = Font or self.font
self.size = Size or self.size
self.color = Color or self.color
self.alignment = Alignment or self.alignment
self.shadow = ShadowColor or self.shadow
return self
end
--- Called when touched by the user (todo)
-- @param touch The touch event passed over including the phase and position
function FlbText:touched(touch)
-- Codea does not automatically call this method
end
--- @type FlbTilemap
-- A tilemap that follows the Tiled json format. Can be multilayered with collision objects.
FlbTilemap = class(FlbObject)
--- The constructor function for the tilemap
-- @param path The path to the tilemap json file.
function FlbTilemap:init(path)
FlbObject.init(self)
self.name = "Tilemap"
-- The tile meshes
self.tilesets = {}
-- The individual layers
self.layers = {}
-- The physical collider bodies
self.bodies = {}
self:loadJSON(path)
end
--- Loads the JSON for the tilemap
-- @param path the path to the JSON file.
function FlbTilemap:loadJSON(path)
if path then
local json = json.decode(readText(path))
if json then
self.tileheight = json.tileheight
self.tilewidth = json.tilewidth
self.height = json.height * self.tileheight
self.width = json.width * self.tilewidth
print("Map dimensions:"..self.width.."/"..self.height)
for k,v in ipairs(json.tilesets) do
local ts = {}
ts.name = v.name
ts.firstgid = v.firstgid
ts.image = v.image
ts.tilewidth, ts.tileheight = v.tilewidth, v.tileheight
ts.cols, ts.rows = v.imagewidth/v.tilewidth, v.imageheight/v.tileheight
ts.c, ts.r = 1/ts.cols, 1/ts.rows
print(ts.cols..":"..ts.rows)
ts.texture = readImage("Dropbox:"..ts.image)
table.insert(self.tilesets, ts)
end
for k,v in ipairs(json.layers) do
if v.type == "tilelayer" then
local l = {}
l.name = v.name
l.drawing = true
l.sprite = FlbSprite(self.x, self.y)
local ts = self.tilesets[1]
l.sprite._mesh.texture = ts.texture
-- l.mesh.shader = shader("Filters:Posterize")
--FlbG:setShader(l.mesh)
l.width = v.width
l.height = v.height
for p, n in ipairs(v.data) do
if n ~= 0 then
local i = l.sprite._mesh:addRect((p%l.width)*ts.tilewidth + ts.tilewidth/2, self.height - math.ceil(p/l.width)*ts.tileheight + self.tileheight/2, ts.tilewidth, ts.tileheight)
local spriteOffsetx = (n%ts.cols) - 1
local spriteOffsety = ts.rows - math.floor(n/ts.cols) -1
if spriteOffsetx < 0 then
spriteOffsetx = ts.cols-1
spriteOffsety = spriteOffsety + 1
end
l.sprite._mesh:setRectTex(i, ts.c * spriteOffsetx, ts.r*spriteOffsety, ts.c, ts.r)
end
end
table.insert(self.layers, l)
elseif v.type == "objectgroup" then
for k,o in ipairs(v.objects) do
o.y = self.height - o.y - o.height
local b = physics.body(POLYGON,
vec2(o.x, o.y),
vec2(o.x, o.y - o.height),
vec2(o.x + o.width, o.y - o.height),
vec2(o.x + o.width, o.y))
b.type = STATIC
table.insert(self.bodies, b)
--print(b.worldCenter)
end
end
end
end
end
end
--- The onAdd function is called when this tilemap is added to a group
function FlbTilemap:onAdd()
FlbObject.onAdd(self)
end
--- The draw function for the tilemap
function FlbTilemap:draw()
for k,l in ipairs(self.layers) do
--FlbG:setShader(l.mesh)
if l.drawing then
l.sprite._mesh:draw()
end
end
FlbObject.draw(self)
end
--- Returns a layer by name (so they can be manually layered)
-- @param name The name of the layer to return
-- @return The sprite of the requested layer, or nil if it doesn't exist.
function FlbTilemap:getLayer(name)
for k,v in ipairs(self.layers) do
if v.name == name then return v end
end
return nil
end
--- The update function for the tilemap
function FlbTilemap:update()
end
-- Fires a callback when the user touches the screen and when
-- they lift their finger again and ignores other touches in
-- the meantime
--
-- Arguments:
--
-- pressed(p) - callback when the user starts the touch (optional)
-- p : vec2 - the location of the touch
--
-- released(p) -- callback when the user ends the touch (optional)
-- p : vec2 - the location of the touch
FlbTouch = class(FlbController)
function FlbTouch:init(args)
self.actionCallback = args.pressed or doNothing
self.stopCallback = args.released or doNothing
self.DOWN = false
self.screenX = 0
self.screenY = 0
self.touchId = nil
end
function FlbTouch:touched(t)
self.screenX, self.screenY = t.x, t.y
if t.state == BEGAN and self.touchId == nil then
self.touchId = t.id
self.actionCallback(touchPos(t))
self.DOWN = true
elseif t.state == ENDED and t.id == self.touchId then
self.touchId = nil
self.stopCallback(touchPos(t))
self.DOWN = false
elseif t.state == CANCELLED then
self.touchId = nil
end
end
--- @module FlbU
-- Another big singleton, this is more for functional utilities that can be quite useful
local flbU = class()
function flbU:init()
-- you can accept and set parameters here
end
function flbU:inTable(tab, object)
for k, v in ipairs(tab) do
if v == object then return true end
end
return false
end
function flbU:removeFromTable(tab, object)
for k,v in ipairs(tab) do
if v == object then
table.remove(tab, k)
return
end
end
end
function flbU:pointOverlaps(Point, Object)
local ox, oy, ow, oh = Object:getBoundingBox()
if Point.x > ox and Point.x < ox + ow and Point.y > oy and Point.y < oy + oh then
return true
end
return false
end
FlbU = flbU()
--- @type FPSCounter
-- A basic frame per second counter. Taken from various examples.
FPSCounter = class()
-- The constructor function for the counter
function FPSCounter:init()
-- you can accept and set parameters here
self.fps = 0
self.fpsStep = 0
self.fpsCount = 0
self.fpsTotal = 0
end
-- The draw function for the counter
function FPSCounter:draw()
-- Codea does not automatically call this method
if self.fpsStep < ElapsedTime then
self.fpsStep = ElapsedTime + 1
self.fps = math.floor(1/DeltaTime)
end
self.fpsCount = self.fpsCount + 1
self.fpsTotal = self.fpsTotal + math.floor(1/DeltaTime)
pushStyle()
fill(0, 0, 0, 255)
rect(WIDTH-80, 20, 80, 20)
if self.fps >= 50 then
fill(0, 255, 0, 255)
else
fill(255, 0, 0, 255)
end
rect(WIDTH - 80, 0, 80, 20)
fill(0, 0, 0, 255)
text("FPS: "..self.fps, WIDTH - 38, 10)
fill(255, 255, 255, 255)
text("AVG: "..math.floor(self.fpsTotal/self.fpsCount), WIDTH - 38, 30)
popStyle()
end
-- Flibble
VERSION = "0.0.1" -- Use this to set Version Numbers Or set Version in class creation
PROJECTNAME = "Flibble"
BUILD = true -- Include this Global if you want a separate Gist for builds *true* creates a build gist
--# Main
-- Use this function to perform your initial setup
function setup()
saveProjectInfo("Author","Darren Royle")
if AutoGist then
autoGist = AutoGist(PROJECTNAME,"A 2D game engine based on Flixel with glowing eyes.",VERSION,false)
autoGist:backup(true)
end
FlbG:log(FlbG:getLibraryName())
parameter.boolean("Debug", false, function(d) FlbG.debug = d end)
parameter.boolean("Bounding Boxes", false, function(d) FlbG.showbounding = d end)
game = FlbGame(TestState, 1)
parameter.number("Zoom", 0.3, 4, 3, function(n) FlbG.camera.zoom = n end)
end
-- This function gets called once every frame
function draw()
game:update()
game:draw()
end
TestChar = class(FlbSprite)
function TestChar:init()
FlbSprite.init(self, 10, 23)
self.name = "char"
self:loadGraphic("Dropbox:Matt", true, true, 16, 24)
self:addAnimation("still", {0,1}, 1)
self:addAnimation("walkleft", {2,3}, 3)
self:addAnimation("walkright", {8,9}, 3)
self:addAnimation("walkforward", {0,1}, 3)
self:addAnimation("walkbackward", {4,5}, 3)
self:play("still")
self.physical = true
self.steer = vec2(0,0)
self.speed = 130
FlbG:usejoystick(STICK_CONTROLLER)
--FlbG.joystick:activate()
end
function TestChar:update()
if self._body then
self:setVelocity(FlbG.joystickpos.x * self.speed)
--print(self._body.linearVelocity)
--print(FlbG.joystickpos)
if math.floor(self:getVelocity().y/3) == 0 and FlbG.joystickpos.y > 0.2 then self:setVelocity(nil, 300) end
end
--self.velocity.y = FlbG.joystickpos.y * self.speed
-- print(self.velocity)
--print(FlbG.joystickpos)
--print(self.steer)
--self.x, self.y = self.x + DeltaTime*12, self.y + DeltaTime*12
FlbSprite.update(self)
self.steer = FlbG.joystickpos
self.facing = RIGHT
if self.steer.x > 0 and self.steer.x > math.sqrt(math.pow(self.steer.y, 2)) then self:play("walkright")
elseif self.steer.x < 0 and -self.steer.x > math.sqrt(math.pow(self.steer.y, 2)) then self:play("walkright") self.facing = LEFT
elseif self.steer.y > 0 then self:play("walkbackward")
elseif self.steer.y < 0 then self:play("walkforward")
else self:play("still") end
--self.x = self.x + self.steer.x * self.speed * DeltaTime
--self.y = self.y + self.steer.y * self.speed * DeltaTime
end
TestMenuState = class(FlbState)
function TestMenuState:init()
FlbState.init(self)
FlbG.camera.tint = color(255, 0, 216, 65)
self:add(FlbText(100,100,100,"Welcome"))
self:add(FlbButton(100,100,"Click Me", function() FlbG:flash(0.4) end))
end
TestState = class(FlbState)
local spr
function TestState:init()
FlbState.init(self)
FlbG:setGravity(0,-800)
self.spr = TestChar()
-- self:add(FlbTilemap("Dropbox:World1"))
self.map = FlbTilemap("Dropbox:World1")
self:add(self.map)--:getLayer("Background"))
self:add(self.spr)
local fg = self.map:getLayer("Foreground")
fg.drawing = false
self:add(fg.sprite)
--self:add(self.map:getLayer("Foreground"))
FlbG.camera.target = self.spr
FlbG.camera.bounds = FlbRect(0,0,2000,400)
local test = FlbSprite(0, 0)
--test.scrollfactor.x = 0
--test.scrollfactor.y = 0
test:loadGraphic("Dropbox:World")
local uicam = FlbCamera()
FlbG:addCamera(uicam)
FlbG:usejoystick()
uicam.bgColor = color(255, 255, 255, 0)
test:setCameras({uicam})
self:add(test)
local txt = FlbText(0, HEIGHT-60, 90, "Hello World")
txt:setCameras({uicam})
txt:setFormat("Menlo-Bold", 60, color(244,211,211,255), LEFT, color(0,0,0,255))
self:add(txt)
end
function TestState:update()
--end
--spr.x = spr.x + 1
--print(FlbG.joystick.steer)
--print(FlbG.touch.DOWN)
FlbState.update(self)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment