Ok so for my game, the last thing I need is a score and best score feature. The score is supposed to go up every time a collision doesn’t happen/when my penguin dodges an ice block. For the most part I can only find tutorials on how to make the score go up every time you tap, so are there any tutorials for what I need? Also any tutorials on how to save the score and display the best score?
You just need another trigger to count those dodges.
For instance, you could add an invisible circle behind the penguin. Then you track the “ended” phase for collisions with that circle and those ice blocks. If a collision ends and the penguin itself doesn’t get hit, then the ice block only touched the circle, i.e. the penguin dodged it.
The best practice would probably be to display the score as a text on the screen. Whenever a new dodge occurs, you simply update the text to match the new score. For a tutorial on this, you could check out https://docs.coronalabs.com/tutorial/games/keepScores/index.html to get started. If you just want to save the top score, then perform a simple check to see if “current score > highscore” and then save it, etc.
Simply increase score per move (distance travelled or whatever) and do not increase if a hit occurred (in that move).
i attempted this and my bar behind the penguin moves and causes a restart cause it hits the floor
my collision function is
local function onCollision(event) if event.phase == "began" then print "collide" -- applyForceToPenguin = false composer.gotoScene( "restart",{ time=800, effect="crossFade" } ) end end
is there a way to create a separate collision function for the bar that’ll cause the score to go up
Yes, you can create a separate collision function, you’d create it like you just created the one above, but that might not be necessary. Also, you are referring to very specific things in your code. Code, that no one of use can see, and so I don’t know what “your bar” is.
In your collision function, you are currently only tracking if a collision began, regardless of what objects actually collided. You should read through https://docs.coronalabs.com/guide/physics/collisionDetection/index.html. In this case, you’d most likely want to add some identifier to your objects, as explained in the collision handling part of the docs. This way, you could can place that gotoScene inside an if statement so that it only runs if two specific objects collide.
I read through your link and I get that I’m supposed to be focused on multi-element collisions but I’m still not sure what to do. Specifically, how would I add identifiers to my objects and how would I change my collision function to only work for one specific object?
You could do something like this:
local penguin = display.newRect(80,80,16,16) penguin.x, penguin.y = display.contentCenterX, display.contentCenterY penguin.id = "penguin" physics.addBody( penguin, "dynamic", { friction=0.2, bounce=0.4 }, { friction=0.2, bounce=0.4, radius=30, isSensor=true } )
The first body belongs to the penguin and the latter to its sensor. If you are using id in your collision function, you can just include it like usual. Whenever a collision begins, ends, etc. you’ll get the usual information in your collision function for all elements of the body. You can differentiate between the specific elements by using “event.selfElement”. They are integers that correspond the table elements, i.e. 1 is the first element in the table, 2 is the second, etc.
So, if you type in print(event.selfElement) and you receive 2, it means that the sensor collided and not the penguin.
You can read more about them at https://docs.coronalabs.com/tutorial/games/multiElementCollision/index.html.
Is there a certain way to reference the id in the collision function
Yes. Read https://docs.coronalabs.com/guide/physics/collisionDetection/index.html#local-collision-handling
Ok I somewhat got it to work to the point where it won’t restart if the bar hits the ground or ice, but now the ice pushes the bar off screen xD
this is my code for the bar
scorebar = display.newImage(sceneGroup, "scorecounter.png", 15, 250) physics.addBody(scorebar, "dynamic", {density=.1, bounce=0.1, friction=.5})
Is there a physics property I’m missing to make the ice go through the bar but still acknowledge the collision?
That’s already been answered and it is getting silly that I’m just pointing you to the same docs over and over again: https://docs.coronalabs.com/api/type/Body/isSensor.html.
oh sorry man it’s a lot of info and it does get a little confusing what I am and am not doing
Don’t worry about it.
It is just getting silly, that’s all.
Hello again, I’ve been trying for a week now but the collision function only works if I add the setLinearVelocity property to the scorebar and it doesn’t allow me to set it to 0. If I set it to 0 there isn’t an indication of a collision in the console and the score doesn’t go up. Sorry for the bombardment of questions I’m just not sure what else to do at this point.
My collision
local function onLocalCollision(event) if event.phase == "began" then print "collide2" score.add( .5 ) print(event.selfElement) end end
The scorebar
scorebar = display.newImage(sceneGroup, "scorecounter.png", 15, 250) scorebar.id = "scorebar" physics.addBody(scorebar, "dynamic") scorebar.gravityScale = 0 scorebar.isSensor = true scorebar:setLinearVelocity( 0,0 )
It would be useful if you could share more of your project that demonstrates your problem. It is really difficult to grasp the issue from what you’ve described now. If you don’t want to share code that illustrates your problem, then please try to at least be more descriptive and add some visual aid, like a screenshot.
it wouldn’t let me upload an image but this is my entire code
display.setStatusBar(display.HiddenStatusBar) local \_W = display.contentWidth local \_H = display.contentHeight local scrollSpeed = 3 local physics = require "physics" physics.start() local composer = require( "composer" ) local scene = composer.newScene() --20 here you declare all your local variables: local score = 0 --23 here you declare all your local functions: -- whatever your code requires... local function move(event) bg2.x = bg2.x - scrollSpeed/2 bg3.x = bg3.x - scrollSpeed/2 bg4.x = bg4.x - scrollSpeed/2 bg5.x = bg5.x - scrollSpeed/2 bg6.x = bg6.x - scrollSpeed/2 bg7.x = bg7.x - scrollSpeed bg8.x = bg8.x - scrollSpeed bg9.x = bg9.x - scrollSpeed bg10.x = bg10.x - scrollSpeed bg11.x = bg11.x - scrollSpeed if(-bg2.x + bg2.contentWidth) \> 1080 then bg2:translate(2000,0) end if(-bg3.x + bg3.contentWidth) \> 1080 then bg3:translate(2000,0) end if(-bg4.x + bg4.contentWidth) \> 1080 then bg4:translate(2000,0) end if(-bg5.x + bg5.contentWidth) \> 1080 then bg5:translate(2000,0) end if(-bg6.x + bg6.contentWidth) \> 1080 then bg6:translate(2000,0) end if(-bg7.x + bg7.contentWidth) \> 1080 then bg7:translate(2000,0) end if(-bg8.x + bg8.contentWidth) \> 1080 then bg8:translate(2000,0) end if(-bg9.x + bg9.contentWidth) \> 1080 then bg9:translate(2000,0) end if(-bg10.x + bg10.contentWidth) \> 1080 then bg10:translate(2000,0) end if(-bg11.x + bg11.contentWidth) \> 1080 then bg11:translate(2000,0) end end local score = require( "score" ) local scoreText = score.init( { fontSize = 40, x = 130, y = 30, maxDigits = 7, leadingZeros = false }) local function isValidPhysics( obj ) return( obj and type(obj.applyForce) == "function" ) end local animation -- Leave it nil local function activateAnimations(event) if( isValidPhysics( animation )) then animation:applyForce(0, -45, animation.x, animation.y) end end -- local peng = {} -- local function activatePengs(event) -- peng:applyForce(0, -45, peng.x, peng.y) -- end local function touchScreen(event) --107 print("touch") if event.phase == "began" then animation.enterFrame = activateAnimations Runtime:addEventListener("enterFrame", animation) end if event.phase == "ended" then Runtime:removeEventListener("enterFrame", animation) end end local function onCollision(event) if event.phase == "began" then print "collide" -- applyForceToPenguin = false composer.gotoScene( "restart",{ time=800, effect="crossFade" } ) print(event.selfElement) end end local function onLocalCollision(event) if event.phase == "began" then print "collide2" score.add( .5 ) print(event.selfElement) end end --now comes four required functions for Composer: function scene:create( event ) local sceneGroup = self.view --129 put any thing you need to create here bg1 = display.newImageRect(sceneGroup, "bg.png", 800, 1000) bg2 = display.newImage(sceneGroup, "ice2.png",140,210) bg3 = display.newImage(sceneGroup, "ice2.png",540,210) bg4 = display.newImage(sceneGroup, "ice2.png",940,210) bg5 = display.newImage(sceneGroup, "ice2.png",1340,210) bg6 = display.newImage(sceneGroup, "ice2.png",1740,210) bg7 = display.newImage(sceneGroup, "ice1.png",140,420) bg8 = display.newImage(sceneGroup, "ice1.png",540,420) bg9 = display.newImage(sceneGroup, "ice1.png",940,420) bg10 = display.newImage(sceneGroup, "ice1.png",1340,420) bg11 = display.newImage(sceneGroup, "ice1.png",1740,420) ceiling = display.newImage(sceneGroup, "invisibleTile.png", 0, -120) physics.addBody(ceiling, "static", {density=.1, bounce=0.1, friction=.5}) theFloor = display.newImage(sceneGroup, "invisibleTile.png", 0, 600) physics.addBody(theFloor, "static", {density=.1, bounce=0.1, friction=.5}) scorebar = display.newImage(sceneGroup, "scorecounter.png", 15, 250) scorebar.id = "scorebar" physics.addBody(scorebar, "dynamic") scorebar.gravityScale = 0 scorebar.isSensor = true scorebar:setLinearVelocity( 1,0 ) local sheetData = { width=50, height=28, numFrames=4, sheetContentWidth=200, sheetContentHeight=28 } local mySheet = graphics.newImageSheet ( "pengs.png", sheetData ) local sequenceData = { { name = "pengFly", start = 1, count = 4, time = 400, loopCount = 0, loopDirection = "foward" } } animation = display.newSprite(sceneGroup, mySheet, sequenceData) animation.x = 80 animation.y = 201 animation.id = "animation" physics.addBody(animation, "dynamic", {density=.45, bounce=.1, friction=.5, radius=27}, {bounce=.1, friction=.5, radius=27, isSensor = true}) animation:play() icebok = display.newImage(sceneGroup, "icebok.png", 480, 301) physics.addBody(icebok, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok.speed = 4 -- icebok.initY = icebok.y + math.random(0,500) -- icebok.amp = math.random(10,50) --157 icebok.angle = math.random(1,720) icebok1 = display.newImage(sceneGroup, "icebok.png", 680, 201) physics.addBody(icebok1, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok1.speed = 4 -- icebok1.initY = icebok1.y + math.random(0,500) -- icebok1.amp = math.random(10,50) -- icebok1.angle = math.random(1,720) icebok2 = display.newImage(sceneGroup, "icebok.png", 880, 301) physics.addBody(icebok2, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok2.speed = 4 -- icebok2.initY = icebok2.y + math.random(0,500) -- icebok2.amp = math.random(10,50) -- icebok2.angle = math.random(1,720) icebok3 = display.newImage(sceneGroup, "icebok.png", 1080, 401) physics.addBody(icebok3, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok3.speed = 4 -- icebok3.initY = icebok3.y + math.random(0,500) -- icebok2.amp = math.random(10,50) -- icebok2.angle = math.random(1,720) icebok4 = display.newImage(sceneGroup, "icebok.png", 1330, 499) physics.addBody(icebok4, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok4.speed = 4 -- icebok4.initY = icebok4.y + math.random(0,500) icebok5 = display.newImage(sceneGroup, "icebok.png", 1580, 499) physics.addBody(icebok5, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok5.speed = 4 -- icebok5.initY = icebok5.y + math.random(0,500) icebok6 = display.newImage(sceneGroup, "icebok.png", 1830, 401) physics.addBody(icebok6, "static", {density=.1, bounce=0.1, friction=.5, radius=10}) icebok6.speed = 4 -- icebok6.initY = icebok6.y + math.random(0,500) function moveiceboks(self,event) if self.x \< -10 then self.x = math.random(480,1830) -- self.x = 480 self.y = math.random(0,500) self.speed = 4 -- self.amp = math.random(10,50) -- self.angle = math.random(1,720) else self.x = self.x - self.speed -- self.angle = self.angle + .1 -- self.y = self.amp \* math.tan(self.angle) + self.initY --196 self.y = math.random(0,500) end end end function scene:show( event ) local sceneGroup = self.view if event.phase == "will" then -- put code here you want to happen just before the scene comes on the screen -- physics.start() Runtime:addEventListener("enterFrame", move) Runtime:addEventListener("touch", touchScreen) icebok.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok) icebok1.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok1) icebok2.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok2) icebok3.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok3) icebok4.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok4) icebok5.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok5) icebok6.enterFrame = moveiceboks Runtime:addEventListener("enterFrame",icebok6) animation.collision = onCollision animation:addEventListener("collision", onCollision) scorebar.collision = onLocalCollision scorebar:addEventListener("collision", onLocalCollision) else -- put code here you want to happen after the scene comes on the screen -- physics.start() -- Runtime:addEventListener("enterFrame", move) Runtime:addEventListener("touch", touchScreen) -- icebok.enterFrame = moveiceboks -- Runtime:addEventListener("enterFrame",icebok) -- icebok1.enterFrame = moveiceboks -- Runtime:addEventListener("enterFrame",icebok1) -- icebok2.enterFrame = moveiceboks -- Runtime:addEventListener("enterFrame",icebok2) -- icebok3.enterFrame = moveiceboks -- Runtime:addEventListener("enterFrame",icebok3) animation.collision = onCollision animation:addEventListener("collision", onCollision) scorebar.collision = onLocalCollision scorebar:addEventListener("collision", onLocalCollision) end end function scene:hide( event ) local sceneGroup = self.view if event.phase == "will" then -- put code here you want to happen just before the scene leaves the screen Runtime:removeEventListener("enterFrame", move) Runtime:removeEventListener("touch", touchScreen) -- icebok.enterFrame = moveiceboks Runtime:removeEventListener("enterFrame",icebok) -- icebok1.enterFrame = moveiceboks Runtime:removeEventListener("enterFrame",icebok1) -- icebok2.enterFrame = moveiceboks Runtime:removeEventListener("enterFrame",icebok2) -- icebok3.enterFrame = moveiceboks Runtime:removeEventListener("enterFrame",icebok3) Runtime:removeEventListener("enterFrame",icebok4) Runtime:removeEventListener("enterFrame",icebok5) Runtime:removeEventListener("enterFrame",icebok6) animation:removeEventListener("collision", onCollision) scorebar:removeEventListener("collision", onLocalCollision) -- composer.removeScene("game") else -- put code here you want to happen after the scene has left the screen -- Runtime:removeEventListener("enterFrame", move) Runtime:removeEventListener("touch", touchScreen) Runtime:removeEventListener("enterFrame",icebok) Runtime:removeEventListener("enterFrame",icebok1) Runtime:removeEventListener("enterFrame",icebok2) Runtime:removeEventListener("enterFrame",icebok3) Runtime:removeEventListener("enterFrame",icebok4) Runtime:removeEventListener("enterFrame",icebok5) Runtime:removeEventListener("enterFrame",icebok6) animation:removeEventListener("collision", onCollision) scorebar:removeEventListener("collision", onLocalCollision) physics.removeBody(peng, "dynamic", {density=.18, bounce=0.1, friction=.5, radius=55}) -- physics.pause() -- composer.removeScene("game") end end function scene:destroy( event ) local sceneGroup = self.view -- put code here if you have things you need to remove that you created in create scene that does NOT go into the sceneGroup) end -- these must be the last 5 lines in the file scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) return scene
Hmm… there seems to be a lot going on and I believe that you could get by with a lot less. For instance, you seem to have a lot of runtime listeners and I’m not sure that you need them.
Here’s a full code sample of what I’ve basically been talking about. Please let me know if this is anywhere close to what you have been trying to achieve. If it isn’t, then please provide me with some images of what you want to do, accompanied with detailed explanations. We’ve both invested too much time here to let this be.
local physics = require("physics") physics.setDrawMode( "hybrid" ) physics.start() local penguin = display.newRect(display.contentCenterX,display.contentCenterY,16,16) penguin.id = "penguin" physics.addBody( penguin, "static", { friction=0.2, bounce=0.4 }, -- index #1 { friction=0.2, bounce=0.4, radius=30, isSensor=true } -- index #2 ) local projectile = {} local shotCount = 1 local function delete(target) display.remove(target) target = nil end local function onLocalCollision(self,event) -- track the "ended" phase, so you'll know if the penguin itself was hit or if only the sensor was hit if ( event.phase == "ended" ) then if self.id == "penguin" then if event.selfElement == 1 then print("Projectile #"..event.other.shotNumber.." hit penguin.") -- penguin was hit, so gameover? -- deleting the projectile on collision display.remove(event.other) event.other = nil else print("Projectile #"..event.other.shotNumber.." missed the penguin.") -- the projectile was dodged, so increase the score timer.performWithDelay( 200, function() delete (event.other) end) -- deleting the dodged projectile end end end end penguin.collision = onLocalCollision penguin:addEventListener( "collision" ) local function shoot() projectile[#projectile+1] = display.newCircle(display.contentCenterX+160,display.contentCenterY-20,5) projectile[#projectile].shotNumber = shotCount physics.addBody( projectile[#projectile], "dynamic", {bounce=0.5, density=1, radius=projectile[#projectile].width\*0.5}) projectile[#projectile].collision = onLocalCollision projectile[#projectile]:addEventListener( "collision" ) if shotCount % 5 == 0 then projectile[#projectile]:applyLinearImpulse( -1, -0.1, projectile[#projectile].x, projectile[#projectile].y ) else projectile[#projectile]:applyLinearImpulse( -1, -0.25, projectile[#projectile].x, projectile[#projectile].y ) end shotCount = shotCount+1 end timer.performWithDelay( 1000, shoot, 0 )
Wow this post keeps going on and on…
A simple solution to collision is to NOT use physics and use simple maths instead
local function isCollision(object1, object2) obj1 = object1.contentBounds obj2 = object2.contentBounds if obj1 and obj2 then local left = obj1.xMin \<= obj2.xMin and obj1.xMax \>= obj2.xMin local right = obj1.xMin \>= obj2.xMin and obj1.xMin \<= obj2.xMax local up = obj1.yMin \<= obj2.yMin and obj1.yMax \>= obj2.yMin local down = obj1.yMin \>= obj2.yMin and obj1.yMin \<= obj2.yMax return ( left or right ) and ( up or down ) else return false end end
Posting this for everyone really… simply pass the above function 2 display objects and if they collide then they have hit and the function returns true.
Note: this uses simple box collisions and is meant for simple sprites. However it works for most objects that can be bound by a simple rect.
@davy1222 your code is so wasteful… You really need to invest some time to understand arrays of objects. It will make your code far more simple and readable.
For example, this code
local function move(event) bg2.x = bg2.x - scrollSpeed/2 bg3.x = bg3.x - scrollSpeed/2 bg4.x = bg4.x - scrollSpeed/2 bg5.x = bg5.x - scrollSpeed/2 bg6.x = bg6.x - scrollSpeed/2 bg7.x = bg7.x - scrollSpeed bg8.x = bg8.x - scrollSpeed bg9.x = bg9.x - scrollSpeed bg10.x = bg10.x - scrollSpeed bg11.x = bg11.x - scrollSpeed if(-bg2.x + bg2.contentWidth) \> 1080 then bg2:translate(2000,0) end if(-bg3.x + bg3.contentWidth) \> 1080 then bg3:translate(2000,0) end if(-bg4.x + bg4.contentWidth) \> 1080 then bg4:translate(2000,0) end if(-bg5.x + bg5.contentWidth) \> 1080 then bg5:translate(2000,0) end if(-bg6.x + bg6.contentWidth) \> 1080 then bg6:translate(2000,0) end if(-bg7.x + bg7.contentWidth) \> 1080 then bg7:translate(2000,0) end if(-bg8.x + bg8.contentWidth) \> 1080 then bg8:translate(2000,0) end if(-bg9.x + bg9.contentWidth) \> 1080 then bg9:translate(2000,0) end if(-bg10.x + bg10.contentWidth) \> 1080 then bg10:translate(2000,0) end if(-bg11.x + bg11.contentWidth) \> 1080 then bg11:translate(2000,0) end end
could be simplified to
local function move(event) for i = 1, 11 do if i \< 7 then gb[i].x = gb[i].x - scrollSpeed/2 else gb[i].x = gb[i].x - scrollSpeed end if(-bg[i].x + bg[i].contentWidth) \> 1080 then bg[i]:translate(2000,0) end end end
Yeah, there’s a lot to improve upon regarding the code.
As for SGS’s math solution, it is a simpler method and it can easily be modified to work with circles as well. If your game doesn’t require physics apart from the collision detection for the dodging itself, then opting to use a purely mathematical solution is better.