Adding Obstacles and Colliding with the Canvas

I'm trying to add some canvas play obstacles that I have, but something seems to be wrong and I can't seem to point a finger at it.

I just want some simple walls here and there to make the game harder and collide with the walls (so if the player hits a wall, the game ends).

var
    // variables
    COLS = 25, // Columns
    ROWS = 25, // Rows
    EMPTY = 0, // Empty Cell
    SNAKE = 1, // Snake
    FRUIT = 2, // Fruit
    LEFT  = 0, // left direction (key)
    UP    = 1, // up direction (key)
    RIGHT = 2, // right direction (key)
    DOWN  = 3, // down direction (key)
    KEY_LEFT  = 37, // key codes for keyboard input (Codes can be found online)
    KEY_UP    = 38, // key codes for keyboard input (Codes can be found online)
    KEY_RIGHT = 39, // key codes for keyboard input (Codes can be found online)
    KEY_DOWN  = 40, // key codes for keyboard input (Codes can be found online)
    obstacle =  [[0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0]],
        //        [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
        //        [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
        //        [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0],
        //        [0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0]],
               
    
    // Objects 
    canvas,	  // Canvas
    ctx,	  // Canvas render
    keystate, // Key inputs
    frames,   // frames per second
    score;	  // player score
    
    grid = {
    	width: null,  // Amount of columns 
    	height: null, // Amount of rows
    	_grid: null,  // Array
    	init: function(d, c, r) { // initiation with direction, columns and rows.
    		this.width = c; // Set width to number of columns (c)
    		this.height = r;  // set height to number of rows (r)
            this._grid = []; // Initiate grid with empty array
    		for (var x=0; x < c; x++) {
    			this._grid.push([]); 
    			for (var y=0; y < r; y++) {
    				this._grid[x].push(d); // set current column and push new value for each row in column
    			}
    		}
    	},
    	set: function(val, x, y) { // set values for the grid cells with x and y position 
    		this._grid[x][y] = val;
    	},
    	
    	get: function(x, y) { // get the value of x and y position
    		return this._grid[x][y];
    	}
        
    }
    
    snake = { // Creating snake
    	direction: null, // Direction of snake
    	last: null,		 // last element in queue pointer
    	_queue: null,	 // queue array
    
        // Sets start position of snake, same initiation method as before
    	init: function(d, x, y) {
    		this.direction = d; // Direction set to d
    		this._queue = []; // Another empty queue array
    		this.insert(x, y); // Inserting x & y position
    	},
        
        // Insert method that adds elements to queue with x and y position
    	insert: function(x, y) {
    		this._queue.unshift({x:x, y:y}); // unshift prepends an element to array
    		this.last = this._queue[0];
    	},
    	
        // Remove function to remove and return element to queue 
    	remove: function() {
    		return this._queue.pop(); // pop returns the last element of array
    	}
    };
    
function obstacle() {
  empty.push({obstacle});

  ctx.beginPath();
  ctx.rect(obstacle);
  ctx.fillStyle = "#7a26ce";
  ctx.fill();
  ctx.closePath();
}

function setFood() { // Food for hungry snake 
  var empty = []; // tracks all empty places in the grid
  // for loop to find all empty cells in grid
  for (var x=0; x < grid.width; x++) {
    for (var y=0; y < grid.height; y++) {
      if (grid.get(x, y) === EMPTY) {
        empty.push({x:x, y:y});
      }
    }
  }
  // variable randomposition to pick random empty cell
  var randpos = empty[Math.round(Math.random()*(empty.length - 1))];
  grid.set(FRUIT, randpos.x, randpos.y);
}

function main() { // call all the functions that we will use in the game
  // canvas 
  canvas = document.createElement("canvas");
  canvas.width = COLS*20;  // Sets canvas width to columns * 20 
  canvas.height = ROWS*20; // Sets canvas height to columns * 20 
  ctx = canvas.getContext("2d");

  document.body.appendChild(canvas); // Adds canvas element to the body of the document
  ctx.font = "12px sans-serif"; // font 
  frames = 0;
  keystate = {};

  document.addEventListener("keydown", function(evt) { // Track all keyboard input
    keystate[evt.keyCode] = true;
  });
  document.addEventListener("keyup", function(evt) { // Track all keyboard input
    delete keystate[evt.keyCode];
  });

  init(); // Initiate Game Loop 
  loop(); // Start Game Loop 
}

function init() { // Reset and intiate game objects
  score = 0; // set start score to 0
  grid.init(EMPTY, COLS, ROWS);


  var sp = {x:Math.floor(COLS/2), y:ROWS-1};
  snake.init(UP, sp.x, sp.y); // Start direction
  grid.set(SNAKE, sp.x, sp.y);
  setFood();
  grid._grid = grid._grid.concat(obstacle);
}

function loop() { // Game loop for rendering and objects
  update();
  draw();
  window.requestAnimationFrame(loop, canvas); // Canvas will call loop function when it needs to redraw
}

function update() { // update function
  frames++;
  // Keyboard input
  if (keystate[KEY_LEFT] && snake.direction !== RIGHT) {
    snake.direction = LEFT;
  }
  if (keystate[KEY_UP] && snake.direction !== DOWN) {
    snake.direction = UP;
  }
  if (keystate[KEY_RIGHT] && snake.direction !== LEFT) {
    snake.direction = RIGHT;
  }
  if (keystate[KEY_DOWN] && snake.direction !== UP) {
    snake.direction = DOWN;
  }
  // Update game every 5 frames.
  if (frames%5 === 0) {
    // last element from the snake queue 
    var nx = snake.last.x;
    var ny = snake.last.y;
    // Updating the position of snake depending on the direction it is heading
    switch (snake.direction) {
      case LEFT:
        nx--;
        break;
      case UP:
        ny--;
        break;
      case RIGHT:
        nx++;
        break;
      case DOWN:
        ny++;
        break;
    }
    // if statement checking conditions whether game should keep running or reset aka game over
    if (0 > nx || nx > grid.width-1  ||
        0 > ny || ny > grid.height-1 ||
        grid.get(nx, ny) === SNAKE
       ) {
      return init();

    }
    // Checks the new position of the snake and if it on a fruit item or not.
    if (grid.get(nx, ny) === FRUIT) {
      // If it is on a fruit item it will increase your score and create a new food in a random cell.
      score++;
      setFood();

    } else {
      // Takes out the tail (first item) from queue and removes identifier from the grid.
      var tail = snake.remove();
      grid.set(EMPTY, tail.x, tail.y);

    }

    // Snake identifier that is created at the new position and is added to the queue 
    grid.set(SNAKE, nx, ny);
    snake.insert(nx, ny);

  }
}

function draw() { // render grid to canvas
  var tw = canvas.width/grid.width;  // Calculate tile width
  var th = canvas.height/grid.height; // Calculate tile height

  for (var x=0; x < grid.width; x++) { // for-loop loops through entire grid to draw cells
    for (var y=0; y < grid.height; y++) {
      // Depending on the identifier of each cell sets certain fillstyle defined below. 
      switch (grid.get(x, y)) {
        case EMPTY:
          ctx.fillStyle = "#5a5a5a";
          break;
        case SNAKE:
          ctx.fillStyle = "#B54548";
          break;
        case FRUIT:
          ctx.fillStyle = "lightblue";
          break;
        case obstacle:
          ctx.fillStyle = "yellow";
          break;
      }
      ctx.fillRect(x*tw, y*th, tw, th);
    }
  }
  // Change fillstyle and show score on the screen.
  ctx.fillStyle = "#000";
  ctx.fillText("SCORE: " + score, 10, canvas.height-10);
}
// Game Start!
main();
      

canvas {
  display: block;
  position: absolute;
  border: 2px solid #000;
  margin: auto;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}
      

Run codeHide result


or Fiddle .

+3


source to share


1 answer


I've been doing collision games for a while now and I find using a collision map is the easiest way to go, instead of creating one object that stores all objects at the same time. Here's an example:

var collisions = [];
function addCollision(x,y){
    if(typeof collisions[x] == "undefined")collisions[x] = []; //If the row is empty, create it
    collisions[x][y] = true;                                   //Set the row and column to true
};
function checkCollision(x,y){
    return (typeof collisions[x] != "undefined")?//If the row is undefined, there nothing in it, so return false
    ((typeof collisions[x][y] != "undefined")    //If not, but the column is undefined, return false
    ?true:false):false;                          //If the row and column is true, return true.
};

      



Now it addCollision(x,y)

will add a new piece, and checkCollision(x,y)

see that there is something in x, y.

This is good for (relatively) small cards, as a large one will overload memory a lot. I was able to do 500x500, but I'm not sure what the maximum size is.

0


source







All Articles