[英]Simple Javascript Collision Detection on a 2D map array
您好:)我為學校項目制作了一個小游戲。 它是一個基本的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個圖塊發生碰撞。 這是img的外觀(紅色是行走的道路(1)白色是不可行走的瓦片(0))( https://imgur.com/a/YLu6HU2 )
希望您能理解我的問題所在。 我對Javascript真的很陌生,但在此之前,它對我來說還可以:)
我試圖對您正在使用的概念進行超級簡化,希望它可以幫助您更好地了解如何修復代碼。 它也介紹了一些您可能不熟悉的概念,但是我相信它們將極大地幫助您以“游戲開發”模式進行思考!
因此,如果我沒有直接修復您的代碼,我深表歉意–我不得不說,變量名稱的“本地語言”沒有幫助:) –但我仍然希望它會有所幫助。
這是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位整數數組。 這樣,該陣列的每個元素代表一個單個像素。 一個像素由四個字節組成,每個通道分別對應一個顏色的字節:紅色,綠色,藍色和Alpha(不透明度)。 當您通過CSS指定RGB顏色(例如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
];
這是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
數組。 根據地圖的值( 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
。 然后,如果“新提議的坐標”在map
返回1
,則表示角色可以在那里移動,因此hero
對象也將更新。 否則它將不會被更新。
function loop() {
update();
drawMap();
drawHero();
// flush
ctx.putImageData(imageData, 0, 0);
requestAnimationFrame(loop);
}
loop();
那只是游戲循環,我相信這很簡單。 當然,我們需要“沖洗”在實際畫布中寫入到buff的所有內容。
這還沒有優化,還有很多事情要做(例如,您應該考慮時間更新坐標,現在太快了),但是我希望它能對您有所幫助。
如果沒有,請讓我知道如何修改它以簡化您的問題,以幫助您更好。
這里還有一個描述代碼的運行示例: https : //codepen.io/zer0/pen/JxxYgQ
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.