The basics of an HTML5 3D Concentration game made with Babylon.js

Read all posts about "" game

Earlier this week I showed you the creation of a 3D Sokoban level with Babylon.js, now it’s time to create something interactive, like a Concentration game.

In this step, I will only focus on the creation and selection of a single tile, but the topics covered are quite a number and pretty advanced, such as:

* Texture mapping
* Using different textures on different meshes of the same body
* Directional lights
* Shadows
* Selection of bodies with the mouse
* Animations and keyframes

This is what you are going to build:

Click on the yellow tile to turn it and reveal its hidden color.

The concept behind the script continues where the creation of 3D Sokoban level with Babylon.js ended, so I recommend to start from the previous example if you are new to Babylon.

The new, fully commented content of game.js used to make this example is:

// just a Boolean variable to say if we picked the tile
var picked=false;
// identifying the canvas id
var canvas = document.getElementById("gameCanvas");
// creation of the engine itself
var engine = new BABYLON.Engine(canvas,true);
// attaching a scene to the engine. This is where our game will take place
var scene = new BABYLON.Scene(engine);
// adding a little fog to the scene, to give some kind of "depth" to the scene
scene.fogMode = BABYLON.Scene.FOGMODE_EXP;
// the density is very high, so a low value is recommended
scene.fogDensity = 0.05;
// creation of a camera, the type is "AcrRotate".
// this mean the camera is bound along two arcs, one running from north to south, the other from east to west
// the first argument is the came of the camera instance
// the second argument is the angle along the north-south arc, in radians (3 * Math.PI / 2)
// the 3rd argumentis the angle along the east-west arc, in radians (3*Math.PI/4)
// the 4th argument is the radius of such arcs (20)
// the 5th argument is the camera target (BABYLON.Vector3.Zero()) in this case the origin
// finally, the scene where to attach the camera ("scene")
var camera = new BABYLON.ArcRotateCamera("camera",3 * Math.PI / 2, 11*Math.PI/16, 20, BABYLON.Vector3.Zero(), scene);
// adding touch controls to camera, that's where hand.js come into play
camera.attachControl(canvas, false);
// we need a directional light in order to cast a shadow
var light = new BABYLON.DirectionalLight("light", new BABYLON.Vector3(5,0,20), scene);
light.position = new BABYLON.Vector3(1,1,-10);
 
// this is the table material. We will map an image called "wood.jpg" on it
var tableMaterial = new BABYLON.StandardMaterial("tableMaterial", scene);
tableMaterial.diffuseTexture = new BABYLON.Texture("wood.jpg", scene);

// card material will be made with 2 different materials.
// The first material is "cardMaterial", a yellow color
var cardMaterial = new BABYLON.StandardMaterial("cardMaterial", scene); 
cardMaterial.diffuseColor = new BABYLON.Color3(1,1,0);
// the second material is "cardBackMaterial", a purple color
var cardBackMaterial = new BABYLON.StandardMaterial("cardBackMaterial", scene); 
cardBackMaterial.diffuseColor = new BABYLON.Color3(1,0,1);

// with these two colors in mind, let's built a multi material
var cardMultiMat = new BABYLON.MultiMaterial("cardMulti", scene);
// here is how we push the materials into a multimaterial
cardMultiMat.subMaterials.push(cardMaterial);
cardMultiMat.subMaterials.push(cardBackMaterial);
// this is the content of our multi material - 0: CardMaterial, 1: CardBackMaterial

// THE TABLE
var table = BABYLON.Mesh.CreateBox("table", 10, scene);
table.scaling.z = 0.025;
table.scaling.x = 2;
table.material=tableMaterial
// we must specify that the table is receving shadows
table.receiveShadows = true;

// THE CARD
var card = BABYLON.Mesh.CreateBox("card", 2, scene);
card.scaling.z = 0.125;
card.position = new BABYLON.Vector3(0,0,-0.25);
// defining two different meshes, one for the bottom face and one for the rest of the card
card.subMeshes=[];
// arguments of Submesh are:
// 1: the index of the material to use
// 2: the index of the first vertex
// 3: the number of verices used
// 4: index of the first indice to use
// 5: the number of indices
// 6: the main mesh 
card.subMeshes.push(new BABYLON.SubMesh(0, 4, 20, 6, 30, card));
card.subMeshes.push(new BABYLON.SubMesh(1, 0, 4, 0, 6, card));

// finally assigning the multi material to the card
card.material=cardMultiMat

// attaching the light to shadow generator
var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
// here is how we say the card should cast shadows
shadowGenerator.getShadowMap().renderList.push(card);
   
engine.runRenderLoop(function () {
     scene.render();
});

// a simple click listener
window.addEventListener("click", function (evt) {
	// with "scene.pick" we can obtain information about the stuff we picked/clicked 
	var pickResult = scene.pick(evt.clientX, evt.clientY);
	// if we haven't already picked anything and we are picking a mesh and that mesh is called "card"...
	if(!picked && pickResult.pickedMesh!=null && pickResult.pickedMesh.name=="card"){
		// set "picked" to true as we won't be able to pick it again
		picked=true;
		// let's start the animation
		var moveAnimation = new BABYLON.Animation(
			"moveAnimation", // name I gave to the animation 
			"position.z", // property I am going to change
			30, // animation speed
			BABYLON.Animation.ANIMATIONTYPE_FLOAT, // animation type
               BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT // animation loop mode
			// play with BABYLON.Animation.ANIMATIONLOOPMODE_RELATIVE,
			// BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
			// BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
		);

        	var rotateAnimation = new BABYLON.Animation(
			"rotateAnimation",
			"rotation.y", // this time I rotate the tile around y axis
			30,
			BABYLON.Animation.ANIMATIONTYPE_FLOAT,
               BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
		);

		// now let's add keyframes to our animations
		var moveKeys = [
			{
				frame: 0,
				value: -0.25
			},
			{
				frame: 20,
				value: -2
			}
		];
    		
    		var rotateKeys = [
			{
				frame: 0,
        			value: 0
			},
			{
				frame: 20,
        			value: 0
			},
			{
				frame: 40,
        			value: Math.PI
			}			    
		]
    
    		// adding keyframes to animation
    		moveAnimation.setKeys(moveKeys);
    		rotateAnimation.setKeys(rotateKeys);

    		// adding animations to the card
    		card.animations.push(moveAnimation);
    		card.animations.push(rotateAnimation);

    		// launching animation
		scene.beginAnimation(card, 0, 40, true);     	
    }
});

As you can see, everything is easy and intuitive with Babylon, next time I will show you a complete 3D Concentration game, meanwhile download the source code, with all required libraries.

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