私はそのように無限の地図を生成しようとしています。私はこれをPythonで行っていますが、ノイズライブラリを正しく機能させることができません(VS2010が見つからないようで、生のPythonで行うのは遅すぎます)。そのため、私はDiamond-Squareアルゴリズムを使用しようとしています。
何らかの方法で、これを技術的に無限にすることは可能でしょうか?
そうでない場合は、Pythonノイズバインディングの1つを機能させるための試行に戻る必要がありますか?
私はそのように無限の地図を生成しようとしています。私はこれをPythonで行っていますが、ノイズライブラリを正しく機能させることができません(VS2010が見つからないようで、生のPythonで行うのは遅すぎます)。そのため、私はDiamond-Squareアルゴリズムを使用しようとしています。
何らかの方法で、これを技術的に無限にすることは可能でしょうか?
そうでない場合は、Pythonノイズバインディングの1つを機能させるための試行に戻る必要がありますか?
ノイズモジュールの最新バージョン(http://pypi.python.org/pypi/noise/1.0b3)には、「WindowsでのVisualC++でのコンパイルの問題が修正された」と記載されています。もう一度試してみてください。インストールして正しく動作します(Python 2.7.1、MinGW、Windows 7)。
タイルをx、yグリッドに配置し、random.seed((x、y))のように各タイルに乱数ジェネレーターをシードすると、同じ世界のパッチに戻るたびに、同じものが再作成されます。地形。
ダイアモンドスクエアアルゴリズムでは、各タイルの2つの辺が隣接するタイルに依存します。ただし、依存関係はタイル全体に伝播しません。各タイルをその前のタイル(つまり、上と左)に依存させ、前のタイルの値がすべて0であると想定するcreate_fake_tile関数を作成すると、右の列と下が「悪い」タイルになります。行は、次のタイルが依存する正しい値です。画面の最初の行と列の前にあるタイルの行と列から始めて各画面を描画すると、世界の表示部分が正しくなります。
これを行うことができます。風景を「タイル」に分割し、各タイル内に中点変位アルゴリズムを適用します(私はそれを呼びたいと思います)。
各タイルは、1つのコーナーの高さが別のコーナーの高さに大きく依存しないように、十分な大きさである必要があります。このようにして、タイルをその場で作成および破棄し、新しく表示されるコーナーの高さを独立したランダムな値に設定できます。
その値(およびタイル内のランダム性)は、毎回同じタイルを取得できるように、タイルの位置からシードする必要があります。
下から上に中点変位を適用できますか?個別のグリッドで作業していて、MxN領域を生成したいとします。重み関数w(i)を取得します。各ポイントに重みw(0)のランダムな変位を適用することから始めます。次に、重みw(1)のランダムな変位を1つおきのポイントに適用し、変位を介在するポイントに伝播します。次に、4つおきのポイントをw(2)で実行し、再び伝播します。これで、サイズについての最初の仮定がなくても、任意のサイズのグリッドを生成できます。
w(i> N)= 0のNがある場合は、ヒットしたときに生成を停止できます。これは、生成を終了させたい場合はかなり重要です。おそらく、1から始まり、ある値まで増加し、その後ゼロに落ちるaw関数が必要です。増加するビットは、最大100 km程度のスケールの地形のべき乗則の粗さをモデル化し、減少するビットは、惑星全体が多かれ少なかれ球形であるという事実をモデル化します。地球ではなく火星をやっているのなら、タルシスの膨らみのために、w(i)をさらに進めたいと思うでしょう。
次に、ランダム関数を、ランダムに見えるが決定論的なポイント座標の関数に置き換えます(たとえば、座標をSHA1ハッシュにフィードします)。これで、グリッドの特定の部分を生成するたびに、同じように見えます。
これとほぼ同じ方法でダイアモンドスクエアを実行できるはずです。変位の形状を変更する必要があります。
無限の風景のDiamondSquaredアルゴリズムを解きました。
function diamondSquaredMap(x, y, width, height, iterations) {
var map = fieldDiamondSquared(x, y, x+width, y+height, iterations);
var maxdeviation = getMaxDeviation(iterations);
for (var j = 0; j < width; j++) {
for (var k = 0; k < height; k++) {
map[j][k] = map[j][k] / maxdeviation;
map[j][k] = (map[j][k] + 1) / 2;
}
}
return map;
function create2DArray(d1, d2) {
var x = new Array(d1),
i = 0,
j = 0;
for (i = 0; i < d1; i += 1) {
x[i] = new Array(d2);
}
return x;
}
function fieldDiamondSquared(x0, y0, x1, y1, iterations) {
if (x1 < x0) { return null; }
if (y1 < y0) { return null; }
var finalwidth = x1 - x0;
var finalheight = y1 - y0;
var finalmap = create2DArray(finalwidth, finalheight);
if (iterations === 0) {
for (var j = 0; j < finalwidth; j++) {
for (var k = 0; k < finalheight; k++) {
finalmap[j][k] = displace(iterations,x0+j,y0+k) ;
}
}
return finalmap;
}
var ux0 = Math.floor(x0 / 2) - 1;
var uy0 = Math.floor(y0 / 2) - 1;
var ux1 = Math.ceil(x1 / 2) + 1;
var uy1 = Math.ceil(y1 / 2) + 1;
var uppermap = fieldDiamondSquared(ux0, uy0, ux1, uy1, iterations-1);
var uw = ux1 - ux0;
var uh = uy1 - uy0;
var cx0 = ux0 * 2;
var cy0 = uy0 * 2;
var cw = uw*2-1;
var ch = uh*2-1;
var currentmap = create2DArray(cw,ch);
for (var j = 0; j < uw; j++) {
for (var k = 0; k < uh; k++) {
currentmap[j*2][k*2] = uppermap[j][k];
}
}
var xoff = x0 - cx0;
var yoff = y0 - cy0;
for (var j = 1; j < cw-1; j += 2) {
for (var k = 1; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k - 1] + currentmap[j - 1][k + 1] + currentmap[j + 1][k - 1] + currentmap[j + 1][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 1; j < cw-1; j += 2) {
for (var k = 2; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k] + currentmap[j + 1][k] + currentmap[j][k - 1] + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 2; j < cw-1; j += 2) {
for (var k = 1; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k] + currentmap[j + 1][k] + currentmap[j][k - 1] + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 0; j < finalwidth; j++) {
for (var k = 0; k < finalheight; k++) {
finalmap[j][k] = currentmap[j+xoff][k+yoff];
}
}
return finalmap;
}
// Random function to offset
function displace(iterations, x, y) {
return (((PRH(iterations,x,y) - 0.5)*2)) / (iterations+1);
}
function getMaxDeviation(iterations) {
var dev = 0.5 / (iterations+1);
if (iterations <= 0) return dev;
return getMaxDeviation(iterations-1) + dev;
}
//This function returns the same result for given values but should be somewhat random.
function PRH(iterations,x,y) {
var hash;
x &= 0xFFF;
y &= 0xFFF;
iterations &= 0xFF;
hash = (iterations << 24);
hash |= (y << 12);
hash |= x;
var rem = hash & 3;
var h = hash;
switch (rem) {
case 3:
hash += h;
hash ^= hash << 32;
hash ^= h << 36;
hash += hash >> 22;
break;
case 2:
hash += h;
hash ^= hash << 22;
hash += hash >> 34;
break;
case 1:
hash += h;
hash ^= hash << 20;
hash += hash >> 2;
}
hash ^= hash << 6;
hash += hash >> 10;
hash ^= hash << 8;
hash += hash >> 34;
hash ^= hash << 50;
hash += hash >> 12;
return (hash & 0xFFFF) / 0xFFFF;
}
};
//CANVAS CONTROL
window.onload = terrainGeneration;
function terrainGeneration() {
"use strict";
var mapDimension,
roughness,
iterations,
mapCanvas = document.getElementById('canvas');
var update = document.getElementById('update');
var xpos = 0;
var ypos = 0;
mapDimension = 512;
mapCanvas.width = mapDimension;
mapCanvas.height = mapDimension;
var updatefunction = function() {
var elIterations = document.getElementById('iterations');
iterations = parseInt(elIterations.value, 10);
iterations = iterations || 6;
MoveMap(10,0);
}
update.onclick = updatefunction;
updatefunction();
function MoveMap(dx, dy) {
xpos -= dx;
ypos -= dy;
var map = diamondSquaredMap(xpos, ypos, mapDimension, mapDimension, iterations);
drawMap(mapDimension, "canvas", map);
}
var m = this;
m.map = document.getElementById("canvas");
m.width = mapDimension;
m.height = mapDimension;
m.hoverCursor = "auto";
m.dragCursor = "url(data:image/vnd.microsoft.icon;base64,AAACAAEAICACAAcABQAwAQAAFgAAACgAAAAgAAAAQAAAAAEAAQAAAAAAAAEAAAAAAAAAAAAAAgAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD8AAAA/AAAAfwAAAP+AAAH/gAAB/8AAAH/AAAB/wAAA/0AAANsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////////////////////////////////////////////////////gH///4B///8Af//+AD///AA///wAH//+AB///wAf//4AH//+AD///yT/////////////////////////////8=), default";
m.scrollTime = 300;
m.mousePosition = new Coordinate;
m.mouseLocations = [];
m.velocity = new Coordinate;
m.mouseDown = false;
m.timerId = -1;
m.timerCount = 0;
m.viewingBox = document.createElement("div");
m.viewingBox.style.cursor = m.hoverCursor;
m.map.parentNode.replaceChild(m.viewingBox, m.map);
m.viewingBox.appendChild(m.map);
m.viewingBox.style.overflow = "hidden";
m.viewingBox.style.width = m.width + "px";
m.viewingBox.style.height = m.height + "px";
m.viewingBox.style.position = "relative";
m.map.style.position = "absolute";
function drawMap(size, canvasId, mapData) {
var canvas = document.getElementById(canvasId),
ctx = canvas.getContext("2d"),
x = 0,
y = 0,
colorFill,
img = ctx.createImageData(canvas.height, canvas.width);
for (x = 0; x < size; x++) {
for (y = 0; y < size; y++) {
colorFill = {r: 0, g: 0, b: 0};
var standardShade = Math.floor(mapData[x][y] * 250);
colorFill = {r: standardShade, g: standardShade, b: standardShade};
var pData = (x + (y * canvas.width)) * 4;
img.data[pData] = colorFill.r;
img.data[pData + 1] = colorFill.g;
img.data[pData + 2] = colorFill.b;
img.data[pData + 3] = 255;
}
}
ctx.putImageData(img, 0, 0);
}
function AddListener(element, event, f) {
if (element.attachEvent) {
element["e" + event + f] = f;
element[event + f] = function() {
element["e" + event + f](window.event)
};
element.attachEvent("on" + event, element[event + f])
} else
element.addEventListener(event, f, false)
}
function Coordinate(startX, startY) {
this.x = startX;
this.y = startY;
}
var MouseMove = function(b) {
var e = b.clientX - m.mousePosition.x;
var d = b.clientY - m.mousePosition.y;
MoveMap(e, d);
m.mousePosition.x = b.clientX;
m.mousePosition.y = b.clientY
};
/**
* mousedown event handler
*/
AddListener(m.viewingBox, "mousedown", function(e) {
m.viewingBox.style.cursor = m.dragCursor;
// Save the current mouse position so we can later find how far the
// mouse has moved in order to scroll that distance
m.mousePosition.x = e.clientX;
m.mousePosition.y = e.clientY;
// Start paying attention to when the mouse moves
AddListener(document, "mousemove", MouseMove);
m.mouseDown = true;
event.preventDefault ? event.preventDefault() : event.returnValue = false;
});
/**
* mouseup event handler
*/
AddListener(document, "mouseup", function() {
if (m.mouseDown) {
var handler = MouseMove;
if (document.detachEvent) {
document.detachEvent("onmousemove", document["mousemove" + handler]);
document["mousemove" + handler] = null;
} else {
document.removeEventListener("mousemove", handler, false);
}
m.mouseDown = false;
if (m.mouseLocations.length > 0) {
var clickCount = m.mouseLocations.length;
m.velocity.x = (m.mouseLocations[clickCount - 1].x - m.mouseLocations[0].x) / clickCount;
m.velocity.y = (m.mouseLocations[clickCount - 1].y - m.mouseLocations[0].y) / clickCount;
m.mouseLocations.length = 0;
}
}
m.viewingBox.style.cursor = m.hoverCursor;
});
}
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Canvas Terrain Generator</title>
<link rel="stylesheet" href="terrain.css" type="text/css">
<style type="text/css"></style><style type="text/css"></style></head>
<body>
Click and Pan.<br>
<div style="cursor: auto; overflow: hidden; width: 512px; height: 512px; position: relative;"><canvas id="canvas" width="512" height="512" style="position: absolute;"></canvas></div>
<br>
<form>
<fieldset>
<legend>Height Map Properties</legend>
<ol class="options">
<li>
<input type="text" name="iterations" id="iterations">
<label for="iterations">
Iterations(6)
</label>
</li>
</ol>
</fieldset>
<input type="button" value="Update" id="update">
</form>
</body></html>
https://jsfiddle.net/Tatarize/1Lrj3s2v/3/jsfiddle をクリックしてドラッグします
そして実際にこの質問が私のデューデリジェンスをしているのを見つけました。はい。ここで提供される答えはほとんど間違っていますが、それは完全に行うことができます。概念的には、アルゴリズムの各反復を無限フィールドと見なし、基本ケースを完全に乱数の無限フィールドと見なす必要があります。したがって、各反復は前の反復のダイアモンドの2乗バージョンです。
そして、反復7でタイル[100-200] [100-200]の範囲を解くために必要なのは、反復6でそのサイズの半分(そのサイズh * wの約4分の1)にブロック全体を加えたものです。それぞれの側。したがって、特定のタイルを解決する関数がある場合は、次のようになります。
getTerrain(x0、y0、x1、y1、iteration)...反復6で[49,101] [49,101]がある場合、そのタイルについてこれを解決できます。次に、で機能しない可能性のあるすべての場所にダイヤモンドの2乗を適用します。エッジですが、他のすべての場所で機能し、反復7でタイル[100-200][100-200]を解決するのに十分なデータを提供します。
反復6でそのタイルを取得するには、反復5から[23-52] [23-52]を要求します。これは、反復0で[-1,3][-1,3]が16になるまで続きます。完全にランダムな値。反復0がまだそのサイズと位置に達していないほど大きなタイルを要求した場合は、とにかくそれらを構成しているだけなので、別のスライバーが必要になります。
このようにして、シードステップを完全に取り除き、アルゴリズムを単純化し、無限にし、妥当なメモリフットプリントを必要とします。座標からハッシュされた決定論的擬似乱数を使用すると、同じタイルをその場で生成および再生成できます。これは、実際に上部と下部の反復を生成し、焦点を絞った方法で生成したためです。
私のブログにはそれに関する詳細情報があります、 http: //godsnotwheregodsnot.blogspot.com/2013/11/field-diamond-squared-fractal-terrain.html
実際には、アルゴリズムの複雑さのほとんどは、基本ケースがループ内に4つのポイントを持つことに起因します。これは、最も単純なケースに近くないためです。そして、ループで1ポイントを実行することで、それを単純化することさえできたということは、ほとんどの人から明らかに逃れています(これはまだ無意味に複雑ですが)。または、アルゴリズムに少なくとも4x4ポイントがあるように見えるもの。4x4ポイントを一度繰り返すことから始めて、未知の値(すべてのエッジ)を持つ行または列をドロップし、次に5x5ブロック、次に7x7ブロック、次に11x11ブロックを作成することもできます...(2n-3)x(2n -3)。
要するに、ベースケースに無限のフィールドがある場合、実際には無限のフィールドを持つことができます。反復によって、コンテンツがどの程度ブレンドされているかが決まります。また、疑似ランダム注入オフセットに決定論的なものを使用すると、非常に高速な無限ランドスケープジェネレーターがほぼ使用できます。
これがjavascript、 http://tatarize.nfshost.com/FieldDiamondSquare.htmでのデモンストレーションです。
これがjavascriptでの実装です。それは単に、その反復、位置、およびサイズでの正しいフィールドのビューを提供します。上記のリンクされた例では、可動キャンバスでこのコードを使用しています。データを保存せず、各フレームを再計算します。
function diamondSquaredMap(x, y, width, height, iterations) {
var map = fieldDiamondSquared(x, y, x+width, y+height, iterations);
var maxdeviation = getMaxDeviation(iterations);
for (var j = 0; j < width; j++) {
for (var k = 0; k < height; k++) {
map[j][k] = map[j][k] / maxdeviation;
}
}
return map;
function create2DArray(d1, d2) {
var x = new Array(d1),
i = 0,
j = 0;
for (i = 0; i < d1; i += 1) {
x[i] = new Array(d2);
}
return x;
}
function fieldDiamondSquared(x0, y0, x1, y1, iterations) {
if (x1 < x0) { return null; }
if (y1 < y0) { return null; }
var finalwidth = x1 - x0;
var finalheight = y1 - y0;
var finalmap = create2DArray(finalwidth, finalheight);
if (iterations === 0) {
for (var j = 0; j < finalwidth; j++) {
for (var k = 0; k < finalheight; k++) {
finalmap[j][k] = displace(iterations,x0+j,y0+k) ;
}
}
return finalmap;
}
var ux0 = Math.floor(x0 / 2) - 1;
var uy0 = Math.floor(y0 / 2) - 1;
var ux1 = Math.ceil(x1 / 2) + 1;
var uy1 = Math.ceil(y1 / 2) + 1;
var uppermap = fieldDiamondSquared(ux0, uy0, ux1, uy1, iterations-1);
var uw = ux1 - ux0;
var uh = uy1 - uy0;
var cx0 = ux0 * 2;
var cy0 = uy0 * 2;
var cw = uw*2-1;
var ch = uh*2-1;
var currentmap = create2DArray(cw,ch);
for (var j = 0; j < uw; j++) {
for (var k = 0; k < uh; k++) {
currentmap[j*2][k*2] = uppermap[j][k];
}
}
var xoff = x0 - cx0;
var yoff = y0 - cy0;
for (var j = 1; j < cw-1; j += 2) {
for (var k = 1; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k - 1] + currentmap[j - 1][k + 1] + currentmap[j + 1][k - 1] + currentmap[j + 1][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 1; j < cw-1; j += 2) {
for (var k = 2; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k] + currentmap[j + 1][k] + currentmap[j][k - 1] + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 2; j < cw-1; j += 2) {
for (var k = 1; k < ch-1; k += 2) {
currentmap[j][k] = ((currentmap[j - 1][k] + currentmap[j + 1][k] + currentmap[j][k - 1] + currentmap[j][k + 1]) / 4) + displace(iterations,cx0+j,cy0+k);
}
}
for (var j = 0; j < finalwidth; j++) {
for (var k = 0; k < finalheight; k++) {
finalmap[j][k] = currentmap[j+xoff][k+yoff];
}
}
return finalmap;
}
// Random function to offset
function displace(iterations, x, y) {
return (((PRH(iterations,x,y) - 0.5)*2)) / (iterations+1);
}
function getMaxDeviation(iterations) {
var dev = 0.5 / (iterations+1);
if (iterations <= 0) return dev;
return getMaxDeviation(iterations-1) + dev;
}
//This function returns the same result for given values but should be somewhat random.
function PRH(iterations,x,y) {
var hash;
x &= 0xFFF;
y &= 0xFFF;
iterations &= 0xFF;
hash = (iterations << 24);
hash |= (y << 12);
hash |= x;
var rem = hash & 3;
var h = hash;
switch (rem) {
case 3:
hash += h;
hash ^= hash << 32;
hash ^= h << 36;
hash += hash >> 22;
break;
case 2:
hash += h;
hash ^= hash << 22;
hash += hash >> 34;
break;
case 1:
hash += h;
hash ^= hash << 20;
hash += hash >> 2;
}
hash ^= hash << 6;
hash += hash >> 10;
hash ^= hash << 8;
hash += hash >> 34;
hash ^= hash << 50;
hash += hash >> 12;
return (hash & 0xFFFF) / 0xFFFF;
}
};
追加するために更新され、私はこれから進んで、ここで同じスコープフィールドアルゴリズムに基づいて独自のノイズアルゴリズムを作成しました。これがそのためのJsfiddleです。
https://jsfiddle.net/rkdzau7o/
クリックしてノイズをドラッグできます。