One by one addEventListener collision - sdk

I'm working on a simple breakout game and I've problem with ball:addEventListener( "collision", removeBricks ), it works fine until the ball hits two bricks at same time, than the up/down direction (vy) switch twice, making the ball continue moving up or down.
How can do one by one addEventListener collision and disable multiple collides at once?
function removeBricks(event)
if event.other.isBrick == 1 then
vy = vy * (-1)
...
end
end

Instead of changing ball's velocity in the removeBricks function, you can just flip a flag that means "a ball has hit some bricks and should change it's direction", and then in your enterFrame handler just change the ball's speed:
local ballHasCollided = false
local function removeBricks(event)
if event.other.isBrick == 1 then
ballHasCollided = true
end
end
local function updateBallVelocity(event)
if ballHasCollided then
ballHasCollided = false
ball.vy = -ball.vy
-- ...
end
-- your game set up code somewhere
Runtime:addEventListener('enterFrame', updateBallVelocity)

Related

How can I handle collisions of multiple blocks as if they were a single entity?

I'm creating a 2D platform game using Corona SDK and I'm stuck with collisions.
Basically there is a character that runs over this ground made of blocks. This is because sometimes there can be holes in the ground.
The game is an endless one, so as the character moves forward new blocks (and holes) are dynamically added - and also removed if they goes off screen.
It works nicely but this approach works against the collision system, let me explain how.
Now that I have the ground in place I want the character to jump but only if it is touching the ground, to avoid jumping while in air.
Whenever a collision is detected between character and ground an event is fired - two times. The first time when the character is entering a ground block and the second time when the character leaves it. So when the character lands on the ground a isGround Boolean is set to true. And when - after a jump - it leaves it the flag is set to false. The problem is that every time it exits a block to enter another - walking along the ground without jumping - the flag get updated. This makes the jump based on the isGround flag less reliable. Sometimes it happens that you can't jump because isGround == false though the character is on the ground.
Ground block creation snippet
-- init() method set the sprite of the ground block and physic to that sprite
function GroundBlock:init()
self.sprite = display.newImageRect(self.path, self.width, self.height)
self.sprite.x = self.x
self.sprite.y = self.y
physics.addBody(self.sprite, 'static', {
density = 0,
friction = 0,
bounce = 0,
box = {
halfWidth = self.width / 2,
halfHeight = self.height / 2,
y = 16,
x = 0
}
})
local collisionObj = {
name = 'ground'
}
self._collision = collisionObj
self.sprite._collision = collisionObj
self.isShow = true
end
Ground placing GroundBlocks snippet
-- init() method initialize the ground with a fixed number of blocks
function Ground:init()
self.offsetX = 0
while self.offsetX < self.camera.borderRight * 2 do
self._createBlock(1)
end
self.lastCameraPos = self.camera.borderRight
end
-- update() is called once per frame
function Ground:update()
if (self.camera.borderRight - self.lastCameraPos > self._blockWidth) then
local rand = math.ceil(math.random() * 10) % 2
if self._skippedBlock >= 2 or rand == 0 then
self._createBlock(1)
self._skippedBlock = 0
else
self._createBlock(0)
self._skippedBlock = self._skippedBlock + 1
end
self.lastCameraPos = self.camera.borderRight
end
for i, block in ipairs(self.blocks) do
if block.sprite.x < self.camera.borderLeft - block.width then
table.remove(self.blocks, i)
self.camera:remove(block.sprite)
block:delete()
end
end
end
Collision detection snippet
function Character:collision(event)
if ( event.phase == "began" ) then
if event.other._collision.name == "ground" then
self.isGround = true
end
elseif ( event.phase == "ended" ) then
if event.other._collision.name == "ground" then
self.isGround = false
print('nope')
end
end
end
A solution would be to make a ground as a single imgRect but how to make holes in it?
You could simplify your code and prevent this issue from ever occurring by tracking if the character can jump instead of tracking if the character is on the ground.
For instance,
function jump( event )
if event.phase == "began" then
if canJump then
canJump = false
-- your code that makes the player jump
end
end
end
You probably use touches to determine whether the player character jumps, right? This way, you'll trigger the jump when the touch starts as long as the character has not already jumped.
You could then reset this value in your collision function by editing it slightly:
function Character:collision(event)
if event.phase == "began" then
if event.other._collision.name == "ground" then
canJump = true
end
end
end
This way, the character's ability to jump is determined by whether or not the player has pressed jump already and if the character has hit the ground since the last jump.
This kind of approach also gives you the ability to pivot towards implementing mechanics like double jump. If instead of using a boolean canJump variable you chose to use a number variable, e.g. jumpsLeft, you could reduce the number of jumps left every time the character jumps and only let the character jump if jumpsLeft is larger than 0. Then you'd simply reset the value back to 1 (or whatever you'd want upon hitting the ground).

Object keeps accelerating as it falls

I am a noob to this but anyway. I am writing an app in which you must tap in order to get an object up but at some point no matter how fast you tap the object keeps going down. it's like the gravity is getting stronger and stronger.
EDIT: I don't know what code samples to give because i don't know if any of it makes sense, but i will try:
this is how i created the object:
super=display.newImage("mine.png")
super:setReferencePoint(display.BottomLeftReferencePoint)
super.y=display.contentHeight+70
super.x=display.contentWidth/2.3
super.gravityscale=1
superIntro = transition.to(super,{time=2000, y=display.contentHeight/1.2, onComplete= supergo})
physics.addBody(super, "dynamic", {density=0, bounce=0, friction=0, radius=12})`
And this is a part of my script that i think it is relevant: `
function scrollCity(self,event)
print("touch")
if self.y > 930 then
self.y = 0
else
self.y=self.y+scrollspeed*6
end
end
function activateJets(self,event)
self.y=self.y-scrollspeed*6
print("run")
end
function touchScreen(event)
print("p")
if event.phase == "began" then
if super.y<display.contentHeight/1.2+6 then
super.y=display.contentHeight/1.2
background1.enterFrame = scrollCity
Runtime:addEventListener("enterFrame", background1)
background2.enterFrame = scrollCity
Runtime:addEventListener("enterFrame", background2)
else
super.enterFrame = activateJets
Runtime:addEventListener("enterFrame", super)
end
end
if event.phase == "ended" then
Runtime:removeEventListener("enterFrame", super)
Runtime:removeEventListener("enterFrame", background1)
Runtime:removeEventListener("enterFrame", background2)
end
end
EDIT2: The gravity is set:
physics.setGravity( 0, 1.5 )
In the beginning a few taps are enough to keep it on the screen, but after a few seconds it is impossible to maintain and it just falls. I want it to go down with the same speed not to accelerate.
The reason it keeps accelerating is because gravity applies a constant force. Anything affected by gravity undergoes acceleration, not a fixed velocity.
For constant speed motion
Cancel the effect of gravity by setting gravityscale to 0 or setting global gravity to 0 - whichever is more appropriate for you - then setting the velocity of super using super:setLinearVelocity( xVelocity, yVelocity ).
For motion with acceleration
If you prefer an accelerating gravity then the approach you are using now is fine but you might consider super:setLinearVelocity(0, 0) when the tap occurs so that the object is falling from rest rather than continuing to fall quickly.

Can someone test my Corona project and identify the issue with my jump function?

I'm all very new to this and everything in this project is just placeholders and scrap work. Some of you may recognize some of the graphics used from other Corona tutorials, but it's to help me get better, so don't be too judgemental. Here's a link to the download for the Corona Simulator: http://www.mediafire.com/download/6a78bsewgwsiyp2/GooMan.rar
Anyways, here's my issue. The jump button seems fine at first. If I hold it down, the character constantly jumps. And if I let go, he stops. If I simultaneously hold down jump while pushing one of the arrow buttons, he'll jump in that direction. However, it seems as though if I jump once, then I hit the jump button again RIGHT as the character is making contact with the ground, that he won't jump. There's a sudden lack of responsiveness. I literally have to pause slightly and wait for the character to fully hit the ground before I can make another successful jump. Why?
And here's all the relevant code for you to look at:
I have this at the beginning to help define my goo character's position:
local spriteInAir = false
local yspeed = 0
local oldypos = 0
local holding = false
I then created the jump button.
local bJump = display.newImage("Images/jumpbutton.png")
bJump.x = 445
bJump.y = 265
bJump.xScale = 0.78
bJump.yScale = 0.78
Followed up by creating an enterFrame Runtime Event which will update my goo character's position in the Y. Running the game at 60fps if that's relevant.
function checkSpeed()
yspeed = sprite.y - oldypos
oldypos = sprite.y
if yspeed == 0 then
spriteInAir = false
else
spriteInAir = true
end
end
Runtime:addEventListener( "enterFrame", checkSpeed )
Then the meat of it all. Created a function called hold which tells the game to not only make my goo character jump, but to keep my goo character constantly jumping as long as the bJump button is held down. Works perfectly. The "jumping" function is a touch event that listens for the hold function, and all of it is executed by the bJump button listening to the jumping function.
local function hold()
if holding and spriteInAir == false then
sprite:applyForce( 0, -8, sprite.x, sprite.y )
sprite:setLinearVelocity(0, -350)
spriteInAir = true
return true
end
end
local function jumping( event )
if event.phase == "began" then
display.getCurrentStage():setFocus( event.target )
event.target.isFocus = true
event.target.alpha = 0.6
Runtime:addEventListener( "enterFrame", hold )
holding = true
elseif event.target.isFocus then
if event.phase == "moved" then
elseif event.phase == "ended" then
holding = false
event.target.alpha = 1
Runtime:removeEventListener( "enterFrame", hold )
display.getCurrentStage():setFocus( nil )
event.target.isFocus = false
spriteInAir = false
return true
end
end
return true
end
bJump:addEventListener("touch",jumping)
Anyone who can help me identify this problem, I'd greatly appreciate it!
You're using a velocity check to detect if the character is on the ground. It can take a short while to set it back to zero after colliding with Box2D, so the better way to do it is to use a collision sensor:
sprite.grounded = 0 -- Use a number to detect if the sprite is on the ground; a Boolean fails if the sprite hits two ground tiles at once
function sprite:collision(event)
if "began" == event.phase then
-- If the other object is a ground object and the character is above it...
if event.other.isGround and self.contentBounds.yMax < event.other.contentBounds.yMin + 5 then
sprite.grounded = sprite.grounded + 1 -- ...register a ground collision
end
elseif "ended" == event.phase then
-- If the other object is a ground object and the character is above it...
if event.other.isGround and self.contentBounds.yMax < event.other.contentBounds.yMin + 5 then
sprite.grounded = sprite.grounded - 1 -- ...unregister a ground collision
end
end
end
sprite:addEventListener("collision")
Then, in your jump function, just check to see if sprite.grounded > 0. If it is, the player is grounded.

Corona SDK arrow to show direction of ball?

I'm new to programming, and I wonder if you can help me out with this problem. I created a simple basketball game, where you pull back then release to fire the ball. I want to know how to use an image of an arrow to show the direction the ball will travel(It doesn't have to be the exact trajectory, just a straight arrow). Also, i want the yScale of the arrow to show how much linear impulse will be applied.Thanks in advance!
here is my function for shooting the ball
local function ballTouched(event)
if ball.x == 100 and ball.y == 100 then
if event.phase == "began" then
display.getCurrentStage():setFocus(ball)
arrow.alpha=1
elseif event.phase == "ended" then
physics.start()
ball:applyLinearImpulse((event.xStart - event.x)/2, (event.yStart - event.y)/2, ball.x, ball.y)
display.getCurrentStage():setFocus(nil)
arrow.alpha=0
man2.alpha=1
man.alpha=0
end
end
end
Runtime:addEventListener("touch", ballTouched)
You should know the angle you are pulling back. Just set the .rotation value of your arrow to the same angle.
arrow.rotation = aimAngle
To follow up to the comments below:
I'm assuming you are pulling the basketball back kind of like AngryBirds slingshot or you have to be touching and dragging somehow to have an angle. You can use some simple angle math where you know the length of two sides of a right triangle and you can use the arctangent function to compute the angle.
local function angleBetween( srcX, srcY, dstX, dstY )
local angle = ( math_deg( math_atan2( dstY-srcY, dstX-srcX ) )+90 )
--if ( angle < 0 ) then angle = angle + 360 end
return angle%360
end
where srcX and srcY would be the position (or start position of the basketball if you're dragging it) and dstX, dstY would be the event.x, event.y of where you let up during the ended phase. The event table has a xStart and yStart members that kept track of where the touch event started, so maybe
aimAngle = angleBetween(event.xStart, event.yStart, event.x, event.y)
You may need to play with some of these numbers to get them lined up the way you want them (i.e. you may need to add 180 or something)

Adding table as a EventListener

How can I add tables as a EventListener?
I'm working on a breakout game as a hello-world project and i would like to add the effect of "double ball". so basically i want to add balls to balls table then check if one of the balls hit the brick
my code works with
balls["ball"]:addEventListener( "collision", removeBricks )
but if i try the following:
balls:addEventListener( "collision", removeBricks )
i'm getting Runtime error ...\main.lua:753: attempt to call method 'addEventListener' (a nil value)
stack traceback:
what i've tried:
local balls = {}
balls["ball"] = crackSheet:grabSprite("ball_normal.png", true)
balls["ball"].name = "ball"
function removeBricks(event)
if event.other.isBrick == 1 then
remove brick...
end
end
balls.collision = removeBricks
balls:addEventListener( "collision", removeBricks )
You can't add event listener to a table. If you want to check bricks vs. ball collisions you should add event listeners to every ball or every brick
you can try creating each instance of a ball instead of using a table and then try to add collision eventlistener on every ball try to look at the code
local Table = {}
local function generateBall(event)
if "ended" == event.phase then
local ball = crackSheet:grabSprite("ball_normal.png", true)
ball.name = "ball"
local function removeBricks(event)
if "ended" == event.phase then
if event.other.isBrick == 1 then
remove brick...
end
end
end
ball:EventListener("collision", removeBricks)
table.insert(Table, ball)
end
end
Runtime:EventListener("touch",generateBall)
this way you can have different listener on every ball
If you want to add balls in your table you can insert objects in table
local ballsTable = {}
function addBall()
local ball = crackSheet:grabSprite("ball_normal.png", true)
ball.name = "ball"
ball.collision = function(self, event)
if event.other.isBrick == 1 then
event.other:removeSelf()
end
end
ball:addEventListener( "collision" )
table.insert(ballsTable, ball)
end

Resources