Crossey Road Game Clone Using HTML CSS And JavaScript

Introduction
This is our HTML code which sets up the basic structure of crossy road game. Our HTML code is divide into several section like head, body and script. Let’s understand each section of the code.
- <!DOCTYPE html>: This tag defines the type of our document and ensures that our file is a HTML document.
- <title>: This sets the title of our webpage as “Crossy Road Game Clone”.
- <link>: This tag links external CSS file into our HTML code.
- <body>: This is main part of our code as it contains the main content of our crossy road game.
- <div id=”counter”>: This div with id counter is used to display a counter with initial value at 0.
- <div id=”controls”>: This div contains a set of buttons for controlling the game. It allows the player to move in different directions.
- <div>: This is another div which contains group of buttons for each and specific direction. Each button has a unique id and an associated SVG icon that represents the direction.
- <button id=”retry”>: This button with id retry within a div with id end. This button works as retry button which allows user to restart the game.
- <script>: This tag links external JavaScript files and libraries. This link an three js external file and a JavaScript file from local directory.
This is all about our HTML code.
HTML (index.html)
Get Discount on Top Educational Courses
This is our HTML code which sets up the basic structure of crossy road game. Our HTML code is divide into several section like head, body and script. Let’s understand each section of the code.
- <!DOCTYPE html>: This tag defines the type of our document and ensures that our file is a HTML document.
- <title>: This sets the title of our webpage as “Crossy Road Game Clone”.
- <link>: This tag links external CSS file into our HTML code.
- <body>: This is main part of our code as it contains the main content of our crossy road game.
- <div id=”counter”>: This div with id counter is used to display a counter with initial value at 0.
- <div id=”controls”>: This div contains a set of buttons for controlling the game. It allows the player to move in different directions.
- <div>: This is another div which contains group of buttons for each and specific direction. Each button has a unique id and an associated SVG icon that represents the direction.
- <button id=”retry”>: This button with id retry within a div with id end. This button works as retry button which allows user to restart the game.
- <script>: This tag links external JavaScript files and libraries. This link an three js external file and a JavaScript file from local directory.
This is all about our HTML code.
CodeWithCurious.com - Crossy Road Game Clone
0
CSS (Style.css)
After completing our HTML code. Now we’ve write our CSS code. As we know that CSS designs and adds interactivity to the webpages. Using our CSS code we’ll style our crossy road game. Let’s understand the code.
- First it imports a font Press Start 2P from the google fonts. It allows you to use this font in your code.
- Body sets up the margin as 0, font family as Press Start 2P, font size of 2em with a text color of white.
- Next, using the button selector button element is styled. Removes the outline of button, cursor sets as pointer, border:none with some box shadow to the button element.
- #counter sets the position as absolute. It sets top and right as 20px.
- #end selector sets position absolute with min-height and min-width of 100%. It centers the element using flexbox property and set its visibility as hidden.
- #end button sets the background color red and padding from all sides. It sets font-family and font-size as inherit.
- #controlls selector sets it position of absolute and min-height and min-width of 100%. It sets display flex , align items as flex end with justify content as center.
- #controlls div sets display grid which makes these div to work as a grid container. Using other grid properties it sets the elements. This adds a margin bottom of 20px.
- #controlls button sets width of 100% with background color of white and border of 1px solid lightgray.
- #controlls button:first-of-type sets the grid column property which spans the button across all columns of the grid layout.
@import url("https://fonts.googleapis.com/css?family=Press+Start+2P");
body {
margin: 0;
font-family: "Press Start 2P", cursive;
font-size: 2em;
color: white;
}
button {
outline: none;
cursor: pointer;
border: none;
box-shadow: 3px 5px 0px 0px rgba(0, 0, 0, 0.75);
}
#counter {
position: absolute;
top: 20px;
right: 20px;
}
#end {
position: absolute;
min-width: 100%;
min-height: 100%;
display: flex;
align-items: center;
justify-content: center;
visibility: hidden;
}
#end button {
background-color: red;
padding: 20px 50px 20px 50px;
font-family: inherit;
font-size: inherit;
}
#controlls {
position: absolute;
min-width: 100%;
min-height: 100%;
display: flex;
align-items: flex-end;
justify-content: center;
}
#controlls div {
display: grid;
grid-template-columns: 50px 50px 50px;
grid-template-rows: auto auto;
grid-column-gap: 10px;
grid-row-gap: 10px;
margin-bottom: 20px;
}
#controlls button {
width: 100%;
background-color: white;
border: 1px solid lightgray;
}
#controlls button:first-of-type {
grid-column: 1/-1;
}
Javascript (Script.js)
After completing the HTML and CSS code, it’s time for our JavaScript code. This JavaScript code adds functionality in our game and updates scores and levels of our game.
- First it accessed essential HTML elements using DOM and stored them in variables.
- Scene initializes a 3D scene using three.js library.
- Creates a orthographic camera using three.js . The Camera is positioned based on its rotation, using trigonometry to calculate X, Y, and Z positions for a tilted view.
- Adds a hemiLight for soft lightning. Adds dirLight for stronger shadows. This sets shadow for the lights to enable shadows.
- Using const we defines some variable like distance, zoom, chickenSize with some initial value.
- lanes, currentLane and currenColumn track the lane and chicken’s position on the road.
- The Texturefunction is defined, which creates a texture using the CanvasTexture class from Three.js. It takes a width, height, and an array of rectangles as parameters, and returns the resulting texture.
- The generateLanesfunction creates and adds lanes to the scene based on the number of columns. Each lane is an instance of the Lane class, and its position is set based on the index.
- The addLane function adds a new lane when the chicken of the game moves forward.
- An instance of the Chicken class is created and added to the scene.
- The car function generates the car based on the properties and truck function generates a truck based on the properties.
- In the movement logic, arrow button control our chicken’s movement in game.
- Move function updates the chicken’s position in game and ensure that it doesn’t collide with trees or any other object.
- Lane function defines the different lanes for the chicken’s like road or forest.
- Vehicles moves along with road lane until they reach end of the screen.
- The animate function is main function of our code as it control all the objects movement in our game.
- Renderer setup the shadows give a realistic 3D effect.
- Event listeners are added for keyboard input and restart the game or move the chicken.
- The initaliseValues function resets game state, positions, and lane objects when retrying the game.
This was all about our JavaScript code. Using this code we’ve made our game functional.
const counterDOM = document.getElementById("counter");
const endDOM = document.getElementById("end");
const scene = new THREE.Scene();
const distance = 500;
const camera = new THREE.OrthographicCamera(
window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
0.1,
10000
);
camera.rotation.x = (50 * Math.PI) / 180;
camera.rotation.y = (20 * Math.PI) / 180;
camera.rotation.z = (10 * Math.PI) / 180;
const initialCameraPositionY = -Math.tan(camera.rotation.x) * distance;
const initialCameraPositionX =
Math.tan(camera.rotation.y) *
Math.sqrt(distance ** 2 + initialCameraPositionY ** 2);
camera.position.y = initialCameraPositionY;
camera.position.x = initialCameraPositionX;
camera.position.z = distance;
const zoom = 2;
const chickenSize = 15;
const positionWidth = 42;
const columns = 17;
const boardWidth = positionWidth * columns;
const stepTime = 200; // Miliseconds it takes for the chicken to take a step forward, backward, left or right
let lanes;
let currentLane;
let currentColumn;
let previousTimestamp;
let startMoving;
let moves;
let stepStartTimestamp;
const carFrontTexture = new Texture(40, 80, [{ x: 0, y: 10, w: 30, h: 60 }]);
const carBackTexture = new Texture(40, 80, [{ x: 10, y: 10, w: 30, h: 60 }]);
const carRightSideTexture = new Texture(110, 40, [
{ x: 10, y: 0, w: 50, h: 30 },
{ x: 70, y: 0, w: 30, h: 30 },
]);
const carLeftSideTexture = new Texture(110, 40, [
{ x: 10, y: 10, w: 50, h: 30 },
{ x: 70, y: 10, w: 30, h: 30 },
]);
const truckFrontTexture = new Texture(30, 30, [{ x: 15, y: 0, w: 10, h: 30 }]);
const truckRightSideTexture = new Texture(25, 30, [
{ x: 0, y: 15, w: 10, h: 10 },
]);
const truckLeftSideTexture = new Texture(25, 30, [
{ x: 0, y: 5, w: 10, h: 10 },
]);
const generateLanes = () =>
[-9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
.map((index) => {
const lane = new Lane(index);
lane.mesh.position.y = index * positionWidth * zoom;
scene.add(lane.mesh);
return lane;
})
.filter((lane) => lane.index >= 0);
const addLane = () => {
const index = lanes.length;
const lane = new Lane(index);
lane.mesh.position.y = index * positionWidth * zoom;
scene.add(lane.mesh);
lanes.push(lane);
};
const chicken = new Chicken();
scene.add(chicken);
hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
scene.add(hemiLight);
const initialDirLightPositionX = -100;
const initialDirLightPositionY = -100;
dirLight = new THREE.DirectionalLight(0xffffff, 0.6);
dirLight.position.set(initialDirLightPositionX, initialDirLightPositionY, 200);
dirLight.castShadow = true;
dirLight.target = chicken;
scene.add(dirLight);
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
var d = 500;
dirLight.shadow.camera.left = -d;
dirLight.shadow.camera.right = d;
dirLight.shadow.camera.top = d;
dirLight.shadow.camera.bottom = -d;
// var helper = new THREE.CameraHelper( dirLight.shadow.camera );
// var helper = new THREE.CameraHelper( camera );
// scene.add(helper)
backLight = new THREE.DirectionalLight(0x000000, 0.4);
backLight.position.set(200, 200, 50);
backLight.castShadow = true;
scene.add(backLight);
const laneTypes = ["car", "truck", "forest"];
const laneSpeeds = [2, 2.5, 3];
const vechicleColors = [0xa52523, 0xbdb638, 0x78b14b];
const threeHeights = [20, 45, 60];
const initaliseValues = () => {
lanes = generateLanes();
currentLane = 0;
currentColumn = Math.floor(columns / 2);
previousTimestamp = null;
startMoving = false;
moves = [];
stepStartTimestamp;
chicken.position.x = 0;
chicken.position.y = 0;
camera.position.y = initialCameraPositionY;
camera.position.x = initialCameraPositionX;
dirLight.position.x = initialDirLightPositionX;
dirLight.position.y = initialDirLightPositionY;
};
initaliseValues();
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
});
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
function Texture(width, height, rects) {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
context.fillStyle = "#ffffff";
context.fillRect(0, 0, width, height);
context.fillStyle = "rgba(0,0,0,0.6)";
rects.forEach((rect) => {
context.fillRect(rect.x, rect.y, rect.w, rect.h);
});
return new THREE.CanvasTexture(canvas);
}
function Wheel() {
const wheel = new THREE.Mesh(
new THREE.BoxBufferGeometry(12 * zoom, 33 * zoom, 12 * zoom),
new THREE.MeshLambertMaterial({ color: 0x333333, flatShading: true })
);
wheel.position.z = 6 * zoom;
return wheel;
}
function Car() {
const car = new THREE.Group();
const color =
vechicleColors[Math.floor(Math.random() * vechicleColors.length)];
const main = new THREE.Mesh(
new THREE.BoxBufferGeometry(60 * zoom, 30 * zoom, 15 * zoom),
new THREE.MeshPhongMaterial({ color, flatShading: true })
);
main.position.z = 12 * zoom;
main.castShadow = true;
main.receiveShadow = true;
car.add(main);
const cabin = new THREE.Mesh(
new THREE.BoxBufferGeometry(33 * zoom, 24 * zoom, 12 * zoom),
[
new THREE.MeshPhongMaterial({
color: 0xcccccc,
flatShading: true,
map: carBackTexture,
}),
new THREE.MeshPhongMaterial({
color: 0xcccccc,
flatShading: true,
map: carFrontTexture,
}),
new THREE.MeshPhongMaterial({
color: 0xcccccc,
flatShading: true,
map: carRightSideTexture,
}),
new THREE.MeshPhongMaterial({
color: 0xcccccc,
flatShading: true,
map: carLeftSideTexture,
}),
new THREE.MeshPhongMaterial({ color: 0xcccccc, flatShading: true }), // top
new THREE.MeshPhongMaterial({ color: 0xcccccc, flatShading: true }), // bottom
]
);
cabin.position.x = 6 * zoom;
cabin.position.z = 25.5 * zoom;
cabin.castShadow = true;
cabin.receiveShadow = true;
car.add(cabin);
const frontWheel = new Wheel();
frontWheel.position.x = -18 * zoom;
car.add(frontWheel);
const backWheel = new Wheel();
backWheel.position.x = 18 * zoom;
car.add(backWheel);
car.castShadow = true;
car.receiveShadow = false;
return car;
}
function Truck() {
const truck = new THREE.Group();
const color =
vechicleColors[Math.floor(Math.random() * vechicleColors.length)];
const base = new THREE.Mesh(
new THREE.BoxBufferGeometry(100 * zoom, 25 * zoom, 5 * zoom),
new THREE.MeshLambertMaterial({ color: 0xb4c6fc, flatShading: true })
);
base.position.z = 10 * zoom;
truck.add(base);
const cargo = new THREE.Mesh(
new THREE.BoxBufferGeometry(75 * zoom, 35 * zoom, 40 * zoom),
new THREE.MeshPhongMaterial({ color: 0xb4c6fc, flatShading: true })
);
cargo.position.x = 15 * zoom;
cargo.position.z = 30 * zoom;
cargo.castShadow = true;
cargo.receiveShadow = true;
truck.add(cargo);
const cabin = new THREE.Mesh(
new THREE.BoxBufferGeometry(25 * zoom, 30 * zoom, 30 * zoom),
[
new THREE.MeshPhongMaterial({ color, flatShading: true }), // back
new THREE.MeshPhongMaterial({
color,
flatShading: true,
map: truckFrontTexture,
}),
new THREE.MeshPhongMaterial({
color,
flatShading: true,
map: truckRightSideTexture,
}),
new THREE.MeshPhongMaterial({
color,
flatShading: true,
map: truckLeftSideTexture,
}),
new THREE.MeshPhongMaterial({ color, flatShading: true }), // top
new THREE.MeshPhongMaterial({ color, flatShading: true }), // bottom
]
);
cabin.position.x = -40 * zoom;
cabin.position.z = 20 * zoom;
cabin.castShadow = true;
cabin.receiveShadow = true;
truck.add(cabin);
const frontWheel = new Wheel();
frontWheel.position.x = -38 * zoom;
truck.add(frontWheel);
const middleWheel = new Wheel();
middleWheel.position.x = -10 * zoom;
truck.add(middleWheel);
const backWheel = new Wheel();
backWheel.position.x = 30 * zoom;
truck.add(backWheel);
return truck;
}
function Three() {
const three = new THREE.Group();
const trunk = new THREE.Mesh(
new THREE.BoxBufferGeometry(15 * zoom, 15 * zoom, 20 * zoom),
new THREE.MeshPhongMaterial({ color: 0x4d2926, flatShading: true })
);
trunk.position.z = 10 * zoom;
trunk.castShadow = true;
trunk.receiveShadow = true;
three.add(trunk);
height = threeHeights[Math.floor(Math.random() * threeHeights.length)];
const crown = new THREE.Mesh(
new THREE.BoxBufferGeometry(30 * zoom, 30 * zoom, height * zoom),
new THREE.MeshLambertMaterial({ color: 0x7aa21d, flatShading: true })
);
crown.position.z = (height / 2 + 20) * zoom;
crown.castShadow = true;
crown.receiveShadow = false;
three.add(crown);
return three;
}
function Chicken() {
const chicken = new THREE.Group();
const body = new THREE.Mesh(
new THREE.BoxBufferGeometry(
chickenSize * zoom,
chickenSize * zoom,
20 * zoom
),
new THREE.MeshPhongMaterial({ color: 0xffffff, flatShading: true })
);
body.position.z = 10 * zoom;
body.castShadow = true;
body.receiveShadow = true;
chicken.add(body);
const rowel = new THREE.Mesh(
new THREE.BoxBufferGeometry(2 * zoom, 4 * zoom, 2 * zoom),
new THREE.MeshLambertMaterial({ color: 0xf0619a, flatShading: true })
);
rowel.position.z = 21 * zoom;
rowel.castShadow = true;
rowel.receiveShadow = false;
chicken.add(rowel);
return chicken;
}
function Road() {
const road = new THREE.Group();
const createSection = (color) =>
new THREE.Mesh(
new THREE.PlaneBufferGeometry(boardWidth * zoom, positionWidth * zoom),
new THREE.MeshPhongMaterial({ color })
);
const middle = createSection(0x454a59);
middle.receiveShadow = true;
road.add(middle);
const left = createSection(0x393d49);
left.position.x = -boardWidth * zoom;
road.add(left);
const right = createSection(0x393d49);
right.position.x = boardWidth * zoom;
road.add(right);
return road;
}
function Grass() {
const grass = new THREE.Group();
const createSection = (color) =>
new THREE.Mesh(
new THREE.BoxBufferGeometry(
boardWidth * zoom,
positionWidth * zoom,
3 * zoom
),
new THREE.MeshPhongMaterial({ color })
);
const middle = createSection(0xbaf455);
middle.receiveShadow = true;
grass.add(middle);
const left = createSection(0x99c846);
left.position.x = -boardWidth * zoom;
grass.add(left);
const right = createSection(0x99c846);
right.position.x = boardWidth * zoom;
grass.add(right);
grass.position.z = 1.5 * zoom;
return grass;
}
function Lane(index) {
this.index = index;
this.type =
index {
const three = new Three();
let position;
do {
position = Math.floor(Math.random() * columns);
} while (this.occupiedPositions.has(position));
this.occupiedPositions.add(position);
three.position.x =
(position * positionWidth + positionWidth / 2) * zoom -
(boardWidth * zoom) / 2;
this.mesh.add(three);
return three;
});
break;
}
case "car": {
this.mesh = new Road();
this.direction = Math.random() >= 0.5;
const occupiedPositions = new Set();
this.vechicles = [1, 2, 3].map(() => {
const vechicle = new Car();
let position;
do {
position = Math.floor((Math.random() * columns) / 2);
} while (occupiedPositions.has(position));
occupiedPositions.add(position);
vechicle.position.x =
(position * positionWidth * 2 + positionWidth / 2) * zoom -
(boardWidth * zoom) / 2;
if (!this.direction) vechicle.rotation.z = Math.PI;
this.mesh.add(vechicle);
return vechicle;
});
this.speed = laneSpeeds[Math.floor(Math.random() * laneSpeeds.length)];
break;
}
case "truck": {
this.mesh = new Road();
this.direction = Math.random() >= 0.5;
const occupiedPositions = new Set();
this.vechicles = [1, 2].map(() => {
const vechicle = new Truck();
let position;
do {
position = Math.floor((Math.random() * columns) / 3);
} while (occupiedPositions.has(position));
occupiedPositions.add(position);
vechicle.position.x =
(position * positionWidth * 3 + positionWidth / 2) * zoom -
(boardWidth * zoom) / 2;
if (!this.direction) vechicle.rotation.z = Math.PI;
this.mesh.add(vechicle);
return vechicle;
});
this.speed = laneSpeeds[Math.floor(Math.random() * laneSpeeds.length)];
break;
}
}
}
document.querySelector("#retry").addEventListener("click", () => {
lanes.forEach((lane) => scene.remove(lane.mesh));
initaliseValues();
endDOM.style.visibility = "hidden";
});
document
.getElementById("forward")
.addEventListener("click", () => move("forward"));
document
.getElementById("backward")
.addEventListener("click", () => move("backward"));
document.getElementById("left").addEventListener("click", () => move("left"));
document.getElementById("right").addEventListener("click", () => move("right"));
window.addEventListener("keydown", (event) => {
if (event.keyCode == "38") {
// up arrow
move("forward");
} else if (event.keyCode == "40") {
// down arrow
move("backward");
} else if (event.keyCode == "37") {
// left arrow
move("left");
} else if (event.keyCode == "39") {
// right arrow
move("right");
}
});
function move(direction) {
const finalPositions = moves.reduce(
(position, move) => {
if (move === "forward")
return { lane: position.lane + 1, column: position.column };
if (move === "backward")
return { lane: position.lane - 1, column: position.column };
if (move === "left")
return { lane: position.lane, column: position.column - 1 };
if (move === "right")
return { lane: position.lane, column: position.column + 1 };
},
{ lane: currentLane, column: currentColumn }
);
if (direction === "forward") {
if (
lanes[finalPositions.lane + 1].type === "forest" &&
lanes[finalPositions.lane + 1].occupiedPositions.has(
finalPositions.column
)
)
return;
if (!stepStartTimestamp) startMoving = true;
addLane();
} else if (direction === "backward") {
if (finalPositions.lane === 0) return;
if (
lanes[finalPositions.lane - 1].type === "forest" &&
lanes[finalPositions.lane - 1].occupiedPositions.has(
finalPositions.column
)
)
return;
if (!stepStartTimestamp) startMoving = true;
} else if (direction === "left") {
if (finalPositions.column === 0) return;
if (
lanes[finalPositions.lane].type === "forest" &&
lanes[finalPositions.lane].occupiedPositions.has(
finalPositions.column - 1
)
)
return;
if (!stepStartTimestamp) startMoving = true;
} else if (direction === "right") {
if (finalPositions.column === columns - 1) return;
if (
lanes[finalPositions.lane].type === "forest" &&
lanes[finalPositions.lane].occupiedPositions.has(
finalPositions.column + 1
)
)
return;
if (!stepStartTimestamp) startMoving = true;
}
moves.push(direction);
}
function animate(timestamp) {
requestAnimationFrame(animate);
if (!previousTimestamp) previousTimestamp = timestamp;
const delta = timestamp - previousTimestamp;
previousTimestamp = timestamp;
// Animate cars and trucks moving on the lane
lanes.forEach((lane) => {
if (lane.type === "car" || lane.type === "truck") {
const aBitBeforeTheBeginingOfLane =
(-boardWidth * zoom) / 2 - positionWidth * 2 * zoom;
const aBitAfterTheEndOFLane =
(boardWidth * zoom) / 2 + positionWidth * 2 * zoom;
lane.vechicles.forEach((vechicle) => {
if (lane.direction) {
vechicle.position.x =
vechicle.position.x aBitAfterTheEndOFLane
? aBitBeforeTheBeginingOfLane
: (vechicle.position.x += (lane.speed / 16) * delta);
}
});
}
});
if (startMoving) {
stepStartTimestamp = timestamp;
startMoving = false;
}
if (stepStartTimestamp) {
const moveDeltaTime = timestamp - stepStartTimestamp;
const moveDeltaDistance =
Math.min(moveDeltaTime / stepTime, 1) * positionWidth * zoom;
const jumpDeltaDistance =
Math.sin(Math.min(moveDeltaTime / stepTime, 1) * Math.PI) * 8 * zoom;
switch (moves[0]) {
case "forward": {
const positionY =
currentLane * positionWidth * zoom + moveDeltaDistance;
camera.position.y = initialCameraPositionY + positionY;
dirLight.position.y = initialDirLightPositionY + positionY;
chicken.position.y = positionY; // initial chicken position is 0
chicken.position.z = jumpDeltaDistance;
break;
}
case "backward": {
positionY = currentLane * positionWidth * zoom - moveDeltaDistance;
camera.position.y = initialCameraPositionY + positionY;
dirLight.position.y = initialDirLightPositionY + positionY;
chicken.position.y = positionY;
chicken.position.z = jumpDeltaDistance;
break;
}
case "left": {
const positionX =
(currentColumn * positionWidth + positionWidth / 2) * zoom -
(boardWidth * zoom) / 2 -
moveDeltaDistance;
camera.position.x = initialCameraPositionX + positionX;
dirLight.position.x = initialDirLightPositionX + positionX;
chicken.position.x = positionX; // initial chicken position is 0
chicken.position.z = jumpDeltaDistance;
break;
}
case "right": {
const positionX =
(currentColumn * positionWidth + positionWidth / 2) * zoom -
(boardWidth * zoom) / 2 +
moveDeltaDistance;
camera.position.x = initialCameraPositionX + positionX;
dirLight.position.x = initialDirLightPositionX + positionX;
chicken.position.x = positionX;
chicken.position.z = jumpDeltaDistance;
break;
}
}
// Once a step has ended
if (moveDeltaTime > stepTime) {
switch (moves[0]) {
case "forward": {
currentLane++;
counterDOM.innerHTML = currentLane;
break;
}
case "backward": {
currentLane--;
counterDOM.innerHTML = currentLane;
break;
}
case "left": {
currentColumn--;
break;
}
case "right": {
currentColumn++;
break;
}
}
moves.shift();
// If more steps are to be taken then restart counter otherwise stop stepping
stepStartTimestamp = moves.length === 0 ? null : timestamp;
}
}
// Hit test
if (
lanes[currentLane].type === "car" ||
lanes[currentLane].type === "truck"
) {
const chickenMinX = chicken.position.x - (chickenSize * zoom) / 2;
const chickenMaxX = chicken.position.x + (chickenSize * zoom) / 2;
const vechicleLength = { car: 60, truck: 105 }[lanes[currentLane].type];
lanes[currentLane].vechicles.forEach((vechicle) => {
const carMinX = vechicle.position.x - (vechicleLength * zoom) / 2;
const carMaxX = vechicle.position.x + (vechicleLength * zoom) / 2;
if (chickenMaxX > carMinX && chickenMinX < carMaxX) {
endDOM.style.visibility = "visible";
}
});
}
renderer.render(scene, camera);
}
requestAnimationFrame(animate);
Output:

Disclaimer: The code provided in this post is sourced from GitHub and is distributed under the MIT License. All credits for the original code go to the respective owner. We have shared this code for educational purposes only. Please refer to the original repository for detailed information and any additional usage rights or restrictions.
how to create a video background with html CSS JavaScript Introduction Hello friends, you all are welcome to today’s new blog post. …
Auto Text Effect Animation Using Html CSS & JavaScript Introduction Hello friends, welcome to today’s new blog post. Today we have created …
Windows 12 Notepad Using Python Introduction: In this article, we will create a Windows 12 Notepad using Python. If you are a …
Animated Search Bar using Html CSS And JavaScript Introduction Hello friends, all of you developers are welcome to today’s beautiful blog post. …