Angry Bunny Slingshot... collision detection between projectile and flying objects not working!

Hello fellow Corona Developers!

I have been having some major issues with implementing the “Angry Bunny” slingshot example by FIXDIT Ltd. (Thank you for sharing your code! :).

Anyways, I have the slingshot part to quite well. However, when I try to make the projectile to trigger and event when it hits a dynamic object flying from the bottom of the screen, it works rather strangely. What I mean is that I have noticed that most of the times the projectile goes THROUGH the objects that’s supposed to hit and create the event (collision). In other cases, the projectile, according to some print I have included within the collision function, hits the flying dynamic objects, but at a rather strange, x,y location: the collision happens at a different X and Y location on the screen.

Here’s the projectile.lua file, from the code example by FIXDIT Ltd.

  
 --   
-- Abstract: Part of a tutorial documenting how to put together an 'Angry Birds'/'Hot Cross Bunnies' elastic catapult in CoronaSDK.  
-- Visit: http://www.fixdit.com regarding support and more information on this tutorial.  
-- Hot Cross Bunnies is now available in the iTunes App Store: http://itunes.apple.com/app/hot-cross-bunnies/id432734772?mt=8  
--  
-- Version: 1.0  
--   
-- Copyright (C) 2011 FIXDIT Ltd. All Rights Reserved.  
--  
-- Permission is hereby granted, free of charge, to any person obtaining a copy of   
-- this software and associated documentation files (the "Software"), to deal in the   
-- Software without restriction, including without limitation the rights to use, copy,   
-- modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,   
-- and to permit persons to whom the Software is furnished to do so, subject to the   
-- following conditions:  
--   
-- The above copyright notice and this permission notice shall be included in all copies   
-- or substantial portions of the Software.  
--   
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   
-- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR   
-- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE   
-- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR   
-- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER   
-- DEALINGS IN THE SOFTWARE.  
  
-- main.lua (Part of a tutorial documenting how to put together an 'Angry Birds'/'Hot Cross Bunnies' elastic catapult in CoronaSDK.)  
  
module(..., package.seeall)  
  
 \_W = display.contentWidth  
 \_H = display.contentHeight  
-- Pass state reference  
state = {};  
-- Bullet starts off in-active  
ready = false;  
-- Pass audio references  
shot = {};  
band\_stretch = {};  
pop = {}  
  
local tin\_sound = audio.loadSound("tin\_sound.mp3")  
  
local playTinSound = function()  
 audio.play(tinsound)  
 end  
  
 function newProjectile()  
  
 -- Import easing plugin  
 local easingx = require("easing");  
  
 -- Bullet properties  
 local bun\_bullet = {  
  
 name = "fruta",  
 type = "bullet",  
 density=0.15,  
 friction=0.2,  
 bounce=0.5,  
 size = 26,  
 rotation = 1  
  
 }  
  
 -- Init bullet  
 local bullet = display.newImageRect("" .. bun\_bullet.name .. ".png", 40,40);  
 -- Place bullet  
 bullet.x = 149; bullet.y = \_H + 123 ;  
  
 bullet.myName = "bullet"  
 bullet.hit = "bullet"  
 bullet.type = "bullet"  
 bullet.isBullet = true  
  
 -- Set up physical properties   
 physics.addBody(bullet, "kinematic", {density=bun\_bullet.density, friction=bun\_bullet.friction, bounce=bun\_bullet.bounce, radius=bun\_bullet.size});  
  
 bullet.linearDamping = 0.3;  
 bullet.angularDamping = 0.2;  
 bullet.isBullet = true;  
 bullet.isSensor = true;  
  
-- add collision here to see..  
 --OnCollision With floor  
 local function onBulletCollision(self, event)   
 bullet.bodyType = "dynamic"  
 if (event.other.hit == "obstacle") then  
 audio.play(tin\_sound)  
 print("Hit BARREL")  
 end  
 if ( event.other.hit == "vase" ) then  
 audio.play(tin\_sound)  
 print("BULLET!")  
 self:removeSelf()  
 return true  
  
 end  
  
 end  
 bullet.collision = onBulletCollision  
 bullet:addEventListener( "collision" , bullet )  
  
 -- Transition the bullet into position each time it's spawned   
 transition.to(bullet, {time=900, y=\_H - 140, transition = easingx.easeOutElastic});  
  
 return bullet;  
end  
  

And here’s the targetVase.lua file which creates a vase object that I later use to spawn flying vases (IGNORE FUNCTION checkThreshold():

module(..., package.seeall);  
  
 \_W = display.contentWidth  
 \_H = display.contentHeight  
--Thresholds fed from game.lua  
thresholds = {};  
  
--Pass the state machine reference  
state = {};  
  
--Helper function for comparing balloon height (obj1) to threshold (obj2)  
local function checkThreshold(obj1,obj2)  
 if(obj1 and obj2) then  
 if(obj1.y \< obj2[1].boundary and obj1.y \> obj2[2].boundary) then  
  
 --Fire a method once, if you want  
 if(obj1.multiplier == nil) then  
 --Do something  
 end  
  
 --In case you want to see the threshold change in the terminal window  
 --print(obj2[1].multiplier .. ": thresh 1");  
 obj1.multiplier = obj2[1].multiplier;  
  
 elseif(obj1.y \< obj2[2].boundary and obj1.y \> obj2[3].boundary) then  
  
 --Fire a method once, if you want  
 if(obj1.multiplier == obj2[1].multiplier) then  
 obj1:flash();  
 end  
  
 --In case you want to see the threshold change in the terminal window  
 --print(obj2[2].multiplier .. ": thresh 2");  
 obj1.multiplier = obj2[2].multiplier;  
 elseif(obj1.y \< obj2[3].boundary and obj1.y \> -10) then  
  
 --Fire a method once, if you want  
 if(obj1.multiplier == obj2[2].multiplier) then  
 obj1:flash();  
 end  
  
 --In case you want to see the threshold change in the terminal window  
 --print(obj2[3].multiplier .. ": thresh 3");  
 obj1.multiplier = obj2[3].multiplier;  
 elseif(obj1.y \< -10) then  
 if(obj1.timer) then  
 obj1:pop();  
 end  
 end  
 end  
end  
  
function newVase(velocity)  
  
 local vase = display.newImageRect("vase.png", 45, 50)  
  
 vase.x = math.random(\_W \* 0.5 + 139, \_W\*0.5 + 149)  
 vase.y = 400  
 vase.rotation = -90  
 physics.addBody(vase, { radius = 25});  
 --physics.addBody(cherries, {density=1.2, bounce = 0, radius = 5})  
 vase.type = "vase"  
 vase.hit = false  
 vase.multiplier = nil  
 vase.points = 1  
 vase.myName = "vase"  
  
--[[local balloon = display.newCircle(0, 0, 40);  
 balloon:setFillColor(m.random(1,255), m.random(1,255), m.random(1,255));  
balloon.x = \_W/2; balloon.y = \_H+20;  
  
 --Set up some custom properties  
 balloon.type = "balloon";  
 balloon.hit = false;  
 balloon.multiplier = nil;  
balloon.points = 1;--]]  
  
 --Set up properties to store transition.to's  
 vase.tween = {start={},finish={}};  
 function vase:flash()  
 local xS = self.xScale;  
 local yS = self.yScale;  
 local factor = 0.3  
  
 local function tweenBack()  
 transition.to(self, {time=150, xScale = 1, yScale = 1})  
 end  
 self.tween.finish = tweenBack;  
 self.tween.start = transition.to(self, {time=100, xScale = xS + factor, yScale = yS + factor, onComplete = self.tween.finish});  
  
 end  
  
  
  
  
 -- Collision Detection for the bullet  
 function vase:collision(e)   
 if(e.phase == "began") then  
  
 if (e.other.type == "bullet") then  
  
 print("Hit Vase!!")  
 audio.play(tin\_sound)  
 -- self: removeSelf()  
  
 local function collide()  
 --do points and stuff here..  
  
 end  
 local tmr = timer.performWithDelay(1, collide, 1)  
  
 elseif(e.other.type == "rightWall") then  
 print("RIGHT WALL")   
 end  
 elseif(e.phase == "ended") then  
  
 --if bullet collides with vase  
 if (e.other.type == "vase") then  
  
 local function collide()  
 --e.other.  
 end  
  
 elseif (e.other.type =="floor") then  
 local function collide()  
 self:removeSelf()  
 self = nil  
 end  
 local tmr = timer.performWithDelay(1,collide, 1)  
 end  
 end  
  
 end --end of bullet function collision  
  
 function vase:pop()  
  
 --Tell the state machine that a pop has happened  
 state:dispatchEvent({name="change", state="pop"})  
  
 --Cancel the object's timer  
 if(self.timer) then  
 timer.cancel(self.timer);  
 self.timer = nil;  
 end  
  
 --immediately hide the object;  
 self.isVisible = false  
  
 if(self.tween.start) then  
 self.tween.start = nil;  
 end  
  
 if(self.tween.finish) then  
 self.tween.finish = nil;  
 end  
  
 self.tween = nil;  
  
 --Finally, remove the object  
 self:removeSelf();  
 self = nil;  
 collectgarbage("collect");  
 end  
  
 --Start checking the y threshold continuously using the helper function defined earlier  
 vase.timer = timer.performWithDelay(20, function(e)  
 checkThreshold(balloon, thresholds);  
 end, -1)  
  
 physics.addBody(vase, "kinematic", {density=0, bounce=0, friction=0, radius=40});  
 vase:setLinearVelocity(0,velocity \* -2.5);  
  
 return vase;  
  
end  

And here’s PART of main.lua file where I call all other files for instantiating the vase and slingshot etc…

--------------------------  
 --THE HEART OF SLINGSHOT-  
 --------------------------  
 -- BULLET COLLISION  
--SPAWN CHERRIES UPWARDS--  
local spawnCherries = function()  
 physics.setGravity(0, - 0.6);  
 --cherries = display.newImage("cherries.png", math.random(206), 406);  
 cherries = display.newImageRect("vase.png", 45, 50)  
 -- cherries = display.newImageRect("cherries.png", 40,40)  
 -- cherries.x = math.random( 12, 50)  
 cherries.x = math.random(\_W \* 0.5 + 139, \_W\*0.5 + 149)  
 cherries.y = 400  
 cherries.rotation = -90  
 physics.addBody(cherries, { radius = 25});  
 --physics.addBody(cherries, {density=1.2, bounce = 0, radius = 5})  
 cherries.hit = "cherries"  
 cherries.myName = "cherries"  
  
local function splashPoints300(object)   
 gushCircle = display.newImageRect ( "points\_300.png",64, 64 )  
 gushCircle.x = \_W\*0.5 + 200  
 gushCircle.y = \_H\*0.5 + 12  
  
 function transtion1( event )  
 trans = transition.to(gushCircle, { tim = 1000, alpha = 0.8, onComplete=finishFunction1})  
 end  
  
 function finishFunction1( event )  
 trans = transition.to(gushCircle, { time = 10100, alpha = 0, onComplete=transition1})  
 end  
  
 transition.to(gushCircle, { time = 1600, y = gushCircle.y - 110, x = gushCircle.x +20, alpha = 0,onComplete= transition1} )  
  
end  
  
 local function splashAnimation800(object)  
  
 audio.play(tin\_sound)  
 print("Vase Hit from splash!")  
 gushCircle = display.newImageRect ( "grapes\_splash.png",64, 64 )  
 gushCircle.x = \_W\*0.5 + 200  
 gushCircle.y = \_H\*0.5 + 59  
  
 function transtion1( event )  
 trans = transition.to(gushCircle, { time = 1000, alpha = 0.8, onComplete=finishFunction1})  
 end  
  
 function finishFunction1( event )  
 trans = transition.to(gushCircle, { time = 10100, alpha = 0, onComplete=transition1})  
 end  
  
 transition.to(gushCircle, { time = 1600, y = gushCircle.y - 110, x = gushCircle.x + 24, alpha = 0,onComplete= transition1} )   
  
  
  
end  
  
 --onCollision With barrel  
 local function onObstacleCollision(self, event)   
  
 if ( event.other.hit == "obstacle" ) then  
 --audio.play(tin\_sound)  
 --splashAnimationGrapes()  
 print("HIT OBSTACLE!")  
 splashAnimation800()  
 splashPoints300()  
 --print("Vase Hit!")  
 self:removeSelf()  
 return true  
 elseif ( event.other.hit =="rightWall") then  
 print("HIT WALL!")  
  
  
 end  
  
 end  
 obstacle.collision = onObstacleCollision  
 obstacle:addEventListener( "collision" , obstacle )  
  
 --OnCollision With floor  
 local function onCherriesCollision(self, event)   
  
 if ( event.other.hit == "bullet" ) then  
 --audio.play(tin\_sound)  
 --splashAnimationGrapes()  
 splashAnimation800()  
 splashPoints300()  
 --print("Vase Hit!")  
 self:removeSelf()  
 return true  
 elseif ( event.other.hit =="rightWall") then  
 self:removeSelf()  
 print("HIT WALL!")  
  
  
 end  
  
 end  
 cherries.collision = onCherriesCollision  
 cherries:addEventListener( "collision" , cherries )  
 end  
local function spawnVase(number)  
  
 local function spawn(e)  
 --Create an instance of a balloon  
 --and assign it a random linear velocity  
 -- local v = vases.newVase(m.random(100,370));  
 local v = vases.newVase(m.random(50, 100))  
  
 --Store the balloon instance in the balloons table  
 --using the table number as the index  
 vaso[v] =v;  
 vaso[v].x = m.random(\_W \* 0.5 + 139, \_W\*0.5 + 149);  
 --Flag the balloon for removal later  
 vaso[v].remove = true;  
  
 --insert the balloon into the foreground group  
 localGroup:insert(vaso[v]);  
  
 --cancel and nil timer when done  
 if(e.count == number) then  
 timer.cancel(tmr);  
 tmr = nil;  
 end  
  
 end  
  
 tmr = timer.performWithDelay(2000, spawn, number);  
  
 end  
  
  
   
local function spawnProjectile()  
  
 -- If there is a projectile available then...  
 if(projectile.ready)then  
  
 projectiles\_container = projectile.newProjectile();  
 -- Flag projectiles for removal  
 projectiles\_container.ready = true;  
 projectiles\_container.remove = true;  
  
 -- Reset the indexing for the visual attributes of the catapult.  
 slingshot\_container:insert(slingshot\_strut\_back);  
 slingshot\_container:insert(projectiles\_container);  
 slingshot\_container:insert(slingshot\_strut\_front);  
  
 ---Add an event listener to the projectile.  
 projectiles\_container:addEventListener("touch", projectileTouchListener);  
  
 end  
  
end  
--[[  
  
GAME STATE CHANGE FUNCTION  
  
]]--  
function state:change(e)  
  
 if(e.state == "fire") then  
  
 --- You fired...  
 -- new projectile please  
 spawnProjectile();  
  
 end  
  
end  
  
 -------------------  
 ---Change Scene-----  
 -------------------  
 local moveToScene = function(event)  
  
 --Example scene change with parameters  
 --director:changeScene( { label="Scene Reloaded" }, "screen2","moveFromRight" )  
  
 --Example scene change without parameters  
 --director:changeScene( "screen1", "crossfade" )  
 end  
  
 ------------------  
 -- Code here  
 ------------------  
 background\_image = display.newImageRect("mode\_3\_bg.png", 480, 320)  
  
 slingshot\_strut\_front = display.newImage("sling\_left.png", true)  
 slingshot\_strut\_back = display.newImage("sling\_right.png", true)  
 --spring = display.newImageRect("spring\_good.png", 98, 98)  
 obstacle = display.newImageRect("barrel.png", 64, 64)  
 obstacle.myName = "obstacle"  
 obstacle.hit = "obstacle"  
  
  
 leftWall = display.newRect(0,0,0, \_H);  
 leftWall.myName = "leftWall"  
 rightWall = display.newRect(\_W, 0, 1, \_H);  
 rightWall.hit = "rightWall"  
  
 --invisible\_floor = display.newImageRect("invisible\_floor.png", 980, 1)  
  
  
 --====================================================================--  
 -- INITIALIZE, Every Display Object must get shoved into the local Display Group  
 -- Example: localGroup:insert( background )  
 --====================================================================--  
 local initVars = function ()  
  
 localGroup:insert(background\_image)  
 localGroup:insert(leftWall)  
 localGroup:insert(rightWall)  
 localGroup:insert(obstacle)  
 --localGroup:insert(invisible\_floor)  
  
 slingshot\_container:insert(slingshot\_strut\_front)  
 slingshot\_container:insert(slingshot\_strut\_back)  
 --slingshot\_container:insert(projectiles\_container)  
  
 --localGroup:insert(spring)  
  
  
 -- Transfer variables to the projectile classes  
 projectile.shot = shot  
 projectile.band\_stretch = band\_stretch  
  
 --POSITION OBJECTS  
 background\_image.x = \_W\*0.5  
 background\_image.y = \_H\*0.5  
  
 --[[invisible\_floor.x = \_W;  
 invisible\_floor.y = \_H;  
 physics.addBody ( invisible\_floor, "static", {bounce=0} )  
 invisible\_floor.hit = "floor"  
 invisible\_floor.myName = "floor"--]]  
  
 --slingshot  
 slingshot\_strut\_front.x = 138  
 slingshot\_strut\_front.y = \_H - 123  
 slingshot\_strut\_front.rotation = -30  
  
 slingshot\_strut\_back.x = 189  
 slingshot\_strut\_back.y = \_H - 147  
 slingshot\_strut\_back.rotation = -23  
  
 obstacle.x = \_W\*0.5 + 120  
 obstacle.y = \_H - 45  
  
 --[[spring.x = \_W\*0.5 + 230  
 spring.y = \_W\*0.5 - 200  
 spring.hit = "trampoline"  
 physics.addBody(spring, "static", { bounce = 1.4, friction = 0.4})  
 spring.rotation = 230--]]  
  
 -- Move catapult up  
 slingshot\_container.y = 98;  
 slingshot\_container.x = -98   
  
  
  
 --WRAPPIN WALLS WITH PHYSICS ENGINE --  
 physics.addBody(leftWall, "static", {bounce = 0.01});  
 physics.addBody(rightWall, "static", {bounce = 0.01});  
 physics.addBody(obstacle, "static", {bounce = 0.04})  
  
  
 ----LISTENERS---  
 -- Tell the projectile it's good to go!  
 projectile.ready = true;  
 -- Spawn the first projectile.  
 spawnProjectile();  
 -- Create listnener for state changes in the game  
 state:addEventListener("change", state);  
  
 timerSpawnCherries = timer.performWithDelay( 2000, spawnCherries, 0)   
 -- spawnVase(21)  
  
  
 end  

I have been trying to figure this one out for a while now, and I can’t seem to find the solution for this. Essentially, all I want to be able to do is have the player stretch the slingshot( which is working fine), and launch the projectile, which in return will hit flying objects and destroy/break that object (or objects if it hits more than one at once). That’s all I want to be able to do. Your help will be highly appreciated.

Thank you in advance. [import]uid: 75258 topic_id: 20126 reply_id: 320126[/import]

Wow, that’s long - you would be better off looking at Premium Support for this because to go through your code would take a good amount of time.

Alternatively you could try to isolate the parts of your code that aren’t working correctly and share those - that would be much easier for people to have a crack at.

Peach :slight_smile: [import]uid: 52491 topic_id: 20126 reply_id: 78649[/import]

You are absolutely right, Peach! I should not have put all that code in here… what was I thinking :(. I was pretty desperate, I guess! :slight_smile: My apologies.

I will follow your advice. Thanks again, Peach! [import]uid: 75258 topic_id: 20126 reply_id: 78656[/import]

Heh, no need to apologize, I’d just like to see it get sorted which is a lot easier with less code :wink:

Peach :slight_smile: [import]uid: 52491 topic_id: 20126 reply_id: 78687[/import]

I have the same problem, I cannot get the projectile to collide with physics body,

any fixs yet ? [import]uid: 111672 topic_id: 20126 reply_id: 85490[/import]

Incicive - read the first comment. Code is too long, isolate problem then ask.

If projectile isn’t colliding you need to also specify details - is it flying straight though, what body type is it, are objects sleeping, etc. [import]uid: 52491 topic_id: 20126 reply_id: 85590[/import]

Might it have anything to do with the “Bounce=0.5” ?
Because on the tutorial for collisions it mentioned they have to be equal to collide, but i tried it and it doesn’t seem to work. [import]uid: 111672 topic_id: 20126 reply_id: 88346[/import]

Bounce doesn’t have to be equal to collide, no.

There it a tutorial on collision on Techority, Corona for Newbies - Part 4 :slight_smile: [import]uid: 52491 topic_id: 20126 reply_id: 88413[/import]

Still trying too figure this out :S

I don’t think it has anything too too with the Kinematic or dymaic or even static settings,
But I’m confused completely, could you link me this newbies tutorial please ? [import]uid: 111672 topic_id: 20126 reply_id: 89086[/import]

Certainly; http://techority.com/2011/06/19/corona-for-newbies-part-4-physics/

Peach :slight_smile: [import]uid: 52491 topic_id: 20126 reply_id: 89221[/import]