Create a Maze Game with JavaScript

codinglabsolution

Discover how to craft your very own maze game using HTML, CSS, and JavaScript with our beginner-friendly tutorial. Dive into the world of web-based game development and embark on an exciting maze adventure of your own creation.



Create a Maze Game with HTML, CSS, and JavaScript (Source Code)





Welcome to the thrilling realm of maze game development! If you've ever relished the challenge of navigating through intricate mazes, then crafting your own maze game is an exhilarating endeavor that merges your enthusiasm for web development with the excitement of game design. In this in-depth tutorial, we'll be your guide, leading you through each step of constructing a captivating maze game using HTML, CSS, and JavaScript.



Why Dive into Maze Game Creation?


Maze games possess an enduring allure, captivating players across generations. They stimulate the mind, assess problem-solving abilities, and offer an immersive escapade. By fashioning your own maze game, you have the chance to forge a distinctive adventure for players to explore and relish. Whether you're a seasoned web developer seeking to enrich your portfolio with an interactive project or a novice eager to plunge into the realm of game development, this tutorial caters to you.



What You'll Master


Designed with beginners in mind, this tutorial also offers insights that developers of all levels can appreciate. We'll furnish lucid explanations and code snippets, ensuring seamless comprehension of the underlying concepts at every stage. Here's what awaits you:



Establishing the HTML Framework: We'll commence by erecting the groundwork of our maze game using HTML. You'll discover how to fabricate the maze grid and designate the player's initial position.

Adorning the Maze with CSS: With the HTML structure laid out, we'll proceed to CSS styling to enhance the visual allure of the maze. You'll infuse colors, borders, and dimensions into the maze cells, while crafting the player's avatar to command attention.

Enacting Maze Logic with JavaScript: The essence of our maze game lies in its dynamism, orchestrated by JavaScript. You'll learn to script player movement within the maze via keyboard inputs and circumvent collisions with walls.

Infusing Interactivity and Controls: A triumphant maze game necessitates user controls and immersive interactions. We'll assist you in integrating event listeners to detect keyboard inputs, defining win conditions, and presenting messages upon triumph or setback.

Evaluating and Rectifying Your Maze Game: Prior to unveiling your creation to the world, we'll underscore the significance of meticulous playtesting and debugging. You'll grasp the art of pinpointing and rectifying potential hitches, ensuring a seamless and gratifying gaming experience.



Prerequisites


To extract the utmost from this tutorial, a rudimentary understanding of HTML, CSS, and JavaScript would be advantageous. While we'll furnish elucidations of the code, a foundational grasp of web development concepts will facilitate a deeper comprehension of the material. Additionally, access to modern web browsers like Chrome, Firefox, or Edge is recommended for optimal performance.


Now that you're acquainted with the thrilling odyssey that awaits, let's embark on this enthralling quest of crafting your very own maze game. So roll up your sleeves, ignite your code editor, and let's commence this electrifying web-based expedition!


Code by: Martin ... 

Source Code

Let's walk through the HTML code step by step:
1. `<!DOCTYPE html>`: This declaration specifies the document type and version being used, indicating that this is an HTML5 document.
2. `<html lang="en-GB">`: This opening tag defines the language of the document as English (Great Britain variant).
3. `<head>`: This section contains metadata and links to external resources.
    - `<meta charset="utf-8">`: Specifies the character encoding for the document as UTF-8, enabling support for various characters and symbols.
    
    - `<title>Maze Game</title>`: Sets the title of the web page, which is displayed on the browser's title bar or tab.
    
    - `<link rel="stylesheet" href="styles.css">`: Links an external CSS file named "styles.css" to style the page's content.
4. `<body>`: This section contains the visible content of the web page.
    - `<div id="gradient"></div>`: An empty div with the ID "gradient," possibly used to create a gradient background effect.
    
    - `<div id="page">`: A div acting as a container for all the content of the page.
    
        - `<div id="Message-Container">`: A container for displaying a congratulatory message when the user completes the maze.
        
            - `<div id="message">`: Contains the congratulatory message text.
            
                - `<h1>Congratulations!</h1>`: Displays the text "Congratulations!" in a larger, bold font.
                
                - `<p>You are done.</p>`: A message acknowledging the user's completion of the maze.
                
                - `<p id="moves"></p>`: An empty paragraph element, likely used to display the number of moves the user made.
                
                - `<input id="okBtn" type="button" onclick="toggleVisablity('Message-Container')" value="Cool!" />`: A button to hide the congratulatory message when clicked.
                
        - `<div id="menu">`: Contains menu options for the maze game.
        
            - `<div class="custom-select">`: Represents a custom-styled select dropdown menu.
            
                - `<select id="diffSelect">`: Allows selecting the difficulty level of the maze game.
                
                    - `<option value="10">Easy</option>`: Sets the value of the "Easy" difficulty level to 10.
                    
                    - `<option value="15">Medium</option>`: Sets the value of the "Medium" difficulty level to 15.
                    
                    - `<option value="25">Hard</option>`: Sets the value of the "Hard" difficulty level to 25.
                    
                    - `<option value="38">Extreme</option>`: Sets the value of the "Extreme" difficulty level to 38.
                    
            - `<input id="startMazeBtn" type="button" onclick="makeMaze()" value="Start" />`: A button to start the maze game.
            
        - `<div id="view">`: Represents the area where the maze will be displayed.
        
            - `<div id="mazeContainer">`: Acts as a container for the maze canvas.
            
                - `<canvas id="mazeCanvas" class="border" height="1100" width="1100"></canvas>`: The canvas element where the maze will be drawn, with a height and width of 1100 pixels.
5. JavaScript libraries and scripts: Includes references to external JavaScript libraries (jQuery and jQuery Touch Swipe) and an external JavaScript file named "script.js" containing game logic and functionality.
This comprehensive breakdown elucidates each component of the HTML structure, setting the stage for the subsequent styling and scripting phases of our maze game development journey.

<html lang="en-GB">
<head>
  <meta charset="utf-8">
  <title>Maze Game</title>
  <link rel="stylesheet" href="styles.css">
  <body>
    <div id="gradient"></div>
    <div id="page">
      <div id="Message-Container">
        <div id="message">
          <h1>Congratulations!</h1>
          <p>You are done.</p>
          <p id="moves"></p>
          <input id="okBtn" type="button" onclick="toggleVisablity('Message-Container')" value="Cool!" />
        </div>
      </div>
      <div id="menu">
        <div class="custom-select">
          <select id="diffSelect">
                    <option value="10">Easy</option>
                    <option value="15">Medium</option>
                    <option value="25">Hard</option>
                    <option value="38">Extreme</option>
                </select>
        </div>
        <input id="startMazeBtn" type="button" onclick="makeMaze()" value="Start" />
      </div>
      <div id="view">
        <div id="mazeContainer">
          <canvas id="mazeCanvas" class="border" height="1100" width="1100"></canvas>
        </div>
      </div>
    </div>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.touchswipe/1.6.18/jquery.touchSwipe.min.js"></script>
    <script src="script.js"></script>
  </body>
</html>

Step 2 (CSS Code):


Once the basic HTML structure of the maze game is in place, the next step is to add styling to the maze game using CSS. CSS allows us to control the visual appearance of the website, including things like layout, color, and typography.

Next, we will create our CSS file. In this file, we will use some basic CSS rules to create our maze game.

Let's break down the code section by section:

The first block of code sets some global styles for the html and body elements. It fixes the width and height to 100% of the viewport (viewport width and height), and it positions the body fixed at the top, bottom, left, and right with no padding or margin. This ensures that the content occupies the entire viewport.

#mazeContainer: This style block applies to an element with the ID "mazeContainer." It sets up some transitions for the element's opacity property, making it fade in/out smoothly over a duration of 1 second. The element is positioned 75 pixels from the top of its containing element, and it initially has an opacity of 0 (fully transparent). It has a background color of semi-transparent black (rgba(0, 0, 0, 0.3)) and is centered horizontally using margin: auto.

#mazeContainer #mazeCanvas: This style block applies to an element with the ID "mazeCanvas" that is a descendant of the "mazeContainer." It sets the margin to 0 and displays the element as a block-level element. Additionally, it gives the element a solid black border with a width of 1 pixel.

inputselect: This style block applies to input and select elements. It sets up transitions for the background-color and opacity properties, making the background color change smoothly over a duration of 0.2 seconds with ease-in-out timing function when hovering over or clicking on these elements. The elements have a semi-transparent black background color (rgba(0, 0, 0, 0.3)), white text color, some padding, and a border radius to give them a rounded appearance.

input:hoverselect:hover: This style block changes the background color to a darker semi-transparent black (rgba(0, 0, 0, 0.7)) when hovering over the input and select elements.

input:activeselect:active: This style block changes the background color to black when the input and select elements are active (clicked).

input:focusselect:focus: This style block removes the outline from input and select elements when they receive focus (clicked or selected).

.custom-select: This style block applies to an element with the class "custom-select." It is an inline-block element, and it styles the nested select element by removing its default appearance (styling) and providing a custom background image, which appears as an arrow next to the select element.

#Message-Container: This style block applies to an element with the ID "Message-Container." Initially, the element's visibility is hidden, and it is positioned fixed to cover the entire viewport with a semi-transparent black background (rgba(0, 0, 0, 0.3)). It has a higher z-index, ensuring it appears above other elements on the page. It's intended to be used as a message modal or overlay.

#Message-Container #message: This style block applies to an element with the ID "message" that is a descendant of the "Message-Container." It positions the element in the center of the viewport using the "top" and "left" properties and transforms it to be centered properly using negative margins.

#page: This style block applies to an element with the ID "page." It sets the font family to "Segoe UI," Arial, sans-serif, and centers the text within it. The width and height are set to auto, and it's horizontally centered using margin: auto.

#page #menu: This style block applies to an element with the ID "menu" that is a descendant of the "page." It centers the content within the element and gives it a height of 65 pixels.

#page #menu h1: This style block applies to an h1 element that is a descendant of the "menu" element. It removes margin and sets the font weight and size for the heading.

#page #view: This style block applies to an element with the ID "view" that is a descendant of the "page." It positions the element absolutely, stretching it from the top to the bottom and from the left to the right within the "page" container.

.border: This style block applies to elements with the class "border." It gives these elements a 1-pixel black solid border with a border-radius of 5 pixels, creating a rounded corner effect.

#gradient: This style block applies to an element with the ID "gradient." It creates a gradient background that smoothly cycles through different colors. The background property uses a linear-gradient with four color stops (#EE7752, #E73C7E, #23A6D5, #23D5AB) and a -45 degree angle. The gradient also uses an animation called "Gradient" that continuously shifts the background position to create a moving gradient effect.

Media Query: The last part is a media query that targets devices with a maximum width of 400 pixels (extra small devices such as phones). It adjusts the width of the input and select elements to 120 pixels to accommodate smaller screens better.

This will give our maze game an upgraded presentation. Create a CSS file with the name of styles.css and paste the given codes into your CSS file. Remember that you must create a file with the .css extension.

html,
body {
  width: 100vw;
  height: 100vh;
  position: fixed;
  padding: 0;
  margin: 0;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

#mazeContainer {
  transition-property: opacity;
  transition-duration: 1s;
  transition-timing-function: linear;
  top: 75px;
  opacity: 0;
  display: inline-block;
  background-color: rgba(0, 0, 0, 0.3);
  margin: auto;
}
#mazeContainer #mazeCanvas {
  margin: 0;
  display: block;
  border: solid 1px black;
}

input,
select {
  transition-property: background-color opacity;
  transition-duration: 0.2s;
  transition-timing-function: ease-in-out;
  cursor: pointer;
  background-color: rgba(0, 0, 0, 0.3);
  height: 45px;
  width: 150px;
  padding: 10px;
  border: none;
  border-radius: 5px;
  color: white;
  display: inline-block;
  font-size: 15px;
  text-align: center;
  text-decoration: none;
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
}
input:hover,
select:hover {
  background-color: rgba(0, 0, 0, 0.7);
}
input:active,
select:active {
  background-color: black;
}
input:focus,
select:focus {
  outline: none;
}

.custom-select {
  display: inline-block;
}
.custom-select select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAh0lEQVQ4T93TMQrCUAzG8V9x8QziiYSuXdzFC7h4AcELOPQAdXYovZCHEATlgQV5GFTe1ozJlz/kS1IpjKqw3wQBVyy++JI0y1GTe7DCBbMAckeNIQKk/BanALBB+16LtnDELoMcsM/BESDlz2heDR3WePwKSLo5eoxz3z6NNcFD+vu3ij14Aqz/DxGbKB7CAAAAAElFTkSuQmCC");
  background-repeat: no-repeat;
  background-position: 125px center;
}

#Message-Container {
  visibility: hidden;
  color: white;
  display: block;
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.3);
  z-index: 1;
}
#Message-Container #message {
  width: 300px;
  height: 300px;
  position: fixed;
  top: 50%;
  left: 50%;
  margin-left: -150px;
  margin-top: -150px;
}

#page {
  font-family: "Segoe UI", Arial, sans-serif;
  text-align: center;
  height: auto;
  width: auto;
  margin: auto;
}
#page #menu {
  margin: auto;
  padding: 10px;
  height: 65px;
  box-sizing: border-box;
}
#page #menu h1 {
  margin: 0;
  margin-bottom: 10px;
  font-weight: 600;
  font-size: 3.2rem;
}
#page #view {
  position: absolute;
  top: 65px;
  bottom: 0;
  left: 0;
  right: 0;
  width: 100%;
  height: auto;
}

.border {
  border: 1px black solid;
  border-radius: 5px;
}

#gradient {
  z-index: -1;
  position: fixed;
  top: 0;
  bottom: 0;
  width: 100vw;
  height: 100vh;
  color: #fff;
  background: linear-gradient(-45deg, #EE7752, #E73C7E, #23A6D5, #23D5AB);
  background-size: 400% 400%;
  -webkit-animation: Gradient 15s ease infinite;
          animation: Gradient 15s ease infinite;
}

@-webkit-keyframes Gradient {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

@keyframes Gradient {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}
/* Extra small devices (phones, 600px and down) */
@media only screen and (max-width: 400px) {
  input, select {
    width: 120px;
  }
}

Step 3 (JavaScript Code):


1. Random Number Generator Function: Generates a random integer within a specified range.


2. Shuffle Function: Randomly shuffles the elements of an array.


3. Change Brightness Function: Adjusts the brightness of an image.


4. Display Victory Message Function: Shows a congratulatory message upon completing the maze.


5. Toggle Visibility Function: Toggles the visibility of an HTML element.


6. Maze Constructor Function: Creates the maze structure using a recursive backtracking algorithm.


7. DrawMaze Constructor Function: Renders the maze on the canvas.


8. Player Constructor Function: Handles player movement and collision detection.


9. Window Events: Set up to initialize the maze and adjust canvas size.


10. makeMaze() Function: Initiates the maze generation and player setup.


These functions and events work together to create the interactive maze game experience.

function rand(max) {
  return Math.floor(Math.random() * max);
}

function shuffle(a) {
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

function changeBrightness(factor, sprite) {
  var virtCanvas = document.createElement("canvas");
  virtCanvas.width = 500;
  virtCanvas.height = 500;
  var context = virtCanvas.getContext("2d");
  context.drawImage(sprite, 0, 0, 500, 500);

  var imgData = context.getImageData(0, 0, 500, 500);

  for (let i = 0; i < imgData.data.length; i += 4) {
    imgData.data[i] = imgData.data[i] * factor;
    imgData.data[i + 1] = imgData.data[i + 1] * factor;
    imgData.data[i + 2] = imgData.data[i + 2] * factor;
  }
  context.putImageData(imgData, 0, 0);

  var spriteOutput = new Image();
  spriteOutput.src = virtCanvas.toDataURL();
  virtCanvas.remove();
  return spriteOutput;
}

function displayVictoryMess(moves) {
  document.getElementById("moves").innerHTML = "You Moved " + moves + " Steps.";
  toggleVisablity("Message-Container");
}

function toggleVisablity(id) {
  if (document.getElementById(id).style.visibility == "visible") {
    document.getElementById(id).style.visibility = "hidden";
  } else {
    document.getElementById(id).style.visibility = "visible";
  }
}

function Maze(Width, Height) {
  var mazeMap;
  var width = Width;
  var height = Height;
  var startCoord, endCoord;
  var dirs = ["n", "s", "e", "w"];
  var modDir = {
    n: {
      y: -1,
      x: 0,
      o: "s"
    },
    s: {
      y: 1,
      x: 0,
      o: "n"
    },
    e: {
      y: 0,
      x: 1,
      o: "w"
    },
    w: {
      y: 0,
      x: -1,
      o: "e"
    }
  };

  this.map = function() {
    return mazeMap;
  };
  this.startCoord = function() {
    return startCoord;
  };
  this.endCoord = function() {
    return endCoord;
  };

  function genMap() {
    mazeMap = new Array(height);
    for (y = 0; y < height; y++) {
      mazeMap[y] = new Array(width);
      for (x = 0; x < width; ++x) {
        mazeMap[y][x] = {
          n: false,
          s: false,
          e: false,
          w: false,
          visited: false,
          priorPos: null
        };
      }
    }
  }

  function defineMaze() {
    var isComp = false;
    var move = false;
    var cellsVisited = 1;
    var numLoops = 0;
    var maxLoops = 0;
    var pos = {
      x: 0,
      y: 0
    };
    var numCells = width * height;
    while (!isComp) {
      move = false;
      mazeMap[pos.x][pos.y].visited = true;

      if (numLoops >= maxLoops) {
        shuffle(dirs);
        maxLoops = Math.round(rand(height / 8));
        numLoops = 0;
      }
      numLoops++;
      for (index = 0; index < dirs.length; index++) {
        var direction = dirs[index];
        var nx = pos.x + modDir[direction].x;
        var ny = pos.y + modDir[direction].y;

        if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
          //Check if the tile is already visited
          if (!mazeMap[nx][ny].visited) {
            //Carve through walls from this tile to next
            mazeMap[pos.x][pos.y][direction] = true;
            mazeMap[nx][ny][modDir[direction].o] = true;

            //Set Currentcell as next cells Prior visited
            mazeMap[nx][ny].priorPos = pos;
            //Update Cell position to newly visited location
            pos = {
              x: nx,
              y: ny
            };
            cellsVisited++;
            //Recursively call this method on the next tile
            move = true;
            break;
          }
        }
      }

      if (!move) {
        //  If it failed to find a direction,
        //  move the current position back to the prior cell and Recall the method.
        pos = mazeMap[pos.x][pos.y].priorPos;
      }
      if (numCells == cellsVisited) {
        isComp = true;
      }
    }
  }

  function defineStartEnd() {
    switch (rand(4)) {
      case 0:
        startCoord = {
          x: 0,
          y: 0
        };
        endCoord = {
          x: height - 1,
          y: width - 1
        };
        break;
      case 1:
        startCoord = {
          x: 0,
          y: width - 1
        };
        endCoord = {
          x: height - 1,
          y: 0
        };
        break;
      case 2:
        startCoord = {
          x: height - 1,
          y: 0
        };
        endCoord = {
          x: 0,
          y: width - 1
        };
        break;
      case 3:
        startCoord = {
          x: height - 1,
          y: width - 1
        };
        endCoord = {
          x: 0,
          y: 0
        };
        break;
    }
  }

  genMap();
  defineStartEnd();
  defineMaze();
}

function DrawMaze(Maze, ctx, cellsize, endSprite = null) {
  var map = Maze.map();
  var cellSize = cellsize;
  var drawEndMethod;
  ctx.lineWidth = cellSize / 40;

  this.redrawMaze = function(size) {
    cellSize = size;
    ctx.lineWidth = cellSize / 50;
    drawMap();
    drawEndMethod();
  };

  function drawCell(xCord, yCord, cell) {
    var x = xCord * cellSize;
    var y = yCord * cellSize;

    if (cell.n == false) {
      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.lineTo(x + cellSize, y);
      ctx.stroke();
    }
    if (cell.s === false) {
      ctx.beginPath();
      ctx.moveTo(x, y + cellSize);
      ctx.lineTo(x + cellSize, y + cellSize);
      ctx.stroke();
    }
    if (cell.e === false) {
      ctx.beginPath();
      ctx.moveTo(x + cellSize, y);
      ctx.lineTo(x + cellSize, y + cellSize);
      ctx.stroke();
    }
    if (cell.w === false) {
      ctx.beginPath();
      ctx.moveTo(x, y);
      ctx.lineTo(x, y + cellSize);
      ctx.stroke();
    }
  }

  function drawMap() {
    for (x = 0; x < map.length; x++) {
      for (y = 0; y < map[x].length; y++) {
        drawCell(x, y, map[x][y]);
      }
    }
  }

  function drawEndFlag() {
    var coord = Maze.endCoord();
    var gridSize = 4;
    var fraction = cellSize / gridSize - 2;
    var colorSwap = true;
    for (let y = 0; y < gridSize; y++) {
      if (gridSize % 2 == 0) {
        colorSwap = !colorSwap;
      }
      for (let x = 0; x < gridSize; x++) {
        ctx.beginPath();
        ctx.rect(
          coord.x * cellSize + x * fraction + 4.5,
          coord.y * cellSize + y * fraction + 4.5,
          fraction,
          fraction
        );
        if (colorSwap) {
          ctx.fillStyle = "rgba(0, 0, 0, 0.8)";
        } else {
          ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
        }
        ctx.fill();
        colorSwap = !colorSwap;
      }
    }
  }

  function drawEndSprite() {
    var offsetLeft = cellSize / 50;
    var offsetRight = cellSize / 25;
    var coord = Maze.endCoord();
    ctx.drawImage(
      endSprite,
      2,
      2,
      endSprite.width,
      endSprite.height,
      coord.x * cellSize + offsetLeft,
      coord.y * cellSize + offsetLeft,
      cellSize - offsetRight,
      cellSize - offsetRight
    );
  }

  function clear() {
    var canvasSize = cellSize * map.length;
    ctx.clearRect(0, 0, canvasSize, canvasSize);
  }

  if (endSprite != null) {
    drawEndMethod = drawEndSprite;
  } else {
    drawEndMethod = drawEndFlag;
  }
  clear();
  drawMap();
  drawEndMethod();
}

function Player(maze, c, _cellsize, onComplete, sprite = null) {
  var ctx = c.getContext("2d");
  var drawSprite;
  var moves = 0;
  drawSprite = drawSpriteCircle;
  if (sprite != null) {
    drawSprite = drawSpriteImg;
  }
  var player = this;
  var map = maze.map();
  var cellCoords = {
    x: maze.startCoord().x,
    y: maze.startCoord().y
  };
  var cellSize = _cellsize;
  var halfCellSize = cellSize / 2;

  this.redrawPlayer = function(_cellsize) {
    cellSize = _cellsize;
    drawSpriteImg(cellCoords);
  };

  function drawSpriteCircle(coord) {
    ctx.beginPath();
    ctx.fillStyle = "yellow";
    ctx.arc(
      (coord.x + 1) * cellSize - halfCellSize,
      (coord.y + 1) * cellSize - halfCellSize,
      halfCellSize - 2,
      0,
      2 * Math.PI
    );
    ctx.fill();
    if (coord.x === maze.endCoord().x && coord.y === maze.endCoord().y) {
      onComplete(moves);
      player.unbindKeyDown();
    }
  }

  function drawSpriteImg(coord) {
    var offsetLeft = cellSize / 50;
    var offsetRight = cellSize / 25;
    ctx.drawImage(
      sprite,
      0,
      0,
      sprite.width,
      sprite.height,
      coord.x * cellSize + offsetLeft,
      coord.y * cellSize + offsetLeft,
      cellSize - offsetRight,
      cellSize - offsetRight
    );
    if (coord.x === maze.endCoord().x && coord.y === maze.endCoord().y) {
      onComplete(moves);
      player.unbindKeyDown();
    }
  }

  function removeSprite(coord) {
    var offsetLeft = cellSize / 50;
    var offsetRight = cellSize / 25;
    ctx.clearRect(
      coord.x * cellSize + offsetLeft,
      coord.y * cellSize + offsetLeft,
      cellSize - offsetRight,
      cellSize - offsetRight
    );
  }

  function check(e) {
    var cell = map[cellCoords.x][cellCoords.y];
    moves++;
    switch (e.keyCode) {
      case 65:
      case 37: // west
        if (cell.w == true) {
          removeSprite(cellCoords);
          cellCoords = {
            x: cellCoords.x - 1,
            y: cellCoords.y
          };
          drawSprite(cellCoords);
        }
        break;
      case 87:
      case 38: // north
        if (cell.n == true) {
          removeSprite(cellCoords);
          cellCoords = {
            x: cellCoords.x,
            y: cellCoords.y - 1
          };
          drawSprite(cellCoords);
        }
        break;
      case 68:
      case 39: // east
        if (cell.e == true) {
          removeSprite(cellCoords);
          cellCoords = {
            x: cellCoords.x + 1,
            y: cellCoords.y
          };
          drawSprite(cellCoords);
        }
        break;
      case 83:
      case 40: // south
        if (cell.s == true) {
          removeSprite(cellCoords);
          cellCoords = {
            x: cellCoords.x,
            y: cellCoords.y + 1
          };
          drawSprite(cellCoords);
        }
        break;
    }
  }

  this.bindKeyDown = function() {
    window.addEventListener("keydown", check, false);

    $("#view").swipe({
      swipe: function(
        event,
        direction,
        distance,
        duration,
        fingerCount,
        fingerData
      ) {
        console.log(direction);
        switch (direction) {
          case "up":
            check({
              keyCode: 38
            });
            break;
          case "down":
            check({
              keyCode: 40
            });
            break;
          case "left":
            check({
              keyCode: 37
            });
            break;
          case "right":
            check({
              keyCode: 39
            });
            break;
        }
      },
      threshold: 0
    });
  };

  this.unbindKeyDown = function() {
    window.removeEventListener("keydown", check, false);
    $("#view").swipe("destroy");
  };

  drawSprite(maze.startCoord());

  this.bindKeyDown();
}

var mazeCanvas = document.getElementById("mazeCanvas");
var ctx = mazeCanvas.getContext("2d");
var sprite;
var finishSprite;
var maze, draw, player;
var cellSize;
var difficulty;
// sprite.src = 'media/sprite.png';

window.onload = function() {
  let viewWidth = $("#view").width();
  let viewHeight = $("#view").height();
  if (viewHeight < viewWidth) {
    ctx.canvas.width = viewHeight - viewHeight / 100;
    ctx.canvas.height = viewHeight - viewHeight / 100;
  } else {
    ctx.canvas.width = viewWidth - viewWidth / 100;
    ctx.canvas.height = viewWidth - viewWidth / 100;
  }

  //Load and edit sprites
  var completeOne = false;
  var completeTwo = false;
  var isComplete = () => {
    if(completeOne === true && completeTwo === true)
       {
         console.log("Runs");
         setTimeout(function(){
           makeMaze();
         }, 500);
       }
  };
  sprite = new Image();
  sprite.src =
    "https://image.ibb.co/dr1HZy/Pf_RWr3_X_Imgur.png" +
    "?" +
    new Date().getTime();
  sprite.setAttribute("crossOrigin", " ");
  sprite.onload = function() {
    sprite = changeBrightness(1.2, sprite);
    completeOne = true;
    console.log(completeOne);
    isComplete();
  };

  finishSprite = new Image();
  finishSprite.src = "https://image.ibb.co/b9wqnJ/i_Q7m_U25_Imgur.png"+
  "?" +
  new Date().getTime();
  finishSprite.setAttribute("crossOrigin", " ");
  finishSprite.onload = function() {
    finishSprite = changeBrightness(1.1, finishSprite);
    completeTwo = true;
    console.log(completeTwo);
    isComplete();
  };

};

window.onresize = function() {
  let viewWidth = $("#view").width();
  let viewHeight = $("#view").height();
  if (viewHeight < viewWidth) {
    ctx.canvas.width = viewHeight - viewHeight / 100;
    ctx.canvas.height = viewHeight - viewHeight / 100;
  } else {
    ctx.canvas.width = viewWidth - viewWidth / 100;
    ctx.canvas.height = viewWidth - viewWidth / 100;
  }
  cellSize = mazeCanvas.width / difficulty;
  if (player != null) {
    draw.redrawMaze(cellSize);
    player.redrawPlayer(cellSize);
  }
};

function makeMaze() {
  //document.getElementById("mazeCanvas").classList.add("border");
  if (player != undefined) {
    player.unbindKeyDown();
    player = null;
  }
  var e = document.getElementById("diffSelect");
  difficulty = e.options[e.selectedIndex].value;
  cellSize = mazeCanvas.width / difficulty;
  maze = new Maze(difficulty, difficulty);
  draw = new DrawMaze(maze, ctx, cellSize, finishSprite);
  player = new Player(maze, mazeCanvas, cellSize, displayVictoryMess, sprite);
  if (document.getElementById("mazeContainer").style.opacity < "100") {
    document.getElementById("mazeContainer").style.opacity = "100";
  }
}

Source Code : Download Files

Final Output:

Create a Maze Game with JavaScript


Conclusion:


Well done! You've accomplished a fantastic feat by building your maze game with HTML, CSS, and JavaScript. This project serves as a solid foundation for diving deeper into game development and web design. Now, it's time to let your imagination run wild and explore new features to elevate your maze game even more.



Keep in mind that continuous practice is key to mastery, so don't hesitate to keep refining your skills and experimenting with different ideas. The journey of coding and game development is an exciting one, so embrace the challenges and enjoy the process. Happy coding, and may your future projects be filled with creativity and success!

Tags: #HTML #CSS #JavaScript #mazeGame #GameDevelopment #Tutorial #BeginnerFriendly #WebBased #StepByStep #SourceCode

إرسال تعليق

ملفات تعريف الارتباط
نستخدم ملفات تعريف الارتباط (Cookies) لفهم كيفية استخدامك لموقعنا وتحسين تجربتك في المحتوى والإعلانات. باستمرارك في تصفح الموقع، فإنك توافق على استخدامنا لملفات تعريف الارتباط وفقًا لسياسة الخصوصية لدينا.
أُووبس!
يبدو أن هناك خطأ ما في اتصالك بالإنترنت. يرجى الاتصال بالإنترنت وبدء التصفح مرة أخرى.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.