Attempt to call getLinearVelocity a nil value

In my ping pong game, I want it so that every time the ball goes off screen it rewards a point to the appropriate pong and then creates a new ball, but this is not working out. Here are the things that are going wrong:

  1. It does not count any points when the ball goes off screen.

  2. An Attempt to getLinearVelocity error appears pointing to line 151:

    local vx3, vy3 = ball:getLinearVelocity()

This is what I am using to add balls, give points, and remove balls: 

local function createBall() ball = display.newCircle(centerX, centerY, 20) physics.addBody(ball, "dynamic") ball:setLinearVelocity(math.random(-400, 400), math.random(-200, 200)) ball.id = "ball" print("ball created") end local function addScore() if (ball.x \< left - 10) then score2 = score2 + 1 score2.text = score2 end if (ball.x \< right + 10) then score1 = score1 + 1 score1.text = score1 end end local function removeBall() if (ball.x \< left - 10) or (ball.x \> right + 10) then addScore() createBall() Runtime:removeEventListener("enterFrame", removeBall) display.remove(ball) print("removed") end end

And here is what I use to detect collisions (globally). The error line is the first line in the function:

local function onCollision(event) local vx3, vy3 = ball:getLinearVelocity() if event.phase == "began" then if ((event.object1.id == "player" and event.object2.id == "ball") or (event.object1.id == "ball" and event.object2.id == "player")) then ball:setLinearVelocity(vx3 \* -1, vy3) end if ((event.object1.id == "enemy" and event.object2.id == "ball") or (event.object1.id == "ball" and event.object2.id == "enemy")) then ball:setLinearVelocity(vx3 \* -1, vy3) end if ((event.object1.id == "topBarrier" and event.object2.id == "ball") or (event.object1.id == "ball" and event.object2.id == "topBarrier")) then ball:setLinearVelocity(vx3, vy3 \* -1) end if ((event.object1.id == "bottomBarrier" and event.object2.id == "ball") or (event.object1.id == "ball" and event.object2.id == "bottomBarrier")) then ball:setLinearVelocity(vx3, vy3 \* -1) end end end

The onCollision function is running on a “collision” eventListener, and the removeBall function is on an enterFrame: 

Runtime:addEventListener("enterFrame", removeBall) Runtime:addEventListener("collision", onCollision)

Thank you to the community for the help so far.

1. I’m not sure why you’re using a global collision listener.  I’m not  fan of those as they are IMHO ambiguous and susceptible to error for new users.

(more on this in a moment)

2. The real problem here is implied/assumed scope.  You are referring to ‘ball’ in your collision listener, but there is no guarantee that variable is valid and in scope.

So, you should extract the object references from the collision event: https://docs.coronalabs.com/daily/api/event/collision/index.html#object-references

3. Back to listeners and types

This is a global listener:

local function onCollision(event) end Runtime:addEventListener( "collision", onCollision )

I much prefer local listeners for collision, touch, etc…  This is  because:

  1. The context is very clear.  
  2. Local listeners are automatically removed when you delete the object

This is an local  listener for collision:

function ball.collision( self, event ) -- Now, we KNOW that self is the ball in the context of this function end ball:addEventListener( "collision" )

In the meantime, It is awesome that you’re working on pong as a learning experience and I hope you’re getting a lot out of it.

-Ed

I see, I was thinking about using a local listener since it all revolved around one object, the ball. So, instead of ball and anotherObject, I would use event.selfElement and event.otherElement for a local collision. Is this correct?

No

1 -   Go look at your code and what I wrote.

You need to write your code like I did, not just look for different event fields.

Better yet, did you click the link I supplied and study the difference between the two listener samples there?

https://docs.coronalabs.com/daily/api/event/collision/index.html#object-references

2 - You would write your code in a simliar fashion to the code I wrote, and refer to  self and event.other.

I already said this in my code sample.  self is the ball and by implication, event.other is the thing it hit.  Forget about the other fields those are for advanced uses.

Also, would I refer to ball as self?

Ah, so instead of event.object1, it would be event.self. And for object2 it would event.other.

Note: I purposely did not give you the exact answer, because I want you to experiment.  But I realize maybe my answer was a little too ambigous.

The collision event (Local and Global) both have lots of arguments so I can see why there is some confusion.

Just to be clear you want:

  • local collision handler
  • self - the ball
  • event.other - the thing it hit

forget about all the other ‘object’ identifying fields.

I am sure that you now have all the info you need to solve the original problem you posted about.

NO.  not event.self.  Just self.  I think you still have not read that docs page I linked or it is confusing?
 
When it says in the docs, 
 
event.target or self
 
It means, that event.target == self
 
i.e. They are the same object and these are two different ways to refer to it.

This is for your convenience and to account for the fact that there is some commonality as well as variation in how events in general are coded and handled.
 
 

function ball.collision( self, event ) local phase = event.phase local other = event.other print(phase, self.x, self.y, other.x, other.y ) end ball:addEventListener( "collision" )

Note: Final answer. I have to get back to work.

One more question about this: I always put the parameters and then put them again in reverse like this:

if ((event.target.id == "player" and event.other.id == "ball") or (event.target.id == "ball" and event.other.id == "player")) then ball:setLinearVelocity(vx3 \* -1, vy3) end

Would I still need to do this with local collisions?

Grumble… grumble…  OK, this really is my last response as I do have to get back to work.

I see a number of issues with your code.  However these are all good things.  You’re exploring and you will learn by doing this.

The issues are:

  • Over-Complex - This is a side-effect of two things:
    1. You started by using a global event listener.  This messed you up.
    2. You’re new and trying to be exact  when you some things you are checking for are actually explicit in a local listener.
  • Next Stumble - Once you get past this ‘nil’ issue, you will find that the engine complains about you setting linear velocity during the collision listener.  You must wait one frame before doing any box2D work during a collision listener.  This is simply how the box2D physics engine works.
  • Manual Direction Changing - Your collision listener is effectively changing the direction of the ball manually.  This  is wrong.  My guess is, you are doing this because you tried to let physics handle the bouncing and found that you were losing velocity or the bounces seemed weird.  Anyways, this is a semi-advanced phenomenon and is related to how the box2D engine works and to your expectations.  I simply can’t address this here.

I will address the first two issues.

Over-Complex

Again.  Your code is super complicated and it doesn’t need to be.  Study this basic framework and I think you will see it is both clean and accounts for all the things you’re trying to resolve in your code:

function ball.collision( self, event ) local phase = event.phase local other = event.other.id if( phase == "began" ) then if( other == "player") then elseif( other == "enemy" ) then elseif( other == "topBarrier" ) then elseif( other == "bottomBarrier" ) then else print("WHAT?") end end return false end

Next Stumble

You are not allowed to make box2D changes in the middle of a collision event because the box2D engine is in the middle of processing this FRAME’s physics data.  Calling any box2D function would change the state of the engine in this moment and that is not allowed.

Fortunately, there is an easy way to resolve this.  Wait 1 millisecond.  This effectively defers your code till the beginning of the next frame and before the next physics processing loop.

timer.performWithDelay( 1, function() self:setLinearVelocity( -vx, vy ) end )

SSK2 To The Rescue?

I want you to know, I am glad you’re working on this, but you should know I’ve decided to help SSK2 owners out.

I will be adding a number of ‘fundamental’ game examples to SSK2 over the next couple of weeks and PONG will be one of them.

Note: I have not yet decided if this will be an SSK2 PRO only thing, or if some will also be available to lite users.

Thank you!

1. I’m not sure why you’re using a global collision listener.  I’m not  fan of those as they are IMHO ambiguous and susceptible to error for new users.

(more on this in a moment)

2. The real problem here is implied/assumed scope.  You are referring to ‘ball’ in your collision listener, but there is no guarantee that variable is valid and in scope.

So, you should extract the object references from the collision event: https://docs.coronalabs.com/daily/api/event/collision/index.html#object-references

3. Back to listeners and types

This is a global listener:

local function onCollision(event) end Runtime:addEventListener( "collision", onCollision )

I much prefer local listeners for collision, touch, etc…  This is  because:

  1. The context is very clear.  
  2. Local listeners are automatically removed when you delete the object

This is an local  listener for collision:

function ball.collision( self, event ) -- Now, we KNOW that self is the ball in the context of this function end ball:addEventListener( "collision" )

In the meantime, It is awesome that you’re working on pong as a learning experience and I hope you’re getting a lot out of it.

-Ed

I see, I was thinking about using a local listener since it all revolved around one object, the ball. So, instead of ball and anotherObject, I would use event.selfElement and event.otherElement for a local collision. Is this correct?

No

1 -   Go look at your code and what I wrote.

You need to write your code like I did, not just look for different event fields.

Better yet, did you click the link I supplied and study the difference between the two listener samples there?

https://docs.coronalabs.com/daily/api/event/collision/index.html#object-references

2 - You would write your code in a simliar fashion to the code I wrote, and refer to  self and event.other.

I already said this in my code sample.  self is the ball and by implication, event.other is the thing it hit.  Forget about the other fields those are for advanced uses.

Also, would I refer to ball as self?

Ah, so instead of event.object1, it would be event.self. And for object2 it would event.other.

Note: I purposely did not give you the exact answer, because I want you to experiment.  But I realize maybe my answer was a little too ambigous.

The collision event (Local and Global) both have lots of arguments so I can see why there is some confusion.

Just to be clear you want:

  • local collision handler
  • self - the ball
  • event.other - the thing it hit

forget about all the other ‘object’ identifying fields.

I am sure that you now have all the info you need to solve the original problem you posted about.

NO.  not event.self.  Just self.  I think you still have not read that docs page I linked or it is confusing?
 
When it says in the docs, 
 
event.target or self
 
It means, that event.target == self
 
i.e. They are the same object and these are two different ways to refer to it.

This is for your convenience and to account for the fact that there is some commonality as well as variation in how events in general are coded and handled.
 
 

function ball.collision( self, event ) local phase = event.phase local other = event.other print(phase, self.x, self.y, other.x, other.y ) end ball:addEventListener( "collision" )

Note: Final answer. I have to get back to work.