textSize() and Touch?

Hello there.

I am currently working on recreating the game “The Battle of Polytopia” (Previously known as Super Tribes) in Codea. At the moment I am working on the menu. Heres what I’ve got so far:


-- __________
-- | MyVars |
RECT = 1

TouchObjects = {}

-- ________________
-- | Animatables |
-- Text
Text = class()
-- Animatable Text

function Text:init(str, x, y, size)
    self.str = str
    self.x = x
    self.y = y
    self.size = size or false
    
    self.originalState = {str = self.str, x = self.x, y = self.y, size = self.size}
end

function Text:draw()
    -- If size is not defined, use the size already set.
    if self.size then
        fontSize(self.size)
    end
    text(self.str, self.x, self.y)
end

-- Colour
Colour = class()
-- Animatable colour

function Colour:init(r,g,b,a)
    -- you can accept and set parameters here
    self.r = r
    self.g = g or false
    self.b = b or false
    self.a = a or 255
    
    if not (g and b) then
        self.g = self.r.g
        self.b = self.r.b
        self.a = self.r.a
        self.r = self.r.r or 255
    end
    
    self.originalState = {r = self.r, g = self.g, b = self.b, a = self.a}
end

function Colour:draw()
    -- Codea does not automatically call this method
      return color(self.r, self.g, self.b, self.a)
end

-- roundRect and animatable (taken from Sound Example)
function roundRect(x,y,w,h,r)
    pushStyle()
    
    insetPos = vec2(x+r,y+r)
    insetSize = vec2(w-2*r,h-2*r)
    
    --Copy fill into stroke
    local red,green,blue,a = fill()
    stroke(red,green,blue,a)
    
    noSmooth()
    rectMode(CORNER)
    rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
    if r > 0 then
        smooth()
        lineCapMode(ROUND)
        strokeWidth(r*2)

        line(insetPos.x, insetPos.y, 
             insetPos.x + insetSize.x, insetPos.y)
        line(insetPos.x, insetPos.y,
             insetPos.x, insetPos.y + insetSize.y)
        line(insetPos.x, insetPos.y + insetSize.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)
        line(insetPos.x + insetSize.x, insetPos.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
    end
    popStyle()
end

RoundRect = class()

function RoundRect:init(x,y,w,h,r)
    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.r = r
    
    self.originalState = {x = self.x, y = self.y, w = self.w, h = self.h, r = self.r}
end

function RoundRect:draw()
    roundRect(self.x, self.y, self.w, self.h, self.r)
end

-- ________
-- | Main |

-- Use this function to perform your initial setup
function setup()
    -- Scenes
    menu = Menu()
    
    -- Set current scene to menu
    setScene(menu)
end

function setScene(s)
    scene = s
    
    -- Setup the current scene
    scene:setup()
end

-- This function gets called once every frame
function draw()
    -- Call draw function for current scene
    scene:draw()
end

function touched(touch)
    scene:touched(touch)
    Touch:touched(vec2(touch.x, touch.y))
end

-- _________
-- | Menu |

Menu = class()

function Menu:init()
    -- you can accept and set parameters here
end

function Menu:setup()
    -- Colours for opening colour sequence
    bg = Colour(235, 69, 217, 255)
    t1 = tween(1, bg, {r = 241, g = 233, b = 45})
    t2 = tween(1, bg, {r = 0, g = 184, b = 255})
    
    -- Animatable text
    title = Text("POLY\
 TOPIA", WIDTH/2, HEIGHT+100, 90)
    t3 = tween(0.6, title, {y = HEIGHT-230})
    t4 = tween(0.4, title, {y = HEIGHT-270, size = 111})
    
    tween.sequence(t1, t2, t3, t4)
    t1, t2, t3, t4 = nil
    
    -- Button Animation
    start = Button("Start", RECT, 12, WIDTH/2, 150, 100, 11)
end

function Menu:draw()
    -- Set the background colour
    background(bg:draw())
    
    -- Title text
    font("GillSans-Light")
    textAlign(CENTER)
    textMode(CENTER)
    fill(255, 255, 255, 255)
    title:draw()
    start:draw()
end

function Menu:touched(touch)
    
end

-- _________
-- | Touch |

-- Problem #1: start:touched() not being triggered

-- Trigger the touch function of an object only if that object is touched
Touch = class()

function Touch:init(object)
    table.insert(TouchObjects, object)
end

function Touch:draw()
    -- Codea does not automatically call this method
end

function Touch:touched(p)
    -- Check if the touch is inside any of the objects
    for i,object in pairs(TouchObjects) do
        local l = object.pos.x - object.size.x/2
        local r = object.pos.x + object.size.x/2
        local t = object.pos.y + object.size.y/2
        local b = object.pos.y - object.size.y/2
        if p.x > l and p.x < r and
            p.y > b and p.y < t then
            object:touched()
        end
    end
end


-- __________
-- | Button |

-- Problem #2: [Solved] What's wrong with textSize()?

Button = class()

function Button:init(str,t,s,x,y,r)
    -- you can accept and set parameters here
    self.displayText = str
    self.form = t
    self.x = x
    self.y = y
    self.w = w
    self.r = r or 0
    if s then
        self.s = s
    else
        self.s = fontSize()
    end
    
    -- Create a custom button to fit the text.
    fontSize(self.s)
    self.text = {}
    self.text.w,self.text.h = textSize(self.displayText)
    print(self.text.w.." "..self.text.h)
    self.w = self.text.w*1.5
    self.h = self.text.h*1.5
    print(self.w)i
    print(self.h)
    
    -- For Touch()
    self.pos = vec2(0,0)
    self.size = vec2(0,0)
    
    -- Save original state
    self.originalState = {str = self.str, form = self.form, s = self.s, x = self.x, y = self.y, r = self.r, w = self.w, h = self.h}
    
    -- Save button to Touch()
    Touch(self)
end

function Button:draw()
    -- Codea does not automatically call this method
    if self.form == RECT then
        fill(255, 0, 196, 255)
        roundRect(self.x-self.w/2, self.y-self.h/2, self.w, self.h, self.r)
        fill(255, 255, 255, 255)
        textAlign(CENTER)
        textMode(CENTER)
        fontSize(self.s)
        text(self.displayText, self.x, self.y)
    end
end

function Button:touched(touch)
    -- Codea does not automatically call this method
    print("Bumbo")
end


I am facing two problems:
One, if you run the code you’ll notice the immense size of the start button. This button was supposed to automatically fit the text perfectly using the textSize() function. If someone could explain to me what is going wrong and how to fix it (even if it a stupidly obvious mistake, which I can assure you is very likely).

Two, you may have noticed my Touch() function. The way this is supposed to work is when creating an object (e.g. A button) you run Touch(self), which adds the object to an table. Every time Codea senses a touch, the function cycles through all the objects within the table and checks if the touch is inside any of these, and then calls the touched() function for that specific objects. In the code above it calls start:touched().

Thank you for taking the time to read this through.
Toodles.

EDIT: Problem #1 solved thanks to @dave1707

In your code where you have start=Button(…) you’re passing 7 parameters but the function only accepts 6, so the size of r is 100. Don’t have time to look at the other problem now.

I’m not sure what you’re trying to do with the touched class. Are you trying to setup stuff for touch when you create a button.

@jaj_TheDeveloper Is this what your trying to do when you create a button. In the code below, you define the button in setup and it creates a round rect button a little larger than the text. The button:touched function checks if a button is touched.

function setup()
    rectMode(CENTER)
    bTab={}
    table.insert(bTab,button(200,HEIGHT-100,"Menu"))
    table.insert(bTab,button(200,HEIGHT-200,"Start"))
    table.insert(bTab,button(200,HEIGHT-300,"Stop"))
    table.insert(bTab,button(200,HEIGHT-400,"Run"))    
    table.insert(bTab,button(200,HEIGHT-500,"Whatever you want."))    
end

function draw()
    background(0)
    for a,b in pairs(bTab) do
        b:draw()
    end    
end

function touched(t)
    for a,b in pairs(bTab) do
        b:touched(t)
    end
end

button=class()

function button:init(x,y,t)
    self.x=x    -- x pos
    self.y=y    -- y pos
    self.t=t    -- text
    self.sel=false  -- selected
    self.w,self.h=textSize(t)
    self.h=self.h*1.5
end

function button:draw()
    stroke(0, 217, 255, 255)
    if self.sel then
        stroke(252, 255, 0, 255)
    end
    strokeWidth(self.h)
    line(self.x-self.w/2,self.y,self.x+self.w/2,self.y)
    fill(255,0,0)
    text(self.t,self.x,self.y)
end

function button:touched(t)
    if t.state==BEGAN then
        self.sel=false
        if t.x>self.x-self.w/2-self.h/2 and t.x<self.x+self.w/2+self.h/2 and
                t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
            self.sel=true
        end
    end
end

@dave1707 Thanks, my btn problem is now solved. In regards to the to Touch, I would prefer to have a function that checks if the touch is in any objects, such as Units or squares of landthat will call the touched() function for the corresponding object and that object alone. You know the saying, “Never type the same code twice.”