HTML5 version of zNumbers game – Defold version

Read all posts about "" game
Defold engine, the free forever engine for 2D games powered by King with more than 40,000 registered users, is gaining more and more popularity and Björn Ritzl, who already ported my version of Mike Dangers game on Defold now released the Defold version of my Phaser zNumbers prototype. The game also looks very good thanks to Kenney‘s icons.
Defold uses LUA which is quite understandable if you are used to programming and you had a look at the commented source code of my Phaser prototype.
local WIDTH = 6
local HEIGHT = 6

local DIRECTIONS = {
	{ x = -1, y = -1 },
	{ x = -1, y =  0 },
	{ x = -1, y =  1 },
	
	{ x =  1, y = -1 },
	{ x =  1, y =  0 },
	{ x =  1, y =  1 },
	
	{ x =  0, y = -1 },
	{ x =  0, y =  0 },
	{ x =  0, y =  1 },
}

local LEVELS = {
	{ -- level 1
		{0, 0, 0, 0, 0, 0},
		{3, 2, 3, 2, 2, 2},
		{0, 0, 2, 3, 2, 2},
		{2, 0, 2, 2, 0, 0},
		{0, 2, 3, 0, 2, 2},
		{2, 3, 0, 2, 0, 4}
	},
	{ -- level 2
		{0, 2, 3, 3, 2, 1},
		{2, 0, 3, 3, 0, 2},
		{1, 4, 3, 3, 0, 1},
		{1, 4, 3, 3, 0, 1},
		{2, 0, 3, 3, 0, 2},
		{0, 2, 3, 3, 2, 1}
	},
	{ -- level 3
		{0, 2, 2, 2, 0, 2},
		{1, 1, 1, 1, 1, 1},
		{1, 3, 0, 3, 3, 1},
		{1, 3, 3, 3, 3, 1},
		{1, 1, 1, 1, 1, 1},
		{4, 4, 0, 4, 0, 1}
	},
	{ -- level 4
		{3, 4, 2, 2, 0, 2},
		{1, 1, 1, 1, 1, 1},
		{0, 1, 3, 0, 3, 1},
		{1, 3, 1, 3, 1, 1},
		{1, 1, 1, 1, 1, 1},
		{3, 0, 0, 4, 0, 1}
	},
	{ -- level 5
		{4, 2, 1, 2, 1, 4},
		{1, 2, 1, 1, 2, 1},
		{1, 3, 2, 3, 3, 1},
		{1, 1, 2, 2, 3, 1},
		{1, 0, 1, 1, 2, 1},
		{4, 0, 0, 4, 1, 4}
	},
	{ -- level 6
		{3, 2, 1, 1, 2, 1},
		{1, 2, 1, 0, 2, 2},
		{2, 1, 1, 1, 2, 1},
		{1, 2, 1, 1, 2, 1},
		{0, 2, 1, 1, 2, 4},
		{3, 0, 0, 4, 0, 4}
	},
	{ -- level 7
		{2, 0, 2, 3, 0, 2},
		{0, 2, 1, 3, 2, 1},
		{2, 0, 1, 3, 0, 2},
		{0, 2, 3, 3, 2, 1},
		{2, 0, 1, 3, 0, 2},
		{0, 2, 0, 3, 2, 1}
	},
	{ -- level 8
		{1, 3, 0, 1, 1, 2},
		{0, 4, 0, 0, 2, 3},
		{3, 1, 0, 0, 3, 1},
		{1, 2, 0, 0, 1, 2},
		{4, 3, 1, 0, 4, 3},
		{2, 4, 1, 1, 2, 4}
	},
	{ -- level 9
		{4, 1, 0, 0, 2, 4},
		{2, 4, 2, 4, 1, 0},
		{0, 0, 2, 0, 4, 2},
		{1, 1, 0, 0, 4, 2},
		{2, 4, 1, 4, 0, 1},
		{3, 0, 4, 1, 0, 4}
	},
	{ -- level 10
		{3, 2, 2, 0, 2, 0},
		{2, 3, 2, 3, 2, 0},
		{0, 1, 3, 1, 2, 2},
		{1, 1, 0, 0, 0, 1},
		{2, 2, 1, 3, 3, 1},
		{3, 2, 1, 1, 2, 2}
	}
}

local function play_animation(id, animation)
	msg.post(msg.url(nil, id, "sprite"), "play_animation", { id = animation })
end

-- update the visual representation of the cell based on its state
local function update_cell(cell, pressed)
	if cell.number and cell.number > 0 then
		label.set_text(msg.url(nil, cell.square_id, "label"), cell.number)
		if pressed then
			play_animation(cell.square_id, hash("buttonSquare_lightblue"))
		elseif cell.locked then
			play_animation(cell.square_id, hash("buttonSquare_darkblue_pressed"))
		else
			play_animation(cell.square_id, hash("buttonSquare_blue"))
		end
	else
		label.set_text(msg.url(nil, cell.square_id, "label"), "")
		play_animation(cell.square_id, hash("buttonSquare_darkblue_pressed"))
	end
end

-- find a cell on the map based on the game object id of the square
local function find_cell(board, square_id)
	for x=1,WIDTH do
		for y=1,HEIGHT do
			local cell = board[x][y]
			if cell.square_id == square_id then
				return cell
			end
		end
	end
end

-- find empty target cells at the correct distance from a specific cell
-- containing a number
local function find_targets(board, cell)
	local targets = {}
	for _,dir in pairs(DIRECTIONS) do
		local tx = cell.x + cell.number * dir.x
		local ty = cell.y + cell.number * dir.y
		if tx > 0 and tx <= WIDTH and ty > 0 and ty <= HEIGHT then
			local target = board[tx][ty]
			if not target.number then
				table.insert(targets, target)
			end
		end
	end
	return targets
end

-- highlight target cells
local function highlight_targets(board, cell)
	local targets = find_targets(board, cell)
	for _,target in ipairs(targets) do
		play_animation(target.square_id, hash("buttonSquare_grey_pressed"))
	end
end

-- reset target cells
local function reset_targets(board, cell)
	local targets = find_targets(board, cell)
	for _,target in ipairs(targets) do
		play_animation(target.square_id, hash("buttonSquare_darkblue_pressed"))
	end
end

local function random(max, fillrate)
	if math.random() > fillrate then return 0 end
	local n = 1
	for i=1,max-1 do
		n = n + math.random(0, 1)
	end
	return n
end

local function new_level(self, level, additional_delay)
	self.level = level
	self.board = {}
	self.numbers_left = 0
	for x=1,WIDTH do
		self.board[x] = {}
		for y=1,HEIGHT do
			local cell = { x = x, y = y, locked = false }
			self.board[x][y] = cell

			-- create the square and let it fall down to it's position
			local duration = 1
			local delay = math.random(1,10) * 0.05 + x * 0.05 + y * 0.1 + (additional_delay or 0)
			local to = vmath.vector3(140 + x * 48, 360 + y * 48, 1 - y / 10)
			cell.square_id = factory.create("#squarefactory", to + vmath.vector3(0, 1000, 0))
			go.animate(cell.square_id, "position", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_OUTBOUNCE, duration, delay)

			-- possibly assign a number
			local number = random(4, 0.5)
			if level <= #LEVELS then
				number = LEVELS[level][x][y]
			end
			if number > 0 then
				cell.number = number
				self.numbers_left = self.numbers_left + 1
			end

			update_cell(cell, false)
		end
	end
end

local function end_level(board)
	local down = vmath.vector3(0, 1000, 0)
	for x=1,WIDTH do
		for y=1,HEIGHT do
			local cell = board[x][y]
			local duration = 0.5
			local delay = math.random(1,10) * 0.05 + x * 0.05 + y * 0.1
			local to = go.get_position(cell.square_id) - down
			go.animate(cell.square_id, "position", go.PLAYBACK_ONCE_FORWARD, to, go.EASING_INQUAD, duration, delay, function()
				go.delete(cell.square_id)
			end)
		end
	end
end

function init(self)
	msg.post(".", "acquire_input_focus")
	msg.post("@render:", "clear_color", { color = vmath.vector4(0.15, 0.15, 0.15, 1.0) })
	math.randomseed(os.time())
	new_level(self, 1)
end

function on_message(self, message_id, message, sender)
	-- keep track of which cell we're currently pointing at
	if message_id == hash("trigger_response") then
		if message.enter then
			self.over_id = message.other_id
		elseif self.over_id == message.other_id then
			self.over_id = nil
		end
	elseif message_id == hash("restart") then
		end_level(self.board)
		new_level(self, self.level, 2)
	end
end

function on_input(self, action_id, action)
	-- move the cursor
	go.set_position(vmath.vector3(action.x, action.y, 0))

	-- handle clicks
	if action_id == hash("touch") and action.released then
		-- get which cell we clicked on
		local current = find_cell(self.board, self.over_id)
		if self.selected_id then
			-- get the cell that the user has selected to move
			local selected = find_cell(self.board, self.selected_id)
			
			-- deselect if clicking on the same as the selected cell
			if selected == current then
				reset_targets(self.board, selected)
				update_cell(selected, false)
				self.selected_id = nil
			-- deselect and select a new one
			elseif not current.locked and current.number then
				reset_targets(self.board, selected)
				update_cell(selected, false)
				self.selected_id = self.over_id
				update_cell(current, true)
				highlight_targets(self.board, current)
			-- try to move the selected cell to the one we clicked on
			else
				local targets = find_targets(self.board, selected)
				for _,target in ipairs(targets) do
					if target == current then
						reset_targets(self.board, selected)
						self.selected_id = nil
						target.number = selected.number
						target.locked = true
						selected.number = nil
						go.set_position(go.get_position(target.square_id), target.number_id)
						update_cell(selected, false)
						update_cell(target, false)
						self.numbers_left = self.numbers_left - 1
						if self.numbers_left == 0 then
							end_level(self.board)
							new_level(self, self.level + 1, 2)
						end
						break
					end
				end
			end
		-- clicked on a non-locked cell with a number
		elseif self.over_id and not current.locked and current.number then
			self.selected_id = self.over_id
			update_cell(current, true)
			highlight_targets(self.board, current)
		end
	end
end
The whole project code is on Björn GitHub page and if you want to give it a try, I would suggest to grab the Defold Editor 2, it’s still in beta but it features a dark interface and the capability of importing projects right from the disk.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers