Finally, I reached the first big milestone of my Rolly Bear World project. In the last couple of days I added the first gameplay interaction to the game. In this post we will start integrating the physics engine provided by Corona SDK, add the functionality to launch Rolly Bear into the physics world, and add some basic new art work to the game.
But first, below you can find some pictures of Indonesia. We rented a motorbike and explore the island Lombok. We zip through jungle roads, past palm trees and little villages. After a hour we suddenly found a very secluded beach called Tanjun Aan. Personally, this was the most beautiful beach I ever saw in my life. Besides a guy selling 2 Dollar coconut drinks we were the only two people on the beach. The water was turquoise and warm, it felt like heaven :).
In the video below you can see the functionality we will be building throughout this tutorial.
Now, lets start to add the first gameplay interactions to Rolly Bear. Open your project file and create a new folder called audio. In the audio folder I added “wee.mp3″ which is the sound effect which will be played when we will launch Rolly Bear into the physics world. You can find the mp3 file in the GitHub repository. Second create a file called level01.lua in your main folder. Note that this naming convention (level01.lua) is important as we defined in levels.lua (the grid with all levels) the following line of code:
levelImg.destination = "level0"..tostring(tablePlace)
Here we pass the the tableplace into “level0″, meaning that when a user clicks on level 1 (s)he will be directed to level01.lua, based on the position and number in the level grid.
Now open your level01.lua file. As you can see in the complete level01.lua file on GitHub the basic storyboard code has also been added to level01.lua. (create, enter and exit a scene). At the end of this post you can find the complete level01.lua code (and of course via GitHub). The snippets below are just some of the pieces.
First step is to include the physics engine, which is very simple (thanks to Corona SDK). With physics.start you enable physics in your game and with physics.gravity you can set the gravity. The physics engine itself is loaded via require(“physics”). In our case I will not add any physics, meaning the objects will not fall down the screen. When you set the gravity like physics.setGravity(0, 9.8) you will notice that the objects fall like the gravity on earth. But for now I did not included any physics into the game that make Rolly Bear fall down.
local physics = require("physics") physics.start() physics.setGravity(0, 0) --physics.setDrawMode( "hybrid" )
As Rolly Bear will be launched from a pipe, it could happen that Rolly Bear flies off the screen. In this case we want the game to be over. This way the game becomes a bit more difficult for the user. To check if Rolly Bear goes off screen I created 4 virtual walls in which we later check for a collision event to happen. Each wall has a myName property for reference (line 6-9). We also add physics to each wall by creating physics.addbody. Each wall has the same physics properties: static, density, friction, bounce and isSensor. Static means that the walls do not respond to the physics engine. Density refers to the mass of the object.
--create wall objects local topWall = display.newRect( 0, 0, display.contentWidth, 0 ) local bottomWall = display.newRect( 0, display.contentHeight - 10, display.contentWidth, 0 ) local leftWall = display.newRect( 0, 0, 0, display.contentHeight ) local rightWall = display.newRect( display.contentWidth - 10, 0, 0, display.contentHeight ) topWall.myName = "topwall" bottomWall.myName = "bottomwall" leftWall.myName = "leftwall" rightWall.myName = "rightwall" physics.addBody(topWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(bottomWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(leftWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(rightWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false})
Next we will create some objects on the screen: a floor, rock, and two wood signs which help the user with information how the game works. The woodSignEnd has a small animation coming from off screen up to the chest. Also on the rock is a switch positioned which the user needs to click to launch Rolly Bear. The switchOff variable only positions the switch on the screen. The green pipe is where Rolly Bear sits before the user launch Rolly Bear into the game.
local floor = display.newImageRect ("images/floortitlescreen.png", 1425 , 156 ) floor.x = centerX; floor.y = heightScrn physics.addBody(floor, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) floor.myName = "floor" group:insert(floor) local rock = display.newImageRect ("images/rock100.png", 300, 96) rock.x = display.screenOriginX + 160; rock.y = heightScrn - floor.height / 1.5 group:insert(rock) local woodSignStart = display.newImageRect ("images/woodsignleft.png", 100, 100) woodSignStart.x = display.screenOriginX + 250; woodSignStart.y = heightScrn - floor.height / 2 woodSignStart.rotation = 10 group:insert(woodSignStart) local woodSignEnd = display.newImageRect ("images/woodsignright.png", 105, 105) woodSignEnd.x = withScrn - 250; woodSignEnd.y = heightScrn + 200 woodSignEnd.rotation = -10 group:insert(woodSignEnd) transition.to( woodSignEnd, { time=2000, x = withScrn - 250, y =heightScrn - floor.height / 3 }) local switchOff = display.newImageRect ("images/woodleverup.png", 64, 64) switchOff.x = rock.x -10; switchOff.y = rock.y + 10 group:insert(switchOff) pipe = display.newImageRect ("images/pipe.png", 144, 224) pipe.rotation = 90 pipe.x = leftScrn; pipe.y = display.screenOriginY + pipe.height/2 physics.addBody(pipe, "static", {density = 1, friction = 0, bounce = 1, isSensor = true }) group:insert (pipe)
The next function will launch Rolly Bear when the user clicks on the switchOff variable that is positioned on the rock. This is initiated by the switchoff:addeventlistener (line 15). We will first remove the art work of the switched off state (line 2) . Second with a lower sound volume we will play the sound effect (wee.mp3) (line 3 and 4). We position the switch on art work so it looks like that the user moved the lever up (line 5 to 7). We launch Rolly Bear with a force of (800,30)(line 8) and make a small scale animation with the green pipe so it look like that the pipe spits out Rolly Bear ( line 9-13).
local function launchRollyBear (event) display.remove( switchOff) audio.play(audioaunchBear) audio.setVolume(0.01, {audioaunchBear} ) local switchOn = display.newImageRect ("images/woodleverdown.png", 64, 64) switchOn.x = rock.x -10; switchOn.y = rock.y + 10 group:insert(switchOn) rollybear:applyForce(800, 30) local function setPipeNormal() pipe.xScale = 1.0 pipe.yScale = 1.0 end transition.to( pipe, {time=100, xScale = 1.2, yScale = 1.2, onComplete=setPipeNormal} ) end switchOff:addEventListener ("tap",launchRollyBear)
We loaded the audio file very simply via this line of code
audioaunchBear = audio.loadSound ("audio/wee.mp3")
The last part that we want is to check for a collision to happen between Rolly Bear and one of the 4 walls or floor (line 2 – 10) We use the myName property to check against this. When a collision happens I print in the console a small text including the wall Rolly Bear hit (line 13). Plus add a gameOver text on the screen. When the user clicks (line 20) on this text (s)he will be directed back to the level selection screen. This flow will be made more nicely in a later post.
Finally we add a runtime eventlistener to check for a collision (line 25). Note that this needs to be a runtime eventlistener as we want to check each moment if a collision is happening.
function onCollision(event) if(event.object1.myName=="rightwall" and event.object2.myName=="rollybear") or (event.object1.myName=="leftwall" and event.object2.myName=="rollybear") or (event.object1.myName=="topwall" and event.object2.myName=="rollybear") or (event.object1.myName=="bottomwall" and event.object2.myName=="rollybear") or (event.object1.myName=="floor" and event.object2.myName=="rollybear") then -- temp solution for gameover print("yeah collision works, I hit the "..event.object1.myName) gameoverbtn = display.newText( "GameOver", 0, 0, "Helvetica", 50) gameoverbtn.x = centerX gameoverbtn.y = centerY display.remove( rollybear) gameoverbtn.destination = "levels" group:insert(gameoverbtn) gameoverbtn:addEventListener ("touch", btnTap) end end Runtime:addEventListener("collision", onCollision)
Here you can see the complete levels01.lua file. In case you are working with the code, I recommend using the code below or the code via GitHub as the code above is not complete and are solely snippets for explanation purpose.
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() -- local forward references should go here -- local physics = require("physics") physics.start() physics.setGravity(0, 0) --physics.setDrawMode( "hybrid" ) local onCollision local audiolaunchBear local function btnTap(event) storyboard.gotoScene ( event.target.destination, {effect = "fade"} ) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view local myStaticgroup = display.newGroup() -- CREATE display objects and add them to 'group' here. -- Example use-case: Restore 'group' from previously saved state. --create wall objects local topWall = display.newRect( 0, 0, display.contentWidth, 0 ) local bottomWall = display.newRect( 0, display.contentHeight - 10, display.contentWidth, 0 ) local leftWall = display.newRect( 0, 0, 0, display.contentHeight ) local rightWall = display.newRect( display.contentWidth - 10, 0, 0, display.contentHeight ) topWall.myName = "topwall" bottomWall.myName = "bottomwall" leftWall.myName = "leftwall" rightWall.myName = "rightwall" physics.addBody(topWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(bottomWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(leftWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) physics.addBody(rightWall, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) local floor = display.newImageRect ("images/floortitlescreen.png", 1425 , 156 ) floor.x = centerX; floor.y = heightScrn physics.addBody(floor, "static", {density = 1.0, friction = 0, bounce = 1, isSensor = false}) floor.myName = "floor" group:insert(floor) local rock = display.newImageRect ("images/rock100.png", 300, 96) rock.x = display.screenOriginX + 160; rock.y = heightScrn - floor.height / 1.5 group:insert(rock) local woodSignStart = display.newImageRect ("images/woodsignleft.png", 100, 100) woodSignStart.x = display.screenOriginX + 250; woodSignStart.y = heightScrn - floor.height / 2 woodSignStart.rotation = 10 group:insert(woodSignStart) local woodSignEnd = display.newImageRect ("images/woodsignright.png", 105, 105) woodSignEnd.x = withScrn - 250; woodSignEnd.y = heightScrn + 200 woodSignEnd.rotation = -10 group:insert(woodSignEnd) transition.to( woodSignEnd, { time=2000, x = withScrn - 250, y =heightScrn - floor.height / 3 }) local switchOff = display.newImageRect ("images/woodleverup.png", 64, 64) switchOff.x = rock.x -10; switchOff.y = rock.y + 10 group:insert(switchOff) pipe = display.newImageRect ("images/pipe.png", 144, 224) pipe.rotation = 90 pipe.x = leftScrn; pipe.y = display.screenOriginY + pipe.height/2 physics.addBody(pipe, "static", {density = 1, friction = 0, bounce = 1, isSensor = true }) group:insert (pipe) local function launchRollyBear (event) display.remove( switchOff) audio.play(audioaunchBear) audio.setVolume(0.01, {audioaunchBear} ) local switchOn = display.newImageRect ("images/woodleverdown.png", 64, 64) switchOn.x = rock.x -10; switchOn.y = rock.y + 10 group:insert(switchOn) rollybear:applyForce(800, 30) local function setPipeNormal() pipe.xScale = 1.0 pipe.yScale = 1.0 end transition.to( pipe, {time=100, xScale = 1.2, yScale = 1.2, onComplete=setPipeNormal} ) end switchOff:addEventListener ("tap",launchRollyBear) rollybear = display.newImage ("images/rollybear40.png") rollybear.x = leftScrn+10; rollybear.y = pipe.y physics.addBody(rollybear, "dynamic", {density = 1, friction = 0, bounce = 1, isSensor = false, radius = 0}) rollybear.isBullet = true rollybear.myName = "rollybear" myStaticgroup:insert(rollybear) group:insert(rollybear) rollybear:toBack() local chestClosed = display.newImageRect("images/chestclosed.png", 128 , 128) chestClosed.x = withScrn - chestClosed.width chestClosed.y = floor.y - chestClosed.height group:insert(chestClosed) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view -- INSERT code here (e.g. start timers, load audio, start listeners, etc.) --soundeffects audioaunchBear = audio.loadSound ("audio/wee.mp3") function onCollision(event) if(event.object1.myName=="rightwall" and event.object2.myName=="rollybear") or (event.object1.myName=="leftwall" and event.object2.myName=="rollybear") or (event.object1.myName=="topwall" and event.object2.myName=="rollybear") or (event.object1.myName=="bottomwall" and event.object2.myName=="rollybear") or (event.object1.myName=="floor" and event.object2.myName=="rollybear") then -- temp solution for gameover print("yeah collision works, I hit the "..event.object1.myName) gameoverbtn = display.newText( "GameOver", 0, 0, "Helvetica", 50) gameoverbtn.x = centerX gameoverbtn.y = centerY display.remove( rollybear) gameoverbtn.destination = "levels" group:insert(gameoverbtn) gameoverbtn:addEventListener ("touch", btnTap) end end Runtime:addEventListener("collision", onCollision) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) local group = self.view -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end -- Called prior to the removal of scene's "view" (display group) function scene:destroyScene( event ) local group = self.view -- INSERT code here (e.g. remove listeners, widgets, save state, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "createScene" event is dispatched if scene's view does not exist scene:addEventListener( "createScene", scene ) -- "enterScene" event is dispatched whenever scene transition has finished scene:addEventListener( "enterScene", scene ) -- "exitScene" event is dispatched before next scene's transition begins scene:addEventListener( "exitScene", scene ) -- "destroyScene" event is dispatched before view is unloaded, which can be -- automatically unloaded in low memory situations, or explicitly via a call to -- storyboard.purgeScene() or storyboard.removeScene(). scene:addEventListener( "destroyScene", scene ) --------------------------------------------------------------------------------- return scene
Find all the code of this tutorial in my GitHub Repository.
1 Response