You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JS游戏大尺寸地图程序化生成卡顿 求类似MC的种子分块生成方案

可行优化方案与实现思路

1. 随机数生成器改造(区域衔接的核心基础)

你当前使用的顺序型随机数生成器无法支持局部生成,因为随机值输出完全依赖调用顺序,生成不同区域的顺序变化会导致结果不一致,更无法保证边缘衔接。你需要换成基于坐标的确定性哈希随机数函数,输入固定种子、x坐标、y坐标就能输出唯一且固定的0-1随机值,和调用顺序完全无关,只要种子不变,同个坐标的随机值永远一致。

2. 全局元胞自动机改为局部带padding的分块计算

原有的全图迭代逻辑是性能瓶颈的核心,你可以把地图拆成固定大小的区块(比如64x64、128x128均可),每次生成单个区块时,根据元胞自动机迭代次数,额外扩展对应宽度的边界padding:比如单次迭代元胞自动机的影响范围是1格,要迭代5次就上下左右各多取5格的padding区域,把区块+padding一起参与迭代计算,计算完成后只保留中间正式区块的内容即可,这样相邻区块的边缘计算结果会完全一致,不会出现裂缝。

3. 实现区块按需加载与缓存

参考类Minecraft的区块加载逻辑:

  • 仅加载光标/玩家视野范围内的区块,比如视距设为3块,只需加载周围7x7的区块即可,无需生成全图
  • 生成过的区块存入内存缓存,移出视距超过一定时间的区块可以销毁,下次需要时因为种子固定,重新生成的结果和原来完全一致,不会出现内容变化
  • 可以搭配Web Worker做后台异步生成,不会卡住主界面的交互操作

4. 现有代码性能优化点

  • 用Uint8Array等类型化数组替代普通数组存储地图数据,读写速度比普通JS数组快3-5倍
  • 渲染逻辑放弃逐点fillRect,改用ImageData批量写入像素数据后一次性提交到canvas,渲染速度能提升几十倍
  • 你的多级缩放生成逻辑可以同步改造成局部化:低分辨率层的数值直接通过坐标哈希生成,无需先生成全量低分辨率地图

原始代码格式化后如下:

const scale = 0.25;

let rngCount = 0;
function* RNG() {
    let seed = Math.floor(Math.random() * 10000) / 10000;
    console.log(seed);
    const factor = 297;
    while(true) {
        seed = (seed * factor) - Math.floor(seed * factor);
        yield Math.floor(seed * 10000) / 10000;
    };
};

let generationRNG = RNG();

function clamp(min, max, value) {
    if(value < min) {
        return min;
    } else if(value > max) {
        return max;
    } else {
        return value;
    };
};

let map = [];
let mapW = 4096;
let mapH = 4096;

function iterate(e, map) {
    const height = map.length;
    const width = map[0].length;
    let updatedMap, mapRow, neighborCount, iy, ix;

    for(i = 0; i < e; i++) {
        updatedMap = [];
        for(y = 0; y < height; y++) {
            mapRow = [];
            for(x = 0; x < width; x++) {
                neighborCount = 0;
                for(k = -1; k <= 1; k++) {
                    for(l = -1 ; l <= 1; l++) {
                        if(y + k < 0) {
                            iy = height - 1;
                        } else if(y + k >= height) {
                            iy = 0;
                        } else {
                            iy = y + k;
                        };

                        if(x + l < 0) {
                            ix = width - 1;
                        } else if(x + l >= width) {
                            ix = 0;
                        } else {
                            ix = x + l;
                        };

                        if(!(l == 0 && k == 0)) {
                            neighborCount += map[iy][ix];
                        };
                    };
                };
                if(neighborCount >= 5) {
                    mapRow.push(1);
                } else if(neighborCount <= 3) {
                    mapRow.push(0);
                } else {
                    mapRow.push(map[y][x]);
                };
            };
            updatedMap.push(mapRow);
        };
        map = updatedMap;
    };

    return map;
};

function terrainGeneration() {
    let width = mapW / 16;
    let height = mapH / 16;
    let mapRow, factor = 4, fillPercent = 0.45, updatedMap, map = [];

    for(y = 0; y < height; y++) {
        mapRow = [];
        for(x = 0; x < width; x++) {
            if(generationRNG.next().value <= fillPercent) {
                mapRow.push(1);
            } else {
                mapRow.push(0);
            };
        };
        map.push(mapRow);
    };
    
    map = iterate(10, map);

    for(a = 0; a < 2; a++) {
        if(a == 0) {
            fillPercent = 0.33;
        } else if (a == 1) {
            fillPercent = 0.2;
        };

        width *= factor;
        height *= factor;
        updatedMap = [];
        for(y = 0; y < height; y++) {
            mapRow = [];
            for(x = 0; x < width; x++) {
                mapRow.push(map[Math.floor(y / factor)][Math.floor(x / factor)]);
            };
            updatedMap.push(mapRow);
        };
        
        map = updatedMap;

        updatedMap = [];
        for(y = 0; y < height; y++) {
            mapRow = [];
            for(x = 0; x < width; x++) {
                if(map[y][x] == 1) {
                    if(generationRNG.next().value <= fillPercent) {
                        mapRow.push(0);
                    } else {
                        mapRow.push(1);
                    };
                } else {
                    if(generationRNG.next().value <= fillPercent) {
                        mapRow.push(1);
                    } else {
                        mapRow.push(0);
                    };
                };
            };
            updatedMap.push(mapRow);
        };
        map = updatedMap;

        map = iterate(5, map);
    };

    return map;
};

map = terrainGeneration();

for(i = 0; i < map.length; i++) {
    for(j = 0; j < map[i].length; j++) {
        c.fillStyle = "rgba(" + map[i][j] * 255 + ", " + map[i][j] * 255 + ", "  + map[i][j] * 255 + ", 1)";
        c.fillRect(j * scale, i * scale, scale, scale);
    };
};
c.fillStyle = "rgba(0, 0, 255, 1)";
c.fillRect(0, 0, 40 * scale, 40 * scale);

生成效果示例

4096x4096规格地图生成效果


内容的提问来源于stack exchange,提问作者Red Wizard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 19:36:03