Коллизия в Tilemap

  1. var canvas = document.getElementById('canvas');
  2. var w = canvas.width; //размеры канваса
  3. var h = canvas.height;
  4. var g = canvas.getContext('2d');
  5. g.font = "10px Verdana"; //дефолтный шрифт
  6.  
  7. var keys = {}; //нажатые клавиши
  8. var player = {
  9.     x:0, y:0, w: 16, h: 32,
  10.     sx: 0, sy: 0, //скорость игрока
  11.     onLand: false //возможность прыгать и тд
  12. };
  13. var tsize = 32; //размер тайла
  14. var map = [
  15.     [0,0,0,0,0,0,0,0,0,0,0,0],
  16.     [0,0,0,0,0,0,0,0,0,0,0,0],
  17.     [0,0,0,0,0,0,0,0,0,1,0,0],
  18.     [0,0,0,0,1,0,0,0,1,0,0,0],
  19.     [0,0,1,0,0,0,0,0,0,1,1,0],
  20.     [0,0,0,0,0,1,0,0,1,0,1,0],
  21.     [1,1,1,1,1,1,1,1,1,1,1,1]
  22. ];
  23. //array object lifehukc
  24. map.w = map[0].length;
  25. map.h = map.length;
  26.  
  27. //обработка клавиш
  28. window.onkeydown = function (e) {
  29.     keys[e.keyCode] = true;
  30. }
  31.  
  32. window.onkeyup = function (e) {
  33.     keys[e.keyCode] = false;
  34. }
  35.  
  36. function drawPlayer() {
  37.     g.fillStyle = "#F9A520";
  38.     g.fillRect(player.x, player.y, player.w, player.h);
  39. }
  40.  
  41. function drawMap() {
  42.     g.fillStyle = "#A0A020";
  43.     for (var iy = 0; iy < map.h; iy++)
  44.     for (var ix = 0; ix < map.w; ix++) {
  45.         var id = map[iy][ix];
  46.         if (id != 0) g.fillRect(ix*tsize, iy*tsize, tsize, tsize);
  47.     }
  48. }
  49.  
  50. function getTile(x, y) {
  51.     var cell = 0; //или 1/undefined/null/etc
  52.     if (x > -1 && y > -1 && x < map.w && y < map.h) {
  53.         cell = map[y][x];
  54.     }
  55.     return cell;
  56. }
  57.  
  58. function updatePlayer() {
  59.     player.sy += 0.1; //гравитация
  60.  
  61.     if (player.sx != 0) {
  62.  
  63.         var sx = Math.abs(player.sx);
  64.         var vx = sx / player.sx; //вектор направления
  65.  
  66.         while(sx > tsize) { //скорость больше размера тайла
  67.             player.x += tsize * vx;
  68.             sx -= tsize;
  69.             collision(player, 0);
  70.             if (player.sx == 0) sx = 0;
  71.         }
  72.  
  73.         player.x += sx * vx;
  74.         collision(player, 0); //коллизия по x
  75.     }
  76.  
  77.     if (player.sy != 0) {
  78.  
  79.         var sy = Math.abs(player.sy);
  80.         var vy = sy / player.sy;
  81.  
  82.         while(sy > tsize) {
  83.             player.y += tsize * vy;
  84.             sy -= tsize;
  85.             collision(player, 1);
  86.             if (player.sy == 0) sy = 0;
  87.         }
  88.  
  89.         player.onLand = false;
  90.         player.y += sy * vy;
  91.         collision(player, 1); //коллизия по y
  92.     }
  93.  
  94.     //замедление по x
  95.     if (player.sx >= 0.1) player.sx -= 0.1;
  96.     if (player.sx <= -0.1) player.sx += 0.1;
  97.     if (Math.abs(player.sx) < 0.1) player.sx = 0;
  98.  
  99.     //управление
  100.     if ((keys[37] || keys[65]) && player.sx > -3) player.sx -= 0.3;
  101.     if ((keys[39] || keys[68]) && player.sx < 3) player.sx += 0.3;
  102.     if ((keys[40] || keys[83])) player.sy = 999; //очень быстро вниз
  103.     if (keys[69]) player.sx = tsize*2; //световые скачки на Q/E
  104.     if (keys[81]) player.sx = -tsize*2;
  105.  
  106.     if ((keys[38] || keys[87]) && player.onLand) { //прыжок
  107.         player.onLand = false;
  108.         player.sy = -4;
  109.     }
  110.  
  111.     if (keys[82]) { //перезапуск на клавишу R
  112.         player.x = 0;
  113.         player.y = 0;
  114.         player.sx = 0;
  115.         player.sy = 0;
  116.     }
  117. }
  118.  
  119. function collision(body, dir) {
  120.  
  121.     var x = Math.floor(body.x / tsize);
  122.     var y = Math.floor(body.y / tsize);
  123.     var maxX = Math.ceil((body.x + body.w) / tsize);
  124.     var maxY = Math.ceil((body.y + body.h) / tsize);
  125.  
  126.     for (var iy = y; iy < maxY; iy++)
  127.     for (var ix = x; ix < maxX; ix++) {
  128.         if (getTile(ix, iy) != 1) continue;
  129.  
  130.         if (dir == 0) { //движение по x
  131.  
  132.             if (body.sx > 0) { //вправо
  133.                 body.x = ix * tsize - body.w;
  134.                 body.sx = 0;
  135.  
  136.             } else if (body.sx < 0) { //влево
  137.                 body.x = ix * tsize + tsize;
  138.                 body.sx = 0;
  139.             }
  140.  
  141.         } else if (dir == 1) { //движение по y
  142.  
  143.             if (body.sy > 0) { //вниз
  144.                 body.onLand = true;
  145.                 body.y = iy * tsize - body.h;
  146.                 body.sy = 0;
  147.  
  148.             } else if (body.sy < 0) { //вверх
  149.                 body.y = iy * tsize + tsize;
  150.                 body.sy = 0;
  151.             }
  152.  
  153.         }
  154.  
  155.     }
  156. }
  157.  
  158.  
  159.  
  160. function main() {
  161.     updatePlayer();
  162.  
  163.     g.clearRect(0, 0, w, h);
  164.     drawMap();
  165.     drawPlayer();
  166.  
  167.   g.fillStyle="#000"; //~~Math.floor~~
  168.   g.fillText(""+~~player.x+","+~~player.y, 0, 10);
  169.   g.fillText(""+~~player.sx+","+~~player.sy, 0, 20);
  170. }
  171.  
  172. setInterval(main, 1000 / 60);
Управление WASD/стрелки, Q/E для сильного ускорения, R для рестарта.
Предлагайте варианты по улучшению кода основной коллизии.

Старая (убогая) версия:
https://jsfiddle.net/RblSb/xw5ybxtj/6/
Текущая:
https://jsfiddle.net/RblSb/xw5ybxtj/

Реклама

Мы в соцсетях

tw tg yt gt