I’m trying to refine the run code for Codea Community but I’m having an issue with physics being left over when a new project is ran. I’m loading code with loadstring and running it under a new environment for Globals. However, if you run the Balls first then the Flick project, you will see that Flick is already getting Physics properties pre set. I’m not sure what I am over looking. Any help would be appreciated!
--# Main
function setup()
--Monitor _G
--No idea why collide is called so i had to remove it do to spam
setmetatable(_G,{__index = function(t,k) if k ~= "collide" then print("_G Can't find ",k)end end ,
__newindex = function(t,k,v) rawset(t,k,v) print("Adding to _G",t,k,v) end})
--Save Original setup,draw and touch
osetup = setup
odraw = draw
otouched = touched
txt = "Original Draw"
parameter.action("Original",function() callBack(1) end)
parameter.action("Balls",function() callBack(2) end)
parameter.action("Flick",function() callBack(3) end)
--Get the projects
local Flick = readProjectTab("Flick")
Flick = string.gsub(Flick,"--%[%[","")
Flick = string.gsub(Flick,"%]%]","")
local Balls = readProjectTab("Balls")
Balls = string.gsub(Balls,"--%[%[","")
Balls = string.gsub(Balls,"%]%]","")
projects = {"empty",Balls,Flick}
end
function draw()
background(255, 255, 255, 255)
text(txt,WIDTH/2,HEIGHT/2)
end
function touched(touch)
if touch.state == ENDED then print("touched") end
end
function callBack(proj)
if proj == 1 then
_G.setup = osetup
_G.draw = odraw
_G.touched = otouched
osetup()
else
print("New Project Loaded.")
--setup new environment
new_Env = nil
new_Env = {}
setmetatable(new_Env,{__index = _G,
__newindex = function(t,k,v) rawset(t,k,v) print("new_Env set ",k,v) end})
local str = projects[proj]
--Point the new setup,draw,touch to _G
str = str..[[
_G.setup = function() pushStyle() pushMatrix() setup() popMatrix() popStyle() end
_G.setup()
_G.draw = draw
_G.touched = touched
]]
--Load the string in its own environment
local f = loadstring(str)
setfenv(f,new_Env)
f()
end
end
--# Balls
--[[
-- class
-- Use this function to perform your initial setup
function setup()
b={}
for i=1,5 do
b[i]=Ball()
end
r={}
for i=1,5 do
r[i]=Rect()
end
CreateWalls()
end
-- This function gets called once every frame
function draw()
background(195, 195, 201, 255)
for i=1,#b do
b[i]:draw()
end
for i=1,#r do
r[i]:draw()
end
end
function CreateWalls()
leftWall = CreateWall(0,0,0,HEIGHT,0.0)
rightWall = CreateWall(WIDTH,0,WIDTH,HEIGHT,0.9)
bottomWall = CreateWall(0,0,WIDTH,0,0.2)
topWall = CreateWall(0,HEIGHT,WIDTH,HEIGHT,0.7)
end
--this function creates one wall (actually just a line)
function CreateWall(x,y,x1,y1,r)
local w = physics.body(EDGE,vec2(x,y),vec2(x1,y1)) -- vec2
w.restitution=r --see comment above
return w
end
Ball = class()
function Ball:init(x,y,d,c)
self.x=x or math.random(0,WIDTH)
self.y=y or math.random(0,HEIGHT)
self.diameter=d or math.random(50,200)
self.colr=c or color(math.random(0,255),math.random(0,255),math.random(0,255))
self.p=Physics(CIRCLE,{self.diameter/2},self.x,self.y) --physics uses radius
end
function Ball:draw()
pushStyle() -- store style settings
fill(self.colr) --set color
pushMatrix()
local x,y,a=self.p:currentPosition()
translate(x,y)
rotate(a)
ellipse(0,0,self.diameter)
popMatrix()
popStyle() -- put back style settings
end
Rect = class()
function Rect:init(x,y,w,h,c)
self.x=x or math.random(0,WIDTH)
self.y=y or math.random(0,HEIGHT)
self.width=w or math.random(60,150)
self.height=h or math.random(30,100)
self.colr=c or color(math.random(0,255),math.random(0,255),math.random(0,255))
local data={}
table.insert(data,vec2(-self.width/2,-self.height/2))
table.insert(data,vec2(-self.width/2,self.height/2))
table.insert(data,vec2(self.width/2,self.height/2))
table.insert(data,vec2(self.width/2,-self.height/2))
self.p=Physics(POLYGON,data,self.x,self.y)
end
function Rect:draw()
pushStyle() -- store style settings
fill(self.colr) --set color
pushMatrix()
local x,y,a=self.p:currentPosition()
translate(x,y)
rotate(a)
rect(-self.width/2,-self.height/2,self.width,self.height)
popMatrix()
popStyle() -- put back style settings
end
Physics = class()
--type=CIRCLE or POLYGON
--data is a table. For circles it is one value, the radius, while for a polygon it is a set of vec2
--x,y is the initial position of the centre
--a is the initial angle
--g is the gravity value, 0=tabletop, 1=normal (things fall down)
--r is the restitution, ie springiness
--f is friction
--lv is linear velocity
--i is any info you want to store to identify this object, eg a name
function Physics:init(type,data,x,y,a,g,r,f,lv,i)
if type==CIRCLE then
self.body=physics.body(CIRCLE,data[1])
elseif type==POLYGON then
self.body=physics.body(POLYGON,unpack(data))
end
self.body.x=x
self.body.y=y
self.body.angle=a or 0
if g then self.body.gravityScale=1 else self.body.gravityScale=0 end
self.body.restitution=r or 1
self.body.friction=f or 0.1
if lv==nil then lv=vec2(100+math.random(400),100+math.random(400)) end
self.body.linearVelocity=lv
self.body.info=i
end
function Physics:currentPosition()
return self.body.x, self.body.y,self.body.angle
end
]]
--# Flick
--[[
-- Use this function to perform your initial setup
function setup()
--displayMode(FULLSCREEN)
PhysCreate:setup()
-- ball
ball = physics.body(CIRCLE,40)
ball.x = WIDTH/4
ball.y = HEIGHT/2
ball.interpolate = true
ball.restitution = 0.4
-- walls
vert={vec2(WIDTH,HEIGHT),vec2(0,HEIGHT),vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT)}
floor = physics.body(CHAIN,unpack(vert))
--Fixed to prevent holding spam to _G
holding = 0
-- store catapult variables
pos = vec2(150,400)
tpos = vec2()
force = 0
-- create boxes in grid
bodies = {}
local y = 1
local x = 1
for i = 1,45 do
bodies[i] = PhysCreate:create(vec2(600+x*41,y*40),vec2(2,2))
-- x < n, n sets the grid width
if x < 5 then
x=x+1
else
y=y+1
x=1
end
end
end
function touchPos()
--print(vec2(CurrentTouch.x,CurrentTouch.y))
return vec2(CurrentTouch.x,CurrentTouch.y)
end
-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(40, 40, 50)
-- start touch and store first position
if CurrentTouch ~= nil and CurrentTouch.state == BEGAN and touchPos().x < 300 then
holding = 1
pos = touchPos()
end
-- keep ball in the air while in red zone
if ball.x < 300 then
if ball.y < 100 then
ball.linearVelocity = vec2(ball.linearVelocity.x*0.3,(100-ball.y)*5)
end
end
-- grab ball and set force
if holding == 1 then
local bl = (touchPos()-vec2(ball.x,ball.y)):normalize()
ball.linearVelocity = bl*touchPos():dist(vec2(ball.x,ball.y))*5
force = touchPos():dist(pos)
end
-- release catapult and set ball velocity
if holding == 1 and CurrentTouch.state == ENDED and touchPos():dist(vec2(ball.x,ball.y)) < 40 then
ball.linearVelocity = (pos-touchPos())*force
holding = 0
end
-- Set catapult starting variable if in red zone
if ball.x > 300 and CurrentTouch.state == BEGAN and touchPos().x < 300 and holding == 0 then
holding = 1
elseif ball.x > 300 and touchPos().x > 300 and CurrentTouch.state == MOVING then
holding = 0
end
-- draw boxes
PhysCreate:draw()
-- draw gui
-- catapult area (red zone)
line(300,0,300,HEIGHT)
strokeWidth(0)
fill(250,50,50,50)
rect(0,0,300,HEIGHT)
-- catapult
if holding == 1 then
strokeWidth(3)
fill(186, 186, 186, 255)
ellipse(pos.x,pos.y,20)
fill(255)
line(pos.x,pos.y,touchPos().x,touchPos().y)
end
-- draw ball
strokeWidth(5)
fill(100)
ellipse(ball.x,ball.y,80)
end
---------
PhysCreate = class()
function PhysCreate:init(x)
self.x = x
end
function PhysCreate:setup()
Di = 0
DTbl = {}
end
function PhysCreate:create(vpos,vsize)
local vert = {vec2(-10*vsize.x,10*vsize.y),vec2(-10*vsize.x,-10*vsize.y),
vec2(10*vsize.x,-10*vsize.y),vec2(10*vsize.x,10*vsize.y)}
local p = physics.body(POLYGON,unpack(vert))
p.mass = 2
p.x = vpos.x
p.y = vpos.y
p.w = vsize.x*10*2
p.h = vsize.y*10*2
p.interpolate = true
p.sleepingAllowed = false
p.m = mesh()
local mr = p.m:addRect(p.x,p.y,p.w,p.h,0)
p.m:setRectTex(mr,0,0,1,1)
Di = Di + 1
DTbl[Di] = {p.m,mr,p}
end
function PhysCreate:draw()
for i=1,#DTbl do
local d = DTbl[i]
d[1]:draw()
d[1]:setRect(d[2],d[3].x,d[3].y,d[3].w,d[3].h,d[3].angle/57.3)
end
end
function PhysCreate:touched(touch)
-- Codea does not automatically call this method
end
]]