Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --# Main
- --Main
- supportedOrientations(LANDSCAPE_ANY)
- resolution=5
- -- Use this function to perform your initial setup
- function setup()
- lineCapMode(ROUND)
- debugDraw = PhysicsDebugDraw()
- defaultGravity = physics.gravity()
- createContainer()
- img = readImage("Tyrian Remastered:Blimp Boss")
- mySprite=Object(img,resolution,300-math.random(150),HEIGHT+50-math.random(75),math.random(360))
- mySprite2=Object(img,resolution,600-math.random(150),HEIGHT-50-math.random(75),math.random(360))
- end
- --create container around sides and floor
- function createContainer()
- walls = {
- physics.body(EDGE, vec2(0, 0), vec2(WIDTH, 0)),
- physics.body(EDGE, vec2(0, 0), vec2(0, HEIGHT)),
- physics.body(EDGE, vec2(WIDTH, HEIGHT), vec2(WIDTH, 0)),
- --physics.body(EDGE, vec2(WIDTH, HEIGHT), vec2(0, HEIGHT)),
- }
- for k, v in ipairs(walls) do
- v.restitution = .5
- end
- end
- function cleanup()
- clearOutput()
- debugDraw:clear()
- end
- -- This function gets called once every frame
- function draw()
- -- This sets the background color
- background(201, 213, 217, 183)
- physics.gravity(defaultGravity)
- debugDraw:draw()
- end
- --# Object
- Object = class()
- function Object:init(img,res,xx,yy,angle)
- self.x = xx
- self.y = yy
- self.angle =angle
- self.image=img
- imge=img -- ?? I wanted to use self.image but the draw routine didn't recognise it
- offsetX=img.width/2+1 --add 1 because vector goes round the outside of the image
- offsetY=img.height/2+1
- points = VectorOutline:CreatePhysics(img,res,true) --create physics object from image
- poly=physics.body(POLYGON, unpack(points))
- poly.x = xx
- poly.y = yy
- poly.angle=angle
- poly.sleepingAllowed = false
- poly.restitution = 0.25
- debugDraw:addBody(poly)
- end
- function Object:draw(xx,yy,angle)
- pushMatrix() -- Preserve the current matrix
- pushStyle() -- Preserve the current style
- resetMatrix() -- Reset the matrix: 'origin' in the bottom left corner
- resetStyle() -- Reset the style
- xd,yd=RotateCoords(offsetY,offsetX,angle)
- x=xx+xd
- y=yy+yd
- translate(x, y) -- Shift (translate) origin of Viewer by (x, y)
- rotate(angle) -- Rotate the Viewer about that origin
- spriteMode(CENTER)
- sprite(imge, 0,0) -- Draw at the (shifted, rotated) origin
- popStyle() -- Restore the style to what it was
- popMatrix() -- Restore the matrix to what it was
- end
- function RotateCoords(w,h,a)
- local c=(w*w+h*h)^.5
- local ang=math.asin(w/c)
- ang = ang + a/180*math.pi
- local aa=c*math.sin(ang)
- local bb=c*math.cos(ang)
- return bb,aa
- end
- --# PhysicsDebugDraw
- PhysicsDebugDraw = class()
- xxxx=0
- yyyy=0
- function PhysicsDebugDraw:init()
- self.bodies = {}
- self.joints = {}
- self.touchMap = {}
- self.contacts = {}
- end
- function PhysicsDebugDraw:addBody(body)
- table.insert(self.bodies,body)
- end
- function PhysicsDebugDraw:addJoint(joint)
- table.insert(self.joints,joint)
- end
- function PhysicsDebugDraw:clear()
- -- deactivate all bodies
- for i,body in ipairs(self.bodies) do
- body:destroy()
- end
- for i,joint in ipairs(self.joints) do
- joint:destroy()
- end
- self.bodies = {}
- self.joints = {}
- self.contacts = {}
- self.touchMap = {}
- end
- function PhysicsDebugDraw:draw()
- pushStyle()
- smooth()
- strokeWidth(5)
- stroke(128,0,128)
- local gain = 2.0
- local damp = 0.5
- for k,v in pairs(self.touchMap) do
- local worldAnchor = v.body:getWorldPoint(v.anchor)
- local touchPoint = v.tp
- local diff = touchPoint - worldAnchor
- local vel = v.body:getLinearVelocityFromWorldPoint(worldAnchor)
- v.body:applyForce( (1/1) * diff * gain - vel * damp, worldAnchor)
- line(touchPoint.x, touchPoint.y, worldAnchor.x, worldAnchor.y)
- end
- stroke(0,255,0,255)
- strokeWidth(5)
- for k,joint in pairs(self.joints) do
- local a = joint.anchorA
- local b = joint.anchorB
- line(a.x,a.y,b.x,b.y)
- end
- stroke(255,255,255,255)
- noFill()
- for i,body in ipairs(self.bodies) do
- pushMatrix()
- translate(body.x, body.y)
- rotate(body.angle)
- if body.type == STATIC then
- stroke(255,255,255,255)
- elseif body.type == DYNAMIC then
- stroke(150,255,150,255)
- elseif body.type == KINEMATIC then
- stroke(150,150,255,255)
- end
- if body.shapeType == POLYGON then
- strokeWidth(3.0)
- if body.x+body.y>0 then
- Object:draw(body.x,body.y,body.angle) --?? draw our bitmap
- else
- local points = body.points
- for j = 1,#points do
- a = points[j]
- b = points[(j % #points)+1]
- line(a.x, a.y, b.x, b.y)
- end
- end
- elseif body.shapeType == CHAIN or body.shapeType == EDGE then
- strokeWidth(3.0)
- local points = body.points
- for j = 1,#points-1 do
- a = points[j]
- b = points[j+1]
- line(a.x, a.y, b.x, b.y)
- end
- elseif body.shapeType == CIRCLE then
- strokeWidth(3.0)
- line(0,0,body.radius-3,0)
- ellipse(0,0,body.radius*2)
- end
- popMatrix()
- end
- stroke(255, 0, 0, 255)
- fill(255, 0, 0, 255)
- for k,v in pairs(self.contacts) do
- for m,n in ipairs(v.points) do
- ellipse(n.x, n.y, 10, 10)
- end
- end
- popStyle()
- end
- function PhysicsDebugDraw:touched(touch)
- local touchPoint = vec2(touch.x, touch.y)
- if touch.state == BEGAN then
- for i,body in ipairs(self.bodies) do
- if body.type == DYNAMIC and body:testPoint(touchPoint) then
- self.touchMap[touch.id] = {tp = touchPoint, body = body, anchor = body:getLocalPoint(touchPoint)}
- return true
- end
- end
- elseif touch.state == MOVING and self.touchMap[touch.id] then
- self.touchMap[touch.id].tp = touchPoint
- return true
- elseif touch.state == ENDED and self.touchMap[touch.id] then
- self.touchMap[touch.id] = nil
- return true;
- end
- return false
- end
- function PhysicsDebugDraw:collide(contact)
- if contact.state == BEGAN then
- self.contacts[contact.id] = contact
- sound(SOUND_HIT, 2643)
- elseif contact.state == MOVING then
- self.contacts[contact.id] = contact
- elseif contact.state == ENDED then
- self.contacts[contact.id] = nil
- end
- end
- --# VectorOutline
- VectorOutline = class()
- --This code creates a vector outline of any image so you can use it as a physics object
- --Created by Dermot Balson (user Ignatz)
- --Version 1.00 Feb 2013
- --USAGE
- -- To have the code return a set of vectors which you can copy into your own code
- -- points=VectorOutline:CreatePhysics(img,res)
- -- poly=physics.body(POLYGON, unpack(points)) --create the actual object
- --To create a vector outline of our image, we need to go through several steps
- --1. We need to identify the outline all the way round
- --2. We need to mark the outline as an array of pixels, working anti clockwise (as the physics code requires)
- --3. We clean it up, removing any unnecessary pixels in the middle of straight lines (we only need the ends)
- --4. We walk around our array of pixels, taking every Nth pixel
- -- (N is specified by the user to balance speed and accuracy)
- --The result is a physics object, or, if you prefer, a set of vectors that you can turn into one yourself
- --Disclaimer - at time of writing, I have just one week's experience in Codea. Improvements are welcome!
- OUTERBLANK=1 --cells which are blank and outside the image, ie not enclosed by filled cells
- FILLED=2 --marks cells in bitmap which are used for the picture
- UNUSEDEDGE=3 --marks blank/transparent cells in the bitmap which border on filled cells
- USEDEDGE=4 --marks edge cells which have been used for creating the vector object, to avoid re-use
- EMPTY=0 --marks blank cells which are none of the above
- rows,cols=0,0
- function VectorOutline:CreatePhysics(img,res)
- cols=img.width
- rows=img.height
- --key array to store map of image that we will work with
- --has 2 extra rows/cols around the edge so we can fit our border around it
- c=array2D(cols+4,rows+4)
- firstBlankCol,firstBlankRow = 0,0 --to store location of first blank cell discovered
- --first set values for nonblank cells
- for i=1,cols do
- for j=1,rows do
- r,g,b,a=img:get(i,j)
- if a>0 then
- c[i+2][j+2]=FILLED --indent two rows and cols to allow for border
- if firstBlankCol==0 then firstBlankCol=i+2 firstBlankRow=j+2 end
- end
- end
- end
- --now fill in outside of image, ie blank cells that are not enclosed by filled cells
- findEdge(c,firstBlankCol,firstBlankRow)
- --make path of edge cells, working anticlockwise
- path=FindEdgePath(c)
- --trim path to remove points within straight lines
- path=trimStraightLines(path)
- --create final path of vectors
- points=createPath(path,res)
- return points
- end
- --this function works recursively to find all blank cells which are not embedded in the image. It does this
- --by starting with a blank cell outside the image, and looking at all neighbours. Any blank neighbours are
- --examined in turn.
- --Blank cells outside the image are labelled OUTERBLANK if they are not adjacent to a filled cell, or
- --UNUSEDEDGE if they are adjacent to a filled cell. These latter cells form the border around our image.
- function findEdge(c,i,j)
- c[i][j]=OUTERBLANK
- local filledNeighbor=false
- for ii=-1,1 do
- for jj=-1,1 do
- iii=i+ii
- jjj=j+jj
- if iii>0 and iii<=cols+4 and jjj>0 and jjj<=rows+4 then
- if c[iii][jjj]==EMPTY then
- findEdge(c,iii,jjj)
- elseif c[iii][jjj]==FILLED then
- filledNeighbor=true
- end
- end
- end
- end
- if filledNeighbor==true then c[i][j]=UNUSEDEDGE end
- end
- --This function creates a path around the image using the chain of border cells identified above.
- --It has to work anticlockwise, which it does by examining the neighbours of the last cell in
- --the chain in a certain order, from lower left, anti clockwise round to the left. It loops
- --round twice, first at a distance of one cell, then two cells
- function FindEdgePath(c)
- path={} --this will be the array of vectors
- colA={ 0, 1, 0,-1, 0, 1, 0,-1} --these two rows specify the x,y offsets to look in
- rowA={-1, 0, 1, 0,-2, 0, 2, 0} --around the current cell
- i1=0
- --find first edge cell
- for i=1,cols+4 do
- for j=1,rows+4 do
- if c[i][j]==UNUSEDEDGE then i1,j1=i,j end
- end
- if i1>0 then break end
- end
- --add the first point
- n=1
- path[n]={i=i1,j=j1}
- c[i1][j1]=USEDEDGE
- --now work around the image anticlockwise
- i,j=i1,j1
- finished=false
- while finished==false do
- for u=1,#colA do
- iii,jjj=i,j
- ii=i+colA[u]
- jj=j+rowA[u]
- if ii>0 and ii<=cols+4 and jj>0 and jj<=rows+4 then
- if c[ii][jj]==UNUSEDEDGE then
- n = n + 1
- if n>1000 then
- print("ERROR - Infinite loop in creating outline")
- finished=true
- end
- path[n]={i=ii,j=jj}
- c[ii][jj]=USEDEDGE
- iii,jjj=ii,jj
- break
- elseif ii==i1 and jj==j1 and n> 5 then --are we done?
- finished=true
- break
- end
- end
- end
- if iii==i and jjj==j then
- --print("ERROR - Unable to complete outline")
- finished=true
- else
- i,j=iii,jjj
- end
- end
- return path
- end
- --we only need to store the start and end cells for straight lines
- --This function removes cells in between
- function trimStraightLines(path)
- local i0,j0=0,0
- local ni,nj=0,0
- for p=#path,1,-1 do
- i,j=path[p].i,path[p].j
- if i==i0 then
- ni = ni + 1
- else
- if ni>2 then
- for u=p+ni-2,p+1,-1 do
- table.remove(path,p)
- end
- end
- ni=1
- i0=i
- end
- if j==j0 then
- nj = nj + 1
- else
- if nj>2 then
- for u=p+nj-2,p+1,-1 do
- table.remove(path,p)
- end
- end
- nj=1
- j0=j
- end
- end
- return path
- end
- --This function creates the final vector array
- --it loops through the array, selecting points at least N pixels apart, where N is specified by the user
- --to balance accuracy and speed
- function createPath(path,N)
- NN=N*N --see below for reason for this
- local points={}
- table.insert(points,vec2(path[1].i,path[1].j))
- i0,j0=path[1].i,path[1].j
- for p=2,#path-1 do
- --skip points until we are at least r from the previous point
- --the calculation of distance between two points is the SQRT of the sum of square of the x and y diffs,
- --but to save doing the SQRT, we compare it with r squared instead
- if (path[p].i-i0)^2+(path[p].j-j0)^2>NN then
- table.insert(points,vec2(path[p].i,path[p].j))
- i0,j0=path[p].i,path[p].j
- end
- end
- return points
- end
- function drawImage(img,c)
- local img2=image(cols+4,rows+4)
- for i=1,cols do
- for j=1,rows do
- r,g,b,a=img:get(i,j)
- if a>0 then img2:set(i+2,j+2,color(r,g,b,a)) end
- end
- end
- return img2
- end
- function drawTestImage(c) --testing only
- local img2=image(cols+4,rows+4)
- for i=1,cols+4 do
- for j=1,rows+4 do
- if c[i][j]==UNUSEDEDGE then
- img2:set(i,j,color(255,255,255,255))
- elseif c[i][j]==OUTERBLANK then
- img2:set(i,j,color(0,0,255,50))
- elseif c[i][j]==FILLED then
- img2:set(i,j,color(255,0,0,100))
- elseif c[i][j]==USEDEDGE then
- img2:set(i,j,color(0,0,255,255))
- elseif c[i][j]==FLAG then
- img2:set(i,j,color(255,0,0,255))
- img2:set(i-1,j,color(255,0,0,255))
- img2:set(i+1,j,color(255,0,0,255))
- img2:set(i,j-1,color(255,0,0,255))
- img2:set(i,j+1,color(255,0,0,255))
- end
- end
- end
- for p=1,#path do
- i,j=path[p].i,path[p].j
- img2:set(i,j,color(255,255,0,255))
- end
- return img2
- end
- function array1D(cols,defaultValue)
- if defaultValue==nil then defaultValue=0 end
- local A={}
- for i=1,cols do
- A[i]=defaultValue
- end
- end
- function array2D(rows,cols,defaultValue,start)
- if defaultValue==nil then defaultValue=0 end
- if start==nil then start=1 end
- local A={}
- for i=start,rows do
- A[i]={}
- for j=start,cols do
- A[i][j]=defaultValue
- end
- end
- return A
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement