Skip to content

Instantly share code, notes, and snippets.

@emendoza2
Last active May 9, 2017 05:28
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 emendoza2/4bb7bd49989de52db82bc1bd78e772ec to your computer and use it in GitHub Desktop.
Save emendoza2/4bb7bd49989de52db82bc1bd78e772ec to your computer and use it in GitHub Desktop.
A Slither.io Clone Created using Codea
--# Button
Button = class()
-- Fast Mesh Button Class courtesy of @Vega
function Button:init(text,x,y,width,height)
self.state = "normal"
self.text = text
self.textColor = color(255,255,255,192)
self.x = x
self.y = y
self.width = width
self.height = height
self.visible = true
self.fontSize = 28
self.font = "ArialRoundedMTBold"
self.color1 = color(255, 255, 255, 96)
self.color2 = color(128,128,128,32)
self.presscolor1 = color(192, 224, 224, 128)
self.presscolor2 = color(96, 192, 224, 128)
self.verts = self:createVerts(self.width, self.height)
self.myMesh = mesh()
self.myMesh.vertices = triangulate(self.verts)
self.vertColor = {}
self:recolor()
self.action = nil
self.tapped = false
end
function Button:setColors(c1,c2,p1,p2)
self.color1 = c1
self.color2 = c2
self.presscolor1 = p1
self.presscolor2 = p2
self:recolor()
end
function Button:textOptions(fn, sz, col)
self.font = fn
self.fontSize = sz
self.textColor = col
end
function Button:draw()
if self.visible == true then
pushStyle()
pushMatrix()
translate(self.x,self.y)
self.myMesh:draw()
fill(self.textColor)
fontSize(self.fontSize)
font(self.font)
text(self.text, self.width/2,self.height/2)
self:drawLines(self.verts)
popMatrix()
popStyle()
end
end
function Button:touched(touch)
self.tapped = false
if self.visible then
if pointInRect(touch.x, touch.y, self.x, self.y, self.width, self.height) then
if touch.state == BEGAN then
self.tapped = true
self.state = "pressing"
self:recolor()
elseif touch.state == ENDED then
if self.state == "pressing" then
self.state = "normal"
self.tapped = true
self:recolor()
end
if self.action then
self.action()
end
end
else
self.state = "normal"
self:recolor()
end
end
end
function Button:createVerts(w,h)
local r
local v = {}
if w > 100 or h > 100 then
if w>=h then r = math.round(h/100) else r = math.round(w/100) end
else
r = 1
end
v[1] = vec2(w,6*r)
v[2] = vec2(w-r,4*r)
v[3] = vec2(w-2*r,2*r)
v[4] = vec2(w-4*r,r)
v[5] = vec2(w-6*r,0)
v[6] = vec2(6*r,0)
v[7] = vec2(4*r,r)
v[8] = vec2(2*r,2*r)
v[9] = vec2(r,4*r)
v[10] = vec2(0,6*r)
v[11] = vec2(0,h-6*r)
v[12] = vec2(r,h-4*r)
v[13] = vec2(2*r,h-2*r)
v[14] = vec2(4*r,h-r)
v[15] = vec2(6*r,h)
v[16] = vec2(w-6*r,h)
v[17] = vec2(w-4*r,h-r)
v[18] = vec2(w-2*r,h-2*r)
v[19] = vec2(w-r,h-4*r)
v[20] = vec2(w,h-6*r)
return v
end
function Button:drawLines(v)
noSmooth()
strokeWidth(1)
stroke(0, 0, 0, 192)
for i=1, #v-1 do
line(v[i].x,v[i].y,v[i+1].x,v[i+1].y)
end
line(v[#v].x,v[#v].y,v[1].x,v[1].y)
end
function Button:recolor()
local lt, dk
if self.state == "normal" then
lt = self.color1
dk = self.color2
else
lt = self.presscolor1
dk = self.presscolor2
end
for i=1,3 * #self.verts - 6 do
if self.myMesh.vertices[i].y > self.height/2 then
self.vertColor[i] = lt
else
self.vertColor[i] = dk
end
end
self.myMesh.colors = self.vertColor
end
-- Math Utilities
function math.round(value)
-- math.round function courtesy of Vega.
return math.floor(value + 0.5)
end
function pointInRect(pointX, pointY, x, y, w, h)
-- Returns true if point (pointX, pointY) is within the rectangle
-- with lower left corner at (x, y) with a width of w and a
-- height of h.
--
-- Reefwing Software (www.reefwing.com.au)
-- Version 1.0
if pointX >= x and pointX <= x + w and pointY >= y and pointY <= y + h then
return true
else
return false
end
end
--# Generatename
vw = {"a","e","i","o","u","oo","ee","ae","ea","oi","io","y","ei","ie"}
cn = {"b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z","ts","th","ph","gh"}
defname = {"snakey","snaker","sneaky","slipper"}
function genname()
local startwith = math.floor(math.random(2))
local name = ""
if startwith == 1 then
name = name..vw[math.ceil(math.random(14))]
else
name = name..cn[math.ceil(math.random(25))]
end
local namelength = math.floor(math.random(10))
local rname = {}
for i = 0, namelength do
if startwith == 1 then
if i%2 == 0 then
rname[i] = vw[math.ceil(math.random(14))]
else
rname[i] = cn[math.ceil(math.random(25))]
end
else
if i%2 == 0 then
rname[i] = cn[math.ceil(math.random(25))]
else
rname[i] = vw[math.ceil(math.random(14))]
end
end
end
for i,v in ipairs(rname) do
name = name..rname[i]
end
local randomPct = 0.8
if math.random() < randomPct then
return name
else
return defname[math.random(1,#defname)]
end
return name
end
--# Main
-- Snake
WORLDWIDTH = 10000
WORLDHEIGHT = 10000
-- Use this function to perform your initial setup
function setup()
print("Hello World!")
socket = require("socket")
s = Snake(WIDTH/2,HEIGHT/2,50)
food = {}
frames = 0
for i = 1,5000 do
table.insert(food,vec3(math.random(WORLDWIDTH),math.random(WORLDHEIGHT),math.random(2,10)))
end
ss = {}
table.insert(ss,Playersnake(math.random(WORLDWIDTH),math.random(WORLDHEIGHT),math.random(5,10)))
for i = 1, 75 do
table.insert(ss,Snake(math.random(WORLDWIDTH),math.random(WORLDHEIGHT),math.random(5,70)))
end
print("done setting up")
print(getLocalIP())
cam = vec2(0,0)
draw = DrawGame
topten = {{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0},{name="",score=0}}
end
-- This function gets called once every frame
function DrawDead()
background(255, 255, 255, 255)
fill(0)
text("You Died",WIDTH/2,HEIGHT/2)
end
function DrawGame()
-- This sets a dark background color
background(40, 40, 50)
-- This sets the line thickness
strokeWidth(5)
translate(cam.x,cam.y)
--s:draw()
fill(255)
rect(0,-1000,WORLDWIDTH,1000)
rect(-1000,0,1000,WORLDHEIGHT)
rect(0,WORLDHEIGHT,WORLDWIDTH,1000)
rect(WORLDWIDTH,0,1000,WORLDHEIGHT)
noStroke()
for i,v in ipairs(food) do
if inView(v) then
ellipse(v.x,v.y,v.z)
end
end
for i,s in ipairs(ss) do
for i=#topten,1,-1 do
v = topten[i]
if #s.segments > v.score then
v.score = #s.segments
v.name = s.name
topten[i] =v
break
end
end
if (s.segments[1].pos - vec2(-cam.x+WIDTH/2,-cam.y+HEIGHT/2)):len() < WIDTH/2 + #s.segments*s.segLen or s.player then
s:draw()
for j,t in ipairs(ss) do
if s ~= t then
s:collide(t)
end
end
--[[ for i,v in ipairs(s.segments) do
if v.pos.x > WIDTH then
v.offset.x = v.offset.x + 1
elseif v.pos.x < 0 then
v.offset.x = v.offset.x - 1
else
v.offset.x = 0
end
v.pos.y = v.pos.y%HEIGHT
end
]]
if s.dead then
if s.player then
draw = DrawDead
end
table.remove(ss,i)
end
end
end
textMode(CORNER)
fill(255)
fontSize(20)
for i,v in ipairs(topten) do
text(v.name.." "..v.score,WIDTH-200,HEIGHT-i*20-20)
end
frames = frames + 1
if frames%100 == 0 then
table.insert(food,vec3(math.random(WORLDWIDTH),math.random(WORLDHEIGHT),math.random(2,10)))
end
if frames%1000 == 0 then
table.insert(ss,Snake(math.random(WORLDWIDTH),math.random(WORLDHEIGHT),math.random(5,10)))
end
cam = vec2(-ss[1].segments[1].pos.x+WIDTH/2,-ss[1].segments[1].pos.y+HEIGHT/2)
end
function touched(touch)
for i,v in ipairs(ss) do
v:touched(vec2(touch.x-cam.x,touch.y-cam.y))
end
end
function bearingOf(v)
return math.deg(math.atan2(v.x, v.y))
end
function bearingToVector(degrees)
local radians = math.rad(degrees)
return vec2(math.sin(radians), math.cos(radians)) -- bearings are relative to "up"
end
function inView(point)
if point.x > -cam.x and point.y > -cam.y and point.x < -cam.x+WIDTH and point.y < -cam.y+HEIGHT then
return true
end
return false
end
function getLocalIP()
--you input your ip here
local randomIP = ""
--this port below is just a place holder it does nothing but it needs to be there
local randomPort = ""
local randomSocket = socket.udp()
randomSocket:setpeername(randomIP,randomPort)
local localIP, _ = randomSocket:getsockname()
randomSocket:close()
randomSocket = nil
return localIP
end
--# Playersnake
Playersnake = class()
function Playersnake:init(x,y,startlen)
-- you can accept and set parameters here
self.segments = {}
self.segLen = 20
for i = 1, startlen do
self.segments[i] = {steer = 0, offset = vec2(0,0), desiredBearing = 0, pos = vec2(x,y), len = 25}
end
self.frames = 0
self.foodCount = 0
self.width = #self.segments
self.player = true
self.near = vec2(x,y)
self.far = vec2(x,y)
local function randomColor()
return color(math.random(0,255),math.random(0,255),math.random(0,255))
end
self.randomColors = {}
for i = 1 , math.random(1,4) do
table.insert(self.randomColors,randomColor())
end
self.name = "mr. snake"
end
function Playersnake:draw()
local seg = self.segments[1]
-- Codea does not automatically call this method
for i,v in ipairs(food) do
if v.x > self.segments[1].pos.x - self.width/2 then
if (self.segments[1].pos - vec2(v.x,v.y)):len() < self.width/2 + v.z/2 then
self.foodCount = self.foodCount + 1
table.remove(food,i)
end
end
end
pushStyle()
resetStyle()
local buff = self.segments
for i = #buff, 1, -1 do
v = buff[i]
local prev = buff[i-1] or v
if inView(v.pos) then
strokeWidth(self.width)
stroke(self.randomColors[i%#self.randomColors+1])
lineCapMode(ROUND)
line(v.pos.x - v.offset.x*WIDTH,v.pos.y,prev.pos.x-prev.offset.x*WIDTH,prev.pos.y)
end
if buff[i-1] then
local prev = buff[i-1]
local d = prev.pos - v.pos
local angle = bearingOf(d)
v.pos = prev.pos -
(bearingToVector(angle))
* v.len
-- v.steer = bearingOf(segments[i-1].pos - v.pos)
end
-- segments[i].pos = segments[i].pos + vec2(math.sin(math.rad(v.steer)),math.cos(math.rad(v.steer)))
end
popStyle()
pushMatrix()
pushStyle()
noStroke()
translate(seg.pos.x,seg.pos.y)
rotate(-seg.desiredBearing)
fill(255, 255, 255, 255)
ellipse(-self.width/6,self.width/5,self.width/3)
ellipse(self.width/6,self.width/5,self.width/3)
fill(0)
ellipse(-self.width/6,self.width/5+self.width/16,self.width/6)
ellipse(self.width/6,self.width/5+self.width/16,self.width/6)
popStyle()
popMatrix()
--[[ v = segments[1]
steer = wrap(v.desiredBearing - v.bearing,-180,180)
maxsteer = clampMagnitude(steer,180)
v.bearing = v.bearing + maxsteer
]]
self.near,self.far = seg.pos, seg.pos
local maxDeltaBearing = (270-#self.segments)*DeltaTime
local desiredDeltaBearing = wrap(
seg.desiredBearing - seg.steer, -180, 180)
local deltaBearing = clampMagnitude(
desiredDeltaBearing, maxDeltaBearing)
seg.steer = seg.steer + deltaBearing
local vel = bearingToVector(seg.steer) * 120
local newPos = seg.pos + vel*DeltaTime
seg.pos = newPos
if self.foodCount > 5 then
self:grow()
end
self.width = math.min(#self.segments,75)
end
function Playersnake:grow()
self.startedGrowing = true
local growingSegment = {steer = 0, offset = vec2(0,0), desiredBearing = 0, pos = self.segments[#self.segments].pos, len = 0}
table.insert(self.segments,growingSegment)
tween(2,growingSegment,{len = 25},tween.easing.cubicOut)
self.foodCount = 0
end
function Playersnake:collide(snake)
for i,v in ipairs(snake.segments) do
local dist = (self.segments[1].pos - v.pos):len()
if dist < self.width/2 + snake.width/2 then
self:die()
end
end
end
function Playersnake:die()
for i,v in ipairs(self.segments) do
for i = 1, math.random(1,5) do
local pellet = vec3(math.random(-25,25) + v.pos.x,math.random(-25,25) + v.pos.y,0)
table.insert(food,pellet)
tween(1,pellet,{z = math.random(10,30)},tween.easing.cubicInOut)
end
end
self.dead = true
end
function Playersnake:touched(touch)
-- Codea does not automatically call this method
self.segments[1].desiredBearing = bearingOf(vec2(touch.x,touch.y) - self.segments[1].pos)
end
--# Snake
Snake = class()
function Snake:init(x,y,startlen)
-- you can accept and set parameters here
self.segments = {}
self.segLen = 20
for i = 1, startlen do
self.segments[i] = {steer = 0, offset = vec2(0,0), desiredBearing = 0, pos = vec2(x,y), len = 25}
end
self.width = 50
self.frames = 0
self.foodCount = 0
self.far = vec2(x,y)
self.near = vec2(x,y)
local function randomColor()
return color(math.random(0,255),math.random(0,255),math.random(0,255))
end
self.randomColors = {}
for i = 1 , math.random(1,4) do
table.insert(self.randomColors,randomColor())
end
self.override = false
self.name = genname()
end
function Snake:draw()
-- Codea does not automatically call this method
local seg = self.segments[1]
local closestFood = vec2(0,0)
for i,v in ipairs(food) do
local dist = (self.segments[1].pos - vec2(v.x,v.y)):len()
if dist < self.width/2 + v.z/2 then
self.foodCount = self.foodCount + 1
table.remove(food,i)
elseif dist < (self.segments[1].pos - vec2(closestFood.x,closestFood.y)):len() then
closestFood = v -- find closest food to snake head
end
end
if (seg.pos-vec2(closestFood.x,closestFood.y)):len() < 100 and
not self.override then
self.override = 1
seg.desiredBearing = bearingOf(vec2(closestFood.x,closestFood.y) - self.segments[1].pos)
else
self.override = false -- return to random walk
end
local buff = self.segments
for i = #buff, 1, -1 do
v = buff[i]
local prev = buff[i-1] or v
strokeWidth(self.width)
stroke(self.randomColors[i%#self.randomColors+1])
lineCapMode(ROUND)
line(v.pos.x - v.offset.x*WIDTH,v.pos.y,prev.pos.x-prev.offset.x*WIDTH,prev.pos.y)
if buff[i-1] then
local prev = buff[i-1]
local d = prev.pos - v.pos
local angle = bearingOf(d)
v.pos = prev.pos -
(bearingToVector(angle))
* v.len
-- v.steer = bearingOf(segments[i-1].pos - v.pos)
end
-- segments[i].pos = segments[i].pos + vec2(math.sin(math.rad(v.steer)),math.cos(math.rad(v.steer)))
end
pushMatrix()
pushStyle()
noStroke()
translate(seg.pos.x,seg.pos.y)
rotate(-seg.steer)
fill(255, 255, 255, 255)
ellipse(-self.width/6,self.width/5,self.width/3)
ellipse(self.width/6,self.width/5,self.width/3)
fill(0)
ellipse(-self.width/6,self.width/5+self.width/16,self.width/6)
ellipse(self.width/6,self.width/5+self.width/16,self.width/6)
popStyle()
popMatrix()
--[[ v = segments[1]
steer = wrap(v.desiredBearing - v.bearing,-180,180)
maxsteer = clampMagnitude(steer,180)
v.bearing = v.bearing + maxsteer
]]
local maxDeltaBearing = 270*DeltaTime
local desiredDeltaBearing = wrap(
seg.desiredBearing - seg.steer, -180, 180)
local deltaBearing = clampMagnitude(
desiredDeltaBearing, maxDeltaBearing)
seg.steer = seg.steer + deltaBearing
local vel = bearingToVector(seg.steer) * 100
local newPos = seg.pos + vel*DeltaTime
seg.pos = newPos
if not self.override then -- do random movement when override is off
if self.frames%60 == 0 then
seg.desiredBearing = seg.desiredBearing + math.random(-50,50)
end
end
if self.foodCount > 5 then
self:grow()
end
self.width = math.min(#self.segments,75)
end
function Snake:grow()
self.startedGrowing = true
local growingSegment = {steer = 0, offset = vec2(0,0), desiredBearing = 0, pos = self.segments[#self.segments].pos, len = 0}
table.insert(self.segments,growingSegment)
tween(2,growingSegment,{len = 25},tween.easing.cubicOut)
self.foodCount = 0
end
function Snake:collide(snake)
local closestSegment = {pos = vec2(0,0)}
for i,v in ipairs(snake.segments) do
local dist = (self.segments[1].pos - v.pos):len()
if dist < (self.segments[1].pos - closestSegment.pos):len() then
closestSegment = v
end
if dist < self.width/2 + snake.width/2 then
self:die()
break
end
end
if not self.steerLocked then
if (self.segments[1].pos - closestSegment.pos):len() < 80 + self.width/2 + snake.width/2 then
self.override = 2 -- override controls with level 2 if snake is near other snake
self.segments[1].desiredBearing = v.steer -90 +(math.random()-0.5)*10 -- leave perpendicular from snake with random vec
self.steerLocked = true
self._=0
tween(1,self,{_=0},nil,function()
self.steerLocked=false
print("@")
end)
elseif (self.segments[1].pos - closestSegment.pos):len() < 150 then
self.override = false
end
end
end
function Snake:die()
for i,v in ipairs(self.segments) do
for i = 1, math.random(1,2) do
local pellet = vec3(math.random(-25,25) + v.pos.x,math.random(-25,25) + v.pos.y,0)
table.insert(food,pellet)
tween(1,pellet,{z = math.random(10,30)},tween.easing.cubicInOut)
end
end
self.dead = true
end
function Snake:touched(touch)
-- Codea does not automatically call this method
end
--# Vector
function wrap(x, min, max)
if x < min then
return max - (min - x)
elseif x >= max then
return min + (x - max)
else
return x
end
end
function clamp(x, min, max)
return math.max(min, math.min(max, x))
end
function clampMagnitude(x, maxMagnitude)
return clamp(x, -maxMagnitude, maxMagnitude)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment