Not able to understand what's going

Hello everyone,

When I press back key the Runtime:enterFrame should stop and on pressing the back key again after 1 sec the Runtime:enterFrame should start, but in that one sec time if I keep on tapping back key again and again something weird starts happening the object start to move fast and fast, the below is my code

coal=0 local ball=display.newCircle( 100, 100, 50 ) function move( ) ball.x=ball.x+1 end Runtime:addEventListener("enterFrame",move) local function onKeyEvent( event ) if ( event.keyName == "back" ) then if ( system.getInfo("platform") == "android" ) then if event.phase=="up" then coal=coal+1 if coal%2~=0 then Runtime:removeEventListener("enterFrame",move) else timer.performWithDelay(1000,function() Runtime:addEventListener( "enterFrame", move ) end,1) end end return true end end end Runtime:addEventListener( "key", onKeyEvent )

Help from everyone is welcome.

Thanks and Regards,

Swanand Thakur.

The timer thing is playing a trick on you, they need to be handled with a variable and if called prematurely, cancelled, or you’ll end up with multiple calls.

Something like this:

coal=0 local ball=display.newCircle( 100, 100, 50 ) function move( ) ball.x=ball.x+1 end Runtime:addEventListener("enterFrame",move) local myTimer local function onKeyEvent( event ) if ( event.keyName == "back" ) then if ( system.getInfo("platform") == "android" ) then if event.phase=="up" then coal=coal+1 if coal%2~=0 then Runtime:removeEventListener("enterFrame",move) else if myTimer then timer.cancel(myTimer) end myTimer=timer.performWithDelay(1000,function() Runtime:addEventListener( "enterFrame", move ) end,1) end end return true end end end Runtime:addEventListener( "key", onKeyEvent )

This will prevent it from getting called multiple times.

Hope it helps and have a great day!

anaqim

Hello brother @Anaqim,

Your solution is working well, but only in case if I choose delay time equivalent to 1 sec or greater than it if I choose time equal to 500ms then its malfunctioning as before.

Regards,

Swanand Thakur.

May contain typos:

local coal = 0 -- local ball = display.newCircle( 100, 100, 50 ) -- ball.finalize = function( self ) if( self.\_lastTimer ) then timer.cancel( self.\_lastTimer ) self.\_lastTimer = nil end if( self.enterFrame ) then Runtime:removeEventListener( "enterFrame", self ) self.enterFrame = nil end end; ball:addEventListener( "finalize" ) -- local function ballMover( self ) self.x = self.x + 1 end -- ball.enterFrame = ballMover Runtime:addEventListener( "enterFrame", ball ) -- ball.timer = function( self ) self.enterFrame = ballMover Runtime:addEventListener( "enterFrame", self ) self.\_lastTimer = nil end -- ball.key = function( self, event ) if ( event.keyName == "back" ) then if ( system.getInfo("platform") == "android" ) then if event.phase=="up" then coal = coal + 1 if coal % 2 ~= 0 then Runtime:removeEventListener( "enterFrame", self ) self.enterFrame = nil elseif( self.\_lastTimer == nil ) then self.\_lastTimer = timer.performWithDelay( 1000, self ) end end return true end end end Runtime:addEventListener( "key", ball)

I worry, that this entire construct  is over-complicated and therefore subject to error.

Can you describe in a couple of short sentences of a bullet list what you want to do here? i.e. What is the over-arching mechanic?

Updated my code post to prevent multiple ongoing timers.

Again, this whole thing is way too complicated.  Tell us what you want to achieve and I’ll see if there is a simpler option.

self.enterFrame = nil

it is possible to reference events directly from objects like that?

it should be in the documentation, its pretty vital stuff

would that mean its possible to say:

if self.enterFrame then …

then?

  1. It is documented.  They are called table listeners:  

https://docs.coronalabs.com/guide/events/detectEvents/index.html#function-and-table-listeners

 
 
2. What I’m doing is clearing the variable that points to the function as a bookkeeping thing for my code.
 
Elsewhere I check to be sure obj.enterFrame is present before removing the listener.
 
If I don’t do this, it will crash.
 
Ex: This would crash:
 

function obj.enterFrame(self) end Runtime:addEventListener( "enterFrame", obj ) local function cancelIt() Runtime:removeEventListener( "enterFrame", obj ) obj.enterFrame = nil end cancelIt() ... later cancelIt()-- CRASH

This logic prevents the issue:
 

function obj.enterFrame(self) end Runtime:addEventListener( "enterFrame", obj ) local function cancelIt() if( not obj.enterFrame ) then return end Runtime:removeEventListener( "enterFrame", obj ) obj.enterFrame = nil end cancelIt() ... later cancelIt()

@anaqim,

Here are the docs on table listeners:
https://docs.coronalabs.com/guide/events/detectEvents/index.html#function-and-table-listeners

@swan…,

Correct me if I’m wrong but essentially this is what you want:

  • Track coal as counter
  • A ‘ball’ that moves automatically from left to right, except in certain cases (below)
  • Allow the user to click the ‘back’ button to increment coal counter
  • When the coal counter is incremented and found to be a multiple of 2, you want to stop the ball from moving for one second.
  • At the end of a one-second stop you want the ball to resume moving.

If that is correct, this is how I would do it:

local getTimer = system.getTimer local isAndroid = (system.getInfo("platform") == "android") local coal = 0 local pauseDuration = 1000 local ball = display.newCircle( 100, 100, 50 ) ball.\_waitTill = getTimer() - pauseDuration -- Ensure it can move immediately function ball.finalize( self ) Runtime:removeEventListener( "enterFrame", self ) end; ball:addEventListener( "finalize" ) function ball.enterFrame( self ) if( getTimer() \< self.\_waitTill ) then return end -- self.x = self.x + 1 end;Runtime:addEventListener( "enterFrame", ball ) if( isAndroid ) then ball.key = function( self, event ) if( event.keyName == "back" and event.phase=="up" ) then coal = coal + 1 if( coal % 2 ~= 0 ) then self.\_waitTill = getTimer() + pauseDuration end end return true end; Runtime:addEventListener( "key", ball ) end

Thanks for the info RG

I’ll be studying it  :slight_smile:

Sir @roaminggamer,

Your first three steps are correct I am correcting your 4th step

  • If counter is a multiple of two, ball should resume after one second and if its not a multiple of two then it should stop.
  • On the first click it should stop and on the second click it should start moving after one second.

https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2018/04/coalBall.zip

local getTimer = system.getTimer local isAndroid = (system.getInfo("platform") == "android") local debugEn = true local coal = 0 local pauseDuration = 1000 local ball = display.newCircle( 100, 100, 50 ) ball.\_waitTill = getTimer() - pauseDuration -- Ensure it can move immediately ball.isMoving = true ball.allowResume = false function ball.finalize( self ) Runtime:removeEventListener( "enterFrame", self ) end; ball:addEventListener( "finalize" ) function ball.enterFrame( self ) print( self.isMoving, self.allowResume ) if( self.isMoving == false ) then if( self.allowResume == false ) then return false end if( getTimer() \< self.\_waitTill ) then return false end end -- self.x = self.x + 1 end; Runtime:addEventListener("enterFrame", ball) local coalLabel = display.newText( coal, 300, 300 ) if( isAndroid or debugEn ) then ball.key = function( self, event ) for k,v in pairs(event) do print(k,v) end if( event.phase=="up" and ( event.keyName == "back" or event.keyName == "space") ) then coal = coal + 1 coalLabel.text = coal .. " : " .. tostring(coal % 2) if( coal % 2 == 0 ) then self.isMoving = false self.allowResume = true self.\_waitTill = getTimer() + pauseDuration else self.isMoving = false self.allowResume = false end end return true end; Runtime:addEventListener( "key", ball ) end

Sir @roaminggamer,

Lots of thanks for this solution, it worked well. It has also taught some really helpful concepts which will surely help me in future. Thanks once again.

Regards,

Swanand Thakur

The timer thing is playing a trick on you, they need to be handled with a variable and if called prematurely, cancelled, or you’ll end up with multiple calls.

Something like this:

coal=0 local ball=display.newCircle( 100, 100, 50 ) function move( ) ball.x=ball.x+1 end Runtime:addEventListener("enterFrame",move) local myTimer local function onKeyEvent( event ) if ( event.keyName == "back" ) then if ( system.getInfo("platform") == "android" ) then if event.phase=="up" then coal=coal+1 if coal%2~=0 then Runtime:removeEventListener("enterFrame",move) else if myTimer then timer.cancel(myTimer) end myTimer=timer.performWithDelay(1000,function() Runtime:addEventListener( "enterFrame", move ) end,1) end end return true end end end Runtime:addEventListener( "key", onKeyEvent )

This will prevent it from getting called multiple times.

Hope it helps and have a great day!

anaqim

Hello brother @Anaqim,

Your solution is working well, but only in case if I choose delay time equivalent to 1 sec or greater than it if I choose time equal to 500ms then its malfunctioning as before.

Regards,

Swanand Thakur.

May contain typos:

local coal = 0 -- local ball = display.newCircle( 100, 100, 50 ) -- ball.finalize = function( self ) if( self.\_lastTimer ) then timer.cancel( self.\_lastTimer ) self.\_lastTimer = nil end if( self.enterFrame ) then Runtime:removeEventListener( "enterFrame", self ) self.enterFrame = nil end end; ball:addEventListener( "finalize" ) -- local function ballMover( self ) self.x = self.x + 1 end -- ball.enterFrame = ballMover Runtime:addEventListener( "enterFrame", ball ) -- ball.timer = function( self ) self.enterFrame = ballMover Runtime:addEventListener( "enterFrame", self ) self.\_lastTimer = nil end -- ball.key = function( self, event ) if ( event.keyName == "back" ) then if ( system.getInfo("platform") == "android" ) then if event.phase=="up" then coal = coal + 1 if coal % 2 ~= 0 then Runtime:removeEventListener( "enterFrame", self ) self.enterFrame = nil elseif( self.\_lastTimer == nil ) then self.\_lastTimer = timer.performWithDelay( 1000, self ) end end return true end end end Runtime:addEventListener( "key", ball)

I worry, that this entire construct  is over-complicated and therefore subject to error.

Can you describe in a couple of short sentences of a bullet list what you want to do here? i.e. What is the over-arching mechanic?

Updated my code post to prevent multiple ongoing timers.

Again, this whole thing is way too complicated.  Tell us what you want to achieve and I’ll see if there is a simpler option.

self.enterFrame = nil

it is possible to reference events directly from objects like that?

it should be in the documentation, its pretty vital stuff

would that mean its possible to say:

if self.enterFrame then …

then?