# Help with Visual Timer

I’m trying to creat a timer that looks like a circle with the center punched out. As the counter ticks down I’d like the circle to diminish in size. Example full timer would be a 360 degree circle half would be 180. I have an idea I’m working on but my FPS are terrible. The idea is creating a circle using Sin and Cos and for every degree I draw a line. Is there a better more efficient way of doing this?

``````
--# Main
function setup()
displayMode(FULLSCREEN)
t = TimerButton({x = x,y = y,size = 150,counterWidth = 15,angleSize = .5})
b = TimerButton()

end

function draw()
background(127, 127, 127, 255)
t:draw()
b:draw()

end

--# TimerButton
TimerButton = class()

function TimerButton:init(tbl)
-- you can accept and set parameters here
if not tbl then tbl = {} end
self.x = tbl.x or WIDTH/2
self.y = tbl.y or HEIGHT/2
self.startColor = tbl.startColor or color(26, 255, 0, 255)
self.midColor = tbl.midColor or color(254, 255, 0, 255)
self.endColor = tbl.endColor or color(255, 0, 0, 255)
self.x1 = 0
self.y1 = 0
self.size = tbl.size or 100
self.counterWidth = tbl.counterWidth or 25
self.angle = 0
self.angleSize = tbl.angleSize or 0.5
self.loc = {}

while self.angle < 360 do
local t = {}
t.x1 = t.x +(self.counterWidth * math.cos(math.rad(self.angle)))
t.y1 = t.y + (self.counterWidth * math.sin(math.rad(self.angle)))

self.angle = self.angle + self.angleSize
table.insert(self.loc,t)
end
end

function TimerButton:draw()
-- Codea does not automatically call this method
pushMatrix()
translate(self.x,self.y)
stroke(self.startColor)
strokeWidth(4)
for i = 1,#self.loc do

line(self.loc[i].x,self.loc[i].y,self.loc[i].x1,self.loc[i].y1)
end
popMatrix()
end

function TimerButton:touched(touch)
-- Codea does not automatically call this method
end

``````

@Chipis Something like this.

``````
function setup()
parameter.integer("time",1,60,30)
st=false
a=0
str="Tap screen to start timer"
end

function draw()
background(40,40,50)
stroke(255)
strokeWidth(2)
for z=a,360,.2 do
line(WIDTH/2,HEIGHT/2,WIDTH/2+x,HEIGHT/2+y)
end
fill(255, 0, 0, 255)
text(str,WIDTH/2,HEIGHT/2)
if st then
t1=os.date("*t")
sec=t1.sec
if t2~=sec then
t2=sec
a=a+360/time
end
if a>=360 then
st=false
str="Time expired"
end
end
end

function touched(t)
if t.state==BEGAN then
st=true
str=""
t1=os.date("*t")
t2=t1.sec
end
end

``````

@Chipis I changed the above code to use a time set with the parameter slider. Select the number of seconds then tap the screen.

EDIT: Changed the above program again. Added os.date to the touched routine so the timer didn’t jump when it started.

Thanks @dave1707 but I’m still getting about 5 fps. Same problem I was having with my test. I feel its the fact that we are drawing to many lines each draw.

What i’m trying to accomplish is similar to the demo arc shader, but I can figure out how to do it without using a shader.

@Chipis I didn’t look at the FPS, so I didn’t realize it was so low. I guess the way I was doing it doesn’t work that well. I’ll have to try something else.

Here’s something I made a little while ago for my game. The color tweens from green to red as time passes and it beeps more and more frequently, but those thigs could easily be removed, just like size changing could easily be added. Here it is:

``````
--# Main
-- Timer

-- Use this function to perform your initial setup
function setup()
parameter.watch("1/DeltaTime")
t = Timer(15)
end

-- This function gets called once every frame
function draw()
-- This sets a dark background color
background(255, 255, 255, 255)

-- This sets the line thickness
strokeWidth(5)

t:draw()
end

function touched(touch)
if touch.state == ENDED then
if touch.tapCount == 1 then
if t.timing == nil then
t:start()
elseif not t.paused then
t:pause()
else
t:resume()
end
else
t:restart()
end
end
end

--# Timer
Timer = class()

function Timer:init(t, cb, x, y, s)
self.time = t or 5
self.callback = cb or function() end
self.x = x or WIDTH / 2
self.y = y or HEIGHT / 2
self.size = s or HEIGHT * 3/4

self.timing = nil
self.beeping = nil
self.amnt = 0
self.beepTime = 10

self.paused = false

self.tMesh = mesh()
self.tMesh.vertices = triangulate({vec2(-self.size / 2, -self.size / 2),
vec2(-self.size / 2, self.size / 2),
vec2(self.size / 2, self.size / 2),
vec2(self.size / 2, -self.size / 2)})
self.tMesh.shader.color = vec4(0, 1, 0, 1)
self.tMesh.texCoords = triangulate({vec2(0,0),vec2(0,1),vec2(1,1),vec2(1,0)})
end

function Timer:beep()

self.beepTime = self.beepTime + 2

self.beeping = tween.delay(self.time / self.beepTime, function() self:beep() end)
end

function Timer:start()
self.amnt = 0
self.beepTime = 10
if self.timing == nil then
self.timing = tween(self.time, self, { amnt = 1 }, tween.easing.linear, self.callback)
end
if self.beeping == nil then
self.beeping = tween.delay(self.time / self.beepTime, function() self:beep() end)
end
end

function Timer:pause()
if self.timing ~= nil then
tween.stop(self.timing)
end
if self.beeping ~= nil then
tween.stop(self.beeping)
end

self.paused = true
end

function Timer:resume()
if self.timing ~= nil then
tween.play(self.timing)
end
if self.beeping ~= nil then
tween.stop(self.beeping)
end

self.paused = false
end

function Timer:stop()
if self.timing ~= nil then
tween.stop(self.timing)
self.timing = nil
end
if self.beeping ~= nil then
tween.stop(self.beeping)
self.beeping = nil
end
end

function Timer:restart()
self:stop()
self:start()
end

function Timer:draw()
-- Update timer
self.tMesh.shader.color = vec4(1 * self.amnt, 1 - (1 * self.amnt), 0, 1)
self.tMesh.shader.a2 = -self.amnt * (math.pi * 2) + math.pi

-- Draw timer
pushMatrix()

translate(self.x, self.y)
local t
if self.timing then t = math.ceil(self.timing.time - self.timing.running) else t = self.time end
fontSize(self.size*2/3) fill(255 * self.amnt, 255 - (255 * self.amnt), 0, 255)
-- text(t)

rotate(270.1)

self.tMesh:draw()

popMatrix()
end

function Timer:touched(touch)
-- Codea does not automatically call this method
end
``````

@Chipis Here’s another version that has a high FPS.

``````
function setup()
parameter.integer("time",1,60,30)
str="Tap screen to start timer"
a=0
st=false
xx()
end

function xx()
tab={}
for z=a,360 do
if z>a then
table.insert(tab,vec2(WIDTH/2,HEIGHT/2))
table.insert(tab,vec2(WIDTH/2+x,HEIGHT/2+y))
table.insert(tab,vec2(WIDTH/2+x1,HEIGHT/2+y1))
end
x1=x
y1=y
end
m=mesh()
m.vertices=tab
m:setColors(255,0,0)
end

function draw()
background(40, 40, 50)
m.draw(m)
t1=os.date("*t")
if st then
sec=t1.sec
if t2~=sec then
t2=sec
a=a+360/time
xx()
if a>=360 then
str="Time expired"
end
end
end
fill(40,40,50)
ellipse(WIDTH/2,HEIGHT/2,WIDTH-50)
fill(255)
text(string.format("%d",1/DeltaTime),WIDTH/2,HEIGHT-50)
text(str,WIDTH/2,HEIGHT/2)
end

function touched(t)
if t.state==BEGAN then
st=true
str=""
xx()
t1=os.date("*t")
t2=t1.sec
end
end

``````

Here is my attempt with the arc shader. I can not figure out how to correctly increment it smoothly or accurately. Maybe someone brighter then I could fix it. It is working great in concept and it is fast.

``````
--# Main
--Visual Timer

supportedOrientations(LANDSCAPE_ANY)
function setup()

t=Timer(20)
t:start()
t1:start()
t2:start()
parameter.watch("time")

end

function draw()
background(255, 255, 255, 255)
time = t.time
t:draw()
t1:draw()
t2:draw()
end

function touched(t)

end

//
//

uniform mat4 modelViewProjection;

attribute vec4 position;
attribute vec2 texCoord;

varying highp vec2 vTexCoord;

void main() {
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}

]]

//
//

precision highp float;

uniform float size;
uniform float a1;
uniform float a2;
uniform vec4 color;

varying vec2 vTexCoord;

void main() {
vec4 col = vec4(0.0);
vec2 r = vTexCoord - vec2(0.5);
float d = length(r);
if (d > size && d < 0.5) {
float a = atan(r.y, r.x);
if (a2 > a1) {
if (a > a1 && a < a2) {
col = color;
}
} else {
if (a > a1 || a < a2) {
col = color;
}
}
}
gl_FragColor = col;
}

]]
--# Timer
Timer = class()

function Timer:init(time,x,y,tbl)
-- you can accept and set parameters here
local tbl = tbl or {}
self.paused = true
self.startTime = 0
self.tickTime = 0
self.time = time or 60

self.width = tbl.width or 25
--Colors
self.startColor = tbl.startColor or color(0, 255, 0, 255)
self.midColor = tbl.midColor or color(255, 255, 0, 255)
self.endColor = tbl.endColor or color(255, 0, 0, 255)
self.x = x or WIDTH/2
self.y = y or HEIGHT/2
self.m = mesh()

end

function Timer:draw()
-- Codea does not automatically call this method
if not self.paused then
local t = tonumber(string.format("%.2f",ElapsedTime))

if t > self.startTime +1 then
self.startTime = t
self.time = self.time - 1
end
if t > self.tickTime then
self.tickTime = t
end
end
pushMatrix()
translate(self.x,self.y)

local clr
clr = self.startColor
clr = self.midColor
else
clr = self.endColor
end
self.m:draw()
popMatrix()
end

function Timer:touched(touch)
-- Codea does not automatically call this method
end

function Timer:start()
self.startTime = ElapsedTime
self.paused = false

end

``````

Glad to see my ideas in action. I used the same method as @JakAttak and @Briarfox in StackIt.

@Chipis - it would be much easier to use a timer where the circle shrank from the outside toward the middle (by just drawing a smaller and smaller circle). I believe it’s usually best to work to the strengths of your program (ie stick to what it can do easily, where possible).

Otherwise, this is a huge amount of work to do, just for a tiny part of your game.

@zoyt I didn’t even notice that @jakattak used the shader mine isn’t done but thats the best I could do before heading to bed

Thank you guys for the posts, I now have a better idea where I’m going with this timer. Don’t go too far, I’m sure I’ll have more questions in the near future.