Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Problem with an animation — Gideros Forum

Problem with an animation

TheCryptoTheCrypto Member
edited July 2012 in General questions
Hi to all, I say immediately that I'm Italian and so I apologize for my English.
I'm using this code for generating an animation but it repeat infinity times:

Frames = gideros.class(Sprite)


function Frames:init(frameList)

self.frames = {}

for i = 1, #frameList do
self.frames[i] = Bitmap.new(TextureRegion.new(Texture.new(frameList[i])))
end

self.nframes = #frameList

-- to find the bounding box we add all childs and then remove them
for i = 1, #self.frames do
self:addChild(self.frames[i])
end

local width = self:getWidth()
local height = self:getHeight()

for i = 1, #self.frames do
self:removeChild(self.frames[i])
end


for i = 1, #self.frames do
self.frames[i]:setX(-width/2)
self.frames[i]:setY(-height/2)
end


self.frame = 1
self:addChild(self.frames[1])


self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)


function Frames:onTimer()
self:removeChild(self.frames[math.floor(self.frame)])
self.frame = self.frame + 0.1
if self.frame >= self.nframes + 1 then
self.frame = 1
end
self:addChild(self.frames[math.floor(self.frame)])
end

end


function Frames:onEnterFrame()
self:removeChild(self.frames[math.floor(self.frame)])
self.frame = self.frame + 0.1
if self.frame >= self.nframes + 1 then
self.frame = 1
end



self:addChild(self.frames[math.floor(self.frame)])

local x = self:getX()
local y = self:getY()


x = application:getContentWidth() / 2

if (y > 165) then y = y else if (self.frame < 15) then y = y else y = y + 6 end end

self.timer = Timer.new(590, 0)
self.timer:addEventListener(Event.TIMER, self.onTimer, self)
self.timer:start()

self:setX(x)
self:setY(y)

end

How i can repeat it only one time??

Comments

  • QuasarCreatorQuasarCreator Member
    edited July 2012
    One thing I noticed was you were using i = 1 in your for loops which means you are assigning i to 1 when you should write i == 1. Another thing I noticed was you wrote i = 1, #self.frames ( which is also equal to 1 if I am reading it right). So, I would start i at 0.

    I would try this code:
    Frames = gideros.class(Sprite)
     
     
    function Frames:init(frameList)
     
    self.frames = {}
     
    for i == 1, #frameList do
    self.frames[i] = Bitmap.new(TextureRegion.new(Texture.new(frameList[i])))
    end
     
    self.nframes = #frameList
     
    for i == 0, #self.frames do
    self:addChild(self.frames[i])
    end
     
    local width = self:getWidth()
    local height = self:getHeight()
     
    for i == 0, #self.frames do
    self:removeChild(self.frames[i])
    end
     
     
    for i == 0, #self.frames do
    self.frames[i]:setX(-width/2)
    self.frames[i]:setY(-height/2)
    end
    Also, you can use < prelang= "lua"> --code-- < / pre>(no Spaces) if you want to make the code look prettier.
    I am a programming noobie, so thanks in advance for helping me out and answering my questions!
  • @Quasar,
    there is a difference between the = and the ==.

    The single = is used for assignment, like
    for i = startNum, endNum, stepNum do
    The double == is used for comparison, so for comparing
    if startNum == endNum then

    Likes: QuasarCreator

    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
    +1 -1 (+1 / -0 )Share on Facebook
  • @TheCrypto,
    if you want that the init function is run only once, then you can have a simple thing like
    local ranOnce = false
    Frames = gideros.class(Sprite)
    function Frames:init(frameList)
      if ranOnce == false then
          -- All of your code comes in here, this will be run once only
     
          ranOnce = true
      end
    end
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • The init function WILL only be run once - at the point when you create an instance of your Frames object with
       local myFrame = Frames.new(frameList)
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • TheCryptoTheCrypto Member
    edited July 2012
    @techdojo i use this code but the Gideros Player is locked without an error:
     
    Frames = gideros.class(Sprite)
     
     
    function Frames:init(frameList)
     
       self.frames = {}
     
       for i = 1, #frameList do
          self.frames[i] = Bitmap.new(TextureRegion.new(Texture.new(frameList[i])))
       end
     
       self.nframes = #frameList
     
       -- to find the bounding box we add all childs and then remove them
       for i = 1, #self.frames do
          self:addChild(self.frames[i])
       end
     
       local width = self:getWidth()
       local height = self:getHeight()
     
       for i = 1, #self.frames do
          self:removeChild(self.frames[i])
       end
     
     
       for i = 1, #self.frames do
          self.frames[i]:setX(-width/2)
          self.frames[i]:setY(-height/2)
       end
       -- Here is your code <-----------------------------------
       self.frames = Frames.new(frameList)
     
       self.frame = 1
       self:addChild(self.frames[1])
     
     
       self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
     
     
       function Frames:onTimer()
       self:removeChild(self.frames[math.floor(self.frame)])
       self.frame = self.frame + 0.1
       if self.frame >= self.nframes + 1 then
          self.frame = 1
       end
       self:addChild(self.frames[math.floor(self.frame)])
    end  
     
    end
     
     
    function Frames:onEnterFrame()
       self:removeChild(self.frames[math.floor(self.frame)])
       self.frame = self.frame + 0.1
       if self.frame >= self.nframes + 1 then
          self.frame = 1
       end
     
     
     
       self:addChild(self.frames[math.floor(self.frame)])
     
       local x = self:getX()
       local y = self:getY()
     
     
       x = application:getContentWidth() / 2
     
       if (y > 165) then y = y else if (self.frame < 15) then y = y else y = y + 6 end end
     
       self.timer = Timer.new(590, 0)
       self.timer:addEventListener(Event.TIMER, self.onTimer, self)
       self.timer:start() 
     
       self:setX(x)
       self:setY(y)
     
    end
    :-/
  • @TheCrypto

    It looks like the line " self.frames = Frames.new(frameList)" is actually called inside the init function (which is called from Frames.new) so your actually re-calling the init function again from within the init function and you've got a recursive infinite loop which will hang the player until you finally run out of memory and crash.

    Likewise your onTimer function is declared within the init function and whilst this would still work as your effectively adding a function to the Frames table, it's very bad practice and should be moved OUT of the function and placed at the same level as the other Frame: functions.

    Functions declared within functions should be considered local to the scope of the function they were declared in - unless using them as closures (but I'd stay away from that area for a while if I were you)
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • @techdojo ok.. But you can post the correct code? It's very important.. i'm start 5 days ago study LUA but this animation is most important of study best, the code.. Thanks
  • I'll try but I can't promise anything as I can't see the code being used in it's wider context, however ...
    Frames = gideros.class(Sprite)
     
     
    function Frames:init(frameList)
     
       self.frames = {}
     
       for i = 1, #frameList do
          self.frames[i] = Bitmap.new(TextureRegion.new(Texture.new(frameList[i])))
       end
     
       self.nframes = #frameList
     
       -- to find the bounding box we add all childs and then remove them
       for i = 1, #self.frames do
          self:addChild(self.frames[i])
       end
     
       local width = self:getWidth()
       local height = self:getHeight()
     
       for i = 1, #self.frames do
          self:removeChild(self.frames[i])
       end
     
     
       for i = 1, #self.frames do
          self.frames[i]:setX(-width/2)
          self.frames[i]:setY(-height/2)
       end
     
       -- Here is your code <-----------------------------------
     
       -- self.frames = Frames.new(frameList)	<<-- this will cause your program to "lockup" as you'll end up calling yourself in an infinite loop
       -- if you want an example of how this would cause a problem uncomment the above line and the one below...
       -- print("In the Frames:init method")
     
       self.frame = 1
       self:addChild(self.frames[1])
     
     
       self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
     
    end
     
    -- --------------------------------------------------------------------------
     
    --[[
     
    	I don't think this code actually serves any purpose...
     
    function Frames:onTimer()
     
       self:removeChild(self.frames[math.floor(self.frame)])
       self.frame = self.frame + 0.1
       if self.frame >= self.nframes + 1 then
    	  self.frame = 1
       end
       self:addChild(self.frames[math.floor(self.frame)])
     
    end  
     
    --]]
     
    -- --------------------------------------------------------------------------
     
    function Frames:onEnterFrame()
     
       self:removeChild(self.frames[math.floor(self.frame)])
     
       self.frame = self.frame + 0.1
       if self.frame >= self.nframes + 1 then
          self.frame = 1
       end
     
       self:addChild(self.frames[math.floor(self.frame)])
     
     
    --[[
     
    	"Frames" is a class and ideally shouldn't be specific to a single instance 
    	so the actual movement of the "instance" of this class is the job of the code
    	that deals with the specific "instance" outside of the class.
     
    	Remember the class is just a blueprint, the instance is the building!
     
       local x = self:getX()
       local y = self:getY()
     
       x = application:getContentWidth() / 2
     
       if (y > 165) then y = y else if (self.frame < 15) then y = y else y = y + 6 end end
     
     
    	-- Not sure what the timer is doing, it seems to be replicating what's going on in
    	-- this function so I've removed that as well...
     
       self.timer = Timer.new(590, 0)
       self.timer:addEventListener(Event.TIMER, self.onTimer, self)
       self.timer:start() 
     
       self:setX(x)
       self:setY(y)
    --]]
     
    end
     
     
    -- --------------------------------------------------------------------------
    -- --------------------------------------------------------------------------
    -- --------------------------------------------------------------------------
    -- Outside of the class do something LIKE this to start the ball rolling...
     
    local myFrame = Frames.new(framelist)
     
    myFrame:setPosition(x,y)
     
    stage:addChild(myFrame)
    I've commented the changes I've made - if your new to lua check out the following books http://www.amazon.com/Beginning-Lua-Programming-Programmer/dp/0470069171 and http://astore.amazon.com/lua-store-20/detail/8590379825.

    Lastly I'm not 100% sure what your trying to achieve here, but you might want to search the forums for examples of how to use the MovieClip library - also if you want to progress - start simple, test your code and make sure you fully understand what's going on before trying to progress too far, then make a small addition, test, understand, repeat...
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • @techdojo There's an error:
    frames.lua:8: attempt to get length of local 'frameList' (a nil value)
    stack traceback:
    frames.lua:8: in function 'init'
    [string "property.lua"]:151: in function 'new'
    frames.lua:66: in main chunk
  • Correct - It would depend on what you pass to the Frames:new function.

    Obviously it has to be a table of some sort - if you've not defined the "frameList" value that I posted AS AN EXAMPLE to show you how to call the code then it will be nil.

    The clue was in the "Outside of the class do something LIKE this to start the ball rolling..." comment line.

    At the end of the day - it's your code, if you don't understand how to call it (or what to call it with) then I suggest you read (and re-read) the final paragraph of my previous post.
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • @TheCrypto: As you are new here, I would suggest you go here to introduce yourself. I understand you're eager to get your first game published but I would suggest you do as @techdojo: says and read a lot of the forum.

    [rant]
    A sure fire way of getting members to ignore your questions would be to jump right in and say please fix my code or please write my code for me and then expect someone to do the work for you.

    If someone tries to help by giving you some pointers and example code it is really impolite to then tell them that their code doesn't work. Members help each other, we don't write for you, we may contribute code to the whole community but as you have seen from the above examples, the onus is on you to understand your own code or at least what you want to achieve.[/rant]
  • I agree with @Scouser, and @techdojo a lot of learning and reading will be needed to actually understand the code you are writing and of course that will come with practice and time. But if I am not mistaken what you actually are looking for is a way to run the animation only one time.

    The code you actually want to fix for this logic is in the onEnterFrame Function or the onTimer function as you appear to have two copies of it.

    So first off get rid of that timer--you are repeating the code that is already handled inside the Frames:onEnterFrame function. Then make a change to the code below to stop the event from firing once the animation is finished.

    As shown here--you will need to edit you file's function to look like this below:
    function Frames:onEnterFrame()
     
       self:removeChild(self.frames[math.floor(self.frame)])
     
       self.frame = self.frame + 0.1
       if self.frame >= self.nframes + 1 then
    --      self.frame = 1 -- THIS RESETS THE ANIMATION TO START SO COMMENT IT OUT.
    -- Now this function will remove the onEnterFrame event and this the animation will stop here and disappear.
    self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
          else
            self:addChild(self.frames[math.floor(self.frame)])
       end
     end
    Hopefully this helps, but as it's been suggested I would certainly say take a look at the examples with Gideros they can show you a lot about using the code for many things.

    Eli
    ThumbHurt Games / FB: ThumbHurt Games / FB: Eli/Teranth | Skype: teranth37
  • What @Teranth says makes sense but if you want to run the animation again then it could become a pain. I would suggest a (only slightly) different approach.
    -- locals make things faster (although called only twice per frame, it shouldn't make much difference)
    local floor = math.floor
     
    -- modified onEnterFrame function
    function Frames:onEnterFrame()
    	-- Get the last frame displayed (if there is one)
    	local frame = self.frames[floor(self.frame)]
    	-- You can only remove the frame if it is a child of self so this should stop any errors
    	if frame and self:contains(frame) then self:removeChild(frame) end
     
    	-- Only add animation frames while the frame number is not the last one
    	if self.frame < self.nframes then
    		-- Increase the frame number
    		self.frame = self.frame + 0.1
    		-- Now add the frame to the scene 
    		self:addChild(self.frames[floor(self.frame)])
    	end
     end
    As you can see, this function will only increase the frame number while it is lower than self.nframes. The advantage of doing it this way is that you can re-start the animation just by setting the self.frame back to 1.

    I Haven't tested this code but reading through and commenting it makes it more understandable and you can see any obvious errors. There don't appear to be any :D

    Hope this helps.
  • Also - incrementing self.frame by 0.1 each "frame" will give you a fixed animation rate of 6 fps for your animations (assuming 60fps, each image for 10 frames), you might want to pass in some kind of time step as part of the init method so you can have control over the playback speed. But until you understand and are comfortable with the how you think and how the code actually works then it might be a step too far.
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • @Scouser , I like your approach much better for this one, I'm afraid I was committing the old sin of coding while drunk--of course I do it a lot so I guess that isn't a real issue usually lol--either way you have a far more elegant solution above and I would suggest that @TheCrypto uses it :)

    @techdojo makes a very important point as most of the time the device is using 60 fps, which makes it very cool to actually know the speed in which your animations will play on most devices--which when I first started playing with Gideros confused me a lot actually heh
    ThumbHurt Games / FB: ThumbHurt Games / FB: Eli/Teranth | Skype: teranth37
Sign In or Register to comment.