Newbie question… I have 8 boxes (images) I’ve loaded in a table. The boxes are spread out across 3 different horizontal platforms on my screen, and each platform is connected by a ladder (think Donkey Kong). When my player jumps over a box I’d like to remove that particular box from the screen, and I guess the table. Since the player jumps over the box and doesn’t collide with it, how do I know when to remove the box, and which box to remove?
There are a few ways to handle that scenario but one way, if you want to use physics, is to use sensors. Put an appropriate sensor directly above and/or around the box and when the player jumps over the box it will trigger the sensor. Also note that you should not remove physics bodies (including sensors) during the collision event so using timer.performWithDelay will be necessary. The docs will shine more light on this.
https://docs.coronalabs.com/api/type/Body/isSensor.html
https://docs.coronalabs.com/daily/guide/physics/physicsBodies/index.html#sensors
https://coronalabs.com/resources/tutorials/physics-box2d/
https://docs.coronalabs.com/guide/physics/collisionDetection/index.html
https://www.youtube.com/watch?v=o_Qrw1dRoSk&feature=youtu.be
Thanks jerejigga. What if the box was something round and rolling like a tire? For example, if the player collides with the tire, I would like to decrease one of the players lives, but if the player jumps over it I would like to add to the players score. So can a sensor be placed on an object that is moving or rolling?
Sorry I posted in the wrong area, and thanks jerejigga. What if the box was something round and rolling like a tire? For example, if the player collides with the tire, I would like to decrease one of the players lives, but if the player jumps over it I would like to add to the players score. So can a sensor be placed on an object that is moving or rolling?
Perhaps this will point you in the right direction. This is basically the game Donkey Kong in 230 lines of code. Note that all/most of the code assumes that the screen dimensions are 320x480. To play the game, you MUST use these dimensions in your config.lua. Also, note that the player (the square) is controlled by the keyboard. iOS does not support keyboards so you must use an Android simulator to run this. Also note that the player can jump again while mid air. I didn’t bother trying to address that.
config.lua
application = { content = { width = 320, height = 480, scale = "letterBox", fps = 30, }, }
main.lua
----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- display.setStatusBar( display.HiddenStatusBar ) --WORLD CREATION---------------------------------------------------------------- -- Set up physics engine local physics = require("physics") physics.start() physics.setGravity( 0,9.8 ) --physics.setDrawMode( "hybrid" ) physics.setDrawMode( "normal" ) -- Declare initial variables local worldGroup = display.newGroup() -- Create "walls" around screen local function createWall(anchorX, anchorY, x, y, width, height, rotation) local wall = display.newRect( worldGroup, x, y, width, height ) wall.anchorX = anchorX wall.anchorY = anchorY wall.rotation = rotation or 0 wall.myName = "wall" wall.fill = {0,1,0} physics.addBody( wall, "static", { bounce=0.4, friction=0.1 } ) end createWall(1, 0.5, 0, display.contentCenterY, 20, 480) createWall(0, 0.5, 320, display.contentCenterY, 20, 480) createWall(0.5, 1, display.contentCenterX, 0, 320, 20) createWall(0.5, 0, display.contentCenterX, 470, 320, 20, -3) local function createFloor(x, y, width, height, rotation) local floor = display.newRect( x, y, width, height ) floor.rotation = rotation floor.myName = "floor" floor.fill = {0,1,0} physics.addBody( floor, "static", { bounce=0.4, friction=0.5 } ) end createFloor(display.contentCenterX - 30, 120, 260, 10, 4) createFloor(display.contentCenterX + 30, 240, 260, 10, -4) createFloor(display.contentCenterX - 30, 360, 260, 10, 4) --create player local player = display.newRect( worldGroup, 40, 440, 20, 20 ) player.myName = "player" player.fill = {0,0,1} physics.addBody( player, "dynamic", { bounce=0.3, friction=0.7 } ) --create barrel reset point local endMarker = display.newRect( worldGroup, 5, 480, 10, 100 ) endMarker.anchorY = 1 endMarker.myName = "endMarker" endMarker.isVisible = false physics.addBody( endMarker, "static", { bounce=0.3, friction=0.7, isSensor=true } ) --COLLISIONS---------------------------------------------------------------- local function hideBarrel(barrel) barrel.y = -2000 barrel.barrelSensor.y = -2000 barrel.isBodyActive = false barrel.isVisible = false barrel.barrelSensor.isBodyActive = false end local function onBarrelCollision( self, event ) if ( event.phase == "began" ) then --print( self.myName .. ": collision began with " .. event.other.myName ) local barrel = self --collision with main barrel body if (event.other.myName == "player") then --hit the player print("Player is DEAD!") player.fill = {1,0,0} --change player color to red timer.performWithDelay(10, function() hideBarrel(barrel) player.x = 40 player.y = 440 end) elseif (event.other.myName == "endMarker") then --hit the endMarker timer.performWithDelay(10, function() hideBarrel(barrel) end) end end end local function onBarrelSensorCollision( self, event ) if ( event.phase == "began" ) then --print( self.myName .. ": collision began with " .. event.other.myName ) local barrelSensor = self local barrel = barrelSensor.barrel --collision with barrel sensor if (event.other.myName == "player") then --the barrel sensor hit the player. they must have jumped over it print("Barrel has been jumped over!") player.fill = {0,1,0} --change player color to green timer.performWithDelay(10, function() hideBarrel(barrel) end) end end end --BARRELS---------------------------------------------------------------- local barrels = {} local function generateBarrel() if #barrels \>= 10 then for i = 1, #barrels do if not barrels[i].isVisible then --recycle existing barrel barrels[i].barrelSensor.isBodyActive = true barrels[i].isBodyActive = true barrels[i].isVisible = true barrels[i].x = 10 barrels[i].y = 95 barrels[i]:applyAngularImpulse(0.2) -- give it a kickstart return end end else local barrelSensor = display.newRect( worldGroup, 0, 0, 5, 60 ) barrelSensor.myName = "barrelSensor" barrelSensor.isVisible = false barrelSensor.anchorY = 1 physics.addBody( barrelSensor, "kinematic", { bounce=0.3, friction=0.7, isSensor=true } ) barrelSensor.gravityScale = 0 barrelSensor.isFixedRotation = true local barrel = display.newCircle( worldGroup, 10, 95, 10 ) barrel.myName = "barrel" physics.addBody( barrel, "dynamic", { bounce=0.3, friction=0.7, radius=10 }) barrel.barrelSensor = barrelSensor barrelSensor.barrel = barrel barrel.collision = onBarrelCollision barrel:addEventListener( "collision", barrel ) barrelSensor.collision = onBarrelSensorCollision barrelSensor:addEventListener( "collision", barrelSensor ) barrels[#barrels + 1] = barrel barrel:applyAngularImpulse(0.2) -- give it a kickstart end end timer.performWithDelay(3000, generateBarrel, -1) --MOVEMENT---------------------------------------------------------------- local velX = 0 local velInc = 60; -- The Key Event Listener -- local function onKeyEvent( event ) if event.phase == "down" then if event.keyName == "left" then velX = -1 \* velInc elseif event.keyName == "right" then velX = velInc elseif event.keyName == "up" then player:applyForce(0, -0.8, player.x, player.y) end elseif event.phase == "up" then if event.keyName == "left" or event.keyName == "right" then velX = 0 elseif event.keyName == "up" or event.keyName == "down" then velY = 0 end end -- If the "back" key was pressed on Android, then prevent it from backing out of the app. -- We do this by returning true, telling the operating system that we are overriding the key. if (event.keyName == "back") and (platformName == "Android") then return true end -- Return false to indicate that this app is \*not\* overriding the received key. -- This lets the operating system execute its default handling of this key. return false end -- Add the key callback Runtime:addEventListener( "key", onKeyEvent ); --GAME LOOP---------------------------------------------------------------------------- local gameLoop = function(event) local vx, vy = player:getLinearVelocity() player:setLinearVelocity(velX, vy) for i = 1, #barrels do if barrels[i].isVisible then local bvx, bvy = barrels[i]:getLinearVelocity() local shift = 0; if bvx \> 0 then shift = -10 elseif bvx \< 0 then shift = 10 end barrels[i].barrelSensor.x = barrels[i].x + shift barrels[i].barrelSensor.y = barrels[i].y - 10 end end end Runtime:addEventListener("enterFrame", gameLoop)
Awesome… Thanks!
Besides using the sensors to detect when the player jumped over the barrels, I also considered using raycasting to detect if a barrel was directly below the player. (See this blog and these api docs.). Depending on one’s needs, raycasting may be more effective. In my game sample though, a raycast would make things more complicated since I don’t want to remove the barrel until the player has jumped most of the way over. A raycast would detect the barrel the moment I started jumping over the barrel so I’d need to implement some tricky code to work around that and it just didn’t seem worth it in this case.
Also, here is a screenshot of the game in action for those that don’t want to or can’t run the code themselves.
There are a few ways to handle that scenario but one way, if you want to use physics, is to use sensors. Put an appropriate sensor directly above and/or around the box and when the player jumps over the box it will trigger the sensor. Also note that you should not remove physics bodies (including sensors) during the collision event so using timer.performWithDelay will be necessary. The docs will shine more light on this.
https://docs.coronalabs.com/api/type/Body/isSensor.html
https://docs.coronalabs.com/daily/guide/physics/physicsBodies/index.html#sensors
https://coronalabs.com/resources/tutorials/physics-box2d/
https://docs.coronalabs.com/guide/physics/collisionDetection/index.html
https://www.youtube.com/watch?v=o_Qrw1dRoSk&feature=youtu.be
Thanks jerejigga. What if the box was something round and rolling like a tire? For example, if the player collides with the tire, I would like to decrease one of the players lives, but if the player jumps over it I would like to add to the players score. So can a sensor be placed on an object that is moving or rolling?
Sorry I posted in the wrong area, and thanks jerejigga. What if the box was something round and rolling like a tire? For example, if the player collides with the tire, I would like to decrease one of the players lives, but if the player jumps over it I would like to add to the players score. So can a sensor be placed on an object that is moving or rolling?
Perhaps this will point you in the right direction. This is basically the game Donkey Kong in 230 lines of code. Note that all/most of the code assumes that the screen dimensions are 320x480. To play the game, you MUST use these dimensions in your config.lua. Also, note that the player (the square) is controlled by the keyboard. iOS does not support keyboards so you must use an Android simulator to run this. Also note that the player can jump again while mid air. I didn’t bother trying to address that.
config.lua
application = { content = { width = 320, height = 480, scale = "letterBox", fps = 30, }, }
main.lua
----------------------------------------------------------------------------------------- -- -- main.lua -- ----------------------------------------------------------------------------------------- display.setStatusBar( display.HiddenStatusBar ) --WORLD CREATION---------------------------------------------------------------- -- Set up physics engine local physics = require("physics") physics.start() physics.setGravity( 0,9.8 ) --physics.setDrawMode( "hybrid" ) physics.setDrawMode( "normal" ) -- Declare initial variables local worldGroup = display.newGroup() -- Create "walls" around screen local function createWall(anchorX, anchorY, x, y, width, height, rotation) local wall = display.newRect( worldGroup, x, y, width, height ) wall.anchorX = anchorX wall.anchorY = anchorY wall.rotation = rotation or 0 wall.myName = "wall" wall.fill = {0,1,0} physics.addBody( wall, "static", { bounce=0.4, friction=0.1 } ) end createWall(1, 0.5, 0, display.contentCenterY, 20, 480) createWall(0, 0.5, 320, display.contentCenterY, 20, 480) createWall(0.5, 1, display.contentCenterX, 0, 320, 20) createWall(0.5, 0, display.contentCenterX, 470, 320, 20, -3) local function createFloor(x, y, width, height, rotation) local floor = display.newRect( x, y, width, height ) floor.rotation = rotation floor.myName = "floor" floor.fill = {0,1,0} physics.addBody( floor, "static", { bounce=0.4, friction=0.5 } ) end createFloor(display.contentCenterX - 30, 120, 260, 10, 4) createFloor(display.contentCenterX + 30, 240, 260, 10, -4) createFloor(display.contentCenterX - 30, 360, 260, 10, 4) --create player local player = display.newRect( worldGroup, 40, 440, 20, 20 ) player.myName = "player" player.fill = {0,0,1} physics.addBody( player, "dynamic", { bounce=0.3, friction=0.7 } ) --create barrel reset point local endMarker = display.newRect( worldGroup, 5, 480, 10, 100 ) endMarker.anchorY = 1 endMarker.myName = "endMarker" endMarker.isVisible = false physics.addBody( endMarker, "static", { bounce=0.3, friction=0.7, isSensor=true } ) --COLLISIONS---------------------------------------------------------------- local function hideBarrel(barrel) barrel.y = -2000 barrel.barrelSensor.y = -2000 barrel.isBodyActive = false barrel.isVisible = false barrel.barrelSensor.isBodyActive = false end local function onBarrelCollision( self, event ) if ( event.phase == "began" ) then --print( self.myName .. ": collision began with " .. event.other.myName ) local barrel = self --collision with main barrel body if (event.other.myName == "player") then --hit the player print("Player is DEAD!") player.fill = {1,0,0} --change player color to red timer.performWithDelay(10, function() hideBarrel(barrel) player.x = 40 player.y = 440 end) elseif (event.other.myName == "endMarker") then --hit the endMarker timer.performWithDelay(10, function() hideBarrel(barrel) end) end end end local function onBarrelSensorCollision( self, event ) if ( event.phase == "began" ) then --print( self.myName .. ": collision began with " .. event.other.myName ) local barrelSensor = self local barrel = barrelSensor.barrel --collision with barrel sensor if (event.other.myName == "player") then --the barrel sensor hit the player. they must have jumped over it print("Barrel has been jumped over!") player.fill = {0,1,0} --change player color to green timer.performWithDelay(10, function() hideBarrel(barrel) end) end end end --BARRELS---------------------------------------------------------------- local barrels = {} local function generateBarrel() if #barrels \>= 10 then for i = 1, #barrels do if not barrels[i].isVisible then --recycle existing barrel barrels[i].barrelSensor.isBodyActive = true barrels[i].isBodyActive = true barrels[i].isVisible = true barrels[i].x = 10 barrels[i].y = 95 barrels[i]:applyAngularImpulse(0.2) -- give it a kickstart return end end else local barrelSensor = display.newRect( worldGroup, 0, 0, 5, 60 ) barrelSensor.myName = "barrelSensor" barrelSensor.isVisible = false barrelSensor.anchorY = 1 physics.addBody( barrelSensor, "kinematic", { bounce=0.3, friction=0.7, isSensor=true } ) barrelSensor.gravityScale = 0 barrelSensor.isFixedRotation = true local barrel = display.newCircle( worldGroup, 10, 95, 10 ) barrel.myName = "barrel" physics.addBody( barrel, "dynamic", { bounce=0.3, friction=0.7, radius=10 }) barrel.barrelSensor = barrelSensor barrelSensor.barrel = barrel barrel.collision = onBarrelCollision barrel:addEventListener( "collision", barrel ) barrelSensor.collision = onBarrelSensorCollision barrelSensor:addEventListener( "collision", barrelSensor ) barrels[#barrels + 1] = barrel barrel:applyAngularImpulse(0.2) -- give it a kickstart end end timer.performWithDelay(3000, generateBarrel, -1) --MOVEMENT---------------------------------------------------------------- local velX = 0 local velInc = 60; -- The Key Event Listener -- local function onKeyEvent( event ) if event.phase == "down" then if event.keyName == "left" then velX = -1 \* velInc elseif event.keyName == "right" then velX = velInc elseif event.keyName == "up" then player:applyForce(0, -0.8, player.x, player.y) end elseif event.phase == "up" then if event.keyName == "left" or event.keyName == "right" then velX = 0 elseif event.keyName == "up" or event.keyName == "down" then velY = 0 end end -- If the "back" key was pressed on Android, then prevent it from backing out of the app. -- We do this by returning true, telling the operating system that we are overriding the key. if (event.keyName == "back") and (platformName == "Android") then return true end -- Return false to indicate that this app is \*not\* overriding the received key. -- This lets the operating system execute its default handling of this key. return false end -- Add the key callback Runtime:addEventListener( "key", onKeyEvent ); --GAME LOOP---------------------------------------------------------------------------- local gameLoop = function(event) local vx, vy = player:getLinearVelocity() player:setLinearVelocity(velX, vy) for i = 1, #barrels do if barrels[i].isVisible then local bvx, bvy = barrels[i]:getLinearVelocity() local shift = 0; if bvx \> 0 then shift = -10 elseif bvx \< 0 then shift = 10 end barrels[i].barrelSensor.x = barrels[i].x + shift barrels[i].barrelSensor.y = barrels[i].y - 10 end end end Runtime:addEventListener("enterFrame", gameLoop)
Awesome… Thanks!
Besides using the sensors to detect when the player jumped over the barrels, I also considered using raycasting to detect if a barrel was directly below the player. (See this blog and these api docs.). Depending on one’s needs, raycasting may be more effective. In my game sample though, a raycast would make things more complicated since I don’t want to remove the barrel until the player has jumped most of the way over. A raycast would detect the barrel the moment I started jumping over the barrel so I’d need to implement some tricky code to work around that and it just didn’t seem worth it in this case.
Also, here is a screenshot of the game in action for those that don’t want to or can’t run the code themselves.