【发布时间】:2018-04-16 02:01:34
【问题描述】:
所以我有一个函数可以模拟代表位置的 2D 数组上火的生长和蔓延。 它是这样工作的:
- 在特定位置起火,例如 (x,y)=(1,1)
- 未访问的像素的值为 0。
- 1 - 9 是对象(如墙壁等)的保留编号。
- 着火像素的强度值介于 10 到 40 之间。
- 10 到 20 由浅红色变为深红色以显示增加 强度。
- 20 到 30 仍为暗红色,显示出停滞的强度。 30 到 40 次 从红色到黑暗以显示垂死的火焰。
- 函数
findingNeighbors找到一个相邻像素 像素内强度并增加其强度。
到目前为止,该函数可以正常工作,但它是非常线性的,并且不像我想象的那样工作。我认为它会以更放射状的方式传播。我不确定我做错了什么。我希望它看起来像真正的火蔓延。我想要一个更放射状的随机增长模式。
所以我的问题是我可以对函数做些什么来随机化一点以显示火从一点蔓延而不会影响性能?寻找邻居的功能是否应该随机化一点?
下面包含的可运行代码
function simulateFire() {
for (let y = 0; y < mainGrid.length; y++) {
for (let x = 0; x < mainGrid[y].length; x++) {
let intensity = mainGrid[y][x];
if (intensity >= 10 && intensity < 40) {
findingNeighbors(y, x);
$(`#cell_${y}_${x}`).empty().addClass(`i-${intensity}`).html(intensity);
}
}
}
}
function findingNeighbors(i, j) {
let rowLimit = mainGrid.length - 1;
let columnLimit = mainGrid[0].length - 1;
for (let x = Math.max(0, i - 1); x <= Math.min(i + 1, rowLimit); x++) {
for (let y = Math.max(0, j - 1); y <= Math.min(j + 1, columnLimit); y++) {
if (x !== i || y !== j) {
increaseIntensity(y, x);
}
}
}
}
function increaseIntensity (y,x) {
if (mainGrid[y][x] === 0 ) {
mainGrid[y][x] = 10;
} else if (mainGrid[y][x] > 9 && mainGrid[y][x] < 40) {
mainGrid[y][x]++;
}
}
let mainGrid = Array(200).fill().map(() => Array(200).fill(0)); // 200 x 200
$(document).ready(() => {
let fireInterval;
startFire(1, 1); // start fire at location (1,1)
addTable(mainGrid, 'table');
$('#start-btn').on('click', () => {
fireInterval = setInterval(() => {
simulateFire();
}, 500);
});
$('#stop-btn').on('click', () => {
clearInterval(fireInterval);
})
});
function increaseIntensity(y, x) {
if (mainGrid[y][x] === 0) {
mainGrid[y][x] = 10;
} else if (mainGrid[y][x] > 9 && mainGrid[y][x] < 40) {
mainGrid[y][x]++;
}
}
function addTable(dataArray, id) {
$(`#${id}`)
.empty()
.append(`<table id="scene-arr-table" style="border-collapse:collapse;"></table>`);
dataArray.forEach((el, r) => {
let row = $(`<tr></tr>`);
$(`#scene-arr-table`).append(row);
el.forEach((el, c) => {
$(row).append(`<td id="cell_${r}_${c}">${el}</td>`)
});
});
}
function startFire(y, x) {
mainGrid[y][x] = 10; // intensity starts at 10
}
function simulateFire() {
for (let y = 0; y < mainGrid.length; y++) {
for (let x = 0; x < mainGrid[y].length; x++) {
let intensity = mainGrid[y][x];
if (intensity >= 10 && intensity < 40) {
findingNeighbors(y, x);
$(`#cell_${y}_${x}`).empty().addClass(`i-${intensity}`).html(intensity);
}
}
}
}
function findingNeighbors(i, j) {
let rowLimit = mainGrid.length - 1;
let columnLimit = mainGrid[0].length - 1;
for (let x = Math.max(0, i - 1); x <= Math.min(i + 1, rowLimit); x++) {
for (let y = Math.max(0, j - 1); y <= Math.min(j + 1, columnLimit); y++) {
if (x !== i || y !== j) {
increaseIntensity(y, x);
}
}
}
}
.i-0 {
background: transparent;
}
.i-10 {
background: red;
}
.i-11 {
background: rgba(255, 0, 0, 0.9);
}
.i-12 {
background: rgba(255, 0, 0, 0.8);
}
.i-13 {
background: rgba(255, 0, 0, 0.7);
}
.i-14 {
background: rgba(255, 0, 0, 0.6);
}
.i-15 {
background: rgba(255, 0, 0, 0.5);
}
.i-16 {
background: rgba(255, 0, 0, 0.4);
}
.i-17 {
background: rgba(255, 0, 0, 0.3);
}
.i-18 {
background: rgba(255, 0, 0, 0.2);
}
.i-19 {
background: rgba(255, 0, 0, 0.1);
}
.i-20 {
background: red;
}
.i-21 {
background: red;
}
.i-22 {
background: red;
}
.i-23 {
background: red;
}
.i-24 {
background: red;
}
.i-25 {
background: red;
}
.i-26 {
background: red;
}
.i-27 {
background: red;
}
.i-28 {
background: red;
}
.i-29 {
background: red;
}
.i-30 {
background: red;
}
.i-31 {
background: #e60404;
}
.i-32 {
background: #cd0808;
}
.i-33 {
background: #b40c0c;
}
.i-34 {
background: #9b1010;
}
.i-35 {
background: #821414;
}
.i-36 {
background: #691818;
}
.i-37 {
background: #501c1c;
}
.i-38 {
background: #372020;
}
.i-39 {
background: #1e2424;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test</title>
</head>
<body>
<button id="start-btn" type="button">START SIM</button>
<button id="stop-btn" type="button">STOP SIM</button>
<div id="table" style="font-size:2px;">
</div>
</body>
</html>
【问题讨论】:
-
您需要两个 mainGrid 副本,一个用于当前帧(您从中读取),另一个用于下一帧(您写入)。否则,当您在阅读之前写入时,您将在一次通过中传播火焰。在每一帧结束时从下一帧复制到当前帧。
-
@samgak 你能再解释一下吗?我不确定我是否完全在阅读部分之前完成写作
-
基本上,您希望根据当前状态计算下一个状态。但是,如果您正在写回 mainGrid,那么您最终会在完成下一个状态的计算之前修改当前状态。在您有机会使用它们之前,您会覆盖尚未使用完的值。例如2,0 的计算应基于 1,0 的原始值,而不是新值
-
我假设每次调用模拟火时,您希望火势蔓延 1 个网格单元(向所有方向)。对于单元格 0,0,您将为包括 1,0 在内的周围单元格调用 increaseIntensity,这将增加 mainGrid[1][0] 的值。然后你增加 x 并为 1,0 周围的单元格调用 increaseIntensity ,这将包括原始的 2,0 等等。因此,火势将在一次通过中从 0,0 变为 2,0(甚至更远)。您可以在运行程序时看到这种情况,它在一帧中从左上角一直传播到右下角。
-
@samgak 哦,我明白了!我现在明白了!我真傻!谢谢
标签: javascript algorithm 2d