Difference between revisions of "Ftf snippets"

From GiderosMobile
(Created page with "__TOC__ '''Here are some examples from the Gideros forum and some other places :-)'''<br/> '''All samples are ready to use. Enjoy!'''<br/> == LUA == === TERNARY OPERATOR...")
 
Line 1,064: Line 1,064:
  
 
== MATH ==
 
== MATH ==
 +
 +
=== Simple AABB Collision '''@Luiji''' ===
 +
AABB collision between two rectangles.
 +
<syntaxhighlight lang="lua">
 +
-- check if box1 and box2 overlap
 +
function CheckCollision(box1x, box1y, box1w, box1h, box2x, box2y, box2w, box2h)
 +
    if box1x > box2x + box2w - 1 or -- is box1 on the right side of box2?
 +
      box1y > box2y + box2h - 1 or -- is box1 under box2?
 +
      box2x > box1x + box1w - 1 or -- is box2 on the right side of box1?
 +
      box2y > box1y + box1h - 1    -- is b2 under b1?
 +
    then
 +
        return false                -- no collision.  Yay!
 +
    else
 +
        return true                -- yes collision.  Ouch!
 +
    end
 +
end
 +
</syntaxhighlight>
  
 
=== AABB Collision with AnchorPoint(0.5, 0.5) '''@mokalux''' ===
 
=== AABB Collision with AnchorPoint(0.5, 0.5) '''@mokalux''' ===

Revision as of 06:00, 23 December 2023

Here are some examples from the Gideros forum and some other places :-)
All samples are ready to use. Enjoy!


LUA

TERNARY OPERATOR @hgy29

New Luau implementation here: Ternary_Operator.


Example 1: assume you have a variable called c, and you want to assign it a value only if a variable is above 10, with a ternary operator you would do this: c = a > 10 ? a : b

-- in lua you do
c = a > 10 and a or b

Example 2: assume you have a boolean called b and you want to set its default value to false

-- in lua you do
-- b = b and true or false -- that doesn't work :-/
b = b and (b ~= nil) -- that works :-)
b = b and not (b == nil) -- that also works ;-)

Example 3: assume you have a boolean called b and you want to set its default value to true

-- in lua you do
-- b = b and false or true -- that doesn't work :-/
b = b or (b == nil) -- that works :-)

STRING

A COLORED STRING @hgy29

local text = TextField.new(nil, "This is a \e[color=#f005]semi transparent red\e[color] text")
text:setPosition(32, 64)
stage:addChild(text)

A COLORED STRING 2 @mokalux

-- beautifying a tf
local tf = TextField.new(nil, "")
local mystr = "Gideros, what a beauty!"
local strsplit = mystr:split(",")
--local mystr2 = strsplit[1] -- gets the first index of the table split (=Gideros)
--local mystr3 = "\e[color=#e5e]"..mystr2.."\e[color]" -- beautifies Gideros
--mystr = mystr:gsub(mystr2, mystr3) -- replaces mystr2 by mystr3
mystr = mystr:gsub(strsplit[1], "\e[color=#e5e]"..strsplit[1].."\e[color]") -- less code!
-- final display
tf:setText(mystr)
tf:setScale(3)
tf:setPosition(128, 128)
stage:addChild(tf)

DATE & TIME @mokalux

Gets system date and time (YYYYMMDD_HHMMSS)

-- CURRENT DATE AND TIME
local myyear = os.date("*t").year
local mymonth = os.date("*t").month; if mymonth < 10 then mymonth = "0"..mymonth end
local myday = os.date("*t").day; if myday < 10 then myday = "0"..myday end
local myhour = os.date("*t").hour; if myhour < 10 then myhour = "0"..myhour end
local mymin = os.date("*t").min; if mymin < 10 then mymin = "0"..mymin end
local mysec = os.date("*t").sec; if mysec < 10 then mysec = "0"..mysec end
local mytime = myyear..mymonth..myday.."_"..myhour..mymin..mysec
print(mytime) -- 20200309_143048

FINDING A NUMBER IN A STRING @xxx

local mystring = "Doe is 26 years old."
local age = tonumber(string.match(mystring, "%d+"))
print(age) -- outputs 26

FINDING ANY INDEXED NUMBERS IN A STRING @mokalux

local mystr = "x64y250s8"
local x, y, speed -- numbers (eg. coordinates)
local index
--
index = mystr:find("s") -- "s" index represents speed
speed = tonumber(mystr:sub(index):match("%d+")) -- find sNNN
index = mystr:find("x") -- index x
x = tonumber(mystr:sub(index):match("%d+")) -- find xNNN
index = mystr:find("y") -- index y
y = tonumber(mystr:sub(index):match("%d+")) -- find yNNNN

CHECK IF STRING X STARTS OR ENDS WITH STRING Y @lua-users wiki

local function starts_with(str, start)
    return str:sub(1, #start) == start
end

local function ends_with(str, ending)
    return ending == "" or str:sub(-#ending) == ending
end

FINDING THE LAST POSITION OF CHARACTER X IN A STRING @mokalux

mystr = "This is a sample string containing many stars *. Here is another one *. And a last one *."
for i = 1, string.len(mystr) do
	if string.sub(mystr, i, i) == "*" then
		counti += 1 -- number of stars *
		countl = i -- index of the last star * in the string
	end
end
print(counti, countl)

GET PATH @xxx

function getPath(str)
	return str:match("(.*[/\\])")
end

x = "/home/user/.local/share/app/some_file"
y = "C:\\Program Files\\app\\some_file"
print(getPath(x)) -- prints: /home/user/.local/share/app/
print(getPath(y)) -- prints: C:\Program Files\app\

GET PATH AND FILENAME @mokalux

function getPathAndFileName(xpath)
	local xpathlength = xpath:len()
	local tmpfolder = xpath:match("(.*[/\\])")
	local tmpfolderlength = tmpfolder:len()
	local finalfolder = tmpfolder:sub(1, tmpfolderlength-1)
	local finalfile = xpath:sub(-(xpathlength-tmpfolderlength))
	return finalfolder, finalfile
end

x = "/home/user/.local/share/app/some_file.png"
local folder, file = getPathAndFileName(x)
print(folder, file) -- prints: "/home/user/.local/share/app/", "some_file.png"

FORMAT 0xRRGGBB COLOR TO STRING @Rrraptor, @keszegh

local mycolor = 0x0000ff -- your color

-- prints "0x" as regular string, then add converted hex number with 6 leading zeros
-- keszegh
local mycolorstring = string.format("0x%06x",mycolor) --> "0x0000ff"
-- Rrraptor
local mycolorstring = ("0x%06x"):format(mycolor) --> "0x0000ff"

-- converts to hex number only
-- keszegh
local mycolorstring2 = string.format("%x",mycolor) --> "ff"
-- Rrraptor
local mycolorstring2 = ("%x"):format(mycolor) --> "ff"

SPLIT A STRING EVERY N CHARACTERS @https://stackoverflow.com/questions/76194310/how-would-i-split-a-string-once-every-n-characters

local mystr = "Hello, World!"
local splitevery = 2
for c in mystr:gmatch(('.'):rep(splitevery)) do
	print(c)
end

Note: a remaining less than n characters will be silently ignored. Could be fixed by using a numeric for loop or .? instead

GFX

DRAWING A CIRCLE (PARTICLE) @hgy29

local mkr=Particles.new()
mkr:addParticles{
	{x=0,y=0,size=100,color=0xFFFFFF}, -- outter circle
	{x=0,y=0,size=70,color=0x4071B5}, -- inner circle
}
stage:addChild(mkr)
mkr:setPosition(120,120)

DRAWING A CIRCLE @xxx

xCircle = Core.class(Sprite)

function xCircle:init(r, steps, color)
   color = color or 0xff0000
   local sin, cos, d2r = math.sin, math.cos, math.pi / 180
   local c = Shape.new()
   c:setFillStyle(Shape.SOLID, color)
   c:setLineStyle(0)
   c:beginPath()
   c:moveTo(r * sin(0 * d2r), r * cos(0 * d2r))
   for i = 0, 360, 360 / steps  do
     c:lineTo(r * sin(i * d2r), r * cos(i * d2r))
   end    
   c:endPath()
   self:addChild(c)
end

--- usage
--xCircle(10, 5, 0xFF0000)

DRAWING ANOTHER CIRCLE (stars, circles, flowers) @gemboy100

Circle = Core.class(Mesh)

function Circle:init(__, r, COLOR, STEPS, PULL, CUT)
   local r1 = r -- h radius
   local r2 = r -- v radius
   local STEPS = STEPS or math.floor((math.pi / 4) * r)
   local COLOR = COLOR or 0
   local PULL = PULL or 0
   local CUT = CUT or 2
   if STEPS < 3 then STEPS = 3 end
   if PULL > r then PULL = 0
   elseif PULL > 0 then
      if CUT then
         while STEPS % CUT ~= 0 do
            STEPS += 1
         end
      elseif STEPS % 2 ~= 0 then STEPS +=1 end
   end

   self.c = {}
   local v = {}
   local ind = {}
   local _i = 0
   local _l = 0
   local ou = 2
   local step = math.pi / (STEPS / 2)
   local pull = 0

   v[#v + 1] = 0; v[#v + 1] = 0
   self.c[#self.c + 1] = COLOR; self.c[#self.c + 1] = 1

   --for i=step,PI*2, step do -- buggy cuz of infinite PI
   for i = 1, STEPS do
      _l += 1
      if _l % CUT == 0 then pull = PULL else pull = 0 end

      v[#v + 1] = (r1 - pull) * math.cos(i * step)
      v[#v + 1] = (r2 - pull) * math.sin(i * step)

      v[#v + 1] = (r1 - pull + ou) * math.cos(i * step)
      v[#v + 1] = (r2 - pull + ou) * math.sin(i * step)
   --
      self.c[#self.c + 1] = COLOR
      self.c[#self.c + 1] = 1

      self.c[#self.c + 1] = COLOR
      self.c[#self.c + 1] = 0.0
   --
      ind[#ind + 1] = _i + 3
      ind[#ind + 1] = _i + 5
      ind[#ind + 1] = _i + 4

      ind[#ind + 1] = _i + 4
      ind[#ind + 1] = _i + 3
      ind[#ind + 1] = _i + 2

      ind[#ind + 1] = _i + 4
      ind[#ind + 1] = _i + 2
      ind[#ind + 1] = 1

      _i = _i + 2
   end

   ind[#ind - 7] = 2
   ind[#ind - 6] = 3
   ind[#ind - 5] = 2
   ind[#ind - 2] = 2

   self:setVertexArray(v)
   self:setIndexArray(ind)
   self:setColorArray(self.c)
end

function Circle:setColor(c, a)
   local a = a or 1
   self.c[1] = c
   self.c[2] = a
   for i = 3, #self.c, 4 do
      self.c[i + 0] = c
      self.c[i + 1] = a
   end
   self:setColorArray(self.c)
end

-- usage
--star
local c1 = Circle.new(false, 80, 0xffaa55, 9, 50)
c1:setColor(0xff0000, 0.75)
c1:setPosition(1 * 64, 1 * 64)
stage:addChild(c1)

--circle
local c2 = Circle.new(false, 32, 0xffaa55)
c2:setPosition(3 * 64, 1 * 64)
stage:addChild(c2)

--flowers
local c3 = Circle.new(false, 32, 0xffaa55, 80, 10, 8)
c3:setPosition(1 * 64, 3 * 64)
stage:addChild(c3)
local c4 = Circle.new(false, 64, 0xffaa55, 100, 5)
c4:setPosition(3 * 64, 3 * 64)
stage:addChild(c4)

DRAWING AN ARC @hgvyas123

function drawArc(xc,yc,xradius,yradius,startAngle,endAngle,isFill)
   if yradius == nil then
      yradius = xradius
   end
   if startAngle == nil then
      startAngle = 0
   end
   if endAngle == nil then
      endAngle = 360
   end
   if isFill == nil then
      isFill = true
   end

   local shape = Shape.new()
   if isFill then
      shape:setFillStyle(Shape.SOLID, 0xff0000, 0.5)
   else
      shape:setLineStyle(3, 0x000000)
   end
   shape:beginPath()
   for i=startAngle,endAngle do
      if i == 1 then
         shape:moveTo(math.sin(math.rad(i)) * xradius, math.cos(math.rad(i)) * yradius)
      else
         shape:lineTo(math.sin(math.rad(i)) * xradius, math.cos(math.rad(i)) * yradius)
      end
   end
   if isFill then
      shape:closePath()
   end
   shape:endPath()

   shape:setPosition(xc, yc)
   return shape
end

--- usage
--function drawArc(xc,yc,xradius,yradius,startAngle,endAngle,isFill)
local myarc = drawArc(128, 128, 64, 64, 0, 5 * 45, false)
stage:addChild(myarc)

CIRCULAR PROGRESS @hgy29

CircularProgress = Core.class(Sprite)
 
function CircularProgress:init(radius, width, color, font, fontscale)
   self.radiusMin, self.radiusMax = radius - width / 2, radius + width / 2
   self.color = color
   self.font = font
   self:addChild(Path2D.new())
   local text = TextField.new(font, "-")
   text:setTextColor(color)
   text:setScale(fontscale or 1)
   self:addChild(text)
--   self:setValue(0.4)
end

function CircularProgress:setValue(val, xcolor, xalpha)
   local angs, ange = 0, math.pi * val * 1.999
   local rl, rh = self.radiusMin, self.radiusMax
   local ta = self:getChildAt(1)
   local ra = math.floor(val * 2)
--   ta:setLineColor(self.color, xalpha)
--   ta:setFillColor(self.color, xalpha)
   ta:setLineColor(xcolor, xalpha)
   ta:setFillColor(xcolor, xalpha)
   ta:setPath("MALAZ",
      math.sin(angs) * rl, -math.cos(angs) * rl,
      rl, rl, 0, ra, 1,
      math.sin(ange) * rl, -math.cos(ange) * rl,
      math.sin(ange) * rh, -math.cos(ange) * rh,
      rh, rh, 0, ra, 0,
      math.sin(angs) * rh, -math.cos(angs) * rh
      )
   ta:setLineThickness(4, 0.5)
   ta = self:getChildAt(2)
   ta:setText(("%d%%"):format(math.floor(val * 100)))
   ta:setTextColor(xcolor)
   ta:setAlpha(xalpha)
   local tax, tay, taw, tah = ta:getBounds(ta)
   ta:setAnchorPosition(tax + taw / 2, tay + tah / 2)
   return ta
end

--- usage
-- meter = CircularProgress.new(40, 5, 0x770000, nil, 2)

HEX TO RGB @rrraptor

function hex2rgb(hex)
	local r = (hex >> 16 & 0xff) / 255
	local g = (hex >> 8 & 0xff) / 255
	local b = (hex & 0xff) / 255
	return r,g,b
end

--- usage
--local r, g, b = hex2rgb(0xFF0000)

HEX TO RGB table @rrraptor

function hex2rgb(hex)
	local rgbtable = {} -- new 211115 XXX
	rgbtable.r, rgbtable.g, rgbtable.b =
		(hex >> 16 & 0xff) / 255, (hex >> 8 & 0xff) / 255, (hex & 0xff) / 255
	return rgbtable
end

--- usage
--local rgbtable = hex2rgb(0xFF0000) -- rgbtable.r, rgbtable.g, rgbtable.b

HEX TO RGB2 @https://gist.github.com/jasonbradley/4357406

function hex2rgb2(hex)
    hex = hex:gsub("#","")
    return tonumber("0x"..hex:sub(1,2)), tonumber("0x"..hex:sub(3,4)), tonumber("0x"..hex:sub(5,6))
end
--- usage
--local r, g, b = hex2rgb2(0xFF0000)

HEX TO RGB3 @keszegh

function hexToRgb(hex)
	local blue = hex%256
	local green = (hex-blue)/256 %256
	local red = (hex-blue-256*green)/256^2
	return red, green, blue
end

--- usage
--local r, g, b = hexToRgb(0x55ffcc)

RGB TO HEX @rrraptor

function rgb2hex(r, g, b)
	return (r << 16) + (g << 8) + b
end

--- usage
--local hex = rgb2hex(128, 96, 32)
--- if you want to print/display hex number
--local hexstr = string.format("0x%06x", hex)
--local hexnum = tonumber(string.format("0x%06x", hex))

RGB TO HEX2 @keszegh

function rgbToHex(r, g, b)
	return r*256*256 + g*256 + b
end

--- usage
----local hex = rgbToHex(0.5*255, 0*255, 1*255)
--local hex = rgbToHex(123, 321, 124)
--application:setBackgroundColor(hex)

PICKING A RANDOM COLOR @rrraptor

function randomColor() return "0x"..(math.random() * (1 << 24) | 0) end

--- usage
--local pixel = Pixel.new(randomColor(), 1, 32, 64)
--stage:addChild(pixel)

SAVING IMAGE TO FILE @mokalux

Saving an image or part of an image to disk.

function xsaveJPGtoDoc(xoriginalimgfile, xposx, xposy, xsizew, xsizeh, xdestimgname)
   local mytexture = Texture.new(xoriginalimgfile)
   local mybmp = Bitmap.new(mytexture)
   local rt = RenderTarget.new(mybmp:getWidth(), mybmp:getHeight())
   rt:draw(mybmp)
   rt:save("|D|tex_"..xdestimgname..".jpg", xposx, xposy, xsizew, xsizeh)
   
   return "|D|tex_"..xdestimgname..".jpg"
end

--- usage
--local xsavedjpg = xsaveJPGtoDoc("gfx/bg/dark.jpg", 0, 0, 64, 64, "dark")

PREVENT FROM DRAGGING A SHAPE OVER OTHER SHAPE @hgvyas123

--[[
Drag the shapes around with your mouse or fingers
This code is MIT licensed, see <a href="http://www.opensource.org/licenses/mit-license.php" target="_blank" rel="nofollow">http://www.opensource.org/licenses/mit-license.php</a>
(C) 2010 - 2011 Gideros Mobile
]]
 
local myShapes = {}

function Sprite:collidesWith(sprite2)
   local x,y,w,h = self:getBounds(stage)
   local x2,y2,w2,h2 = sprite2:getBounds(stage)

   return not ((y+h < y2) or (y > y2+h2) or (x > x2+w2) or (x+w < x2))
end

local function onMouseDown(self, event)
   if self:hitTestPoint(event.x, event.y) then
      self.isFocus = true
      self.x0 = event.x
      self.y0 = event.y

      event:stopPropagation()
   end
end

local function onMouseMove(self, event)
   if self.isFocus then
      self.lastX,self.lastY = self:getPosition()
      local dx = event.x - self.x0
      local dy = event.y - self.y0
      self:setX(self:getX() + dx)
      self:setY(self:getY() + dy)
      self.x0 = event.x
      self.y0 = event.y
      for i=1,#myShapes do
         if myShapes[i] ~= self then
            if self:collidesWith(myShapes[i]) then
               self:setPosition(self.lastX,self.lastY)
            end
         end
      end
      event:stopPropagation()
   end
end

local function onMouseUp(self, event)
   if self.isFocus then
      self.isFocus = false
      event:stopPropagation()
   end
end

for i=1,5 do
   local shape = Shape.new()
   shape:setLineStyle(3, 0x000000)
   shape:setFillStyle(Shape.SOLID, 0xff0000, 0.5)
   shape:beginPath()
   shape:moveTo(0, 0)
   shape:lineTo(100, 0)
   shape:lineTo(100, 50)
   shape:lineTo(0, 50)
   shape:closePath()
   shape:endPath()

   shape:setX(math.random(0, 320 - 100))
   shape:setY(math.random(0, 480 - 50))

   shape.isFocus = false

   shape:addEventListener(Event.MOUSE_DOWN, onMouseDown, shape)
   shape:addEventListener(Event.MOUSE_MOVE, onMouseMove, shape)
   shape:addEventListener(Event.MOUSE_UP, onMouseUp, shape)

   stage:addChild(shape)
   table.insert(myShapes, shape)
end

local info = TextField.new(nil, "drag the shapes around with your mouse or fingers")
info:setPosition(23, 50)
stage:addChild(info)

CHECK POINT IS IN POLYGON @atilim

function pointInPolygon(vertices, x, y)
   local intersectionCount = 0

   local x0 = vertices[#vertices].x - x
   local y0 = vertices[#vertices].y - y

   for i= 1, #vertices do
      local x1 = vertices[i].x - x
      local y1 = vertices[i].y - y

      if y0 > 0 and y1 <= 0 and x1 * y0 > y1 * x0 then
         intersectionCount = intersectionCount + 1
      end

      if y1 > 0 and y0 <= 0 and x0 * y1 > y0 * x1 then
         intersectionCount = intersectionCount + 1
      end

      x0 = x1
      y0 = y1
   end

   return (intersectionCount % 2) == 1
end

local vertices = {
   {x = 0, y = 0},
   {x = 0, y = 100},
   {x = 100, y = 100},
   {x = 0, y = 0},
}

local shape = Shape.new()
shape:setFillStyle(Shape.SOLID, 0xff0000)
shape:setLineStyle(5, 0x0000ff, 1)
shape:beginPath()
shape:moveTo(vertices[1].x,vertices[1].y)
shape:lineTo(vertices[2].x,vertices[2].y)
shape:lineTo(vertices[3].x,vertices[3].y)
shape:lineTo(vertices[4].x,vertices[4].y)
shape:endPath()

shape:setPosition(64, 64)
stage:addChild(shape)

local vertices_shape = {}
for i = 1, #vertices do
   vertices_shape[i] = {x = vertices[i].x + shape:getX(), y = vertices[i].y + shape:getY()}
end

shape:addEventListener(Event.MOUSE_HOVER, function(e)
   if pointInPolygon(vertices_shape, e.x, e.y) then
      print("hovering the shape")
   else
      print("not hovering the shape")
   end
end)

STENCIL OPERATION @hgy29

Stencil operation ex01.png

local star = Bitmap.new(Texture.new("gfx/star.png", true))
stage:addChild(star)

local bluestar_tex = RenderTarget.new(star:getWidth(), star:getHeight())
local bluestar = Bitmap.new(bluestar_tex)
stage:addChild(bluestar) bluestar:setPosition(0, 128)

-- Here is the magic!
-- Clear our render target to all alpha
bluestar_tex:clear(0x000000, 0)
--[[Gideros don't draw pixels with alpha value of 0,
they are discarded at early stage. Use the stencil to get a
footprint of visible pixels of source image]]
star:setStencilOperation {
	stencilClear = true,
	stencilWriteMask = 1,
	stencilFunc = Sprite.STENCIL_ALWAYS,
	depthPass = Sprite.STENCIL_INCR
}
bluestar_tex:draw(star)
-- Render the footprint in the color we want
local mask = Pixel.new(0x00FFFF, 1, star:getWidth(), star:getHeight())
mask:setStencilOperation {
	stencilClear = false,
	stencilMask = 1,
	stencilWriteMask = 1, 
	stencilRef = 0,
	stencilFunc = Sprite.STENCIL_NOTEQUAL,
	depthPass = Sprite.STENCIL_KEEP,
	stencilFail = Sprite.STENCIL_KEEP
}
bluestar_tex:draw(mask)
 
--[[ The result is possibly enough here, but we lost the smoothed edges of
the original texture in the process. We can restore them with a smart alpha
blending trick:]]
star:setBlendMode(Sprite.ZERO, Sprite.SRC_ALPHA)
bluestar_tex:draw(star)
 
-- Restore our initial bitmap blending mode, to show it on screen as usual
star:setBlendMode(Sprite.ALPHA)

ZOOM SPRITE TO MOUSE POSITION @hgy29, @keszegh

This one is just awesome!

application:setBackgroundColor(0x333333)

-- sprites
local canvaslayer = Sprite.new()
local pixel = Bitmap.new(Texture.new("gfx/pot/1K-triangle_buble-diffuse.jpg"))
-- order
canvaslayer:addChild(pixel)
canvaslayer:setAnchorPoint(0.5, 0.5)
stage:addChild(canvaslayer)
-- position
canvaslayer:setPosition(application:getContentWidth()/2, application:getContentHeight()/2)

-- current zoom
local currzoom = 0.5
local zoomstart = currzoom
canvaslayer:setScale(currzoom)

-- thank you very much hgy29 & keszegh :-)
local pointerstartx, pointerstarty = 0, 0
local canvasposx, canvasposy = 0, 0
local offsetx, offsety = 0, 0

stage:addEventListener(Event.MOUSE_DOWN, function(e)
	pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry)
	e:stopPropagation()
end)
stage:addEventListener(Event.MOUSE_MOVE, function(e)
	canvasposx, canvasposy = canvaslayer:getPosition()
	offsetx = e.rx - pointerstartx
	offsety = e.ry - pointerstarty
	canvasposx += offsetx
	canvasposy += offsety
	canvaslayer:setPosition(canvasposx, canvasposy)
	pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry)
	e:stopPropagation()
end)
stage:addEventListener(Event.MOUSE_WHEEL, function(e) -- e.wheel = +- 120
	zoomstart = currzoom
	pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry)
	canvasposx, canvasposy = canvaslayer:getPosition()
	-- local viewportZoom=math.clamp(self.zoomStart*2^((self.panStart.ry-newY)/100),_MIN_ZOOM,_MAX_ZOOM)
	currzoom = zoomstart + e.wheel/120/4
	if currzoom <= 0.2 then currzoom = 0.2 end
	if currzoom >= 2 then currzoom = 2 end
	canvaslayer:setScale(currzoom)
	local viewportx = canvasposx + (pointerstartx - canvasposx) * (1 - currzoom/zoomstart)
	local viewporty = canvasposy + (pointerstarty - canvasposy) * (1 - currzoom/zoomstart)
	canvaslayer:setPosition(viewportx, viewporty)
	e:stopPropagation()
end)

VFX

PARTICLES EFFECT 1 @hgy29

function EffectExplode(s, scale, x, y, r, speed, texture)
	local p = Particles.new()
	p:setPosition(x, y)
	p:setTexture(texture)
	p:setScale(scale)
	s:addChild(p)
	local parts = {}
	for i = 1, 50 do
		local a = math.random() * 6.3
		local dx, dy = math.sin(a), math.cos(a)
		local sr = math.random() * r
		local px, py = dx * sr, dy * sr
		local ss = (speed or 1) * (1 + math.random())
		table.insert(parts,
			{
				x = px, y = py,
				speedX = dx * ss,
				speedY = dy * ss,
				speedAngular = math.random() * 4 - 2,
				decayAlpha = 0.95 + math.random() *0.04,
				ttl = 1000,
				size = 10 + math.random() * 20
			}
		)
	end
	p:addParticles(parts)
	Core.yield(2)
	p:removeFromParent()
end

-- usage
stage:addEventListener(Event.MOUSE_DOWN,function (e)
	Core.asyncCall(EffectExplode, stage,
		1, e.x, e.y, 100, 2,
		Texture.new("gfx/smoke.png", true)
	)
end)

ANIMATION

FPS @atilim

local fps = TextField.new(nil, "")
fps:setScale(2)
fps:setPosition(16, 16)
fps:setTextColor(0xff0000)
stage:addChild(fps)

local frame = 0
local timer = os.timer()
local qFloor = math.floor
local function displayFps(event)
   frame = frame + 1
   if frame == 60 then
      local currentTimer = os.timer()
      fps:setText(qFloor(60 / (currentTimer - timer)))
      frame = 0
      timer = currentTimer
      -- optional
      --print ("memory used: "..qFloor(collectgarbage("count")/1000),"Mb")
   end
end

-- Add FPS Counter to stage
stage:addEventListener(Event.ENTER_FRAME, displayFps)

MOVING A SPRITE IN AN ARC PATH @Disciple

local bone = Sprite.new()
--local boneImg = Bitmap.new(Texture.new("gfx/enemy01.png")) -- add your gfx!
-- or use this one (Pixel)
local boneImg = Pixel.new(0x0000ff, 0.75, 32, 48)
stage:addChild(bone)
bone:addChild(boneImg)
boneImg:setAnchorPoint(0.5, 0.5)

local grav = -15 / 2
local velx = -3 / 2
local rotvel = -3 / 2
local x = 300 / 2
local y = 400 / 2
local rotation = 0

function enterFrame ()
   grav = grav + 0.3
   x = x + velx
   y = y + grav
   rotation = rotation + rotvel
   bone:setRotation(rotation)
   bone:setPosition(x, y)
end

stage:addEventListener(Event.ENTER_FRAME, enterFrame)

MOVING A SPRITE IN AN HORIZONTAL SINE WAVE @Harrison

local blimp1 = Pixel.new(0x0000ff, 0.5, 32, 64)
blimp1:setAnchorPoint(0.5, 0.5)
blimp1:setPosition(0, 128)
blimp1.speedx = 1.2
stage:addChild(blimp1)

function onEnterFrame(event)
   local posx = blimp1:getX() + blimp1.speedx
   local posy = blimp1:getY() + (1.5 * math.sin(0.075 * posx))
   if blimp1:getX() > application:getContentWidth() + blimp1:getWidth() / 2 then
      blimp1:setPosition(-blimp1:getWidth() / 2, 128)
      posx = blimp1:getX()
      posy = blimp1:getY()
   end
   blimp1:setPosition(posx, posy)
end

stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)

MOVING A SPRITE IN A VERTICAL COS WAVE @Harrison

local blimp2 = Pixel.new(0x00ff00, 0.5, 64, 32)
blimp2:setAnchorPoint(0.5, 0.5)
blimp2:setPosition(128, 0)
blimp2.speedy = 1.3
stage:addChild(blimp2)

function onEnterFrame(event)
   local posy = blimp2:getY() + blimp2.speedy -- posy first
   local posx = blimp2:getX() + 2.5 * math.cos(0.1 * posy) -- posy first
   if blimp2:getY() > application:getContentHeight() + blimp2:getHeight() then
      blimp2:setPosition(128, -blimp2:getHeight())
      posx = blimp2:getX()
      posy = blimp2:getY()
   end
   blimp2:setPosition(posx, posy)
end

stage:addEventListener(Event.ENTER_FRAME, onEnterFrame)


FILES

CSV @hgy29

Load a CSV file.

function loadCSV(file)
   local function csvsplit(line)
      local t, w, l2 = {}, nil, nil
      while #line > 0 do
         w, l2 = line:match("\"([^\"]*)\"[,\r\n]?(.*)") -- Check for quoted string
         if not w then w, line = line:match("([^,]*)[,\r\n]?(.*)") -- Non quoted 
         else line = l2 end
         if not w then break end --Nothing or issue, break
         t[#t + 1] = w
      end
      return t
   end
   local headers, csv = nil, {}
   for line in io.lines(file) do
      f = csvsplit(line)
      if not headers then -- Assume first line is headers
         headers = {} for n, v in ipairs(f) do headers[v] = n end
      else
         csv[#csv + 1] = f
      end
   end
   csv.getField = function(self, row, field) return self[row][headers[field]] end
   return csv
end

--- usage
--csv=loadCSV("135-bis-bt.csv")
--print(csv:getField(4,"Value"))
--print(csv:getField(7,"Time")

JSON @xxx

Loading and saving preferences.

require "json" -- you must add JSON plugin to your app

function saveData(filepath, value)
   local contents = json.encode(value)
   local file = io.open("|D|"..filepath, "w") -- create file
   file:write(contents) -- save json string in file
   io.close(file)
end

function getData(filepath)
   local value
   local file = io.open("|D|"..filepath, "r")
   if file then
      local contents = file:read("*a") -- read contents
      value = json.decode(contents) -- decode json
      io.close(file)
   end
   return value
end

-- globals
g_configfilepath = "myplayground.txt"
g_language = application:getLanguage()
g_theme = "dark"
g_isaudio = true
g_level = 1

-- init prefs
local mydata = getData(g_configfilepath) -- try to read information from file

-- if no prefs file, create it
if not mydata then
   mydata = {g_language, g_theme, g_isaudio, g_level}
   saveData(g_configfilepath, mydata) -- create file and save datas
else
   g_language = mydata[1]
   g_theme = mydata[2]
   g_isaudio = mydata[3]
   g_level = mydata[4]
end

-- save prefs
function mySavePrefs(xlanguage, xtheme, xisaudio, xlevel)
   local mydata = {xlanguage or g_language, xtheme or g_theme, xisaudio or g_isaudio, xlevel or g_level}
   saveData(g_configfilepath, mydata) -- save new datas
   g_language = mydata[1]
   g_theme = mydata[2]
   g_isaudio = mydata[3]
   g_level = mydata[4]
end

--- usage
--mySavePrefs("en", "dark", true, 1)
--print(g_language)
--print(g_theme)

JSON V2 @mokalux

require "json" -- you must add JSON plugin to your app

function saveData(filepath, value)
	local contents = json.encode(value)
	local file = io.open(filepath, "w") -- create file
	file:write(contents) -- save json string in file
	io.close(file)
end

function getData(filepath)
	local result, value
	local file = io.open(filepath, "r")
	if file then
		local contents = file:read("*a") -- read contents
--		value = json.decode(contents) -- decode json
		result, value = pcall(json.decode, contents) -- try to decode json
		io.close(file)
	end
	return result, value
end

--[[
-- usage

-- init.lua
g_configfilepath = "|D|myconfigfile.txt" -- C:\Users\xxx\AppData\Roaming\myappname on Windows
-- ...

-- main.lua
function myLoadPrefs()
	local _result, mydata = getData(g_configfilepath) -- try to read information from file
	if not mydata then -- if no prefs file, create it
		print("* creating prefs data file")
		mydata = {}
		-- your data
		mydata.g_language = g_language
		mydata.g_bgcolor = g_bgcolor
		-- ...
		-- create file and save datas
		saveData(g_configfilepath, mydata)
		myLoadPrefs() -- reload!
	else -- data exist, use them!
		g_language = mydata.g_language -- global variable
		g_bgcolor = mydata.g_bgcolor or 0x333333 -- global variable
		-- ...
	end
end
function mySavePrefs()
	local mydata = {} -- clear the table
	-- prefs
	mydata.g_language = g_language
	mydata.g_bgcolor = g_bgcolor or 0x333333
	-- save new data
	saveData(g_configfilepath, mydata)
end

-- main.lua or elsewhere
myLoadPrefs() -- load initial prefs

-- use the data
application:setBackgroundColor(g_bgcolor)
-- ...
]]

MATH

Simple AABB Collision @Luiji

AABB collision between two rectangles.

-- check if box1 and box2 overlap
function CheckCollision(box1x, box1y, box1w, box1h, box2x, box2y, box2w, box2h)
    if box1x > box2x + box2w - 1 or -- is box1 on the right side of box2?
       box1y > box2y + box2h - 1 or -- is box1 under box2?
       box2x > box1x + box1w - 1 or -- is box2 on the right side of box1?
       box2y > box1y + box1h - 1    -- is b2 under b1?
    then
        return false                -- no collision.  Yay!
    else
        return true                 -- yes collision.  Ouch!
    end
end

AABB Collision with AnchorPoint(0.5, 0.5) @mokalux

Checks for AABB collision between two rectangles which have their anchor point set at (0.5, 0.5).

function Bullet:checkMiddleCollision(bullet, ship)
	local bulletx, bullety, bulletw, bulleth, shipx, shipy, shipw, shiph =
		bullet:getX(), bullet:getY(), bullet:getWidth(), bullet:getHeight(),
		ship:getX(), ship:getY(), ship:getWidth(), ship:getHeight()
	if
		(bulletx + bulletw/2 > shipx and
		bulletx < shipx + shipw/2 and
		bullety + bulleth/2 > shipy and
		bullety < shipy + shiph/2)
		or
		(bulletx - bulletw/2 < shipx and
		bulletx > shipx - shipw/2 and
		bullety - bulleth/2 < shipy and
		bullety > shipy - shiph/2)
	then
		if bullet.isplayer and not ship.isplayer or -- player bullet vs enemy ship
			not bullet.isplayer and ship.isplayer then -- enemy bullet vs player ship
				return true, bullet, ship -- collision
		end
	else
			return false -- no collision
	end
end

-- sample usage in ENTER_FRAME
function Bullet:tick(delay)
	local x,y=self:getPosition()
	x+=self.dx*self.speed
	y-=self.dy*self.speed
	self:setPosition(x,y)
	for k, _ in pairs(BULLETS) do
		for k1, _ in pairs(ACTORS) do
			local c, b, s = self:checkMiddleCollision(k, k1)
			if c then
				b:destroy()
				s:hit(self.damage)
			end
		end
	end
end

CLAMP @rrraptor

Limits the value to the specified range.

-- v - value to limit
-- min, max - range
function clamp(v, min, max)
	return (v <> min) >< max
end

--- usage
--clamp(20, 0, 100) --> 20
--clamp(-20, 0, 100) --> 0
--clamp(120, 0, 100) --> 100

LERP @xxx

Lerps between 2 values.

local function lerp(a, b, t)
	return a + (b-a) * t
end

MAP @rrraptor

Transforms one range to another.

-- v - value to map
-- minSrc - lower bound of the value's current range
-- maxSrc - upper bound of the value's current range
-- minDst - lower bound of the value's target range
-- maxDst - upper bound of the value's target range
-- clampValue - constrain the value to the newly mapped range
function map(v, minSrc, maxSrc, minDst, maxDst, clampValue)
	local newV = (v - minSrc) / (maxSrc - minSrc) * (maxDst - minDst) + minDst
	return not clampValue and newV or clamp(newV, minDst >< maxDst, minDst <> maxDst)
end

--- usage
--map(0.5, 0, 1, 0, 200) -> 100
--map(10, 0, 10, 0, 200) -> 200
--map(0, -1, 1, 0, 1) -> 0.5

ROUND @http://lua-users.org/wiki/FormattingNumbers

Rounds a number to the nearest decimal.

--[[
	round(val, n) - Rounds a number to the nearest decimal places.
	val - The value to round.
	n - Number of decimal places to round to.
	-- usage
	--print( round( 1.5678 ) ) -- prints: 1.57
]]
function xround(val, n)
	if (n) then
		return math.floor( (val * 10^n)+0.5 ) / (10^n)
	else
		return math.floor(val+0.5)
	end
end

ROUND2 @https://stackoverflow.com/a/58411671/870125

Rounds a number to the nearest decimal.

local function round(num)
	return num + (2^52 + 2^51) - (2^52 + 2^51)
end

ROUND3 @http://lua-users.org/wiki/SimpleRound

Rounds a number to the nearest decimal. Seems to be fast.

function round(n, mult)
	mult = mult or 1
	return math.floor((n+mult/2)/mult) * mult
end

Move Towards @https://devforum.roblox.com/t/new-lua-functions-mathsign-and-mathclamp/37309/14

local function moveTowards(current, target, maxDelta)
	if current < target then return math.min(target, current + maxDelta)
	elseif current > target then return math.max(target, current - maxDelta)
	else return target
	end
end

Shoot Towards @mokalux

function shoot()
	local projectiletex = "gfx/projectiles/bullet1.png"
	local projectilespeed = 8*1
	-- a turret targeting a player
	local angle = math.atan2(player.y - turret.y, player.x - turret.x)
	-- projectile direction
	local vx, vy = projectilespeed * math.cos(angle), projectilespeed * math.sin(angle)
	-- ...
	-- projectile implementation example
	local g = Projectile.new(
		texture=projectiletex,
		x=turret.x + 8*4, y=turret.y + 0, -- your projectile start location
		speedx=vx, speedy=vy,
	})
	g = nil -- cleanup
	-- ...
end

TABLE

SELF VALUE TABLE TRICK @mokalux

local myvalue == "myvalue"
self[myvalue] -- this is equivalent to self.myvalue

HIGHEST / LOWEST NUMBER IN A TABLE @https://stackoverflow.com/questions/5178087/how-do-i-get-the-highest-integer-in-a-table-in-lua

Gets the highest or the lowest number in a table.

print(math.max(unpack{4, 5, 62, -5, 12, 9})) -- 62
print(math.min(unpack{4, 5, 62, -5, 12, 9})) -- -5

MERGING 2 TABLES @xxx

method 1:

z = {}
n = 0
for _,v in ipairs(x) do n=n+1; z[n]=v end
for _,v in ipairs(y) do n=n+1; z[n]=v end

method 2:

levelsetup = {color=0x5E3C3A, isshape=true, restitution=0, friction=1}
tablebase = { x = 64, y = 200, w = 400, h = 250, rotation = 0}
for k, v in pairs(levelsetup) do
	tablebase[k] = v
end

NICE TABLE PRINT FORMATTING @xxx

function print_r(t, name, indent)
	local tableList = {}
	local function table_r(t, name, indent, full)
		local id = not full and name or type(name) ~= "number" and tostring(name) or "["..name.."]"
		local tag = indent .. id .. " = "
		local out = {} -- result
		if type(t) == "table" then
			if tableList[t] ~= nil then
				table.insert(out, tag .. "{} -- " .. tableList[t] .. " (self reference)")
			else
				tableList[t] = full and (full .. "." .. id) or id
				if next(t) then -- Table not empty
					table.insert(out, tag .. "{")
					for key,value in pairs(t) do
						table.insert(out, table_r(value, key, indent .. "|  ", tableList[t]))
					end
					table.insert(out, indent .. "}")
				else
					table.insert(out, tag .. "{}")
				end
			end
		else
			local val = type(t) ~= "number" and type(t) ~= "boolean" and "\""..tostring(t).."\"" or tostring(t)
			table.insert(out, tag .. val)
		end

		return table.concat(out, "\n")
	end
	print(table_r(t, name or "Value", indent or ""))
end

-- usage
a = {x=1, y=2}
print("TEST: ", a["x"])
print_r(a, "table...", " ****")

-- result
--TEST: 	1
-- ****table... = {
-- ****|  y = 2
-- ****|  x = 1
-- ****}

DEBUGGING

TEXTURE MEMORY USAGE @SinisterSoft

local temp, oldusage
temp=(application:getTextureMemoryUsage()/1024)
if temp~=oldusage then
	oldusage=temp
	print("* 1 Texture usage: "..temp.." MB")
end


More to come God's willing...