For the benefit of others reading this…
Also, there is not a use case (realistically) that needs more than a single enterFrame event.
For the benefit of others reading this…
Also, there is not a use case (realistically) that needs more than a single enterFrame event.
your code works similarly
https://www.youtube.com/watch?v=TpnH_jmcpOM&feature=youtu.be
but this is closer to what I’m trying to achieve outside of the bar moving forward
https://www.youtube.com/watch?v=KmVkTCg30OM&feature=youtu.be
This happens when I use the code from the isSensor thread and set the linear velocity (1,0)
Yeaa I know my code is a bit messy, I was hoping it wouldn’t be too obvious but I got a little excited when I found this app and got right into making the game instead of looking into lua.
Also my code needs physics for the touch function otherwise the penguin stays stationary.
Also I appreciate you guys’s help I know how frustrating it must be talking to someone who has no clue what they’re doing.
Alright, seems like I had a completely different idea of your game in my head. This is why visual aids are so great.
From what I can see, you could achieve all of that without the use of physics. Since those ice blocks aren’t perfect rectangles, you’d sacrifice a bit of accuracy in terms of collision detection, but your game would be less resource heavy and it would require less code as a result.
For scoring, you currently have a large rect that detects when those ice blocks reach the left side of the screen, but you shouldn’t use collision detection here. Just write an enterFrame function that loops through all ice blocks. If an ice block’s x location is to the left of the penguin and it hasn’t collided with the penguin, then obviously it was dodged. At this point, you’d add one point to the player (and you could stop tracking ice blocks that have yielded points already to make the function lighter). For detecting if an ice block collides with the penguin, you could use SGS’s function.
As for the penguin remaining stationary, from the looks of it, you could just add a few lines of code to the enterFrame function to drag the penguin down if the player isn’t touching the screen. If the player is touching the screen, then you’d move the penguin up.
Now, probably the reason as to why you need to move the scorebar in your code (as it is now), is if you move those iceblocks with transitions, then most likely the collisions don’t occur. What you could do, if you want to stick to physics, is to do something along the lines of:
local physics = require("physics") physics.setDrawMode( "hybrid" ) physics.start() local iceblock, iceblockTransition = {}, {} local function onLocalCollision( self, event ) if ( event.phase == "began" ) then if self.name == "scorebar" then print("Collision") end end end local scorebar = display.newRect(100,160,20,320) physics.addBody( scorebar, "dynamic", {density=1, isSensor=true} ) scorebar.collision = onLocalCollision scorebar.name = "scorebar" scorebar.gravityScale = 0 scorebar:addEventListener( "collision" ) scorebar:setFillColor(0,1,1) local function moveBlock() iceblock[#iceblock+1] = display.newRect(400,math.random(40,280),20,20) physics.addBody( iceblock[#iceblock], "kinematic" ) iceblock[#iceblock].collision = onLocalCollision iceblock[#iceblock]:addEventListener( "collision" ) iceblockTransition[#iceblockTransition+1] = transition.to( iceblock[#iceblock], {time=1500,x=0} ) end local iceblockTimer = timer.performWithDelay( 500, moveBlock, 0 )
But, once again, I would advice against the use of physics in this case as you don’t (at least yet) have anything that makes the use of physics compulsory.
Now, I hope that we’ve solved this issue, so that we can lay this thread to rest.
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.