Howdy, Stranger!

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

Class variable issues in codea - nil value when attempting arithmetic - help

edited February 2014 in Questions Posts: 318

Hi, I've been programming in lua/codea for a week now and finding it brilliant to learn especially with Ignatz's two learning PDFs as well as excellent examples from you all.

I'm making headway with my game with code currently in main. Now I'm branching out and starting to build classes and this one consistent issue is causing me to want to throw my iPad against something made of brick.

Each time I write a class and use variables within it and not pulled in from main, they come up as nil as defined and the program won't run.

Here is my code (please note groundspeed is a variable defined and used in main)

Pond = class()

function Pond:init(pondX,pondY,imagepond)
    -- you can accept and set parameters here
    self.pondX = 1240
    self.pondY = 248
    self.imagepond = readImage("Documents:pond")
end

function Pond:draw()
    self.pondX = self.pondX - groundspeed
    sprite(imagepond,self.pondX,248)
end

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

Here is the error:

error: [string "Pond = class()..."]:12 attempt to perform arithmetic on field 'pondX' (a nil value)

I've tried moving and adding various 'self.' In front of things and to no avail.

I'm thinking its something simple I've done wrong or missed like the time I was evaluating a variable string without speech marks and when I put them in speech marks it all started working.

Can you guys help me?

I've tried it in different classes and its not working.

Thank you
The major

Comments

  • IgnatzIgnatz Mod
    edited February 2014 Posts: 5,396

    @Majormorgan - the sprite command should be

    sprite(self.imagepond,self.pondX,248)
    

    Your image is stored in self.imagepond, which is not the same as imagepond (which doesn't exist and will create an error).

  • @Ignatz hey thank you. I've corrected that and now get the same error for line 11:

        self.pondX = self.pondX - groundspeed
    

    I've tried with with and without the self. in front if it but no avail. It makes me think I messed up somewhere else.

    By the way, your two code beginner guides have helped me get leaps and bounds in this - thank you!

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Majormorgan -

    Perhaps you are calling the draw function like this

    P.draw()  --where P is whatever you called your class
    

    when it needs to be

    P:draw()  --use a semi colon not a full stop
    

    Failing that, I suspect your variable groundspeed either is not a global variable (so it can't be seen inside the class) or else it hasn't been initialised.

    (& thanks for the nice feeback, it makes the effort worthwhile).

  • Posts: 41

    It sounds as if you are calling the draw function with the class name instead of the instance name.

    if your instance is created with something like myPond = Pond() you need to call draw with myPond:draw() not Pond:draw()

    This may not be the problem but will result in the error you are getting.

    Just curious. You have an init function that accepts 3 parameters but you are setting the class properties manually?

  • @pops I'll check into that - thanks. The Init class is where my understanding is limited, so if I'm setting the parameters manually I take it I can remove them from inside the init() ?

    With regards to the instance class, this sounds like the bit I'm missing. To answer @Ignatz at the same time, in main in the draw function I'm using:

        Pond:draw()
    

    Which sounds like I'm calling the class as you say and not the instance. So my question is how and where I define the instance.

    Is it in function setup() in main or somewhere else?

    The game is one where the background is scrolling by, using an X measurement to loop a background image so it looks continuous (think of the scrolling ground in flappy bird).

    The pond is an obstacle you have to jump over so it tracks its X position and deducts ground speed defined in main.

    I know groundspeed is global as I managed to test it in a fudged class of pond done a couple of versions ago that also passed its x and y position defined in main. But I wanted to keep the x and y in the class hence why I've stripped that out.

    Lol. Once I crack this I imagine I'll be making classes much easier!

    Thank you all for your help.

  • IgnatzIgnatz Mod
    edited February 2014 Posts: 5,396

    @Majormorgan, try this in the setup function

    p=Pond(1240,248,"Documents:pond")
    

    Then in Pond:init

    function Pond:init(pondX,pondY,imagepond)
        self.pondX = pondX
        self.pondY = pondY
        self.imagepond = readImage(imagepond)
    end
    

    Then in your draw function

    p:draw()
    

    or maybe better still, to give you more flexibility

    p:draw(groundspeed) --pass ground speed to class
    
    --and then
    function Pond:draw(gs) --groundspeed passed as parameter
        self.pondX = self.pondX - gs
        sprite(self.imagepond,self.pondX,248)
    end
    

    This means your class doesn't need to know anything about the outside world, which is a good thing

  • edited February 2014 Posts: 318

    That's it! Thanks @Ignatz and @Pops

    It was the fact I was calling the class straight and not defining an instance. Oh god that make a whole lotta sense now.

    Thank you both for your help and patience!

    Cracking one class now opens the door to creating a range of classes now I know how they work.

  • So calling the classes is now sorted and Pond is working fine. For a sprite its cool. But when it comes to sprite sheets and meshes, I've run into some confusion what to have with self in front of.

    This new class has code for a sprite sheet that someone on the forums posted, which I got to work well in main for the hero character, but putting it into a new class causes some issues,

    Ive used the Init function that @Ignatz wrote for me above in the new class Fountain. I'm passing the x,y and file name to load in read image from main like in pond.

    Eventually the fountain will have a working state (0) and a cracked state (1) where the fountain is broken. In the working state are 3 frames of a water fountain animating.

    I've got all these variables set up in fountain with self. in front of them but when I come to draw and use this rectangle to extract the part of the sprite sheet it flags up an error.

    So bear in mind this code is all within the class Fountain

    Fountain = class()
    
    function Fountain:init(fountainX,fountainY,imagefou)
        -- you can accept and set parameters here
        self.fountainX = fountainX
        self.fountainY = fountainY
        self.fou=mesh()
        self.imagefou=readImage(imagefou)
        self.fou.texture=imagefou
        self.fourows=1 --number of rows in the sprite sheet
        self.foucols=3 -- number of columns in the sprite sheet
        --moaction=0
        self.fousizex=324
        self.fousizey=200
        --constants
        self.fouworking=0
        self.foucracked=1
        --arrays to hold the animation
        self.fouanimdelay=0
        self.fouamimdelaymax=3
        self.fouanimx={}
        self.fouanimy={}
       -- self.foumovex={}
      --  self.foumovey={}
        --define the frames
        self.fouanimx[0]={0,1,2}
        self.fouanimy[0]={0,0,0}
     --   self.foumovex[0]={0,0,0}
     --   self.foumovey[0]={0,0,0}
        --fountain cracked
      --  pondanimx[1]={0,0,0}
     --   pondanimy[1]={0,0,0}
      --  pondmovex[1]={0,0,0}
      --  pondmovey[1]={0,0,0}
        --defining sprite
        self.fouy=200 --coordinate of fountain sprite
        self.foux=WIDTH/3 -- coordinate of fountain sprite
        self.foucurFrame=1
        self.foustate=self.fouworking
    end
    
    function Fountain:draw()
        -- Codea does not automatically call this method    
        self.fou:clear() --clear the mesh
        self.fountainX = self.fountainX - groundspeed
        --cycle through the animation
    --add a delay to solw the animation down
        self.fouanimdelay = self.fouanimdelay + 1
            if self.fouanimdelay>3 then
        --move to the next frame in the animation
            self.foucurFrame = self.foucurFrame + 1
        --check to see if this frame is past the end of the animation array for the current state and if so, reset
        if self.foucurFrame>#self.fouanimx[self.foustate] and self.foustate==self.fouworking then
       self.state=self.fouworking
            self.foucurFrame=1
    end
    local idx=self.fou:addRect(self.fountainX,self.fountainY,self.fousizex,self.fousizey)
        self.fou:setRectTex(self.idx,(self.foanimx[self.state][self.foucurFrame])/self.foucols,(self.fouanimy[self.state][self.curFrame])/self.fourows,1/self.foucols,1/self.fourows)   
        --draw the mesh
        self.fou:draw()
        --reset the animation delay counter
        self.fouanimdelay=0
        end
    end
    
    --function Fountain:touched(touch)
        -- Codea does not automatically call this method
    --end
    

    The line that flags a problem is the line below local idx. I may well have too many 'self.' Or it may even be local is not the right thing here, as I said before the code ran well in main, but bringing it into class Fountain might be the issue.

  • @Majormorgan, because the local idx=... and self.fou.setRectTex... lines are after the end statement that closes the Fountain:init function, I think that self then has no reference to any instance of the class.

    It looks like you are adding the rectangle and setting the texture as part of the instance initialisation, so these statements should be inside your init function.

    Also, it appears you have lost the function Fountain:draw() line before --draw the mesh.

  • @time_trial thanks for looking at the code. There was a lot to go through so I appreciate you all looking.

    I've been tinkering all afternoon and got rid of the errors by rebuilding the code bit by bit.

    I see what you're thinking, but the function Fountain:draw() is there in the code above and I've got it working without reordering.

    What I did do was simplify all the names to make it easier to track the changes in the code.

    It is a challenge!

    I've almost got it working fully. Currently it does exactly what I need in the new code except there is one frame that turns off so it flashes briefly.

    Once I comb through I'll fix that issue.

    Thanks all for your help,

  • And the last bit is now fixed. I had to draw the mesh after the last frame calculation.

    So now I have two types of classes. One is the sprite class for static non animated graphics and one is a sprite sheet class for animation.

    Thank you all for your help,

  • Ah, sorry @Majormorgan, I think the line wrapping confused me. I should have learnt by now not to try and debug code without running it. Doh! Glad you nearly have it working.

  • Posts: 41

    @Majormorgan good to see you got it sorted.

  • @time_trial not at all. You looked at the code and thought you spotted something from afar and for that I'm extremely grateful. That people look and bounce ideas is ace. Regardless of wether you run test it. I love that everyone's experiences come together and spot things!

    Thanks @Pops!

  • @pops you and @Ignatz gave me the breakthrough earlier with your help on the first problem and the confidence to push on with the second problem.

Sign In or Register to comment.