我有一个简单的方法,可以从SimpleX Noise生成2D岛图,但是这还存在一个更大的问题。它运作良好,但留下了尖角。
我想做的就是计算并计算邻居以添加正确的边和角瓷砖,但是我不确定如何添加它。什么是最好的计算方法?
generateMap()
{
let outputMap = [];
for(let y = 0; y < this.tileCount; y++)
{
outputMap[y] = [];
for(let x = 0; x < this.tileCount; x++)
{
let nx = x / this.tileCount - 0.5, ny = y / this.tileCount - 0.5;
let e = 1 + +Math.abs(this.heightMapGen.noise2D(1 * nx, 1 * ny));
e += 0.5 + +Math.abs(this.heightMapGen.noise2D(2 * nx, 2 * ny));
e += 0.25 + +Math.abs(this.heightMapGen.noise2D(4 * nx, 4 * ny));
let output = (Math.pow(e, 0.21) % 1).toFixed(2).split(".");
outputMap[y][x] = parseFloat(output[1]);
if (outputMap[y][x] <= 25)
{
outputMap[y][x] = 0; // Water //
} else {
// Terrain //
switch(outputMap[y][x])
{
case 28:
case 29:
outputMap[y][x] = 2;
break;
case 27:
outputMap[y][x] = 1;
break;
case 26:
outputMap[y][x] = 4;
break;
default:
outputMap[y][x] = 3;
}
}
}
}
return outputMap;
}
答案 0 :(得分:0)
确定那些黄色(沙色)边缘砖的位置,外角有4种情况,内角有4种情况。
+--> +-----------+ <---+ corner tile (one case)
| |_|
| land | sea here
| here |
| |
| |
+--> +-----------+ <----+
4 external corners (sea shore, beach)
+-----------+
| land here |
+-------> +---+ <-------+
| |wat| |
| |er | |
+-------> +---+ <-------+
| |
+-----------+
4 potential internal corners (lake shore or pond edge)
要确定在哪种情况下,您要查看单元格邻居(是水吗?是朝哪个方向...)。 您可以针对这些情况使用8个特定的图块(有关说明,请参见classic tilesets),也可以在alpha通道上使用透明效果。 正如您在图块示例中看到的那样,可能还有更多的情况:只有一个单元格的水,只有两个单元格的水...您可以从生成的地图中消除它们以避免它们。
祝你好运。
答案 1 :(得分:0)
(据我所知)关于如何完成此操作的最佳解释是Enhancing Procedural Maps - Tiles Generation。
我已将本文用作改善我正在制作的游戏的地图生成的起点(距离预期结果还很遥远,但可以作为起点):
map-generator.ts
); 这是当前的地图生成代码,尽管它现在存在一些故障:
'use strict';
import {range, sample} from 'lodash';
import SimplexNoise from 'simplex-noise';
enum TileEdge {
A, B, L, R,
BOTTOM, TOP,
ITL, ITR, IBL, IBR,
OTL, OTR, OBL, OBR
};
const tileEdgeNames = (() => {
const names = [];
for (let item in TileEdge) {
if (isNaN(Number(item))) {
names.push(item.toLowerCase());
}
}
return names;
})();
console.log(tileEdgeNames);
export const desertTileIndexes = {
a: [16, 46],
b: [50, 51, 60, 61,62, 63],
l: [15],
r: [17],
top: [6],
bottom: [26],
itl: [5],
itr: [7],
ibl: [25],
ibr: [27],
otl: [8],
otr: [9],
obl: [18],
obr: [19]
};
export const cloudTileIndexes = {
a: [13, 20, 21],
b: [-1],
l: [12],
r: [14],
top: [3],
bottom: [23],
itl: [2],
itr: [4],
ibl: [22],
ibr: [24],
otl: [0],
otr: [1],
obl: [10],
obr: [11]
};
export const rockTileIndexes = {
a: [76, 70, 71, 72, 73, 74],
b: [-1],
l: [75],
r: [77],
top: [66],
bottom: [86],
itl: [68],
itr: [69],
ibl: [78],
ibr: [79],
otl: [65],
otr: [67],
obl: [85],
obr: [87]
};
const generateMainPlanes = (position, {tileCount = 10, threshold = 0, noiseFunction} = {}): TileEdge[] => {
return range(tileCount).map((j) => noiseFunction(position, j) > threshold ? TileEdge.B : TileEdge.A;
};
const generateTileIndexes = ([bottom, current, top], {tileTypeIndexes} = {}) => {
return current.map((idx, col) => sample(tileTypeIndexes[tileEdgeNames[idx]]));
};
const createStripGenerator = ({tileCount = 10, threshold = 0} = {}) => {
const simplex = new SimplexNoise();
const noiseFunction = (x, y) => simplex.noise2D(x, y);
const options = {tileCount, threshold, noiseFunction};
let position = 0;
return () => {
return generateMainPlanes(position++, options);
}
}
const holePatchingAutomata = [
{
pattern: [
'???',
'*.*',
'???',
],
result: TileEdge.A
},
{
pattern: [
'?*?',
'?.?',
'?*?',
],
result: TileEdge.A
},
{
pattern: [
'??*',
'?.?',
'*??',
],
result: TileEdge.A
},
{
pattern: [
'*??',
'?.?',
'??*',
],
result: TileEdge.A
},
];
const cornerAutomata = [
{
pattern: [
'?.?',
'?.*',
'?.?',
],
result: TileEdge.L
},
{
pattern: [
'?.?',
'*.?',
'?.?',
],
result: TileEdge.R
},
{
pattern: [
'???',
'...',
'?*?',
],
result: TileEdge.TOP
},
{
pattern: [
'?*?',
'...',
'???',
],
result: TileEdge.BOTTOM
},
// Inner corner
{
pattern: [
'?*?',
'*.?',
'???',
],
result: TileEdge.ITL
},
{
pattern: [
'?*?',
'?.*',
'???',
],
result: TileEdge.ITR
},
{
pattern: [
'???',
'*.?',
'?*?',
],
result: TileEdge.IBL
},
{
pattern: [
'???',
'?.*',
'?*?',
],
result: TileEdge.IBR
},
// Outer corners
{
pattern: [
'???',
'?..',
'?.*',
],
result: TileEdge.OTL
},
{
pattern: [
'???',
'..?',
'*.?',
],
result: TileEdge.OTR
},
{
pattern: [
'?.*',
'?..',
'???',
],
result: TileEdge.OBL
},
{
pattern: [
'*.?',
'..?',
'???',
],
result: TileEdge.OBR
},
];
const patternTokenHandlers = {
'?': () => true,
'.': x => x === TileEdge.B,
'*': x => x === TileEdge.A
};
const patternMatches = (expected, actual, offset) => {
for (let i = 0, j = offset - 1; i < expected.length; i++, j++) {
if (!patternTokenHandlers[expected[i]](actual[j])) {
return false;
}
}
return true;
}
const applyAutomata = (automata, [bottom, current, top]) => {
const currentModified = current.map((idx, col) => {
return automata.reduce((val, {pattern, result}) => {
if (col > 0 && col < current.length - 1) {
if (val !== TileEdge.A && val !== TileEdge.B) {
return val;
}
const [patTop, patCurrent, patBottom] = pattern;
if (patternMatches(patBottom, bottom, col) &&
patternMatches(patCurrent, current, col) &&
patternMatches(patTop, top, col)) {
return result;
}
}
return val;
}, idx);
});
return [bottom, currentModified, top];
}
export const mapGenerator = ({tileCount = 10, threshold = 0, tileTypeIndexes} = {}) => {
const generateStrip = createStripGenerator({tileCount, threshold});
let bottom;
let current = generateStrip();
let top = generateStrip();
return () => {
const currentStrips = [bottom, current, top] = [current, top, generateStrip()];
const withHolesPatched = applyAutomata(holePatchingAutomata, currentStrips);
const withCorners = applyAutomata(cornerAutomata, withHolesPatched);
return generateTileIndexes(withCorners, {tileTypeIndexes});
}
};