Skip to content

Instantly share code, notes, and snippets.

@dermotbalson
Created July 10, 2013 02:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dermotbalson/5963146 to your computer and use it in GitHub Desktop.
Save dermotbalson/5963146 to your computer and use it in GitHub Desktop.
zombies2
--# Main
--Main
displayMode(FULLSCREEN)
function setup()
LoadImages()
end
function setup2()
parameter.integer("Viewpoint",-2000,2000,20)
parameter.integer("Visibility",10,1000,300)
parameter.boolean("Mist",true)
sh=ZombieShader
speed,angle=0,0
ds,da=0,0
posX,posY,posZ=-60,82,205 --starting position
score=0
health=100
wraiths=25
Joystick.init()
minLight=0
meshTable={}
meshTable[1],meshTable[2]={},{}
--AddLevel(-500,0,0,1000,1000,gravel,.1)
HM={} --holds height maps, one for each terrain area we build -- NEW
--add a terrain area --NEW
AddTerrain({x=-400,y=0.5,z=100,width=800,depth=800,c=color(20,50,20,255),
tileSize=50,minY=10,maxY=100,random=false,img=grass,
noiseLevel=.2,noiseStep=.5,scaleFactor=.1,heightMaps=HM})
--add trees
for i=1,50 do
local x=math.random(-375,375)
local z=math.random(150,750)
local w=math.random(20,60)
AddItem(x,0,z,w,tree) --rotating to face us
end
--add chars
chars={}
charFrameSpeed=10
charSpeed=.5
attackDist=100
local w=20
charHeight=(anim[1].Height/2*char.height)*(w/anim[1].Width/char.width)
for i=1,wraiths do
local x=math.random(-375,375)
local z=math.random(150,750)
local a=math.random(1,2)
chars[i]={a=a,f=math.random(0,#anim[a].Left)}
AddChar(x,30,z,w,char,nil,i) --rotating to face us
end
count=0
FPS=60
end
function draw()
if imageStatus~="Ready" then return end
count=count+1
ThreeDT.preDraw()
if Mist==true then background(220) else background(220*math.max(minLight,Visibility/1000)) end
perspective(45,WIDTH/HEIGHT)
if Viewpoint<10 then Viewpoint=10 end
angle=angle+da*DeltaTime
posX,posZ=posX+ds*DeltaTime*math.sin(math.rad(angle)),posZ+ds*DeltaTime*math.cos(math.rad(angle))
local h0=posY --NEW
posY=math.max(Viewpoint,HeightAtPos(posX,posZ)+10) --NEW
local h1=posY --NEW
--try to look up if climbing or down if descending --NEW
if posY==Viewpoint then lookY=20 else lookY=(h1-h0)*100/ds+10 end
--look in the same direction as we are facing
lookX,lookY,lookZ=posX+1000*math.sin(math.rad(angle)),20,posZ+1000*math.cos(math.rad(angle))
camera(posX,posY,-posZ,lookX,lookY,-lookZ, 0,1,0)
if count%61==1 then
table.sort(meshTable[2],
function(a,b) return
vec2(posX,-posZ):dist(vec2(a.pos.x,-a.pos.z))>vec2(posX,-posZ):dist(vec2(b.pos.x,-b.pos.z)) end)
end
for k=1,2 do
for i,m in pairs(meshTable[k]) do
--rotate free standing objects if required, so they face us
m.shader.dist=Visibility
m.shader.minLight=minLight
--m.shader.id=0
if Mist==true then m.shader.mist=1 else m.shader.mist=0 end
if m.rotate==true and count%31==1 then
pushMatrix()
if m.id~=nil then m.pos.y=HeightAtPos(m.pos.x,m.pos.z)+charHeight end
translate(m.pos.x,m.pos.y,-m.pos.z)
local dx,dz=m.pos.x-posX,-m.pos.z+posZ
local ang=math.deg(math.atan(dx/-dz))
rotate(-ang,0,1,0)
m.shader.pos=vec2(posX-m.pos.x,m.pos.z-posZ)
m.ang=ang
ThreeDT.drawMesh(m)
--m:draw()
popMatrix()
elseif m.ang==nil then
m.shader.pos=vec2(posX,-posZ)
--m:draw()
ThreeDT.drawMesh(m)
else
pushMatrix()
translate(m.pos.x,m.pos.y,-m.pos.z)
rotate(-m.ang,0,1,0)
m.shader.pos=vec2(posX-m.pos.x,m.pos.z-posZ)
ThreeDT.drawMesh(m)
--m:draw()
popMatrix()
end
end
end
local hit=ThreeDT.CheckHit()
if hit~=nil then
print("hit",hit)
sound(SOUND_RANDOM, 47959)
score = score + 1
DeleteItem(hit)
end
ortho() --this is needed to get text written on the screen
viewMatrix(matrix()) --and this
FPS=FPS*.9+.1/DeltaTime
pushStyle()
fill(0) fontSize(18) strokeWidth(3) textMode(CORNER)
text("FPS: "..string.format("%d",FPS),50,HEIGHT-50)
text("Score="..string.format("% d of % d",score,wraiths),50,HEIGHT-70)
text("Health="..string.format("% d",health),50,HEIGHT-90)
if beingAttacked then
fill(255,0,0,100)
strokeWidth(0)
rect(0,0,WIDTH,HEIGHT)
end
popStyle()
Joystick:draw()
end
-- x,z = bottom left corner
-- y = height around the edges
-- w,d = width and depth of area to be terrained (pixels)
-- p = pixels per tile
-- f = minimum height (pixels), can be lower than y
-- h = max height (pixels)
-- r = whether to randomise terrain each run (true/false)
-- n = noise level (bigger numbers result in bumpy terrain, smaller gives smoother results)
-- tbl = table of heights, if provided
-- img = image or string name of image (do not include library name)
-- sc = image scaling factor (see above) 0-1
-- hm = height map storing heights of each point in
-- ns = the fraction by which noise level increases for each tile as we go deeper into the terrain
-- eg a value of 0.3 means that the outside ring of tiles has nil noise (as always, so these tiles
-- exactly meet the surface around), the next ring of tiles has noise of 0.3 of the full level,
-- the third ring of tiles has noise of 0.6, and so on, up to a maximum of 1.0. The bigger ns, the
-- the more you will get cliffs at the edges of the terrain
function AddTerrain(...)
local x,y,z,w,d,c,p,f,h,r,n,tbl,img,sc,hm,ns=Params(
{"x","y","z","width","depth","c","tileSize","minY","maxY","random","noiseLevel",
"heightTable","img","scaleFactor","heightMaps","noiseStep"},arg)
local nw,nd=math.floor(w/p),math.floor(d/p) --number of tiles in each direction
--if no table of heights provided, generate one with noise
if tbl==nil then
n=n or 0.2 --default noise level
tbl1,tbl2={},{}
--noise starts at 0 on the outside, increases by the step as we move inward, max of 1
--noise is nil at edge, increases by 30% per tile inwards, to 100%
local min,max=9999,0
--if user wants a random result each time. add a random number to the noise results
if r==true then rnd=math.random(1,10000) else rnd=0 end
for i=1,nw+1 do
tbl1[i],tbl2[i]={},{}
for j=1,nd+1 do
--noise fraction for this tile, based on how close to the edge it is
--this formula counts how many rows in from the edge this tile is
tbl1[i][j]=math.min(1,math.min(i-1,j-1,nw+1-i,nd+1-j)*ns)
--the noise itself
tbl2[i][j]=noise(rnd+i*n, rnd+j*n)
--keep track of min and max values of noise
if tbl2[i][j]<min then min=tbl2[i][j] end
if tbl2[i][j]>max then max=tbl2[i][j] end
end
end
--now go back through the whole array and scale it
--we know the user wants values between f and h
--we know our noise varies between min and max
--so now we pro rate all our values so they vary between f and h, based on the noise values
for i=1,nw+1 do
for j=1,nd+1 do
local f1=y
--pro rate
local f2=f+(h-f)*(tbl2[i][j]-min)/(max-min)
--now apply noise fraction, to reduce noise around the edges
tbl2[i][j]=f1*(1-tbl1[i][j])+f2*tbl1[i][j]
end
end
end
--store details for later use in determining height
table.insert(hm,{x1=x,z1=z,x2=x+w,z2=z+d,p=p,t=tbl2})
--create the vectors and mesh
if img==nil then
img=image(10,10)
setContext(img)
background(c)
setContext()
end
local wx,wz=img.width*sc,img.height*sc
v,t={},{}
for i=1,nw do
for j=1,nd do
local x1,x2=x+(i-1)*p,x+i*p local x3,x4=x2,x1
local y1,y2,y3,y4=tbl2[i][j],tbl2[i+1][j],tbl2[i+1][j+1],tbl2[i][j+1]
local z1,z3=z+(j-1)*p,z+j*p local z2,z4=z1,z3
v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz) --bottom left
v[#v+1]=vec3(x2,y2,-z2) t[#t+1]=vec2(x2/wx,z2/wz) --bottom right
v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz) --top right
v[#v+1]=vec3(x3,y3,-z3) t[#t+1]=vec2(x3/wx,z3/wz) --top right
v[#v+1]=vec3(x4,y4,-z4) t[#t+1]=vec2(x4/wx,z4/wz) --top left
v[#v+1]=vec3(x1,y1,-z1) t[#t+1]=vec2(x1/wx,z1/wz) --bottom left
end
end
local m=mesh()
m.texture=img
m.vertices=v
m:setColors(color(255))
m.texCoords=t
m.pos=vec3(x,y,z)
m.shader = shader(sh.vertexShader, sh.fragmentShader)
table.insert(meshTable[1],m)
end
--calculates height at x,z
function HeightAtPos(x,z)
--identify the location and calculate height
local h=0 --set default as 0
for i,v in pairs(HM) do --look in each terrain area
if x>=v.x1 and x<=v.x2 and z>=v.z1 and z<=v.z2 then --if in this area...
--calc which square we are in
local mx,mz=1+math.floor((x-v.x1)/v.p),1+math.floor((z-v.z1)/v.p)
--use bilinear interpolation (most common method) for interpolating 4 corner values
local px,pz=1+(x-v.x1)/v.p-mx,1+(z-v.z1)/v.p-mz
h=v.t[mx][mz]*(1-px)*(1-pz)+
v.t[mx+1][mz]*px*(1-pz)+
v.t[mx+1][mz+1]*px*pz+
v.t[mx][mz+1]*(1-px)*pz
break
end
end
return h
end
function AddItem(x,y,z,w,i,r,id) --centred on x
y=HeightAtPos(x,z)
local h=i.height*w/i.width
local m=mesh()
m.texture=i
local v,t={},{}
v,t=AddImage(-w/2,0,0,w,h,0,v,t)
m.pos=vec3(x,y,z)
if r~=nil then m.ang=r else m.rotate=true end
m.vertices=v
m:setColors(color(255))
m.texCoords=t
m.shader = shader(sh.vertexShader, sh.fragmentShader)
if id~=nil then m.id=id end
table.insert(meshTable[2],m)
end
function AddChar(x,y,z,w,i,r,id) --centred on x
y=HeightAtPos(x,z)
local h=i.height*w/i.width
local m=mesh()
m.texture=i
local u=m:addRect(-w/2,0,w,h)
m:setRectTex(u,0,0,1,1)
--local v,t={},{}
--v,t=AddImage(-w/2,0,0,w,h,0,v,t)
m.pos=vec3(x,y,z)
if r~=nil then m.ang=r else m.rotate=true end
--m.vertices=v
m:setColors(color(255))
--m.texCoords=t
m.shader = shader(sh.vertexShader, sh.fragmentShader)
if id~=nil then m.id=id end
table.insert(meshTable[2],m)
end
function DeleteItem(id)
for i,m in pairs(meshTable[2]) do
if m.id==id then table.remove(meshTable[2],i) return end
end
end
function AddImage(x,y,z,w,h,d,v,t)
v[#v+1]=vec3(x,y,z) t[#t+1]=vec2(0,0)
v[#v+1]=vec3(x+w,y,z-d) t[#t+1]=vec2(1,0)
v[#v+1]=vec3(x+w,y+h,z-d) t[#t+1]=vec2(1,1)
v[#v+1]=vec3(x+w,y+h,z-d) t[#t+1]=vec2(1,1)
v[#v+1]=vec3(x,y+h,z) t[#t+1]=vec2(0,1)
v[#v+1]=vec3(x,y,z) t[#t+1]=vec2(0,0)
return v,t
end
function AddLevel(x,y,z,w,d,i,s)
local m=mesh()
m.texture=i
local v,t={},{}
local nx,nz=w/i.width/s,d/i.height/s
v[#v+1]=vec3(x,y,-z) t[#t+1]=vec2(0,0)
v[#v+1]=vec3(x+w,y,-z) t[#t+1]=vec2(nx,0)
v[#v+1]=vec3(x+w,y,-z-d) t[#t+1]=vec2(nx,nz)
v[#v+1]=vec3(x+w,y,-z-d) t[#t+1]=vec2(nx,nz)
v[#v+1]=vec3(x,y,-z-d) t[#t+1]=vec2(0,nz)
v[#v+1]=vec3(x,y,-z) t[#t+1]=vec2(0,0)
m.vertices=v
m:setColors(color(255))
m.texCoords=t
m.pos=vec3(x,y,z)
m.shader = shader(sh.vertexShader, sh.fragmentShader)
table.insert(meshTable[1],m)
end
function touched(touch)
if touch.state==BEGAN then return end
print(touch.state)
local dds,dda=Joystick.touched(touch)
if dda~=nil then
ds=ds+dds
da=da+dda
elseif dds==0 then ds,da=0,0
else ThreeDT.touched(touch) end
end
function Params(list,tbl) --tbl is params
if type(tbl[1])=="table" then --was named table
local t={} --match the named params with the list provided
for n,v in pairs(tbl[1]) do
for i=1,#list do
if n==list[i] then t[i]=v break end
end
end
return unpack(t)
else --was an unnamed list, return it as is
return unpack(tbl)
end
end
function LoadImages()
imageStatus="Ready" --tells draw it's ok to draw the scene (will be turned off if we have to download images)
output.clear()
--pass through Codea name of image and internet url
--if not in Codea, will be downloaded and saved
grass=LoadImage("Dropbox:3D-grass2",
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/map-grass10_zps7573c68c.png")
tree=LoadImage("Dropbox:3D-tree2",
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/map-tree21c_zps655e421c.png")
char=LoadImage("Dropbox:Wraith",
"http://i1303.photobucket.com/albums/ag142/ignatz_mouse/Wraith_zps8d799843.png")
if imageStatus=="Ready" then setup2() end
end
--downloads images one by one
function LoadImage(fileName,url)
local i=readImage(fileName)
if i~=nil then return i end
--not found, we need to download, add to queue (ie table)
if imageTable==nil then imageTable={} end
imageTable[#imageTable+1]={name=fileName,url=url}
print('Queueing',fileName)
imageStatus='Loading'
--if the first one, go ahead and download
if #imageTable==1 then
http.request(imageTable[1].url,ImageDownloaded)
print('loading',imageTable[1].name)
end
end
--saves downloaded images
function ImageDownloaded(img)
print(imageTable[1].name,'loaded')
saveImage(imageTable[1].name,img) --save
table.remove(imageTable,1)
--load next one if we have any more to do
if #imageTable>0 then
http.request(imageTable[1].url,ImageDownloaded)
print('loading',imageTable[1].name)
else
LoadImages()
end
end
--1=facing toward,2=facing away,3=attack,4=dying
anim={{Bottom=.815,Height=.199,Width=.103,Left={.004,.111,.222,.339,.455,.582,.704,.817}},
{Bottom=.604,Height=.199,Width=.103,Left={.004,.114,.225,.341,.458,.580,.704,.817}},
{Bottom=.369,Height=.199,Width=.103,Left={.222,.354,.222,.354,.004,.111,.222,.354}},
{Bottom=.128,Height=.199,Width=.103,Left={.004,.111,.222,.339}}}
--# ThreeDT
--ThreeDT
ThreeDT={}
function ThreeDT.setShader(m)
m.shader=shader(idShader.vertexShader,idShader.fragmentShader)
m.shader.id=0
end
function ThreeDT.touched(touch)
if touch.state==ENDED then
ThreeDT.t=vec2(touch.x,touch.y)
end
end
function ThreeDT.preDraw()
if ThreeDT.t~=nil then --draw on hidden image if we've had a hit
setContext(ThreeDT.img)
clip(ThreeDT.t.x-1,ThreeDT.t.y-1,2,2)
end
--set up hidden image if not done
if ThreeDT.img==nil then
ThreeDT.img=image(WIDTH,HEIGHT)
ThreeDT.t=nil
end
beingAttacked=false
end
function ThreeDT.drawMesh(m)
--if we have a touch, set pixels with id number
if ThreeDT.t~=nil then
if m.id~=nil then m.shader.id=m.id/255 end
m:draw()
m.shader.id=0
else
if m.id~=nil then
local c=chars[m.id]
local u,d
d=vec2(posX,posZ):dist(vec2(m.pos.x,m.pos.z))
if d<attackDist then u=3 else u=1 end
if u~=c.a then c.a=u c.f=0 end
if count%charFrameSpeed==1 or c.f==0 then
local a=anim[c.a]
c.f=c.f+1
if c.f>#anim[c.a].Left then c.f=1 end
m:setRectTex(1,a.Left[c.f],a.Bottom,a.Width,a.Height)
end
if c.a==3 then
if d<5 then
health=health-0.2
beingAttacked=true
else
m.pos.x=m.pos.x+(posX-m.pos.x)*charSpeed/d
m.pos.z=m.pos.z+(posZ-m.pos.z)*charSpeed/d
end
end
end
m:draw()
end
end
function ThreeDT.CheckHit()
if ThreeDT.t==nil then return end
sound(SOUND_EXPLODE, 21148)
setContext()
clip()
local r,g,b=ThreeDT.img:get(ThreeDT.t.x,ThreeDT.t.y)
ThreeDT.t=nil
if g+b==0 and r>0 then return r else return nil end
end
ZombieShader = {
vertexShader = [[
//
// A basic vertex shader
//
//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform vec2 pos;
uniform float dist;
uniform float mist;
uniform float minLight;
//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;
void main()
{
//Pass the mesh color to the fragment shader
float d=distance(pos,vec2(position.x,position.z));
float j=clamp(1.-d/dist,0.,1.);
float jj=j*j;
vec4 c=color;
if (mist==0.) {c.rg = c.rg*max(minLight,j);
c.b = c.b*jj;}
else c=c*j;
vColor=c;
vTexCoord = texCoord;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * position;
}
]],
fragmentShader = [[
//
// A basic fragment shader
//
//Default precision qualifier
precision highp float;
//This represents the current texture on the mesh
uniform lowp sampler2D texture;
uniform float id;
//The interpolated vertex color for this fragment
varying lowp vec4 vColor;
//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;
void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0))) * vColor;
//Set the output color to the texture color
if (col.a==0.) discard;
else if (id==0. || col.a<0.2) gl_FragColor=col;
else gl_FragColor = vec4(id,0.,0.,1.);
}
]]}
--# Joystick
--Joystick
Joystick={}
function Joystick.init(diam,corner,dm,da,sens)
Joystick.diam=diam or 150
Joystick.centreDiam=Joystick.diam*.4
Joystick.r=Joystick.diam/2*.707
Joystick.changeLocation(corner or 1) --1 to 4
Joystick.deltaMove=dm or 2
Joystick.deltaAngle=da or 2
Joystick.sensitivity=sens or 3 --1 to 5
Joystick.drawImage()
end
function Joystick.drawImage()
Joystick.img=image(Joystick.diam,Joystick.diam)
setContext(Joystick.img)
pushStyle()
local m=2
local d,r=Joystick.diam,Joystick.r
fill(134, 186, 210, 255)
ellipse(m+d/2,m+d/2,d)
local xc,yc=m+d/2,m+d/2
strokeWidth(4)
stroke(0,0,0,20)
line(xc-r,yc-r,xc+r,yc+r)
line(xc-r,yc+r-2,xc+r,yc-r-2)
fill(110, 146, 185, 255)
strokeWidth(1)
ellipse(m+d/2,m+d/2,Joystick.centreDiam)
popStyle()
setContext()
end
function Joystick.changeLocation(c)
local a={{0,50},{0,HEIGHT-Joystick.diam},{WIDTH-Joystick.diam,0},
{WIDTH-Joystick.diam,HEIGHT-Joystick.diam}}
Joystick.pos=vec2(a[c][1],a[c][2])
--print(a[c][1],a[c][2])
end
function Joystick.draw()
ortho() --this is needed to get text written on the screen
viewMatrix(matrix()) --and this
spriteMode(CORNER)
sprite(Joystick.img,Joystick.pos.x,Joystick.pos.y)
end
function Joystick.touched(touch)
local d,x,y=Joystick.diam,Joystick.pos.x,Joystick.pos.y
local dd=vec2(touch.x,touch.y):dist(vec2(x+d/2,y+d/2))
if dd>Joystick.diam then return
elseif dd<Joystick.centreDiam/2 then return 0 end
local dMove,dAngle=0,0
local x,y=touch.x-x-d/2,touch.y-y-d/2 --print(x,y)
if math.abs(y)>math.abs(x) then --moving forward or backward
if y>0 then --forward
dMove = Joystick.deltaMove --print("forward")
else --backward
dMove = -Joystick.deltaMove --print("backward")
end
else --moving left or right
if x>0 then
dAngle = Joystick.deltaAngle --print("right")
else
dAngle = - Joystick.deltaAngle --print("left")
end
end
return dMove,dAngle
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment