Skip to content

Instantly share code, notes, and snippets.

@simon-engledew
Created February 3, 2015 12:11
Show Gist options
  • Save simon-engledew/c6fdbfbdede60ef0e6e3 to your computer and use it in GitHub Desktop.
Save simon-engledew/c6fdbfbdede60ef0e6e3 to your computer and use it in GitHub Desktop.
Flibble Release v0.2.1 -A 2D game engine based on Flixel with glowing eyes.
Flibble Tab Order Version: 0.2.1
------------------------------
This file should not be included in the Codea project.
#Main
#FlbAnim
#FlbButton
#FlbCamera
#FlbController
#FlbEmitter
#FlbParticle
#FlbRect
#FlbSliderController
#FlbSplitController
#FlbSprite
#FlbStickController
#FlbText
#FlbTilemap
#FlbTouch
#FlbBasic
#FPSCounter
#FlbGame
#FlbGroup
#FlbObject
#FlbState
#FlbG
#FlbU
#TestChar
#TestMenuState
#TestState
--- @classmod 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
--- @classmod 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.exists = true
self.alive = true
self._destroy = false
-- 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
--- @classmod 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
--- Update function
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
--- Override the getBoundingBox function for FlbObject
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
--- @classmod 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
--- Checks to see whether or not an object is in the view of the camera or not
-- @param object the object that you want to test
function FlbCamera:onCamera(object)
if object.x == nil or object.y == nil then return false end
object.onCamera = object.x + self.x + object.width > 0 and object.x + self.x - object.width < self.width / self.zoom and object.y + self.y + object.height > 0 and object.y + self.y - object.height < self.height / self.zoom
return object.onCamera
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.bounds.right ~= 0 then
if self.x + (self.width/self.zoom) > self.bounds.right then self.x = self.bounds.right - (self.width/self.zoom) end
end
if -self.y < self.bounds.bottom then self.y = -self.bounds.bottom end
if self.bounds.top ~= 0 then
if self.y + self.height/self.zoom > self.bounds.top then self.y = self.bounds.top - self.height/self.zoom end
end
end
end
--- Makes the camera flash!
-- @param Time the time in seconds to flash for
-- @param Color the color of the flash
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
--- Takes a touch point (screen coords) and coverts it to camera coordinates, used for touch handling
-- @param Point the point that you want to get the local coords for.
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
--- @classmod 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
--- @classmod FlbEmitter
-- Emits a set of random sprites. The number of sprites as well as the frequency
-- that they are emitted can be controlled. There is also the 'explode' setting that makes
-- all of the particles (sprites) emit at once.
FlbEmitter = class(FlbGroup)
--- The FlbEmitter constructor
-- @param X the x coord for the emitter
-- @param Y the y coord for the emitter
-- @param Size the max number of particles that the emitter will control. The slots are reused when a particle dies.
function FlbEmitter:init(X, Y, Size)
-- you can accept and set parameters here
FlbGroup.init(self)
self.name = "emitter"
self.x, self.y, self.size = X, Y, Size
self.counter = 0
self.on = false
end
--- The update function
function FlbEmitter:update()
if self.on then
self.counter = self.counter + DeltaTime
if self.counter > self.frequency then
self.counter = self.counter - self.frequency
FlbEmitter.emitParticle(self)
end
end
FlbGroup.update(self)
end
--- Triggered when the emitter is added to a group
function FlbEmitter:onAdd()
FlbGroup.onAdd(self)
end
--- Starts the emitter emitting particles. If explode is set to true then all particles emit once and at once.
-- The lifespan for the particles can also be set, and if explode is false the frequency that the particles emits
-- be set
-- @param Explode[opt] a boolean denoting whether all particles emit at once
-- @param Lifespan[opt] the time in seconds that each particle stays on the stage for
-- @param Frequency[opt] the frequency at which particles are emitted (ignore for explode).
function FlbEmitter:start(Explode, Lifespan, Frequency)
self.explode = Explode
if self.explode == nil then self.explode = true end
self.lifespan = Lifespan or 0
self.frequency = Frequency or 0.1
self.on = true
end
--- Mostly used internally to emit a particle (or all particles for explode).
function FlbEmitter:emitParticle()
if self.explode then self.on = false end
if self.explode then
for k,v in ipairs(self.members) do
v:reset(self.x, self.y, self.lifespan)
local d = FlbU:randomDirection()
v:setForces(math.random(10,80)*d, math.random(50,100),math.random(1000,6000)*-d)
v.exists = true
end
else
local p = FlbGroup.getFirstAvailable(self, FlbParticle)
p:reset(self.x, self.y, self.lifespan)
local d = FlbU:randomDirection()
p:setForces(math.random(10,80)*d, math.random(50,100),math.random(1000,6000)*-d)
p.exists = true
end
end
--- Creates the array of particles from the supplied graphic
-- @param Graphic the name of the image to use for the individual sprites
-- @param Quantity the number of particles to generate
-- @param Multiple whether the spritesheet is one individual image or made up of multiple (currently ignored)
function FlbEmitter:makeParticles(Graphic, Quantity, Multiple)
for i=1, Quantity, 1 do
local p = FlbParticle(self.x, self.y)
p:loadGraphic(Graphic)
p.frame = math.random(0, p._cols-1)
--table.insert(self.particles, p)
FlbGroup.add(self, p)
-- p.exists = false
end
end
--- @module FlbG
-- A jolly big helper singleton that can do all sorts of stuff that is game specific.
local flbg = class()
--- The constructor for the singleton (yeah I know!)
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 = 2
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._cache = {}
self.collisions = {}
end
--- Loads a graphic into the cache, or returns the cached graphic is already loaded
-- @treturn image the graphic as retrieved from cache or created.
function flbg:loadGraphic(Graphic)
if self._cache[Graphic] == nil then self._cache[Graphic] = readImage(Graphic) end
return self._cache[Graphic]
end
--- Starts using a joystick on the stage
-- @param Type the named type for the joystick (see joystick types)
-- @param Moved the callback for when the joystick moves (will send the steer over as a param)
-- @param Release the callback for when the joystick is released.
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
--- Sets up the singleton by adding a reference to the game as needed, as well as a default zoom
-- @param game an instance of the game object to be used later
-- @param the default camera zoom
function flbg:setup(game, zoom)
self._game = game or nil
self.zoom = zoom or 1
self:setCamera(FlbCamera())
end
--- Think this is defunct, at least I have no idea what it is doing anymore.
function flbg:setShader(Mesh)
if self.shader then
Mesh.shader = self.shader
end
end
--- Sets the main camera to the given camera instance
-- @param cam The FlbCamera instance that is to become the default camera.
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
--- Sets the gravity for the physics
-- @param X the required gravity in the x plane (pixels per second)
-- @param Y the required gravity in the y plane (pixels per second)
function flbg:setGravity(X,Y)
physics.gravity(X,Y)
end
--- Adds a camera to the camera list
-- @param cam the FlbCamera instance to add to the list
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
--- Returns the library name
-- @treturn string the library name.
function flbg:getLibraryName()
return self.LIBRARY_NAME .. " v" .. self.LIBRARY_MAJOR_VERSION .. "." .. self.LIBRARY_MINOR_VERSION;
end
function flbg:isColliding(object)
for k,v in ipairs(self.collisions) do
--if v.
end
end
function flbg:addBitmap(Graphic, Reverse, Unique, Key)
end
--- Prints a message to the log
-- @param Message the message to print to the log.
function flbg:log(Message)
print(Message)
end
STICK_CONTROLLER = 1
SLIDER_CONTROLLER = 2
SEPIA_SHADER = "Dropbox:Sepia" -- factor
FOG_SHADER = "Dropbox:Fog" -- offset vec2, size float
DITHER_SHADER = "Dropbox:Dithered"
HALFTONE_SHADER = "Dropbox:Halftone"
SINE_SHADER = "Dropbox:Sine" -- phase, amplitude, frequency
KNIT_SHADER = "Dropbox:Knit"-- tilesize vec2, threads
SCROLL_SHADER = "Dropbox:Scroller" -- position (0..1)
FlbG = flbg()
--- @classmod 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
if v.exists then
v:update()
end
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
if table.getn(FlbG.collisions) > 0 then
FlbG.collisions = {}
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)
--print(v.tint)
--tint(112, 32, 32, 255)
for l,b in ipairs(v.members) do
if b.active and b.exists and v:onCamera(b) then
b:draw()
end
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
--- Not sure where this is going...
function collide(collision)
--print(collision.bodyB.info.name)
table.insert(FlbG.collisions, collision)
end
--- @classmod 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
--print(v.name
if v._destroy then
v:destroy()
FlbU:removeFromTable(self.members, v)
else
--if v and v.camera and v.camera:onCamera(v) then
--v:update()
--elseif v and v.camera == nil then
--must be a group right
if v.exists then
v:update()
end
--end
end
end
end
--- The draw function for the group
function FlbGroup:draw()
-- FlbBasic.draw(self)
for k,v in ipairs(self.members) do
if v.onCamera then
--v:draw()
end
end
end
--- Triggered when the group is added to another group
function FlbGroup:onAdd()
for i,v in ipairs(self.members) do
--print(v.name)
v:onAdd()
end
end
function FlbGroup:getFirstAvailable(ObjectClass)
if self.members then
for k,v in ipairs(self.members) do
if v.exists == false and ObjectClass._type == v._type then
return v
end
end
end
return nil
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
--- @classmod 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...maybe not
self.velocity = vec2(0,0)
self.angularVelocity, self._prevVelocityX, self._prevVelocityY, self._prevAngularVelocity = 0,0,0,0,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
self.sleepingAllowed = true
-- 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)
self.onCamera = false;
self.friction = 0.2
self.rotation, self._prevRotation = 0, 0
self.restitution = 0.20
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
function FlbObject:makeBody()
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.interpolate = true
--self._body.restitutions = 0.25
self._body.x = self.x
self._body.y = self.y
if self.categories then self._body.categories = self.categories end
if self.mask then self._body.mask = self.mask end
self._body.info = self
self._body.linearVelocity = self.velocity
self._body.angularVelocity = self.angularVelocity
self._body.restitution = self.restitution
self._body.friction = self.friction
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)
FlbObject.makeBody(self)
end
-- If the object has been set up as a physical entity.
if self.physical then
self._body.type = self.immovable and STATIC or DYNAMIC
self._body.sleepingAllowed = self.sleepingAllowed
if self.sleepingAllowed then self._body.active = self.onCamera end
-- 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
if self.rotation == self._prevRotation then
self.rotation = self._body.angle
else
self._body.angle = self.rotation
end
if self.velocity.x == self._prevVelocityX then
self.velocity.x = self._body.linearVelocity.x
else
self._body.linearVelocity = vec2(self.velocity.x, self._body.linearVelocity.y)
end
if self.velocity.y == self._prevVelocityY then
self.velocity.y = self._body.linearVelocity.y
else
self._body.linearVelocity = vec2(self._body.linearVelocity.x, self.velocity.y)
end
self._body.friction = self.friction
end
self._prevRotation = self.rotation
-- Keeps track of the previous position
self._prevPos.x, self._prevPos.y = self.x, self.y
self._prevVelocityX, self._prevVelocityY, self._prevAngularVelocity = self.velocity.x, self.velocity.y, self.angularVelocity
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
--- Sets the current angular velocity of the objects body
-- @param Velocity the angular velocity to set to in degrees per second
function FlbObject:setAngularVelocity(Velocity)
self._body.angularVelocity = Velocity
end
--- Gets the current angular velocity of the objects body
-- @treturn float Representing the bodies angular velocity
function FlbObject:getAngularVelocity()
return self._body.angularVelocity
end
--- The draw function for the FlbObject
function FlbObject:draw()
if FlbG.showbounding then
pushStyle()
noFill(200, 0, 0, 200)
stroke(103, 255, 0, 255)
strokeWidth(2)
ellipse(self.x, self.y, 10)
rect(self.x - self.width/2, self.y - self.height/2, self.width, self.height)
popStyle()
end
end
function FlbObject:collides(ObjectOrGroup, Callback)
if ObjectOrGroup.members then
for k,v in ipairs(ObjectOrGroup) do
FlbObject.collide(self, v, Callback)
end
else
print(ObjectOrGroup)
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
--- @classmod FlbParticle
-- An basic particle to be used mostly with emitters. It is pretty much a sprite but with some randomly generated forces
FlbParticle = class(FlbSprite)
--- The constructor for the particle
-- @param X the x coord for the particle (should match the emitter)
-- @param Y the y coord for the particle (should match the emitter)
function FlbParticle:init(X,Y)
-- you can accept and set parameters here
FlbSprite.init(self, X, Y)
self.name = "particle"
self.physical = true
self.exists = false
self.mask = {1}
self.categories = {2}
self.restitution = 0.6
self.lifespan = 3
self.lifecounter = 0
--FlbObject.makeBody(self)
end
--- When the particle is picked up for reuse (i.e. it has 'died') the emitter will reset it's parameters
-- @param X the x coord to reset the particle to (should match the emitter)
-- @param Y the y coord to reset the particle to (should match the emitter)
-- @param Lifespan the new lifespan for the particle
function FlbParticle:reset(X,Y,Lifespan)
self.x, self.y = X,Y
if self._body then self._body.x = self.x self._body.y = self.y end
self.rotation = math.random()*math.pi
self.lifespan = Lifespan
self.lifecounter = 0
end
--- Sets the various forces for the particle as randomised by the emitter
-- @param VelocityX the velocity in the x plane to set the particle to
-- @param VelocityY the velocity in the y plane to set the particle to
-- @param Angula the angular velocity to set the particle to
function FlbParticle:setForces(VelocityX, VelocityY, Angular)
self.velocity = vec2(VelocityX, VelocityY)
self.angularVelocity = Angular
end
function FlbParticle:loadGraphic(...)
FlbSprite.loadGraphic(self, ...)
end
function FlbParticle:onAdd()
FlbSprite.onAdd(self)
end
function FlbParticle:update()
if self.exists then
FlbSprite.update(self)
if self.lifecounter and self.lifespan ~= 0 then
self.lifecounter = self.lifecounter + DeltaTime
if self.lifecounter > self.lifespan then
self.exists = false
end
end
end
if self._body then
self._body.active = self.alive
end
end
function FlbParticle:destroy()
FlbSprite.destroy(self)
end
function FlbParticle:draw()
if self.exists then
FlbSprite.draw(self)
end
end
FlbParticle._type = "FlbParticle"
--- @classmod 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
--- @classmod 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
--- @classmod 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
--- Forces the sprite to be tinted with the supplied color.
-- Tint can also be set by updating the .tint attribute, which will change the tint on next update
-- @param Tint the color to tint the sprite
function FlbSprite:setTint(Tint)
self.tint = Tint
self._mesh:setColors(self.tint)
end
--- Sets the shader for this sprite.
-- @param Shader the name of the shader to set. The shader can then be directly accessed using FlbSprite.shader
function FlbSprite:setShader(Shader)
self.shader = shader(Shader)
self._mesh.shader = self.shader
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 current angular velocity for the sprite
-- @param Velocity the required velocity in degrees per second.
function FlbSprite:setAngularVelocity(Velocity)
FlbObject.setAngularVelocity(self, Velocity)
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)
if self.tint then self._mesh:setColors(self.tint) end
end
if self._mesh.shader then
self._mesh.shader.position = vec2(self.x, self.y)
end
local r = 1
if self.frame ~= -1 then
if self.reverse then
if self.facing == LEFT then r = -1 end
end
end
if self._index ~= nil then
self._mesh:setRect(self._index, self.x , self.y , self.width*r, self.height, math.rad(self.rotation))
end
end
--- The destroy function, for memory management
function FlbSprite:destroy()
self._body:destroy()
self._mesh:clear()
for k,v in ipairs(self.cameras) do
v:remove(self)
end
end
--- Colors in the sprite with a solid color as a rectangle, useful for block colors or placeholders
-- @param Width the required width of the sprite
-- @param Height the required height of the sprite
-- @param Color the color to fill the rectangle with
function FlbSprite:makeGraphic(Width,Height,Color,Unique,Key)
self.width = Width
self.height = Height
if self._index == nil then
self._index = self._mesh:addRect(self.x, self.y, self.width, self.height, math.rad(self.rotation))
end
self._mesh:setColors(Color)
end
--- The draw function
function FlbSprite:draw()
self._mesh:draw()
FlbObject.draw(self)
end
--- Loads the graphic into this sprite.
-- @param Graphic The location of the texture.
-- @param[opt] Animated whether this graphic is an animated spritesheet or a single image
-- @param[opt] Reverse whether this graphic should be reversed when its facing attribute is updated
-- @param[opt] Width if the graphic is a spritesheet this sets the width of the viewable area
-- @param[opt] Height if the graphic is a spritesheet this sets the height of the viewable area
function FlbSprite:loadGraphic(Graphic, Animated, Reverse, Width, Height)
self.reverse = Reverse
local texture = FlbG:loadGraphic(Graphic)--readImage(Graphic)
self._mesh.texture = texture
self.width, self.height = Width or texture.height, Height or texture.height
if Animated then
self._rows, self._cols = math.floor(texture.height/self.height), math.floor(texture.width/self.width)
else
self._rows = 1
self._cols = math.floor(texture.width/self.width)
end
self._index = self._mesh:addRect(self.x, self.y, self.width, self.height, self.rotation)
self.frame = 0
if self.tint then self._mesh:setColors(self.tint) end
end
--- Adds an animation for the sprite based on a name and the individual frames from the spritesheet
-- @param Name this has to be unique for the given sprite and is the name used to subsequently play the animation
-- @param Frames a table of the frame numbers from within the spritesheet, these will be played in order
-- @param FrameRate
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
--- Starts to play a named animation
-- @param Name the name of the animation as created on 'addAnimation' to start playing
-- @param Forced[opt] whether to instantly redraw the frame (currently todo).
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
--- Called when the sprite is added to a group
function FlbSprite:onAdd()
FlbObject.onAdd(self)
end
--- @classmod 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
--- @classmod 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 + 2, self.y - 2)
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
--- @classmod 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
--- Sets the shader for the tilemap to the supplied shader class
-- @param Shader the shader class to set the mesh's shader to
function FlbTilemap:setShader(Shader)
self.shader = shader(Shader)
self.shader.iResolution = vec2(self.width, self.height)
for k,l in ipairs(self.layers) do
--FlbG:setShader(l.mesh)
if l.drawing then
l.sprite._mesh.shader = self.shader
end
end
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
--print(ts.tilewidth)
p = p-1
local i = l.sprite._mesh:addRect((p%l.width)*ts.tilewidth + ts.tilewidth/2, self.height - math.floor(p/l.width)*ts.tileheight + self.tileheight/2 - self.tileheight, ts.tilewidth, ts.tileheight)
local spriteOffsetx = (n%ts.cols) - 1
local spriteOffsety = ts.rows - math.floor(n/ts.cols) -1
if n == 5 then
print(p%l.width)
end
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
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)
if FlbG.showbounding then
pushStyle()
fill(200, 0, 0, 200)
stroke(200, 0, 0, 255)
strokeWidth(2)
for k,v in ipairs(self.bodies) do
local points = v.points
for j = 1,#points do
a = points[j]
b = points[(j % #points)+1]
line(a.x, a.y, b.x, b.y)
end
--rect(self.x, self.y, self.width, self.height)
end
popStyle()
end
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()
if self.shader then
self.shader.position = vec2(self.x, self.y)
end
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
--- Checks to see if a given object is a member of a table or not
-- @param tab the table that you want to scan
-- @param object the object that you want to see if it is a member or not.
function flbU:inTable(tab, object)
for k, v in ipairs(tab) do
if v == object then return true end
end
return false
end
--- Remove an object from a table.
-- @param tab the table to remove the object from
-- @param object the object to remove from the table.
function flbU:removeFromTable(tab, object)
for k,v in ipairs(tab) do
if v == object then
table.remove(tab, k)
return
end
end
end
--- Checks to see if a point overlaps the given object
-- @param Point a vec2 that is the point to test
-- @param Object the object that is going to be checked. The object must have a 'getBoundingBox()' function (FlbObject).
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
--- Returns a random number that is either 1 or -1. Good for directions.
function flbU:randomDirection()
return (math.random(1,2)*2)-3
end
FlbU = flbU()
--- @classmod 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.2.1" -- Use this to set Version Numbers Or set Version in class creation
PROJECTNAME = "Flibble"
BUILD = false -- 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, 100)
self.name = "char"
self:loadGraphic("Dropbox:Matt", true, true, 16, 24)
self:addAnimation("still", {0}, 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.rotation = 1
--self._body.applyTorque(30)
self.speed = 130
--self:setShader(KNIT_SHADER)
--self.shader.tileSize = vec2(50,50)
--self.shader.threads = 3
--self:setShader(SINE_SHADER)
--self.shader.amplitude = 110
--self.shader.frequency = 10
FlbG:usejoystick(STICK_CONTROLLER)
--FlbG.joystick:activate()
end
function TestChar:update()
if self._body then
self:setVelocity(FlbG.joystickpos.x * self.speed)
self:setAngularVelocity(FlbG.joystickpos.y * self.speed)
--self.rotation = FlbG.joystickpos.y
--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)
self.counter = 0
self.count = 0
self.timer = 0
self.spawntime = 0.1
self.addtime = 0.1
self.stress = false
FlbG:setGravity(0,-800)
self.spr = TestChar()
parameter.number("EmitRate", 0.01, 0.5, 0.1, function(n) self.spawntime = n end)
-- self:add(FlbTilemap("Dropbox:World1"))
self.map = FlbTilemap("Dropbox:World1")
--self.map:setShader(SEPIA_SHADER)
--self.map.shader.factor = 1
--self.map:setShader(KNIT_SHADER)
--self.map.shader.tileSize = vec2(50,50)
--self.map.shader.threads = 3
--self.map.shader.frequency = 10
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)
--self.sq = FlbSprite(0,0)
--self.sq:makeGraphic(200,90, color(200,240,20,100))
--self.sq:setShader()
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)
--self:add(self.sq)
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)
self.e = FlbEmitter(100, 200)
self.e:makeParticles("Dropbox:Particle", 50)
parameter.action("Emit", function()
self.ep:start(false, 1.5, 0.1)
end)
parameter.action("Explode", function()
self.e:start(true, 2)
end)
self:add(self.e)
self.ep = FlbEmitter(100, 200)
self.ep:makeParticles("Dropbox:Particle", 100)
self:add(self.ep)
parameter.action("Color", function()
self.spr:setTint(color(255,0,0,255))
end)
end
function TestState:update()
--[[
self.timer = self.timer + DeltaTime
if self.timer > self.spawntime then
self.timer = self.timer - self.spawntime
self.e:makeParticles("Dropbox:Particle",2)
end
]]
--end
--spr.x = spr.x + 1
--print(FlbG.joystick.steer)
--print(FlbG.touch.DOWN)
if self.stress then
if self.counter > self.addtime then
self.counter = self.counter - self.addtime
local s = FlbSprite(10 + math.random()*400, math.random()*300)
s:loadGraphic("Dropbox:Matt", true, true, 16, 24)
s:addAnimation("walkbackward", {4,5}, 3)
--s:play("walkbackward")
s.physical = true
self:add(s)
self.count = self.count + 1
end
self.counter = self.counter + DeltaTime
end
FlbState.update(self)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment