Простое обнаружение столкновений Javascript на массиве двумерных карт - PullRequest
0 голосов
/ 18 февраля 2019

Привет :) Я делаю небольшую Игру для школьного проекта.Это базовая Javascript 2D Canvas игра с картой тайлов из массива.

Моя проблема в том, что я не могу заставить столкновение работать.В моем примере я хочу «ходить» с моим «Героем» по плиткам с 1. 0 - это плитки, по которым «Герой» не ходит.

У меня есть простое обнаружение столкновений для границы холста, поэтому мой герой не может выйти за пределы холста.Но я не знаю, как заставить это работать для остальных.

HTML

  <script type="text/javascript">
    var Spielfeld, Spieler, Zuletzt, Collision;         


    function initialisieren( Anzeige ) {    
        Spielfeld = Anzeige ;                                               
        Spieler = new Spieler( Spielfeld ) ;                                
        Spieler.Name = 'Ich' ;                                              
        Zuletzt = 0 ;
        document.getElementsByTagName('body')[0].onkeydown = steuern ;      
        document.getElementsByTagName('body')[0].onkeyup = steuern ;        
        window.requestAnimationFrame( aktualisieren ) ; 

    }

    function steuern( Ereignis ) {                                          
        switch( Ereignis.keyCode ) {                                        
            case 87: Spieler.setOben( Ereignis.type == 'keydown'); break;   
            case 83: Spieler.setUnten( Ereignis.type == 'keydown'); break;  
            case 65: Spieler.setLinks( Ereignis.type == 'keydown'); break;  
            case 68: Spieler.setRechts( Ereignis.type == 'keydown'); break;
            case 38: Spieler.setOben( Ereignis.type == 'keydown'); break;
            case 40: Spieler.setUnten( Ereignis.type == 'keydown'); break;
            case 37: Spieler.setLinks( Ereignis.type == 'keydown'); break;
            case 39: Spieler.setRechts( Ereignis.type == 'keydown'); break;
        }
    }
    function aktualisieren() {                                              
        var Jetzt = new Date();                                             
        var Dauer = Jetzt.getTime() - Zuletzt ;                             
        Zuletzt = Jetzt.getTime() ;                                         
        loeschen( Spielfeld ) ;                                             
        Spieler.aktualisieren( Dauer ) ;                                                                        
        window.requestAnimationFrame( aktualisieren ) ;                     
    }

    function loeschen( Anzeige ) {
        Stift = Anzeige.getContext('2d') ;                                  
        Stift.clearRect( 0,0 , Anzeige.width, Anzeige.height ) ;}</script>
 </head>





  <body onload="initialisieren( document.getElementById('Spielfeld') ) ;">      
<canvas id="Laufweg" width="1280" height="768" style="position: absolute; z-index: 3">Funktioniert nicht!</canvas>
<canvas id="Hintergrund" width="1280" height="768" style="position: absolute; z-index: 1">Funktioniert nicht!</canvas>
<canvas id="Spielfeld" width="1280" height="768" style="position: absolute; z-index: 2">Funktioniert nicht!</canvas>
    <script type="text/javascript" src="./javascript/Charakter.js"></script>    
 </body>    

Charakter.js

 function Spieler( Spielfeld ) {
var Held = document.createElement('img');                       
Held.src = '../Arbeitsdateien/items/item_berliner.png';                         
var Anzeige = Spielfeld ;                                                                       
var Breite = 32 ;                                                                       
var Hoehe = 32 ;                                                
var PosX = 32 ;                                                 
var PosY = 192 +32 ;                                                    
var Schritt = 400 ;                                                                                         

var hero = Held;


var Oben, Unten, Links, Rechts ;                                                                        
    Oben = Unten = Links = Rechts = false ;                                 

this.setOben = function( Schalter ) { Oben = Schalter == true ; }                                           
this.setUnten = function( Schalter ) { Unten = Schalter == true ; }                                         
this.setLinks = function( Schalter ) { Links = Schalter == true ; }                                         
this.setRechts = function( Schalter ) { Rechts = Schalter == true ; }                                       
this.aktualisieren = function( Dauer ) {                                                                    
    bewegen( Dauer ) ;                                                                                      
    anzeigen() ;                                                                        
}

//--------------------------------------------------------------------------------------------------------------------------
//Kollision für die Laufwege -----------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------

var fliese = {
    fliesenGroesse: 32
};

var mapKollision = [                                                                                                                                                                            //004
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
    [1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0],
    [0,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0],
    [0,0,0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
    [0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
];
var viewport = document.getElementById('Laufweg');
var ctx = viewport.getContext('2d');

 function renderMap() {
var i, j;

ctx.clearRect( 0, 0, 1280, 768 );
ctx.fillStyle = "rgba(255,0,0,0.5)";

for( i = 0; i < mapKollision.length; i++ ) {
    for( j = 0; j < mapKollision[ i ].length; j++ ) {
        if( mapKollision[ i ][ j ] !== 0 ) {
            ctx.fillRect(
                j * fliese.fliesenGroesse, i * fliese.fliesenGroesse,
                fliese.fliesenGroesse, fliese.fliesenGroesse
            );
        }
    }
}
 }
 renderMap();

 function bewegen( Dauer ) {
var Etappe = Dauer / 1000 ;
if( Links ) PosX -= Schritt * Etappe ;
if( Rechts ) PosX += Schritt * Etappe ;
if( Oben ) PosY -= Schritt * Etappe ;
if( Unten ) PosY += Schritt * Etappe ;


if( PosX  < 0 ) PosX = 0 ;
if( PosX  > Anzeige.width -32  ) PosX = 1248 ;
if( PosY  < 0 ) PosY = 0 ;
if( PosY  > Anzeige.height -32 ) PosY = 736 ;
 }

function anzeigen() {
    Stift = Anzeige.getContext('2d') ;  
    Stift.drawImage( Held, PosX,PosY) ;
}
 }  

С этой частью кода я получил мое столкновение границыработа:

if( PosX  < 0 ) PosX = 0 ;
if( PosX  > Anzeige.width -32  ) PosX = 1248 ;
if( PosY  < 0 ) PosY = 0 ;
if( PosY  > Anzeige.height -32 ) PosY = 736 ;

Может быть, вы можете помочь мне с моей проблемой, чтобы получить работу столкновения для моих 0 плиток.Вот как это выглядит (красный - путь, по которому можно ходить (1) белый - это плитки, которые нельзя пройти (0)) (https://imgur.com/a/YLu6HU2)

Надеюсь, вы понимаете, в чем заключается моя проблема. Ядействительно плохо знаком с Javascript, но до этого он работал нормально для меня:)

1 Ответ

0 голосов
/ 18 февраля 2019

Я попытался создать суперсемплифицированную версию концепции , над которой вы работаете, и я надеюсь, что она поможет вам лучше понять, как исправить ваш код.Он также ввел некоторую концепцию, с которой вы, возможно, не знакомы, но они очень помогут вам думать в режиме «игровой разработки», я считаю!

Итак, мои извинения, если я не исправил ваш код напрямую- Я должен сказать, что «родной язык» для имени переменной не помог :) - но я все еще надеюсь, что это поможет.

Здесь файл HTML :

<canvas width="7" height="6"></canvas>

Как видите, я создаю очень супер-крошечный холст.Это, вероятно, не полезно для вас, но удивительно на самом деле тестировать вещи и в стиле «ретро-игры».

Здесь CSS :

canvas {
  border: 1px solid silver;
  width: 350px;
  height: 300px;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
}

В основном 1 пиксельхолст занимает 50 пикселей CSS.У нас ОГРОМНЫЕ пиксели:)

А теперь интересная часть. JS :

const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
const w = canvas.width; // 7
const h = canvas.height; // 6

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

const imageData = ctx.getImageData(0, 0, w, h);
const buff = new Uint32Array(imageData.data.buffer);

Я не хочучтобы использовать Canvas 2D API, я хочу, чтобы "блиц" был прямо на пикселе.Таким образом, я получаю весь холст как ImageData и вижу его буфер данных как массив 32-битного целого без знака.Таким образом, каждый элемент этого массива представляет один пиксель.Пиксель состоит из четырех байтов, по одному байту для каждого канала цвета: красный, зеленый, синий и альфа (непрозрачность).Это более или менее то же самое, когда вы задаете цвет RGB с помощью CSS (например, 0xffeedd), единственное отличие состоит в том, что порядок байтов инвертирован (так что вы не возражаете RGBA, но ABGR) из-за endianess .

Примечание: этот код предполагает, что мы находимся в порядке с прямым порядком байтов (вы можете смело предположить, что в общем случае в наше время для такого рода вещей).

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

Здесь map, 7x6.

const palette = [0xff000000, 0xffffffff, 0xff0000ff];

И здесь мы определяем наш palette: поэтому наша «игра» в настоящее время имеет только три цвета, первый из которых черный (максимальная непрозрачность, 0xff и 0 для R, G и B), второй - белый (0xff для каждого компонента), третий - красный (запомните порядок байтов, 0xff для альфа-канала и красного, 0 для зеленого и синего).

function drawMap() {
  for (let k = 0; k < h; k++) {
    for (let j = 0; j < w; j++) {
      let i = j + k * w;
      buff[i] = palette[map[i]]
    }
  }
}

Это должно быть больше илименее знакомый: у нас есть циклы для перебора массива map.Основываясь на значении карты (0 или 1), мы выбираем цвет из палитры, поэтому стены там черные, где коридор белый.

const hero = {x: 0, y: 1};

Используется heroхранить текущие координаты, где его рисовать.

function drawHero() {
  buff[hero.x + hero.y * w] = palette[2];
}

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

// listen to keyboard

const Keys = {
  pressed: {},
  handleEvent({type, code}) {
    this.pressed[code] = type === "keydown"
  }
}

document.addEventListener("keydown", Keys);
document.addEventListener("keyup", Keys);

Используется для «сохранения» нажатой клавиши.Это не оптимизировано, но для этого было достаточно.Он использует объект в качестве прослушивателя событий вместо функций (см .: https://developer.mozilla.org/en-US/docs/Web/API/EventListener/handleEvent)

function update() {
  let x = hero.x, y = hero.y;

  if (Keys.pressed["ArrowLeft"]) {
    x = Math.max(0, hero.x - 1);
  }
  if (Keys.pressed["ArrowRight"]) {
    x = Math.min(w - 1, hero.x + 1);
  }
  if (Keys.pressed["ArrowUp"]) {
    y = Math.max(0, hero.y - 1);
  }
  if (Keys.pressed["ArrowDown"]) {
    y = Math.min(h - 1, hero.y + 1);
  }

  if (map[x + y * w]) {
    hero.x = x;
    hero.y = y;
  }
}

А вот где обнаруживается столкновение! Функция update обновляет координаты героя на основе нажатых клавиш,стены и край холста. Итак, прежде всего, установите x и y на основе нажатых клавиш (если они есть), но в пределах границ холста. Затем, если «новые предложенные координаты» вернутся 1 в map, это означает, что персонаж может перемещаться туда, и поэтому объект hero также обновляется. В противном случае он не будет обновляться.

function loop() {
  update();
  drawMap();
  drawHero();

  // flush
  ctx.putImageData(imageData, 0, 0);

  requestAnimationFrame(loop);
}


loop();

Это всего лишь игровой циклЯ полагаю, это достаточно просто. Нам нужно, конечно, «сбросить» все, что мы написали, в положительный слой на фактическом холсте.

Это не оптимизировано, и есть много вещей, которые нужно сделать (например, вы должны обновитькоординаты, учитывая время, сейчас слишком быстрые), но я надеюсь, что это поможет вам.

Если нет, пожалуйста, дайте мне знать, как его изменить, чтобы иметь упрощенную версию вашей проблемы, чтобыпомочь вам лучшеer.

Здесь также приведен пример кода, описанного ниже: https://codepen.io/zer0/pen/JxxYgQ

...