2D地图数组上的简单Javascript冲突检测

时间:2019-02-18 17:25:17

标签: javascript canvas collision-detection collision

您好:)我为一个学校项目制作了一个小游戏。它是一个基本的Javascript 2D Canvas游戏,带有来自数组的Tile Map。

我的问题是,我无法使碰撞起作用。在我的示例中,我要在带有“ 1”的图块上以“英雄”“行走”。0是“英雄”不行走的图块。

我对Canvas边界有一个简单的碰撞检测,所以我的英雄不能走出Canvas的外面。但是我不知道如何使它正常工作。

HTML

  const inputElements = parentElement.getElementsByTagName('input')
  if (inputChilds.length > 0) {
    inputChilds.item(0).focus();
  }

Charakter.js

  <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>    

使用此代码部分,我完成了边框碰撞工作:

 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) ;
}
 }  

也许您可以帮助我解决我的问题,以使我的0个图块发生碰撞。 这是img的外观(红色是行走的路径(1)白色是不可行走的瓦片(0)) (https://imgur.com/a/YLu6HU2

希望您能理解我的问题是什么。 我对Javascript真的很陌生,但在此之前,它对我来说还可以:)

1 个答案:

答案 0 :(得分:1)

我尝试制作一个超级简化的版本,该版本简化了您正在使用的 concept ,并且希望它可以帮助您更好地了解如何修复代码。我相信它也介绍了一些您可能不熟悉的概念,但是它们将极大地帮助您以“游戏开发”模式进行思考!

因此,如果我没有直接修复您的代码,我深表歉意–我不得不说,变量名称的“本地语言”没有帮助:) –但是我仍然希望它会有所帮助。

这是 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,我希望ti直接“像素化”像素。 因此,我将整个画布设为ImageData,并将其数据缓冲区视为无符号32位整数数组。这样,该阵列的每个元素代表一个单个像素。一个像素由四个字节组成,每个通道分别对应一个颜色的字节:红色,绿色,蓝色和Alpha(不透明度)。当您通过CSS指定RGB颜色(例如0xffeedd)时,它几乎相同,唯一的区别是由于endianess,字节的顺序被颠倒了(所以您不必拥抱RGBA而是ABGR)。

注意:此代码假定我们使用小尾数法(对于这种情况,您可以放心地假设现在通常来说)。

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
];

这里的地图7x6

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

在这里我们定义palette:因此,我们的“游戏”目前只有三种颜色,第一种是黑色(最大不透明度,0xff,R,G和B为0),第二种是白色(每个分量为0xff),第三个为红色(记住字节序,Alpha和Red通道为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数组。根据地图的值(01),我们从调色板中选择颜色,因此墙壁为黑色,走廊为白色。

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函数根据所按下的按键,墙壁和画布的边缘来更新英雄坐标。 因此,首先,根据所按下的键(如果有)在画布的边界内设置xy。 然后,如果“新提议的坐标”在1中返回map,则表示角色可以移动到那里,因此hero对象也被更新。 否则它将不会被更新。

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

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

  requestAnimationFrame(loop);
}


loop();

那只是游戏循环,我相信这很简单。当然,我们需要“冲洗”我们在实际画布中写入到buff的所有内容。

这不是最优化的,还有很多事情要做(例如,您应该考虑时间更新坐标,现在太快了),但是希望对您有所帮助。

如果没有,请让我知道如何修改它,以简化您的问题,以便为您提供更好的帮助。

这里还有一个正在运行的示例代码: https://codepen.io/zer0/pen/JxxYgQ