2D地图阵列上的简单Javascript碰撞检测

问题描述 投票:1回答:1

你好:)我为一个学校项目做了一个小游戏。它是一个基本的Javascript 2D Canvas游戏,带有来自阵列的Tile Map。

我的问题是,我不能让碰撞工作。在我的例子中,我想用“我的英雄”“走”在瓷砖上,其中“0”是“英雄”不走的瓷砖。

我对Canvas边框有一个简单的碰撞检测,所以我的Hero不能走出Canvas。但我不知道如何让它为其他人工作。

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瓦片碰撞起作用。这里有一个img它看起来如何(红色是走路的路径(1)白色是非步行瓷砖(0))(https://imgur.com/a/YLu6HU2

我希望你能理解我的问题是什么。我是Javascript的新手,但在此之前,它对我有用:)

javascript canvas collision-detection collision
1个回答
1
投票

我试着对你正在研究的概念做一个超级简化的版本,我希望它能帮助你更好地理解如何修复你的代码。它还介绍了一些你可能不熟悉的概念,但我相信它们将极大地帮助你思考“游戏开发”模式!

所以,如果我没有直接修复你的代码,我很抱歉 - 我不得不说,变量名称的“本地语言”没有帮助:) - 但我仍然希望它会有所帮助。

这里是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“blit”直接像素。所以我将整个画布作为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,0,R,G和B),第二种是白色(每个组件为0xff),第三个是红色(记住字节序,Alpha和红色通道为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用于存储当前的coords绘制它的位置。

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

© www.soinside.com 2019 - 2024. All rights reserved.