Tag: lua

How to make a simple physics based shooter game with the Corona SDK

 

Corona Simple ShooterIn this article I’ll share with you a very super simple shooter with Corona. The aim of this code is to show you how simple is to setup physic based behaviors with the Corona SDK, how to setup a game loop, handle basic touches, score system and play sound effects.

About the game

  • We have the player (which is a plane) and this plane is constantly shooting bullets automatically with random speed.
  • Physics based, where the enemies just fall down thanks to the gravity, so we don’t need to worry about moving them.
  • The player and the bullets are of the type “kinematic” so they aren’t affected by gravity.
  • For each bullet that hits an enemy plane, we earn one point, show on the top left of the screen.
  • The only way of losing the game is colliding with an enemy plane.
  • There is pew-pew!

The Complete Game with Assets

I’m using 3 sprites from the SpriteLib by Ari Feldman (those sprites are under GPL, which means we can freely using it under some conditions). For the sounds I used the excellent cfxr.

Download everything here.

NOTE: the code is not ready for production use.

The Source Code

Here is the complete code for the game. It is commented, and you can read it up down, it is placed in a form of tutorial. If you have any doubts/questions, please drop a comment below.

The game code itself without comments is around 150 lines. Also it took me around 20 minutes to make. So you bet it? How simple is that? :)

-- Hide status bar, so it won't keep covering our game objects
display.setStatusBar(display.HiddenStatusBar)

-- Load and start physics
local physics = require("physics")
physics.start()

-- A heavier gravity, so enemies planes fall faster
-- !! Note: there are a thousand better ways of doing the enemies movement,
-- but I'm going with gravity for the sake of simplicity. !!
physics.setGravity(0, 20)

-- Layers (Groups). Think as Photoshop layers: you can order things with Corona groups,
-- as well have display objects on the same group render together at once.
local gameLayer    = display.newGroup()
local bulletsLayer = display.newGroup()
local enemiesLayer = display.newGroup()

-- Declare variables
local gameIsActive = true
local scoreText
local sounds
local score = 0
local toRemove = {}
local background
local player
local halfPlayerWidth

-- Keep the texture for the enemy and bullet on memory, so Corona doesn't load them everytime
local textureCache = {}
textureCache[1] = display.newImage("assets/graphics/enemy.png"); textureCache[1].isVisible = false;
textureCache[2] = display.newImage("assets/graphics/bullet.png");  textureCache[2].isVisible = false;
local halfEnemyWidth = textureCache[1].contentWidth * .5

-- Adjust the volume
audio.setMaxVolume( 0.85, { channel=1 } )

-- Pre-load our sounds
sounds = {
	pew = audio.loadSound("assets/sounds/pew.wav"),
	boom = audio.loadSound("assets/sounds/boom.wav"),
	gameOver = audio.loadSound("assets/sounds/gameOver.wav")
}

-- Blue background
background = display.newRect(0, 0, display.contentWidth, display.contentHeight)
background:setFillColor(21, 115, 193)
gameLayer:insert(background)

-- Order layers (background was already added, so add the bullets, enemies, and then later on
-- the player and the score will be added - so the score will be kept on top of everything)
gameLayer:insert(bulletsLayer)
gameLayer:insert(enemiesLayer)

-- Take care of collisions
local function onCollision(self, event)
	-- Bullet hit enemy
	if self.name == "bullet" and event.other.name == "enemy" and gameIsActive then
		-- Increase score
		score = score + 1
		scoreText.text = score

		-- Play Sound
		audio.play(sounds.boom)

		-- We can't remove a body inside a collision event, so queue it to removal.
		-- It will be removed on the next frame inside the game loop.
		table.insert(toRemove, event.other)

	-- Player collision - GAME OVER
	elseif self.name == "player" and event.other.name == "enemy" then
		audio.play(sounds.gameOver)

		local gameoverText = display.newText("Game Over!", 0, 0, "HelveticaNeue", 35)
		gameoverText:setTextColor(255, 255, 255)
		gameoverText.x = display.contentCenterX
		gameoverText.y = display.contentCenterY
		gameLayer:insert(gameoverText)

		-- This will stop the gameLoop
		gameIsActive = false
	end
end

-- Load and position the player
player = display.newImage("assets/graphics/player.png")
player.x = display.contentCenterX
player.y = display.contentHeight - player.contentHeight

-- Add a physics body. It is kinematic, so it doesn't react to gravity.
physics.addBody(player, "kinematic", {bounce = 0})

-- This is necessary so we know who hit who when taking care of a collision event
player.name = "player"

-- Listen to collisions
player.collision = onCollision
player:addEventListener("collision", player)

-- Add to main layer
gameLayer:insert(player)

-- Store half width, used on the game loop
halfPlayerWidth = player.contentWidth * .5

-- Show the score
scoreText = display.newText(score, 0, 0, "HelveticaNeue", 35)
scoreText:setTextColor(255, 255, 255)
scoreText.x = 30
scoreText.y = 25
gameLayer:insert(scoreText)

--------------------------------------------------------------------------------
-- Game loop
--------------------------------------------------------------------------------
local timeLastBullet, timeLastEnemy = 0, 0
local bulletInterval = 1000

local function gameLoop(event)
	if gameIsActive then
		-- Remove collided enemy planes
		for i = 1, #toRemove do
			toRemove[i].parent:remove(toRemove[i])
			toRemove[i] = nil
		end

		-- Check if it's time to spawn another enemy,
		-- based on a random range and last spawn (timeLastEnemy)
		if event.time - timeLastEnemy >= math.random(600, 1000) then
			-- Randomly position it on the top of the screen
			local enemy = display.newImage("assets/graphics/enemy.png")
			enemy.x = math.random(halfEnemyWidth, display.contentWidth - halfEnemyWidth)
			enemy.y = -enemy.contentHeight

			-- This has to be dynamic, making it react to gravity, so it will
			-- fall to the bottom of the screen.
			physics.addBody(enemy, "dynamic", {bounce = 0})
			enemy.name = "enemy"

			enemiesLayer:insert(enemy)
			timeLastEnemy = event.time
		end

		-- Spawn a bullet
		if event.time - timeLastBullet >= math.random(250, 300) then
			local bullet = display.newImage("assets/graphics/bullet.png")
			bullet.x = player.x
			bullet.y = player.y - halfPlayerWidth

			-- Kinematic, so it doesn't react to gravity.
			physics.addBody(bullet, "kinematic", {bounce = 0})
			bullet.name = "bullet"

			-- Listen to collisions, so we may know when it hits an enemy.
			bullet.collision = onCollision
			bullet:addEventListener("collision", bullet)

			bulletsLayer:insert(bullet)

			-- Pew-pew sound!
			audio.play(sounds.pew)

			-- Move it to the top.
			-- When the movement is complete, it will remove itself: the onComplete event
			-- creates a function to will store information about this bullet and then remove it.
			transition.to(bullet, {time = 1000, y = -bullet.contentHeight,
				onComplete = function(self) self.parent:remove(self); self = nil; end
			})

			timeLastBullet = event.time
		end
	end
end

-- Call the gameLoop function EVERY frame,
-- e.g. gameLoop() will be called 30 times per second ir our case.
Runtime:addEventListener("enterFrame", gameLoop)

--------------------------------------------------------------------------------
-- Basic controls
--------------------------------------------------------------------------------
local function playerMovement(event)
	-- Doesn't respond if the game is ended
	if not gameIsActive then return false end

	-- Only move to the screen boundaries
	if event.x >= halfPlayerWidth and event.x <= display.contentWidth - halfPlayerWidth then
		-- Update player x axis
		player.x = event.x
	end
end

-- Player will listen to touches
player:addEventListener("touch", playerMovement)

Learning Diary #10: Lua, socket server, path finding, quaternions

 

12/Jul/2010

Using Lua and a Socket Server to test and debug iPhone Games

Damn, too many balloons. Ops, just change the Lua code real time!

Damn, too many balloons. Ops, just change the Lua code real time!

I already talked about #iDevBlogADay. But let’s say it again: I just love this “group”! The first thing I do when I wake up is checking the feed for the day’s articles. And how sad I get if an author hadn’t published his take yet.

Everyday we get 2 new nice articles from fellow indie (or established or beginners – like me)  game developers. One of these articles “Tweak Away” from Mystery Coconut brings Lua and socket servers into your iPhone game development.

Main points you’ll learn from the article:

  • How to open a debug server for your game.
  • How to integrate Lua into your game and how it can access your game elements.
  • Change your game behaviors and state via command line (e.g.: no more re-compiling to change the speed of a ball).
  • Scripting all of your game content and behavior.

And yes, Miguel makes everything that easy for us :)

Path Finding

  • Path finding class for a RTS game: ASIPathFinder. “ASIPathFinder is a complete implementation of a cooperative path finding algorithm, and will probably be most useful for writing Real Time Strategy games. It is written in Objective-C, and is compatible with Mac OS and iPhone OS.”
  • Cocos2D, Path finding and Tile maps – sample app: cool working code which I found on Cocos2D forums.

Cocos2D

  • RenderTexture: let’s explain via an example: for a shooter game which leaves bullet holes, we could use sprites for these holes, but a RenderTexture is memory saver.
    a) Sprites: 500 holes – 500 sprites – 1000 triangles – 2000 vertexes.
    b) RenderTexture: will draw only one sprite – it takes a picture and work in that picture.
  • EAGLView: create the OpenGL context.

Drawing with Quartz2D

  1. Save current context
  2. Perform a single combined matrix transformation
  3. Draw graphics to this transformation
  4. Restore context

Source: Beginning iPhone Games Development

Quaternion

  • Sum of a scalar and a vector
  • The quotient of two vectors
  • Imaginary number: i * i = -1
  • A quaterion is a complex number extension: 3 numbers all square roots of -1: i, j, k
  • j * j = -1, k * k  = -1
  • q = w + xi + yj + zk (w = real, x, y, z = complex).
  • or q = [w, v] (w = scalar, v = (x,y,z) – vector)

Source: http://www.gamedev.net/reference/articles/article1095.asp, http://www.paradeofrain.com/2010/07/lessons-learned-in-tilt-controls/