Making a mobile 2D shooter game
Today I started working on a simple shooter game. It will be a fast and fun 2D shooter that is easy to learn, imposible to master! The art comes from the Kenney.nl donation package. I want to take advantage of the accelerometer to create some cool gameplay mechanics. It runs on mobile devices (android and iOS) and is built using Lua and CoronaSDK.
I will be updating this blog post as I continue to develop this game.
1. Background & Layers
First thing I did was the background environment. I wanted to take advantage of parallax and layers to give the game some depth. I ended up dividing the scene into 12 groups or layers. These 12 layers help me sort the objects into different Z depths. The Z depth array determines which object will appear in front of others so the scene looks correct.
After dividing the scene, I used a nice little trick for creating a parallax effect between the 12 layers. It gives the game simulated 3d depth. I then connected the accelerometer so the different parallax groups react to accelerometer movement. You can read more about the parallax in this previous post.
Here is the finished Background with layering.
The system that controls the background environment is the Background.lua module. In order to use the module you must initialize by passing in the Z sorting layers:
function Background:init(zSorter) -- Setup the parallax groups createWoodBack(zSorter) createGrassFar(zSorter) createWavesFar(zSorter) createWavesNear(zSorter) createGrassNear(zSorter) createBackCurtain(zSorter) createMidCurtain(zSorter) createFrontCurtain(zSorter) createWoodTable(zSorter) end
This function calls a bunch of helper functions that take in a Z sort group (layer) as parameter. If we want to change the draw order, we just pass in a different layer.
I’m now working on a spawn system for the targets. More updates soon!
2. Spawning Ducks
The next thing I worked on was spawning some Duck targets than can be destroyed by the player. My strategy was to create a factory pattern that builds and sets up the Duck with a single function call. This allows me to script the behavior of the ducks and when/where they spawn and make fast tweaks to the game.
This code creates a new Duck using the Factory
function Factory:makeDuck(id, generator, x, laneIndex) -- Create a new Duck object. local duck = Duck:new(id, generator, x, spawnY[laneIndex]) -- Register this duck in our management lists table.insert(self.ducks, duck) -- Keeps track of the ducks created parallaxInit(duck) -- Enables this duck for parallax effect parallaxInit(duck.img02) -- Enables the duck's second image for parallax -- Assigns the Duck's images to a specific lane (1 - 5) for proper zSorting lane[laneIndex]:insert(duck.img02) lane[laneIndex]:insert(duck) -- Remembers the lane it is so it can correcty display in parallax effect duck.laneIndex = laneIndex -- Register this duck for user's touch event duck:addEventListener("touch", onObjectTouch) -- Return the new duck instance that has already been registered for gameplay events return duck end
The ducks are composed of 2 images, so I had to override some functions (like setPos(x,y) ) so that it would affect both images. Currently there is no animation for spawning the ducks, but the functions are in place so they can be easily added in the near future.
As soon as the ducks go offscreen (left-side or right-side) they are re-spawned at the opposite side, giving the illusion that there are many ducks, but they are just being recycled. This is good for mobile phones since they have limited memory resources.
Here is a screenshot of the Ducks on screen:
3. Particle System
Got the particle system working. I am using Particle Designer 2 to export json settings for the particles.
I worte a Particle Sytem Manager that serves as an Emitter factory. Here is some come that is used to load the particles:
function P:init(jsonNames) -- Loop the json names and load the emitter configs for index,name in pairs(jsonNames) do local filepath = system.pathForFile("sprites/".. name ..".json") if(filepath ~= nil) then local f = io.open(filepath, "r") local fileData = f:read("*a") f:close() -- Decode the string and map it using the emitter name emittersParams[name] = json.decode(fileData) else print("Failed to load emitter from json: " .. name) end end end
Work in progress… more soon…
4. Sound Effects
5. Gameplay polish
6. Play-test, tweak, repeat.