Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Changing variables from another class...

edited June 2012 in Questions Posts: 68

Okay. So I'm trying to learn to code in Codea but I feel there's a lack of tutorials so I've just been trying to figure things out.
My goal is simply to move my character using buttons.
I have a LeftBtn class and Character class and I'm wondering how to change the x position of the character from the button class as I don't want my Main class to deal with my character's movements. I like a clean Main class.
Here's My code:

MAIN CLASS


-- Use this function to perform your initial setup function setup()     displayMode(FULLSCREEN)     char = Character()     charX = WIDTH/2     charY = HEIGHT/2     char:init(charX,charY)     LeftBtn:init(WIDTH/4-200,HEIGHT/4,100,100)     RightBtn:init(WIDTH/4-50,HEIGHT/4,100,100) 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)     textSize(20)     text(charX, 500, 500)     -- Do your drawing here     char:draw()     LeftBtn:draw()     RightBtn:draw()      end function touched(touch)     -- Codea does not automatically call this method     LeftBtn:touched(touch) end

CHARACTER CLASS


Character = class() function Character:init(x,y)     -- you can accept and set parameters here     self.x = x     self.y = y end function Character:draw()     -- Codea does not automatically call this method     sprite("Planet Cute:Character Boy", self.x, self.y, 101, 171) end function touched(touch)     -- Codea does not automatically call this method      end

LEFTBTN CLASS

LeftBtn = class()

function LeftBtn:init(x,y,w,h)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.w = w
    self.h = h
end

function LeftBtn:draw()
    -- Codea does not automatically call this method
    fill(87, 87, 87, 50)
    rectMode(CENTER)
    noStroke()
    rect(self.x, self.y, self.w, self.h)
    fill(0)
    font("MarkerFelt-Wide")
    fontSize(80)
    text("L", self.x, self.y)
    
end

function LeftBtn:touched(touch)
    -- Codea does not automatically call this method
    if touch.state == BEGAN then character.x = character.x - 4 else end
end

Tagged:

Comments

  • Posts: 2,161

    (Firstly, code blocks are delimited by three tildes, not three dashes)

    Your code updates the x field of the character table. But there is not character table. There is a Character class (note the capital) which is really just a table in disguise, and a char object which is an instance of the Character class, but again is just a table in disguise. What you really want to update is char.x so you could put char.x in place of character.x.

    But I wouldn't do that. I'd rather make LeftBtn not know anything about what it is acting on - it makes it easier to change things later on. So when initialising LeftBtn, I'd pass it the char object:

    LeftBtn:init(WIDTH/4-200,HEIGHT/4,100,100,char)
    

    then in the initialisation code:

    function LeftBtn:init(x,y,w,h,o)
        self.acton = o
    

    and in the touched function

    function LeftBtn:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == BEGAN then self.acton:move(-4) end
    end
    

    finally, the character has to know what to do:

    function Character:move(x)
        self.x = self.x + x
    end
    

    In fact, I'd probably go a bit further and define one button class of which both the left and right were instances, which would mean passing the text as well. I'd also make it even less tied to the Character class and send a callback function, but that would need a bit more restructuring.

    The last point to make is that charX never gets updated. This is because in the line (using my code, but it's the same in yours) self.x = self.x + x then this assigns to self.x (a pointer to) the result of the computation self.x + x. What it does not do is update the value that self.x was pointing to before. So charX happily goes on pointing to WIDTH/2 while self.x wanders all over the screen.

  • Posts: 68

    You responded so fast that I can only assume ur awesome. Thank u, let me try out what u suggested :)

  • edited June 2012 Posts: 68

    I think I did what you said but nothing happens :\
    DId I miss something? I don't think the left button's "touch" function are being called but I clearly put it in the Main class, right?

    MAIN



    -- Use this function to perform your initial setup function setup()     displayMode(FULLSCREEN)     char = Character()     char:init(WIDTH/2,HEIGHT/2)     LeftBtn:init(WIDTH/4-200,HEIGHT/4,100,100,char) 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)     textSize(20)     -- Do your drawing here     char:draw()     LeftBtn:draw()      end function touched(touch)     -- Codea does not automatically call this method     LeftBtn:touched(touch) end

    CHARACTER

    Character = class()
    
    function Character:init(x,y)
        -- you can accept and set parameters here
        self.x = x
        self.y = y
    end
    
    function Character:draw()
        -- Codea does not automatically call this method
        sprite("Planet Cute:Character Boy", self.x, self.y, 101, 171)
    end
    
    function touched(touch)
        -- Codea does not automatically call this method
        
    end
    
    function Character:move(x)
        self.x = self.x + x
    end
    

    LEFTBTN

    LeftBtn = class()
    
    function LeftBtn:init(x,y,w,h,o)
        -- you can accept and set parameters here
        self.acton = o
        self.x = x
        self.y = y
        self.w = w
        self.h = h
    end
    
    function LeftBtn:draw()
        -- Codea does not automatically call this method
        fill(87, 87, 87, 50)
        rectMode(CENTER)
        noStroke()
        rect(self.x, self.y, self.w, self.h)
        fill(0)
        font("MarkerFelt-Wide")
        fontSize(80)
        text("L", self.x, self.y)
        
    end
    
    function LeftBtn:touched(touch)
        -- Codea does not automatically call this method
        if touch.state == BEGAN then self.acton:move(-4) end
    end
    
    

    It seems like it should work :\

  • Posts: 20

    I usually put a print() statement in the function I am testing to see if it is called. Really helps !

  • Posts: 563

    How about something like the following? I used the mesh button class provided by @Vega. I suggest you put the Button() class in a separate tab.

    Regarding your comment on tutorials, there are some over at http://codeatuts.blogspot.com.au/ I will add this code to the Tutorials with some supplementary explanations.

    At the moment the ship only moves when you tap the button. Bonus points if you modify this code so that the ship moves as long as you hold the button down.



    --# Main -- Use this function to perform your initial setup function setup() -- This block of code is optional. We just include it in -- every project so that we have a method of version control. version = 1.0 saveProjectInfo("Description", "Move Object Demonstration") saveProjectInfo("Author", "Reefwing Software") saveProjectInfo("Date", "11th July 2012") saveProjectInfo("Version", version) print("MoveShip v"..version.."\n") -- Initialise the co-ordinates of your "Ship" -- These will be updated by the action methods -- associated with each directional button. shipPosition = vec2(WIDTH/2, HEIGHT/2) -- This slider parameter will control the ship "speed" -- The larger the number the further the ship will -- move with each button tap. -- -- The format of this function is: -- parameter("name", min value, max value, init value) parameter("shipSpeed", 1, 10, 4) -- Define the four Buttons used to move the ship -- They wont be visible until you draw() them. -- Note that 50 pixels is the minimum height for the default -- button font size. local mButtonSize = vec2(100, 50) local mLocX = WIDTH - 250 local mLocY = 100 leftButton = Button("Left", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) leftButton.action = function() leftButtonTapped() end mLocX = mLocX + 150 rightButton = Button("Right", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) rightButton.action = function() rightButtonTapped() end mLocX = mLocX - 75 mLocY = mLocY + 60 upButton = Button("Up", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) upButton.action = function() upButtonTapped() end mLocY = mLocY - 120 downButton = Button("Down", vec2(mLocX, mLocY), mButtonSize.x, mButtonSize.y) downButton.action = function() downButtonTapped() end -- Assign the colours that you want to use in your game. blackColour = color(0,0,0) end -- This function gets called once every frame -- Codea will attempt to call draw() 60 times per second, it -- can be much less than this if you have a lot going on in your code. function draw() -- This sets a black background color background(blackColour) -- Do your drawing here -- Draw your ship at the current co-ordinates stored in shipPosition sprite("Tyrian Remastered:Boss D", shipPosition.x, shipPosition.y) -- Draw the four directional buttons leftButton:draw() rightButton:draw() upButton:draw() downButton:draw() end -- Button action methods function leftButtonTapped() -- Update ship position as long as the ship isnt off the screen. -- math.max() returns the maximum value of the arguments, so -- if shipPosition < 0 it will set it to 0. shipPosition.x = math.max(shipPosition.x - shipSpeed, 0) end function rightButtonTapped() -- Update ship position as long as the ship isnt off the screen. -- math.min() returns the minimum value of the arguments, so -- if shipPosition > WIDTH it will set it to WIDTH. shipPosition.x = math.min(shipPosition.x + shipSpeed, WIDTH) end function upButtonTapped() shipPosition.y = math.min(shipPosition.y + shipSpeed, HEIGHT) end function downButtonTapped() shipPosition.y = math.max(shipPosition.y - shipSpeed, 0) end -- Handle screen touches -- Note that you need to pass any touches through -- to your button touch handlers. function touched(touch) leftButton:touched(touch) rightButton:touched(touch) upButton:touched(touch) downButton:touched(touch) end --# Button Button = class() -- Mesh Button Class courtesy of @Vega -- 26 May 2012 -- -- Modified: Call Back Functionality added -- pushStyle() & popStyle() added to draw() -- -- Version 1.1 function Button:init(text,location,width,height) self.state = "normal" self.text = text self.textColor = color(255,255,255,192) self.location = location 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 end function Button:setColors(c1,c2,p1,p2) self.color1 = c1 self.color2 = c2 self.presscolor1 = p1 self.presscolor2 = p2 end function Button:textOptions(fn, sz, col) self.font = fn self.fontSize = sz self.textColor = col end function Button:draw() pushStyle() if self.visible == true then self:recolor() pushMatrix() translate(self.location.x,self.location.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() end popStyle() end function Button:touched(touch) if self.visible then if touch.x >= self.location.x and touch.x <= self.location.x + self.width and touch.y >= self.location.y and touch.y <= self.location.y + self.height then if touch.state == BEGAN then self.state = "pressing" sound(SOUND_HIT, 14227) elseif touch.state == ENDED then if self.state == "pressing" then self.state = "normal" end if self.action then self.action() end end else self.state = "normal" 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 function math.round(num) -- math.round function courtesy of @Vega return math.floor(num + 0.5) end
  • Posts: 68

    Thank u. I figured out a way to do it but simply forgot to update it here. I will, however, try out what u suggested as the code greatly differs from mine. (kinda makes mine look too simple and mediocre)

  • Posts: 226

    @RichGala1, nice if you update your code here. Thanks in advance (I'm a newbie too) :-D

  • Posts: 179

    Hi, @Reefwing. Thanks for using the button mesh class and adding some functionality. When I made that I promised to update with callbacks and such and never got around to it. I think the way you did works pretty well.

  • edited July 2012 Posts: 563

    @RichGala1 - nothing wrong with simple if it gets the job done. Well done on sorting it out on your own, that is one of the joys of coding. Most of the code in my solution is the general button class. Actually moving the Sprite doesn't take much code at all.

    @Vega - great button class, I am using it exclusively now, along with some of the other code you have contributed. I used the call back pattern from @Simeon's button class so I cant take any credit for it.

  • Posts: 68

    how does the global "local" thing work?

  • Posts: 563

    Global variables are available throughout your code. If you dont specify then a variable is global.

    If you specify a variable as local (by using the local keyword) then the variable is only available in the block where they are declared. A block can be the body of a control structure, the body of a function, or a chunk (unit of execution). For example, in the contrived example below:

    1. a is a global variable, available throughout your App.
    2. b is a local variable available only within someFunction().
    3. c is a local variable only available inside the while loop. Useless I know in this example.

    You should try to use local variables as much as possible, access to them is much faster than for globals and using globals can make your code harder to read and maintain.

    function someFunction()
    
        a = 0
        local b = 10
    
        while b > 0 do
            local c = 0
            b = b - 1
        end
    
    end
    
Sign In or Register to comment.