你好:)我为一个学校项目做了一个小游戏。它是一个基本的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的新手,但在此之前,它对我有用:)
我试着对你正在研究的概念做一个超级简化的版本,我希望它能帮助你更好地理解如何修复你的代码。它还介绍了一些你可能不熟悉的概念,但我相信它们将极大地帮助你思考“游戏开发”模式!
所以,如果我没有直接修复你的代码,我很抱歉 - 我不得不说,变量名称的“本地语言”没有帮助:) - 但我仍然希望它会有所帮助。
这里是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
数组。根据地图的值(0
或1
),我们从调色板中选择颜色,因此墙是黑色的,走廊是白色的。
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
功能根据按下的按键,墙壁和画布边缘更新英雄坐标。所以,首先,根据按下的键(如果有的话)设置x
和y
,但是在画布的边界内。然后,如果“新建议的坐标”在1
中返回map
,则表示角色可以移动到那里,因此hero
对象也会更新。否则它将不会更新。
function loop() {
update();
drawMap();
drawHero();
// flush
ctx.putImageData(imageData, 0, 0);
requestAnimationFrame(loop);
}
loop();
这只是游戏循环,我相信这很简单。我们当然需要在实际画布中“刷新”我们写入buff的所有内容。
这没有优化,并且有很多事情要做(例如你应该考虑时间来更新坐标,现在太快了),但我希望它能帮到你。
如果没有,请告诉我如何修改它以获得问题的简化版本,以便更好地帮助您。
这里还有一个运行的代码示例:https://codepen.io/zer0/pen/JxxYgQ