我是JavaScript的新手,因此我目前正在研究MDN的官方2D突破游戏教程并自行更改内容以更好地了解工作原理。作为我在教程第6步(称为“构建砖块”)中的更改的一部分,我编写了以下代码,它完全符合我的要求,但我觉得我可以在某种程度上缩短代码,并且我不知道怎么做。
function drawBricks(){
for(c = 0; c < bricksColCount-3; c++) {
for(r = 0; r < bricksRowCount-1; r++) {
setRowsCol();
}
}
for(c = 2; c < bricksColCount-2; c++) {
for(r = 0; r < bricksRowCount-4; r++) {
setRowsCol();
}
}
for(c = 3; c < bricksColCount; c++) {
for(r = 0; r < bricksRowCount; r++) {
setRowsCol();
}
}
}
我在这段代码中引用的setRowsCol()
函数是这样写的:
function setRowsCol() {
bricks[c][r].x = bricksOffsetLeft + (c*(bricksWidth + bricksPadding));
bricks[c][r].y = bricksOffsetTop + (r*(bricksHeight + bricksPadding));
ctx.beginPath();
ctx.rect(bricks[c][r].x, bricks[c][r].y, bricksWidth, bricksHeight);
ctx.fillStyle = "#fff";
ctx.fill();
ctx.closePath();
}
是否有任何想法减少线条并缩短代码而不改变其工作方式?由于我是JavaScript的新手,我想确保我应用适当的技巧。谢谢你们。
答案 0 :(得分:5)
代码行数与代码运行速度没有直接关系。某些操作比其他操作更昂贵。
在您的示例中,canvas操作是目前最昂贵的操作。您的目标应该是通过减少画布操作的数量来提高速度。
我看到您在循环中调用setRowsCol()
,在setRowsCol()
内执行beginPath()
和closePath()
。因为只有在开始绘图后才需要执行beginPath()
,而在完成绘图后才需要执行closePath()
,所以最好采取循环函数中的那两行。此外,fillStyle
只需设置一次,fill()
只需执行一次。它们也可以从setRowsCol()
函数中删除。你可以这样写:
function setRowsCol() {
bricks[c][r].x = bricksOffsetLeft + (c*(bricksWidth + bricksPadding));
bricks[c][r].y = bricksOffsetTop + (r*(bricksHeight + bricksPadding));
ctx.rect(bricks[c][r].x, bricks[c][r].y, bricksWidth, bricksHeight);
}
function drawBricks(){
for(c = 0; c < bricksColCount-3; c++) {
for(r = 0; r < bricksRowCount-1; r++) {
setRowsCol();
}
}
for(c = 2; c < bricksColCount-2; c++) {
for(r = 0; r < bricksRowCount-4; r++) {
setRowsCol();
}
}
for(c = 3; c < bricksColCount; c++) {
for(r = 0; r < bricksRowCount; r++) {
setRowsCol();
}
}
}
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.fillStyle = "#fff";
drawBricks();
ctx.fill();
ctx.closePath();
代码可能更好(例如,不依赖于全局范围变量),但想法是你已经在循环代码之外移动了一些昂贵的画布操作。
答案 1 :(得分:1)
function drawBricks(){
function loop(startCol,endColOffset,endRowOffset){
for(c = startCol; c < bricksColCount+endColOffset; c++) {
for(r = 0; r < bricksRowCount+endRowOffset; r++) {
setRowsCol();
}
}
}
loop(0,-3,-1);
loop(2,-2,-4);
loop(3,0,0);
}
答案 2 :(得分:0)
您询问如何缩短代码并提高效率,但我认为您应该解决更重要的问题。
列上的for循环仅在bricksColumnCount
为5时有效。要了解原因,请尝试在bricksColumnCount
等于10的情况下运行代码。您会注意到一些列被多次绘制。
作为第一步,我会忘记bricksColumnCount
并明确使用五列:
function drawBricks() {
// columns 0 and 1 each have (bricksRowCount-1) bricks
for (c = 0; c < 2; c++) {
for (r = 0; r < bricksRowCount-1; r++) {
setRowsCol();
}
}
// column 2 has (bricksRowCount-4) bricks
for (c = 2; c < 3; c++) {
for (r = 0; r < bricksRowCount-4; r++) {
setRowsCol();
}
}
// columns 3 and 4 each have bricksRowCount bricks
for (c = 3; c < 5; c++) {
for (r = 0; r < bricksRowCount; r++) {
setRowsCol();
}
}
}
要概括代码以使其适用于任意列计数,您需要决定如何对列进行分区并将其写为涉及bricksColumnCount
的公式。问题中的原始代码是朝这个方向迈出的一步,但并不是很正确。
函数setRowsCol
的行为由调用代码需要设置的全局变量c
和r
控制。这种编程风格非常脆弱。有以下一些原因,请参阅http://wiki.c2.com/?GlobalVariablesAreBad。
另一种方法是使用函数参数。有关简介,请参阅MDN Guide to Functions。
您已经了解了在函数调用期间如何将值传递给函数:
ctx.rect(bricks[c][r].x, bricks[c][r].y, bricksWidth, bricksHeight);
函数ctx.rect
需要知道要绘制的矩形的位置和尺寸,并通过在函数调用的括号内写入它们来传入它们。
要声明setRowsCol
有两个参数,请将声明更改为以下内容:
function setRowsCol(c, r) {
// the body remains the same
}
setRowsCol
中对drawBricks
的来电需要更新,因此它们看起来像这样:
setRowsCol(c, r);
调用中值的顺序必须与函数声明中参数的顺序相匹配,但名称不需要匹配。实际上,任何表达式都可以在函数调用中使用:
setRowsCol(1, 4);
这会调用setRowsCol
,在setRowsCol
正文中,c
的值为1,r
的值为4.
通过这些更改,变量c
和r
不再需要全局变量,因此您应该将它们声明为drawBricks
中的局部变量:
function drawBricks() {
var c;
var r;
// columns 0 and 1 each have (bricksRowCount-1) bricks
for (c = 0; c < 2; c++) {
for(r = 0; r < bricksRowCount-1; r++) {
setRowsCol(c, r);
}
}
// etc.
}
如果您编写的程序运行时间超过三秒,并且您希望让它运行得更快,则需要提高效率。但就目前而言,优先级列表上的效率应该非常低。