Как я могу обнаружить столкновение в 2D карте игры плитки - PullRequest
0 голосов
/ 02 июля 2018

Я сделал эту основную игру, где я нарисовал карту и игрока, игрок может двигаться куда угодно, но как я могу сделать так, чтобы он не двигался, когда он находится на плитке [1] на карте? также, когда я пытаюсь проверить, больше ли player.x больше 50, он может пойти влево, он работает, но если я нажму 2 клавиши сразу, он пройдет const context = document.querySelector ("canvas"). getContext ("2d");

var rgb = 'rgb(' + Math.random()*256 + ',' + Math.random()*256 + ',' + Math.random()*256 + ','+Math.random() + ')';

document.onload = Loop();

var width = 1500;
var height = 800;

function Loop(){

  var width = 1500;
  var height = 800;

  context.canvas.height = height;
  context.canvas.width = width;

  this.interval = setInterval(Update, 1000/100);

}



const Player = function(x, y, w, h, color) {
  this.x = x; this.y = y; this.w = w; this.h = h;

  this.speedY = 0; this.speedX = 0;
  this.Draw = function(){
    context.fillStyle = this.color;
    context.fillRect(this.x, this.y, this.w, this.h);
  };
  this.Move = function(){
    this.x += this.speedX;
    this.y += this.speedY;
  };
};<code>

var player = new Player(100,100,50, 50, rgb);


var Key = {};
function Update(){
  context.clearRect(0, 0, width, height);
  Map();
  player.Draw();
  player.Move();

onkeydown = onkeyup = function(e){
  player.speedX = 0;
  player.speedY = 0;
  e = e || event;
  Key[e.keyCode] = e.type == 'keydown';
    if(Key[37] || Key[65]) {player.speedX -= 2}
    if(Key[38] || Key[87]) {player.speedY -= 2}
    if(Key[39] || Key[68]) {player.speedX += 2}
    if(Key[40] || Key[83]) {player.speedY += 2}
    if(Key[32]) {player.color = 'rgb(' + Math.random()*256 + ',' + Math.random()*256 + ',' + Math.random()*256 + ','+Math.random()*1 + ')';}
  };
}

var map = [
1, 1, 1, 1, 1,
1, 0, 0, 0, 1,
1, 0, 0, 0, 1,
1, 0, 0, 0, 1,
1, 1, 1, 1, 1
    ];

var row = 5;
var column = 5;


function Map(){
  for(let y = -1; y < column; y++){

    for(let x = -1; x < row; x++){
      switch(map[((y*row) + x)]) {
        case 0: context.fillStyle = player.color;
          break;
        case 1: context.fillStyle = "#ffffff";
          break;
        default: context.fillStyle = "#000000";
      }
    context.fillRect(x*50, y*50, 50, 50);

    }
  }
}

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				display: block;
				margin: auto;
				border: solid 1px white;
				border-radius: 10px;
			}
			
			script {
				display: none;
			}
		</style>
	</head>
	
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
		
		void function() {
			
			"use strict";
			
			// Classes
			function Camera(x,y) {
				this.x = x || 0.0;
				this.y = y || 0.0;
			}
			
			Camera.prototype = {
				set: function(x,y) {
					this.x = x || 0.0;
					this.y = y || 0.0;
				},
				
				pan: function(x,y) {
					this.x += x || 0.0;
					this.y += y || 0.0;
				}
			};
			
			var nextID = 0;
			
			function Tile(colour) {
				this.id = nextID++;
				this.colour = colour || "black";
			}
			
			function Map(width,height) {
				this.width = width || 1;
				this.height = height || 1;
				this.map = [];
				this.map.length = this.height;
				
				for (var y = 0; y < this.height; ++y) {
					this.map[y] = [];
					this.map[y].length = width;
					
					for (var x = 0; x < this.width; ++x) {
						this.map[y][x] = Math.random() < 0.2 ?
							this.TILE_WALL:
							this.TILE_GRASS;
					}
					
					this.map[y][0] = this.TILE_WALL;
					this.map[y][this.width - 1] = this.TILE_WALL;
				}
				
				for (var x = 0; x < this.width; ++x) {
					this.map[0][x] = this.TILE_WALL;
					this.map[this.height - 1][x] = this.TILE_WALL;
				}
			}
			
			Map.prototype = {
				TILE_WIDTH: 32.0,
				TILE_HEIGHT: 32.0,
				INV_TILE_WIDTH: 0.0,
				INV_TILE_HEIGHT: 0.0,
			
				TILE_AIR: new Tile("#00000000"),
				TILE_GRASS: new Tile("#00AA00FF"),
				TILE_WALL: new Tile("#555555FF"),
			
				set: function(x,y,tile) {
					this.map[y][x] = tile;
				},
			
				scaleX: function(x) {
					return (x * this.INV_TILE_WIDTH) | 0;
				},
			
				scaleY: function(y) {
					return (y * this.INV_TILE_HEIGHT) | 0;
				},
			
				isColliding: function(x,y) {
					return x > -1 && x < this.width
						&& y > -1 && y < this.height
						&& this.map[y][x].id > 1;
				},
			
				render: function(ctx,camera) {
					for (var y = 0; y < this.height; ++y) {
						for (var x = 0; x < this.width; ++x) {
							var tile = this.map[y][x];
							var _x = x * this.TILE_WIDTH - camera.x;
							var _y = y * this.TILE_HEIGHT - camera.y;
							
							ctx.fillStyle = tile.colour;
							ctx.fillRect(_x,_y,this.TILE_WIDTH - 1,this.TILE_HEIGHT - 1);
						}
					}
				}
			};
			
			Map.prototype.INV_TILE_WIDTH = 1.0 / Map.prototype.TILE_WIDTH;
			Map.prototype.INV_TILE_HEIGHT = 1.0 / Map.prototype.TILE_HEIGHT;
			
			function Player(x,y) {
				this.x = x || 0.0;
				this.y = y || 0.0;
				this.dx = 0.0;
				this.dy = 0.0;
				this.isUp = false;
				this.isDown = false;
				this.isLeft = false;
				this.isRight = false;
			}
			
			Player.prototype = {
				WIDTH: 20.0,
				HEIGHT: 20.0,
				
				ACCELERATION: 1.0,
				DEACCELERATION: 0.5,
				MAX_SPEED: 3.0,
				
				tick: function(map) {
					// Movement
					if (this.isUp) {
						this.dy -= this.ACCELERATION;
						
						if (this.dy < -this.MAX_SPEED) {
							this.dy = -this.MAX_SPEED;
						}
					} else if (this.dy < 0.0) {
						this.dy += this.DEACCELERATION;
						
						if (this.dy > 0.0) {
							this.dy = 0.0;
						}
					}
					
					if (this.isDown) {
						this.dy += this.ACCELERATION;
						
						if (this.dy > this.MAX_SPEED) {
							this.dy = this.MAX_SPEED;
						}
					} else if (this.dy > 0.0) {
						this.dy -= this.DEACCELERATION;
						
						if (this.dy < 0.0) {
							this.dy = 0.0;
						}
					}
					
					if (this.isLeft) {
						this.dx -= this.ACCELERATION;
						
						if (this.dx < -this.MAX_SPEED) {
							this.dx = -this.MAX_SPEED;
						}
					} else if (this.dx < 0.0) {
						this.dx += this.DEACCELERATION;
						
						if (this.dx > 0.0) {
							this.dx = 0.0;
						}
					}
					
					if (this.isRight) {
						this.dx += this.ACCELERATION;
						
						if (this.dx > this.MAX_SPEED) {
							this.dx = this.MAX_SPEED;
						}
					} else if (this.dx > 0.0) {
						this.dx -= this.DEACCELERATION;
						
						if (this.dx < 0.0) {
							this.dx = 0.0;
						}
					}
					
					// Collision
					if (this.dx !== 0.0) {
						var minY = map.scaleY(this.y);
						var maxY = map.scaleY(this.y + this.HEIGHT);
						var minX = 0;
						var maxX = 0;
						
						if (this.dx < 0.0) {
							minX = map.scaleX(this.x + this.dx);
							maxX = map.scaleX(this.x);
						} else {
							minX = map.scaleX(this.x + this.WIDTH);
							maxX = map.scaleX(this.x + this.WIDTH + this.dx);
						}
						
						loop:
						for (var y = minY; y <= maxY; ++y) {
							for (var x = minX; x <= maxX; ++x) {
								if (map.isColliding(x,y)) {
									this.x = this.dx < 0.0 ?
										(x + 1) * map.TILE_WIDTH:
										x * map.TILE_WIDTH - this.WIDTH - 1;
								
									this.dx = 0.0;
									break loop;
								}
							}
						}
					}
					
					if (this.dy !== 0.0) {
						var minX = map.scaleX(this.x);
						var maxX = map.scaleX(this.x + this.WIDTH);
						var minY = 0;
						var maxY = 0;
						
						if (this.dy < 0.0) {
							minY = map.scaleY(this.y + this.dy);
							maxY = map.scaleY(this.y);
						} else {
							minY = map.scaleY(this.y + this.HEIGHT);
							maxY = map.scaleY(this.y + this.HEIGHT + this.dy);
						}
						
						loop:
						for (var y = minY; y <= maxY; ++y) {
							for (var x = minX; x <= maxX; ++x) {
								if (map.isColliding(x,y)) {
									this.y = this.dy < 0.0 ?
										(y + 1) * map.TILE_HEIGHT:
										y * map.TILE_HEIGHT - this.HEIGHT - 1;
								
									this.dy = 0.0;
									break loop;
								}
							}
						}
					}
					
					this.x += this.dx;
					this.y += this.dy;
				},
				
				render: function(ctx,camera) {
					camera.set(this.x,this.y);
				
					ctx.lineWidth = 1;
					ctx.strokeStyle = "black";
					ctx.fillStyle = "darkred";
					ctx.beginPath();
					ctx.rect(this.x - camera.x,this.y - camera.y,this.WIDTH,this.HEIGHT);
					ctx.fill();
					ctx.stroke();
				}
			};
			
			// Variables
			var canvasWidth = 180;
			var canvasHeight = 160;
			var canvas = null;
			var ctx = null;
			var camera = null;
			var map = null;
			var player = null;
			
			// Functions
			function onKeyDown(e) {
				switch(e.key.toUpperCase()) {
					case "W": player.isUp = true; break;
					case "S": player.isDown = true; break;
					case "A": player.isLeft = true; break;
					case "D": player.isRight = true; break;
				}
			}
			
			function onKeyUp(e) {
				switch(e.key.toUpperCase()) {
					case "W": player.isUp = false; break;
					case "S": player.isDown = false; break;
					case "A": player.isLeft = false; break;
					case "D": player.isRight = false; break;
				}
			}
			
			function loop() {
				// Tick
				player.tick(map);
				
				// Render
				ctx.fillStyle = "gray";
				ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
				
				map.render(ctx,camera);
				player.render(ctx,camera);
				
				//
				requestAnimationFrame(loop);
			}
			
			// Entry point (first to execute)
			onload = function() {
				canvas = document.getElementById("canvas");
				canvas.width = canvasWidth;
				canvas.height = canvasHeight;
				
				ctx = canvas.getContext("2d");
				ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
				
				camera = new Camera(0.0,0.0);
				map = new Map(10,10);
				player = new Player(40.0,40.0);
				
				map.set(1,1,map.TILE_GRASS);
				
				addEventListener("keydown",onKeyDown);
				addEventListener("keyup",onKeyUp);
				
				loop();
			}
			
		}();
		
		</script>
	</body>
</html>
0 голосов
/ 02 июля 2018

Во-первых, глядя на ваш код, вам не хватает некоторых вещей, необходимых для реализации базового обнаружения столкновений:

  1. Текущее направление игрока, в котором он / она движется. Это важно, поскольку позволяет функции, определяющей обнаружение столкновения, различать, с какой стороны он проверяет наличие столкновения (вверх, вниз, влево или вправо). поскольку игрок может столкнуться только с одной стороной одновременно.

  2. Положение и размер плитки. Это также очень важно, потому что, как и в первом пункте, есть только одна сторона тайла, с которой игрок может столкнуться, и зная размер и положение, можно определить, является ли это столкновением или нет, основываясь на размере и позиции игрока.

Кроме того, поскольку вы упомянули, что это базовая игра, приведенная ниже реализация представляет собой базовое обнаружение столкновений. Если вы хотите сделать более сложную и большую игру, вы должны попробовать изучить четырехугольные деревья для более эффективного обнаружения столкновений: https://gamedevelopment.tutsplus.com/tutorials/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space--gamedev-374

Теперь это функция для обнаружения столкновения, для удобства чтения и краткости, p будет представлять объект игрока, а t будет представлять объект плитки. Эта функция возвращает информацию о столкновении игрока с плиткой в ​​зависимости от направления его движения.

function isColliding(p, t){
  if (p.direction == 'up') {
   return p.y +(p.height/2)-p.speedY< t.y + t.height && p.y > t.y
      && p.x + p.width > t.x && p.x < t.x + t.width;
  }
  if (p.direction == 'down') {
    return p.y + (p.height/2)+p.speedY > t.y && p.y < t.y
      && p.x + p.width > t.x && p.x < t.x + t.width;
  }
  if (p.direction == 'right') {
    return p.x + p.width+p.speedX > t.x && p.x < t.x
      && p.y +(p.height/2)> t.y && p.y + p.height < t.y +t.height+ (p.height / 2);
  }
  if (p.direction == 'left') {
    return p.x -p.speedX< t.x + t.width && p.x > t.x
      && p.y +(p.height/2)> t.y && p.y + p.height < t.y +t.height+ (p.height / 2);
  }
  return false;
}

Возможно, вы захотите включить это в функцию перемещения игрока, чтобы постоянно обнаруживать плитки по мере их движения. Чтобы сделать это, вам нужно изменить обнаружение нажатия клавиш таким образом, чтобы при каждом нажатии клавиши обновлялось направление игрока, вот простой пример:

document.onkeydown = function(event){
    if (event.keyCode == 87)
        player.up = true;
    else if (event.keyCode == 65)
        player.left = true;
    else if (event.keyCode == 83)
        player.down = true;
    else if (event.keyCode == 68)
        player.right = true;
}

и еще один простой пример для каждого движения игрока (пользователь нажимает клавишу):

const Player= function(/*Param stuff*/){
  /*Property stuff*/
  //tileArray is the array (or object, your choice) of all the current tiles in the map
  this.move=function(tileArray){
    //Go through all tiles to see if player is colliding with any of them
    for(var t in tileArray){
      if(this.up){
        if(isColliding(this, tileArray[t]){
          //functionality for when player collides
        }else{
          //functionality for when player doesn't collide
        }
      }
      //check if player is going down, left, etc
    }
  }
}

Это всего лишь примеры того, как реализовать обнаружение. Вы должны использовать его как справочник, чтобы реализовать его относительно того, как функционирует ваш код, потому что я не писал его на основе того, что вы опубликовали.

PS.

Не забудьте также преобразовать направления в ложные после того, как пользователь перестанет нажимать клавишу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...