我正在尝试学习 JavaScript,制作我的第一个游戏。如何将所有图像
onload
制作在一张 function
中,然后将其绘制在画布上,从而使我的代码更短?
如何将大量图像放入数组中,然后在函数中使用它。
这是我学习 JavaScript 的第三天。
提前致谢。
var cvs = document.getElementById('canvas');
var ctx = cvs.getContext('2d');
//load images
var bird = new Image();
var bg = new Image();
var fg = new Image();
var pipeNorth = new Image();
var pipeSouth = new Image();
//images directions
bg.src = "assets/bg.png";
bird.src = "assets/bird.png";
fg.src = "assets/fg.png";
pipeNorth.src = "assets/pipeNorth.png";
pipeSouth.src = "assets/pipeSouth.png";
var heightnum = 80;
var myHeight = pipeSouth.height+heightnum;
var bX = 10;
var bY = 150;
var gravity = 0.5;
// Key Control :D
document.addEventListener("keydown",moveUP)
function moveUP(){
bY -= 20;
}
//pipe coordinates
var pipe = [];
pipe[0] = {
x : cvs.width,
y : 0
}
//draw images
//Background img
bg.onload = function back(){
ctx.drawImage(bg,0,0);
}
//pipe north
pipeNorth.onload = function tubo(){
for(var i = 0; i < pipe.length; i++){
ctx.drawImage(pipeNorth,pipe[i].x,pipe[i].y);
pipe[i].x--;
}
}
pipeSouth.onload = function tuba(){
ctx.drawImage(pipeSouth,pipe[i].x,pipe[i].y+myHeight);
}
bird.onload = function pajaro(){
ctx.drawImage(bird,bX,bY);
bY += gravity;
requestAnimationFrame(pajaro);
}
fg.onload = function flor(){
ctx.drawImage(fg,0,cvs.height - fg.height);
}
moveUP();
back();
tuba();
pajaro();
flor();
Promise.all
来完成。我们将为要加载的每个图像做出新的承诺,并在调用 onload
时解决。一旦 Promise.all
解析,我们就可以调用 initialize
函数并继续其余的逻辑。这避免了从 requestAnimationFrame
调用主游戏循环的 bird.onload
的竞争条件,但管道实体等可能尚未加载。
这是一个最小的完整示例:
const initialize = images => {
// images are loaded here and we can go about our business
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 200;
const ctx = canvas.getContext("2d");
Object.values(images).forEach((e, i) =>
ctx.drawImage(e, i * 100, 0)
);
};
const imageUrls = [
"https://picsum.photos/90/100",
"https://picsum.photos/90/130",
"https://picsum.photos/90/160",
"https://picsum.photos/90/190",
];
Promise.all(imageUrls.map(e =>
new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = e;
})
)).then(initialize);
请注意,我在上面的示例中使用了数组来存储图像。这解决的问题是
var foo = ...
var bar = ...
var baz = ...
var qux = ...
foo.src = ...
bar.src = ...
baz.src = ...
qux.src = ...
foo.onload = ...
bar.onload = ...
baz.onload = ...
qux.onload = ...
模式极难管理和扩展。如果您决定在游戏中添加其他内容,则需要重新编写代码来解释它,并且游戏逻辑变得非常湿。错误变得难以发现和消除。另外,如果我们想要一个特定的图像,我们更愿意像
images.bird
而不是 images[1]
那样访问它,保留各个变量的语义,但让我们能够循环遍历对象并调用每个实体的 render
函数,例如。
所有这些都会激励对象聚合游戏实体。我们希望每个实体获得的一些信息可能包括,例如,实体的当前位置、死/活状态、移动和渲染它的功能等。
拥有某种包含所有初始游戏状态的单独原始数据对象(通常是外部 JSON 文件)也是一个好主意。
显然,这可能会变成一个重要的重构,但当游戏变得越来越小时,这是必要的一步(我们可以逐步采用这些设计思想)。一般来说,预先咬紧牙关是个好主意。
这是一个概念验证,说明了上面的一些想法。希望这能为您如何管理游戏状态和逻辑提供一些想法。
const entityData = [
{
name: "foo",
path: "https://picsum.photos/80/80",
x: 0,
y: 0
},
{
name: "baz",
path: "https://picsum.photos/80/150",
x: 0,
y: 90
},
{
name: "quux",
path: "https://picsum.photos/100/130",
x: 90,
y: 110
},
{
name: "corge",
path: "https://picsum.photos/200/240",
x: 200,
y: 0
},
{
name: "bar",
path: "https://picsum.photos/100/100",
x: 90,
y: 0
}
/* you can add more properties and functions
(movement, etc) to each entity
... try adding more entities ...
*/
];
const entities = entityData.reduce((a, e) => {
a[e.name] = {...e, image: new Image(), path: e.path};
return a;
}, {});
const initialize = () => {
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = innerWidth;
canvas.height = innerHeight;
const ctx = canvas.getContext("2d");
for (const key of Object.keys(entities)) {
entities[key].alpha = Math.random();
}
(function render () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Object.values(entities).forEach(e => {
ctx.globalAlpha = Math.abs(Math.sin(e.alpha += 0.005));
ctx.drawImage(e.image, e.x, e.y);
ctx.globalAlpha = 1;
});
requestAnimationFrame(render);
})();
};
Promise.all(Object.values(entities).map(e =>
new Promise((resolve, reject) => {
e.image.onload = () => resolve(e.image);
e.image.onerror = () =>
reject(`${e.path} failed to load`)
;
e.image.src = e.path;
})
))
.then(initialize)
.catch(err => console.error(err))
;