首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >积木游戏.第2部分

积木游戏.第2部分
EN

Code Review用户
提问于 2014-12-16 23:32:42
回答 1查看 196关注 0票数 10

我使用了我的先前的积木游戏并添加了一些性能改进,并评论了一些需要注释的内容,以便进行更好的解释。

代码语言:javascript
运行
复制
/** Here are few instructions before you start...
  * IMPORTANT (ON KHANACADEMY ONLY): When you make changes to the code, all tiles will be black.
  * Just hit the restart button to fix that.
  *
  * CONTROLS:
  * MOUSE - Move around the current selected tile.
  * LEFT MOUSE BUTTON - Add a tile to the screen.
  * RIGHT MOUSE BUTTON - Delete a tile from the screen.
  * CONTROL KEY - Change the block type forward.
  * SHIFT KEY - Change the block type backward.
  * WASD or LEFT RIGHT DOWN UP - Move around the map.
*/

/* Global program constants */
var CURSOR_FONT = createFont("monospace", 15);
var WHITE = color(255, 255, 255);
var SUN_WIDTH = 100;
var SUN_HEIGHT = 100;
var TILE_SIZE = 10;
var COLORS = [
    color(180, 120, 20),
    color(20, 150, 20),
    color(100, 100, 100),
    color(240, 200, 10),
    color(5, 44, 117),
    color(255, 255, 255),
    color(110, 70, 10),
    color(10, 210, 20),
];
var TILE_TYPES = [
    "Dirt",
    "Grass",
    "Stone",
    "Sand",
    "Water",
    "Snow",
    "Wood",
    "Leaves",
];

/* Colors for the sun and moon */
var skyObjectColor1 = color(194, 181, 33);
var skyObjectColor2 = color(255, 140, 0);

/* Variables concerning map movement */
var movingUp = false;
var movingDown = false;
var movingLeft = false;
var movingRight = false;

/* Variables that control the darkness of the sky */
var starVisibilityChange = 0;
var starVisibility = 15;
var skyDarknessChange = 0.25;
var skyDarkness = 125;

/* Array containing cloud data */
var cloudArray = [];

/* Current selected color */
var selectedColor = 0;

/* Array contaning all tile data */
var tileArray = [];

/* Array containing all star data */
var starArray = [];

/* Variables controlling the sun and moon's position */
var skyObjectYChange = -0.05;
var SKY_OBJ_X_POS = 189;
var skyObjectYPos = 200;

/* Populate the starArray */
var generateStars = function() {
    for(var d = 0; d <= round(random(100, 150)); d++) {
        starArray.push({
            xPos: random(0, 400),
            yPos: random(0, 400),
        });
    }
};

/* Draw stars in the sky */
var drawStars = function() {
    for(var s = starArray.length-1; s >= 0; s--) {
        var star = starArray[s];
        noStroke();
        fill(255, 255, 255, starVisibility);
        rect(star.xPos, star.yPos, 2, 2);
    }
};

/* Render the day and night cycle*/
var renderDayNightCycle = function() {
    if(skyObjectYPos >= 250) {
        starVisibilityChange = 1;
        skyObjectColor1 = color(122, 117, 117);
        skyObjectColor2 = color(71, 68, 68);
        drawStars();
    }
    if(skyObjectYPos < 250) {
        starVisibilityChange = 0;
        skyObjectColor1 = color(194, 181, 33);
        skyObjectColor2 = color(255, 140, 0);
    }
    noStroke();
    fill(skyObjectColor2);
    rect(SKY_OBJ_X_POS, skyObjectYPos, 45, 45);
    fill(skyObjectColor1);
    rect(SKY_OBJ_X_POS+5, skyObjectYPos+5, 35, 35);
    skyObjectYPos += skyObjectYChange;
    skyDarkness += skyDarknessChange;
    starVisibility += starVisibilityChange;
    if(skyObjectYPos <= -50) {
        skyObjectYChange = 0.05;
        skyDarknessChange = -0.05;
    }
    if(skyObjectYPos >= 450) {
        skyObjectYChange = -0.05;
        skyDarknessChange = 0.05;
        starVisibilityChange = -2.5;
    }
};

/* Generate new terrain for the world */
var generateTerrain = function() {
    var c = random(1, 3);

    /* Variable concerning the current Y position of the tile generator */
    var blockY = 300;

    /* Overarching for loop, this loop determines world size */
    for(var x = -2500; x <= 2500; x += TILE_SIZE) {

        /* Create part of the dirt section, with grass on top*/
        var rb = round(random(-1, 0));
        tileArray.push({
            xPos: x,
            yPos: blockY+TILE_SIZE*rb,
            colr: COLORS[1],
        });
        if(rb === -1) {
            tileArray.push({
                xPos: x,
                yPos: blockY,
                colr: COLORS[0],
            });
        }

        /* If this statement is true, then place a tree down */
        if(random() >= random()*random()/random()+random()) {
            var treeHeight = round(random(2, 6));

            /* Create the wood section of the tree */
            for(var h = blockY-TILE_SIZE; h >= blockY-TILE_SIZE*treeHeight; h -= TILE_SIZE) {
                tileArray.push({
                    xPos: x,
                    yPos: h,
                    colr: COLORS[6],
                });
            }

            /* Create the leaf top of the tree */
            for(var i = 0; i <= 1; i++) {
                tileArray.push({
                    xPos: x,
                    yPos: blockY-TILE_SIZE*(treeHeight+i)-TILE_SIZE,
                    colr: COLORS[7],
                });
            }

            /* Add two extra leaf blocks on the sides for a more realistic tree */
            tileArray.push({
                xPos: x+TILE_SIZE,
                yPos: blockY-TILE_SIZE*treeHeight-TILE_SIZE,
                colr: COLORS[7]
            });
            tileArray.push({
                xPos: x-TILE_SIZE,
                yPos: blockY-TILE_SIZE*treeHeight-TILE_SIZE,
                colr: COLORS[7]
            });
        }

        /* Create the last part of the dirt section */
        for(var y = blockY; y <= blockY+TILE_SIZE*round(random(2, 4)); y += TILE_SIZE) {
            tileArray.push({
                xPos: x,
                yPos: y+TILE_SIZE,
                colr: COLORS[0],
            });
        }

        /* Create the stone section */
        for(var y = blockY+TILE_SIZE*round(c); y <= blockY+TILE_SIZE*random(18, 22); y += TILE_SIZE) {
            tileArray.push({
                xPos: x,
                yPos: y+TILE_SIZE,
                colr: COLORS[2],
            });
        }

        /* Change the blockY by a random, but rounded amount */
        blockY += (ceil(random(-1, 1)/TILE_SIZE)*TILE_SIZE)*round(random(-2, 2));
    }
};

/* Initalize the array of clouds */
var generateClouds = function() {
    for(var i = 0; i <= round(random(2, 12)); i++) {
        cloudArray.push({
            xPos: random(50, 350),
            yPos: random(50, 150),
            w: random(30, 60),
            h: random(10, 20),
        });
    }
};

/* Draw the background */
var drawBackground = function() {
    for(var c = cloudArray.length-1; c >= 0; c--) {
        var cloud = cloudArray[c];
        noStroke();
        fill(WHITE);
        rect(cloud.xPos, cloud.yPos, cloud.w, cloud.h);
        cloud.xPos += random(0.01, 0.09);
        if(cloud.xPos >= 400) {
            cloud.xPos = 0-cloud.w;
        }
    }
};

/* Draw a hitbox over the selected position */
var drawHitbox = function(x, y) {
    noCursor();
    fill(WHITE);
    textFont(CURSOR_FONT);
    text("+", mouseX-1, mouseY+4);
    noFill();
    strokeWeight(1);
    stroke(WHITE);
    rect(x, y, TILE_SIZE, TILE_SIZE);
};

/* Add a block to the screen */
var addBlock = function(x, y) {
    drawHitbox(x, y);
    if(mouseIsPressed && mouseButton === LEFT) {
        if(x >= 20 && y >= 20) {
            tileArray.push({
                xPos: x,
                yPos: y,
                colr: COLORS[selectedColor],
            });
        }
    }
};

/* Delete a block from the screen */
var deleteBlock = function(x, y) {
    drawHitbox(x, y);
    if(mouseIsPressed && mouseButton === RIGHT) {
        for(var t = tileArray.length-1; t >= 0; t--) {
            var tile = tileArray[t];
            if(x === tile.xPos && y === tile.yPos) {
                tileArray.splice(t, 1);
            }
        }
    }
};

/* Render the tileArray */
var renderTiles = function() {
    for(var t = tileArray.length-1; t >= 0; t--) {
        var tile = tileArray[t];

        /* Check if tile is off screen, if so, don't render */ 
        if(tile.xPos >= 0 && tile.xPos <= 400 && tile.yPos >= 0 && tile.yPos <= 400) {
            noStroke();
            fill(tile.colr);
            rect(tile.xPos, tile.yPos, TILE_SIZE, TILE_SIZE);
        }
    }
};

/* Check for specific key actions */
var checkForKeyActions = function() {
    keyPressed = function() {
        if(keyCode === LEFT || keyCode === 65) {
            movingLeft = true;
        }
        if(keyCode === RIGHT || keyCode === 68) {
            movingRight = true;
        }
        if(keyCode === UP || keyCode === 87) {
            movingUp = true;
        }
        if(keyCode === DOWN || keyCode === 83) {
            movingDown = true;
        }
        if(keyCode === CONTROL) {
            selectedColor++;
            if(selectedColor >= COLORS.length) {
                selectedColor = 0;
            }
        }
        if(keyCode === SHIFT) {
            selectedColor--;
            if(selectedColor < 0) {
                selectedColor = COLORS.length-1;
            }
        }
    };

    /* If key released, change keypress variable to false */
    keyReleased = function() {
        if(keyCode === LEFT || keyCode === 65) {
            movingLeft = false;
        }
        if(keyCode === RIGHT || keyCode === 68) {
            movingRight = false;
        }
        if(keyCode === UP || keyCode === 87) {
            movingUp = false;
        }
        if(keyCode === DOWN || keyCode === 83) {
            movingDown = false;
        }
    };

    /* Move up */
    if(movingUp) {
        for(var t = tileArray.length-1; t >= 0; t--) {
            var tile = tileArray[t];
            tile.yPos += TILE_SIZE;
        }
    }

    /* Move down */
    if(movingDown) {
        for(var t = tileArray.length-1; t >= 0; t--) {
            var tile = tileArray[t];
            tile.yPos -= TILE_SIZE;
        }
    }

    /* Move left */
    if(movingLeft) {
        for(var t = tileArray.length-1; t >= 0; t--) {
            var tile = tileArray[t];
            tile.xPos += TILE_SIZE;
        }
    }

    /* Move right */
    if(movingRight) {
        for(var t = tileArray.length-1; t >= 0; t--) {
            var tile = tileArray[t];
            tile.xPos -= TILE_SIZE;
        }
    }
};

/* Draw the current selected tile */
var drawSelectedTile = function() {
    strokeWeight(1.5);
    stroke(255, 255, 255);
    fill(COLORS[selectedColor]);
    rect(5, 5, 15, 15);
    fill(255, 255, 255);
    textFont(CURSOR_FONT);
    text(TILE_TYPES[selectedColor], 25, 17.4); 
};

/* Load the world, clouds and stars before the draw loop begins */
generateTerrain();
generateClouds();
generateStars();

/* Main draw loop */
draw = function() {
    background(0, 0, skyDarkness);
    renderDayNightCycle();
    drawBackground();
    renderTiles();
    drawSelectedTile();
    checkForKeyActions();

    /* Check if the user wants to add a block */
    addBlock(ceil(mouseX/TILE_SIZE)*TILE_SIZE-TILE_SIZE, 
             ceil(mouseY/TILE_SIZE)*TILE_SIZE-TILE_SIZE);

    /* Check if the user wants to delete a block */
    deleteBlock(ceil(mouseX/TILE_SIZE)*TILE_SIZE-TILE_SIZE, 
                ceil(mouseY/TILE_SIZE)*TILE_SIZE-TILE_SIZE);

    /* Draw a hitbox over the current selected block */
    drawHitbox(ceil(mouseX/TILE_SIZE)*TILE_SIZE-TILE_SIZE, 
               ceil(mouseY/TILE_SIZE)*TILE_SIZE-TILE_SIZE);
};

我主要要找的是如何改进让玩家在世界各地移动的代码。目前,它所做的是遍历包含世界数据的整个数组,并改变每个块的位置。这是不好的,特别是当我想让世界在某一时刻无限。因此,任何解决这一问题的建议或解决方案都是值得欢迎的。谢谢!

EN

回答 1

Code Review用户

回答已采纳

发布于 2015-01-28 20:09:16

您的代码风格很好,但首先,我想谈谈代码的两个主要逻辑问题:随机性和相机控制。

随机性

你已经提到过,你有一种愿望,在某一时刻使世界变得无限。好吧,如果你想这么做,你需要重新设计你现在处理随机数生成的方式。

为了使世界无限,你必须能够懒洋洋地产生世界的大块。当需要一个新块时,它需要按需生成。实际的块生成和管理本身就是一个完整的主题,但我希望您能够解决这个问题。我只想概述一下您需要做的更改,以使这种机制成为可能。

您的generateTerrain函数非常大。它为tileArray提供了游戏所需的所有数据。您将需要将其更改为类似于generateChunk的内容,它将用新的瓷砖填充chunkArray。然而,目前,这并不容易,因为你是如何生成随机数。

代码语言:javascript
运行
复制
/* Change the blockY by a random, but rounded amount */
blockY += (ceil(random(-1, 1)/TILE_SIZE)*TILE_SIZE)*round(random(-2, 2));

在这里,您正在使用random,并且以增量的方式调整迭代的位置。这是有问题的,原因有几个。首先,random是完全不确定的。没有办法为这种世界留一个“种子”。您可以实现某种可以在块边界上保存状态的系统,但这不仅会造成问题,而且会不必要地困难。

幸运的是,处理提供了一个noise函数。与noiseSeednoiseDetail一起,您可以为任意给定的x/y坐标创建确定性的连续随机数!这显然是有益的,有几个原因。

  1. 您的生成函数将本质上成为无状态的。目前,您正在增量地调整诸如blockY这样的功能,但这有点麻烦。使用noise,每个单独的块可以通过种子和x/y对的组合来唯一定义--不需要状态。
  2. 区块边界将是连续的。这是上述问题的一个副作用.由于Perlin噪声(噪声处理生成的类型)是连续的,而且由于您没有保持任何状态,所以您不必担心处理边界。
  3. 调整下一代将变得更简单。调整噪声函数本身将有助于调整输出,而不是担心单个随机数的产生。

显然,使用noise是正确的方法。但是,生成像树和其他特征这样的东西呢?这也可以通过noise来完成,但是在一个块一个块的基础上使用一个不同的调整噪声函数。您还可以将randomrandomSeed结合使用,种子本身可能是噪声的一个函数!这允许块生成具有确定性,同时能够利用对生成器的特定控制的方便。(请记住,这将产生不连续的数据,因此只能用于生成本地化的地图功能。)

所以这是随机数的产生。接下来是更简单的改变。

周围移动

这就是你目前处理移动的方法。

代码语言:javascript
运行
复制
/* Move up */
if(movingUp) {
    for(var t = tileArray.length-1; t >= 0; t--) {
        var tile = tileArray[t];
        tile.yPos += TILE_SIZE;
    }
}

这是个非常可怕的主意。瓷砖的位置永远不应该改变。事实上,它们甚至不需要与瓷砖一起存储-它们应该是它们在平铺数组中的位置的函数(或者最终是块数组)。

不,数据本身不应该移动。相反,你应该有一个“相机”在世界各地移动,改变在视口看到的东西。最简单的方法是使用处理的translate函数。这将导致所有绘图操作在屏幕上被抵消。只需调用translate,执行绘图,然后再次调用translate以撤消转换。

这将改变renderTilescheckForKeyActions的工作方式。与其四处移动瓦片,不如保留cameraXcameraY变量(或camera.x/camera.y)。在checkForKeyActions中修改它们,然后将它们传递给renderTiles中的translate。这意味着不再循环通过所有的瓷砖只是为了移动他们,它也意味着你可以连续移动,而不仅仅是在一个网格上!

即使实现了这一点,如果你使世界变得无限,你显然不能把它全部画出来,因为这将花费无限的时间。相反,您需要计算与视口相交的块,并绘制这些块。如果应该绘制一个块,但它还不存在,那么就应该立即生成它了。

杂乱无章

除了上面提到的那些要点(当然是主要的)之外,您的代码也相当可靠。以下是其他一些随机的建议。

代码语言:javascript
运行
复制
var COLORS = [
    color(180, 120, 20),
    color(20, 150, 20),
    color(100, 100, 100),
    color(240, 200, 10),
    color(5, 44, 117),
    color(255, 255, 255),
    color(110, 70, 10),
    color(10, 210, 20),
];
var TILE_TYPES = [
    "Dirt",
    "Grass",
    "Stone",
    "Sand",
    "Water",
    "Snow",
    "Wood",
    "Leaves",
];

为什么不把这些封装在一起呢?如果TILE_TYPES是一个包含这两部分信息的结构,就更有意义了。例如,您可以拥有tile.nametile.color。这将允许您以相对轻松的方式向tiles类型添加更多信息。

代码语言:javascript
运行
复制
var generateStars = function() {
    for(var d = 0; d <= round(random(100, 150)); d++) {
        starArray.push({
            xPos: random(0, 400),
            yPos: random(0, 400),
        });
    }
};

虽然这可能不是什么大不了的事情,但是您可以重写这个函数来使用noise来使它更加清晰。

票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/73876

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档