Генератор карт местности с JavaScript и HTML5 и CSS


Я пытаюсь генерировать карты (как в карты), используя только JavaScript, но в настоящее время производительность очень плохо, в лучшем случае.

Что в данный момент мой скрипт:

  1. Создайте квадратное основание сетки на карте и распечатать его
  2. Для каждой ячейки, проверьте тип местности смежную ячейку и выберите местность для текущей ячейки
  3. Печатать результаты

Как я могу улучшить производительность? (Я в курсе, что нынешний результат выглядит ничего, как реальная карта, но одна вещь за один раз).

Я принимал мой текущий скрипт здесь.

(base и terrain свойства CSS-классов)

$(document).ready(function () {
    var x = 50;
    var y = 50;
    var worldMap = [];

    worldMap = genesis(x, y, worldMap);
    worldMap = terraform(x, y, worldMap);
});

function genesis(x, y, worldMap) {
    for (i = 0; i < x; i++) {
        worldMap[i] = [];
        for (var j = 0; j < y; j++) {
            worldMap[i][j] = {
                xCoord: i,
                yCoord: j,
                base: "cell",
                terrain: "baren"
            };
            //console.log(worldMap[i][j]);
        }
    }

    var world = $(".world");

    for (i = 0; i < x; i++) {
        var row = document.createElement("div");
        row.className = "row";

        for (j = 0; j < y; j++) {
            var cell = document.createElement("div");
            cell.className = worldMap[i][j].base + " " + worldMap[i][j].terrain;
            cell.setAttribute('data-row', i);
            cell.setAttribute('data-col', j);
            row.appendChild(cell);
        }

        world.append(row);
    }

    return worldMap;
}


function selectTerrain(worldMap, cell) {
    var terrain = ["river", "river", "forest", "forest", "plain", "plain", "plain", "desert", "mountain"];

    var i = cell.xCoord;
    var j = cell.yCoord;

    console.log(i + " " + j);

    for (var a = -1; a < 2; a++) {
        for (var b = -1; b < 2; b++) {
            var m = parseInt(i) + parseInt(a);
            var n = parseInt(j) + parseInt(b);

            if (typeof worldMap[m] != "undefined") {
                if (typeof worldMap[m][n] != "undefined") {
                    var adj = worldMap[m][n];
                    console.log(adj);
                    console.log("Is defined");
                    if (adj.terrain != "baren") {
                        console.log("Is not baren");
                        for (z = 0; z < 4; z++) {
                            terrain.push(adj.terrain);
                        }
                        console.log(terrain);
                    }
                }
            }

        }
    }

    var selectedIndex = Math.floor(Math.random() * (terrain.length - 0)) + 0;
    var selectedTerrain = terrain[selectedIndex];

    return selectedTerrain;
}

function terraform(x, y, worldMap) {
    for (var i = (x-1); i-- ;) {
        for (var j = (y-1); j--; ) {
            var mappingCell = worldMap[i][j];
            console.log(mappingCell);
            var selectedTerrain = selectTerrain(worldMap, mappingCell);

            worldMap[i][j].terrain = selectedTerrain;

            console.log(mappingCell.terrain);

            var cell = $("[data-row=" + i + "][data-col=" + j + "]");
            cell[0].className = mappingCell.base + " " + mappingCell.terrain;
        }
    }

    return worldMap;
}


Комментарии
2 ответа

Эта карта выглядит супер круто! Результативность не так уж плохо, учитывая, что ты генерирующая 2500 элементы div :)

Что вы могли бы сделать, это создать один холст, а не так много дивов. ГПУ может сделать холст супер-быстро. Это также будет полезно, если вы начинаете добавлять больше элементов в вашей игре.

Нет никаких причин, чтобы использовать jQuery, так что вы можете избавиться от этого тоже :)

1
ответ дан 30 марта 2018 в 10:03 Источник Поделиться

Холст && плитками

Первая капля с jQuery вам не нужно и все портят.

Холст

Показывать на карте гораздо лучше сделано через холст. Каждое полотно пиксель не нужен интерфейс и может быть миллион (без преувеличения) раз быстрее, чем DOM-элемент и использовать малую часть памяти.

Хотя вы потеряете функциональность DOM-элемент, очень простой в реализации базовых потребностей, используя холст.

Холст-это очень мощный рендеринг поверхности. В 2D-контекста является полностью аппаратным ускорением и может сделать полный экран 2D-игр в 60fps на средних машинах и на хороших машинах это просто потрясающе. Он также может сделать 3D Через в WebGL или как гибрид контексте 2D и WebGL работать вместе.

Обходя дом с помощью холста делает многие приложения летают и это лучший выбор для игрового / графического приложения.

Плиточный карте

Этот тип линейной карты лучше как простой массив (листов карты). Это экономит пространство, доступ к гораздо быстрее (включая соседей), и такие вещи, как координаты могут быть вычислены из индекса.

Каждый элемент на карте плиточный содержит указатель на тип местности. Мира карта описание объект содержит массив описаний местности типа. Для того чтобы получить детали для плитки вы получите пункт карту и использовать его как индекс в массив описание местности.

например, если в мире 50 на 50 листов и world.map это плиточный карте массив. Чтобы получить описание местности для плитки

   function getTerrainDesc(x,y){
const index = x + y * 50; // get the array index
const terrainType = world.map[index]
return world.terrainDesc[terrainType];
}
// see example for better examples of using a tile map.

Пример

Я сделал очень быстро переписать вашего генератора карт, с помощью некоторых моделей вы не можете быть комфортно. Возьмите его только в качестве примера и использовать то, что вы хотите, это не единственный способ делать такого рода вещи.

Я предоставлять различные ссылки в коде МДН в связи с JS функции языка вы можете и не знать. МДН является хорошим источником для всех вещей-браузера и стоит иметь закладки.

Пример генерирует карту, используя ту же логику, которую вы использовали. Дополнительные вызовы createCanvas и drawWorld нужны.

Вы можете определить множество миров, каждый с различным набором местностей и множество окон. Пример использует только один по имени world нажмите на карту, чтобы восстановить новую (он использует старые карты, так что не совсем новая карта)



// replacement for jQuery ready event
addEventListener("load",() => {
world.container = document.querySelector(".world"); // get container
createCanvas(world);
genesis(world);
terraform(world);
drawWorld(world);
world.canvas.addEventListener("click", () => {
// comment the following to clear the world before regeneration.
// world.map.fill(0);
terraform(world);
drawWorld(world);

})

});

// some functionality for a cell
// const declaration see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const
const cell = {
draw(x, y) { // x,y is map coord
this.world.ctx.fillStyle = this.color;
this.world.ctx.fillRect(x * this.width, y * this.height, this.width, this.height);
}
}

// the ... is a spread operator link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
// set defaults for terrain types.
const terrains = {
empty : {
id : 0, // type arrays are set to zero when created. This is the default cell type.
odds : 0,
addedOdds : 0,
color : "#fff",
...cell,
},
river : {
id : 1, //
odds : 1, // default odds for the terrain
addedOdds : 2, // additional odds if cell has river neighbour
color : "#29b6f6",
...cell, // add the default functions for this cell.
},
forest : {
id : 2,
odds : 2,
addedOdds : 4,
color : "#1b5e20",
...cell,
},
plain : {
id : 3,
odds : 3,
addedOdds : 4,
color : "#66bb6a",
...cell,
},
desert : {
id : 4,
odds : 1,
addedOdds : 4,
color : "#ffee58",
...cell,
},
mountain : {
id : 5,
odds : 1,
addedOdds : 4,
color : "#b0bec5",
...cell,
},
}

const world = {
cells : {
width : 100,
height : 100,
},
cell : {
width : 5,
height : 5,
},
terrains : "river,forest,plain,desert,mountain".split(","), // terrains to use with this world
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.assign(world, { // using assign so that we can access the variable world
terrainDesc : [
{...terrains.empty }, // first one must always be empty
// Now add the terrain types you want for this map
...world.terrains.map((name, i) => {
return {
...terrains[name], // make a copy of the terrain description
id : i + 1, // add custom details for this world
...world.cell, // add cell width and height for this world
world, // add a reference to the world so terrains can get details if needed
};
}),
],
});

function genesis(world) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
world.map = new Uint8Array(world.cells.width * world.cells.height);
}
function createCanvas(world){
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
var canvas = document.createElement("canvas");
canvas.width = world.cells.width * world.cell.width;
canvas.height = world.cells.height * world.cell.height;
canvas.ctx = canvas.getContext("2d");
world.canvas = canvas;
world.ctx = canvas.ctx;
world.container.appendChild(canvas);
}
const selectTerrain = (() => { // As a singleton.
// This encasulates the functionality
// so you dont polute the global scope.
const tSelect = [];
var tSelectBaseCount = 0;
var neighbours, world, map, w, h;
return { // the selectTerrain object
// next is a setter see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
set world(worldDesc) {
world = worldDesc;
map = world.map;
w = world.cells.width;
h = world.cells.height;
neighbours = [ // pre calculate neighbour coord and map array offet
{x : -1, y : -1, offset : -w-1},
{x : 0, y : -1, offset : -w},
{x : 1, y : -1, offset : -w+1},
{x : -1, y : 0, offset : -1},
{x : 1, y : 0, offset : 1},
{x : -1, y : 1, offset : w-1},
{x : 0, y : 1, offset : w},
{x : 1, y : 1, offset : w+1},
];
tSelect.length = 0;
tSelectBaseCount = 0;
for (let i = 0; i < world.terrainDesc.length; i ++) {
var odds = world.terrainDesc[i].odds;
while(odds-- ){ tSelect.push(i) }
}
tSelectBaseCount = tSelect.length;
},
select(index) {
const x = index % w;
const y = index / w | 0; // the | 0 (bitwize OR 0 is the same as
// Math.floor( ONLY use for positive numbers))
var count = tSelectBaseCount;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
for (const n of neighbours) { // n as n and prefix for neighbour
if (x + n.x >= 0 && y + n.y >= 0 && x + n.x < w && y + n.y < h) {
var nType = map[index + n.offset];
var addOdds = world.terrainDesc[nType].addedOdds
while (addOdds--) { tSelect[count ++] = nType }
}
}
map[index] = tSelect[Math.random() * count | 0]; // the | 0 (bitwize OR 0 is the
// same as Math.floor(
// ONLY use for positive numbers))
}
}
})();

function terraform(world) {
var i = 0;
selectTerrain.world = world; // set up terrain selector
while (i < world.map.length) { selectTerrain.select(i++) }
}

function drawWorld(world) {
var ctx = world.ctx; // get the 2D render context
ctx.clearRect(0, 0, world.canvas.width, world.canvas.height);
var index = 0;
var width = world.cells.width;
var cellW = world.cell.width;
var cellH = world.cell.width;
while (index < world.map.length) {
const terrainType = world.map[index];
world.terrainDesc[terrainType]
.draw(index % width | 0, (index++) / width | 0);
}
}


.world {
position: absolute;

border: solid #a1a1a1 3px;
}


<div class="world"></div>



0
ответ дан 30 марта 2018 в 10:03 Источник Поделиться