How To Make a Game Like Doodle Jump with Corona Tutorial Part 2

This is a blog post by iOS Tutorial Team member Jacob Gundersen, an indie game developer who runs the Indie Ambitions blog. Check out his latest app – Factor Samurai! In this tutorial series, you’ll learn how to use the Corona game engine to create a neat game like Doodle Jump. In the previous tutorial, […] By Jake Gundersen.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 5 of this article. Click here to view the first page.

Adding Touch Handling

We're ready to move on to handling touch functions. Touch events can either be handled globally by registering the event with the Runtime object or the touch events can be directed towards specific objects. Because we are using the screen to shoot and to move the player, we are going to use the Runtime object.

We will be adding our touch code function at the root level of our program, so this code can be added anywhere as long as it's not inside another function. Our touch code function looks like this:

function touchListener(event)
	if event.phase == "began" then 
		player:shootArrow(event.x, event.y)
		
		local vx, vy = player:getLinearVelocity()
		if event.x > player.x then
			player:setLinearVelocity(70, vy)
		elseif event.x < player.x then
			player:setLinearVelocity(-70, vy)
		end
	end
	
	if event.phase == "moved" then
		local vx, vy = player:getLinearVelocity()
		if event.x > player.x then
			player:setLinearVelocity(70, vy)
		elseif event.x < player.x then
			player:setLinearVelocity(-70, vy)
		end
	end
	
	if event.phase == "ended" then
		local vx, vy = player:getLinearVelocity()
		player:setLinearVelocity(0, vy)
	end
end

This code looks long, but it's actually the same logic repeated several times. The touch function has an argument, that we have named event. Event has several properties, including a phase property. The phase corresponds to touchBegan, touchMoved, and touchEnded events in Cocos2d or UIKit touch handling.

We want to fire one arrow each time the screen is touched, so we'll place that code in the "began" phase of our code.

The event object inside this function has an x and y property that correspond to the touch position on the screen. We call our shootArrow method on our player property and pass in the touch coordinates. This will now shoot an arrow.

We'll also be using touch code to move our player across the screen. This is a convenience for those who wish to play the game in the simulator, where the accelerometer isn't available.

First we need to get the y linear velocity of the player so that we can pass that value in to retain the constant velocity of the player in the vertical dimension. The getLinearVelocity call always returns a pair of values, so we need to set the variables up as a pair, even though we won't need the current x velocity.

Once we have the vy variable we test to see whether we touched to the left or right of our player, and based on that we set the linear velocity with our current y velocity (vy) and a strength of 70 in the x dimension toward the touch.

We apply this logic on both the touch began and the touch moved, this way we can drag the touch around and change the direction of the player. Once we release the touch, we want the player to no longer move toward the previous touch so in that case, event.phase == "ended", we set the x velocity to 0.

That's are touch listener. We just need to add it to the Runtime object. Add this code beneath the touch listener code:

Runtime:addEventListener("touch", touchListener)

Lets go through the anatomy of an addEventListener call. It will always be called on an object, object:addEventListener. The first argument is the event type. The event type will determine what kinds of arguments that the function will pass through to the body of the function.

The second argument is either the object or the name of the function. In the case of a listener added to a specific object, any other than Runtime, this argument will be the name of the object. If the call is on the Runtime object, then the second parameter is the name of the function without the () included.

You may wonder how the listener knows what function to call if we don't pass a function in. In the case of a collision listener, we must set the .collision property before we call the addEventListener method. In the case of an enterFrame function, the name of the function must be called 'enterFrame.'

Save and run now. You should be able to shoot arrows and control the movement of your character by touching (or clicking in the simulator) on the screen.

Player shooting arrows in our level

It's starting to get pretty awesome, huh! Just a bit more to go to wrap up this game!

Gratuitous Music and Sound Effects

Lets take a quick break and do something easy, add audio. Audio calls all start with a method call to the audio object, this object is built into Corona and doesn't require and special imports to use it.

Start by copying all of the sound files from the resources for this project into the same folder as the main.lua file.

Then go back to the top of the file and after the 'require("LevelHelperLoader")' line add the following:

bgMusic = audio.loadStream("Enchanted Journey.mp3")
backgroundMusicChannel = audio.play( bgMusic, { channel=2, loops=-1, fadein=5000 })
audio.setMaxVolume(0.5, {channel = 2})
shoot = audio.loadSound("shoot.wav")
explode = audio.loadSound("explode.wav")
jump = audio.loadSound("jump3.wav")
monsterKill = audio.loadSound("monsterkill.wav")

Adding audio is pretty straight forward. The first line preloads the Enchanted Journey.mp3 file into memory and prepares to play it. In our second line we are calling the play method. This call takes two arguments, the first is our object we created when we preloaded the music file.

The second is actually a table (tables are like dictionaries and arrays combined in Lua). This table has a number of parameters, not all of which are included in this call. We are just giving it a channel, telling it to loop forever, and asking it to fade in over 5000 ms. We set the channel to 2 in order to reduce the volume of that channel in the next line.

The next four lines are preloading four audio sound effects that we will use to play at different events in our code. We will call audio.play() on each of these variables to play the audio event.

Place the following lines of code in the following places to add sound effects to those events:

--pCollision function - inside newPlayer function

			if vy > 0 then 
				if object.tag == LevelHelper_TAG.CLOUD then
					self:setLinearVelocity(0, -350)
					audio.play(jump)
				elseif object.tag == LevelHelper_TAG.BCLOUD then
					loader:removeSpriteWithUniqueName(object.uniqueName)
					audio.play(explode)
				end
			end

--p:shootArrow function - inside newPlayer function

		arrow = loader:newObjectWithUniqueName("arrow", physics)
		loader:startAnimationWithUniqueNameOnSprite("shoot",frontarm)
		localGroup:insert(arrow)
		audio.play(shoot)
		
--arrowCollision function - inside shootArrow function

		object = event.other
		loader:removeSpriteWithUniqueName(object.uniqueName)
		audio.play(monsterKill)

One other thing that we want to add is a blue sky background. Add the following code to the top after the audio file loading:

blueSky = display.newRect(0, 0, 320, 480)
blueSky:setFillColor(160, 160, 255)
score = display.newText("0", 30, 10, "Helvetica", 20)

These first two lines create a rectangle the size of the screen. There are a number of display functions that draw primitives, load sprites, or create groups (as you've seen). The newRect function takes an x and y position along with a width and height as arguments.

The second call fills the screen with the color r = 160, g = 160, and b = 255.

Finally, we create a new display object, a text label with the newText call. This function takes the string, an x and y position, the font name, and font size as its arguments.

The fonts available on the device natively are available through this call as well as additional fonts included in the folder. Additional fonts need to be added to the build.settings file which mirrors the info.plist. That discussion is beyond the scope of this tutorial.

If you run it now, it should look more like your hero is jumping around in the sky, and the score will be onscreen. We're going to slowly change the background as we climb up the level to have fewer clouds and look more like outer space by adding stars.

Adding a blue background for the level

Jake Gundersen

Contributors

Jake Gundersen

Author

Over 300 content creators. Join our team.