I am a complete newbie to writing code, having only started with codea six or seven months ago. And having never even dabbled in programming before that. So if it seems presumptuous of me to attempt a tutorial type post I sincerely apologize, but a friend of mine urged me to do this, so here goes.
Early on I found some really great tutorials on buttons and basically it came down to
If touch.x > than button.x - size/2 and touch.x < button.x + size/2
Then repeat that for y.
Not really difficult but when you start to have multiple it can get cumbersome.
I did find that if you were dealing with round or square buttons you could do this;
Make your touch a vec2 so, tvec =vec2(touch.x,touch.y)
Then use that to get the distance between the touch and the button center, so;
tDist = tvec:dist(button)
If tDist < size/2 then do whatever your button does.
But you still have to write this out for every button.
So I came up with this and I guess I am asking if this makes sense and seems practical?
Full code here
https://gist.github.com/Circussmith/7450391
For the purposes of this type of button we will think of the screen as a grid. Ignatz does a great tutorial on tables were he describes how to set up a grid and then place stuff in it. For our purposes we won’t need to actually create a grid table, just think of the screen that way for now.
First I set up a table for all of the buttons. Then I set up the Boolean that determine if we do something or not. This is what the button will change in this simple example. Then I set up another one that we will use later to make sure the action doesn’t take place until after the button has been released.
Now we create a vec2 that we will use to calculate both the size and position of the button. This is where the grid idea comes in. Thinking of the screen as a grid of so many positions (buttons) wide and so many high we decide how big we want our button. In this case I decided about 100 pixels wide by 75 high. Or in other words a 10 x 10 grid. I divide the screen width and height by my grid numbers, 10 and 10 and I get, rounded out, 102 and 77. We put that into btnSize, and it will, in fact be our buttons size but we will also use it for other calculations.
Lastly, we call the function that actually creates our buttons.
Button = class()
function Button:init()
btnTab = {}
txt = false
chBtn = false
self.btnSize = vec2(102,77)
self:makeButton()
end
Now we make our buttons. First we define the image we want for each button. We then insert into our buttons table all of the info that button will need. The first thing you see there is x and y. These are the grid coordinates based on our 10 x 10 grid.
IMPORTANT NOTE - the grid starts at 0,0 in the lower left corner, I will explain later why.
IN this case the first button is going to show up at 2,2 on our grid. So pretty low and to the left.
In the table we say size = btnSize.
Next we do what I think is a really cool thing. As I said, I have only ever used codea so I don’t know if it is a standard thing or not. We insert an orphan function into our table. We’re gonna go all “please sir, I’d like some more” on this tables ass. We say btnDoes and then add a function that has no name and only exists in the table and gets called when the this particular button is touched. This function does whatever we want this button to do. In this case we are setting txt to true or false but it could be any number of things, including changing parameters or changing the state our app is in. We can actually use this concept to determine what buttons we are drawing. I plan on getting to that later but if I don’t please feel free to ask me.
function Button:makeButton()
local fImg = readImage("Tyrian Remastered:Arrow Left")
local bImg = readImage("Tyrian Remastered:Arrow Right")
table.insert(btnTab,{x = 2,y = 2,img = fImg,size = self.btnSize,btnDoes = function() txt=false end})
table.insert(btnTab,{x = 6, y = 2,img = bImg,size = self.btnSize,btnDoes = function() txt = true end})
end
This is where we draw the buttons.
NOTE FOR BEGINNERS LIKE ME - Creating a button as a “thing” and drawing it are two very different things. The buttons we created in the makeButton function are real in terms of the code, but they are invisible in terms of seeing them on the screen. In other words we could make them and interact with them but never actually see them on the screen. Drawing them doesn’t really affect what they do, it just gives us a visual reference to know where to touch the screen.
I know that’s a little esoteric but when you are writing the code it becomes important. In this case everything they actually do happens under the checkButton function and not the draw function. It can seem like a trivial distinction but it can become really important as your code gets more complicated.
ok, let’s look at the draw function.
first we run through the btnTab and draw our buttons. It is important to set the spriteMode to CORNER here for reasons I will explain in a bit. We take local x ( use local whenever possible to speed up your code as global variables take longer to process) and multiply it by our btnSize variable, same with y. This gives us the actual pixel positions for the sprite. We have already defined our image as img in our buttons table as well as our size and position.
Well the position is a little different. We defined that by a grid position (2,2). Now we are using our btnSize variable to convert it back into pixel coordinates. So multiplying our grid coordinates by our btnSize we get a corner position of 204,154. But honestly, we don’t need to worry about that as long as we are thinking in terms of a 10,10 grid.
Now we say if txt is true type “It Worked!” In the center of the screen. For simplicity’s sake I am putting this here but it could be in another function that is called from the main draw function.
function Button:draw()
for i,v in ipairs (btnTab) do
pushStyle()
spriteMode(CORNER)
local x = v.x * self.btnSize.x
local y = v.y * self.btnSize.y
sprite(v.img,x,y,v.size.x,v.size.y)
end
if txt == true then
fill(255, 255, 255, 255)
fontSize(40)
text("It Worked!",WIDTH/2,HEIGHT/2)
end
end
And this is where the rubber meets the road. Here we cycle through the buttons table and see if we are actually touching an active button. We are calling this from the button:touched function. Sending our x and y grid coordinates through in the (). This is where we send through the 2,2 numbers. In the next section I explain how we convert the actual touch.x and touch,y to those simple grid numbers. Once we determine we are touching the right coordinate we then call the orphan function from that button table to do what it needs to do. In this case change txt to true or false, but again, it could be anything.
function Button:checkButton(x,y)
for i,v in ipairs(btnTab) do
if v.x == x and v.y == y then
v.btnDoes()
end
end
end
We can see the button and we have touched it, so now what?
The first thing we do is make sure nothing happens until we have released the touch. This is a bit of a personal preference but I like things to happen after I release the button, not the instant I touch it. When we touch it it sets the chBtn variable to true, then when we let go it checks to see if chBtn is true. We do this because the normal state is ENDED so we have to something else in there to help it make that decision to activate the button.
so this is kind of cool. first we take touch x and y and divide them by btnSize. However there is a problem.
If we divide touch x 422 and y 572 by btnSize we get x = 4.13 y = 7.4, not an exact grid coord for comparisons sake.
Now, if we run that number through math.random it returns the number with no decimal values. I feel like there is a better command for this and would be open to suggestions, but this does work.
So we put in 4.13 and 7.4 and we get back 4 and 7, which we can use as a coord.
If you notice, these numbers are the lowest possible for that grid coord. Basically x is farthest left possible since 4.1 would be a bit right of that and 7.4 is a bit higher than 7. When we convert these back to pixels what we get is the bottom left corner of each grid position. Which is why we set spriteMode to corner and why our grid starts at 0,0 (our first possible touches, once divided by btnSize are less than 1 so come back 0)
We then send the coords through to checkButton to do their thing.
function Button:touched(touch)
if touch.state == BEGAN then
chBtn = true
end
if chBtn == true and touch.state == ENDED then
local bx = touch.x/self.btnSize.x
local by = touch.y/self.btnSize.y
local z = math.random(bx,bx)
local d = math.random(by,by)
chBtn = false
self:checkButton(z,d)
end
end
call the button class from main and draw and add touched(touch) function to your main tab and then call button touched(touch) from there.
That’s it. The cool thing about this is to add as many buttons as you want you simply have add on line of code under the makeButton function. There is also a way to determine which buttons you want to show using a state machine. Under draw and checkButton you put in a line like if v.state == state then… And in the table for each button you put in the state you want them to show up in.
I hope this is helpful and please let me know any suggestions you might have.