Autor Zpráva
Dase
Profil *
Ahoj, pracuju na generátoru mapy pro jednu hru a potřeboval bych poradit. Mapu mám organizovanou jako jednorozměrné pole objektů kde každý má souřadnice [x, y]. Potřebuji z tohoto pole vybírat úseky náhodného tvaru které budou reprezentovat například jezera nebo lesy. Používám následující algoritmus, ale jeho problémem je, že mi uvnitř jezer vznikají ostrovy. Potřeboval bych použít nějaký lepší algoritmus. Nevím ani pořádně co mám vlastně googlit, proto píšu o radu sem, jestli někoho něco nenapadne.

Algoritmus funguje tak, že na začátku vybere náhodou bunku z mapy, z jejího okolí vezme náhodný prvek u něj znovu zjistí okolí a znovu vezme náhodný a tak pořád dál. Díky náhodnému výběru z okolí bodu mi vznikají uprostřed volná místa.
start = nahodnaBunka(); //vybere prázdnou bunku (neobsahuje jezero)
i = 0;
while(i < velikostMapy / 10){ //jezero zabírá maximálně 1/10 mapy
 sousedi = najdiSousedy(start)  //Vrací pole buněk 8-okolí bodu
 soused = nahodnySoused(sousedi) //zde je ten problém. Díky náhodnému výběru z pole vznikají ostrovy
 soused->typ = jezero
 start = soused
 i++
}

Děkuji za radu
juriad
Profil
Pro jezera jsem používal tuto metodu: www.playfuljs.com/realistic-terrain-in-130-lines
Vygenerovat si 3D terén a pak oříznout. Cokoli má výšku <= 0 je voda. Cokoli > 0 je země.

Les také záleží na nadmořské výšce; původně byly snad všude, ale zemědělská činnost je z určitých ploch odstranila (z těch, které jsou dostatečně rovné). To můžeš zkusit detekovat a vytvořit tam pole/louky. A iterovat pro simulaci lidské činnosti.

Možná by ten generátor šel zjednodušit: vygeneruješ si hodně hrubou mapu biotopů a pak ji budeš zjemňovat - počítat pravděpodobnosti (nikoli výšku) podle sousedů v hrubé mapě + nějaký šum. Můžeš začít třeba mapou 8 x 8, která je pokrytá náhodně a pak zjemňovat až na požadovanou velikost.
V každém případě ti neuškodí si přečíst odkázaný článek.

Mimochodem, jaké jsou požadavky na mapu:
- realističnost
- konečnost/nekonečnost/omezenost
- jak moc vadí extrémní mapy (souostroví, vysoké hory)?
Dase
Profil *
Díky za odpověď. bude to pouze 2D mapa, takže výšku nepotřebuji řešit. Ideální by bylo aby jezera, lesy a další objekty v mapě měli realističtější tvary. mapa má přesně danou velikost. Jednotlivé objekty na mapě nejsou zatím nic jiného, než prvek v poli označený jako jezero nebo les. nejde o grafiku. zatím je to jenom práce s polem.
tiso
Profil
A čo je zlé na ostrovoch? Prípadne čistinkách v lese?
Skús niečo ako: ak je 5+ susedov rovnakého typu (jazero, les), tak aj ja budem taký typ
juriad
Profil
Ukázka zjemňovacího algoritmu:
<?php

# definice typů políček
$types = array('voda', 'les', 'louka', 'pole');
$marks = array('~', '#', '.', ',');
$probs = array(1, 2, 1, 0.5);

# počáteční velikost (2^start + 1)
$start = 0; # mapa 2x2
# koncová velikost (2^end + 1)
$end = 6; # mapa 65x65

# šum (relativně k počtu sousedů)
$noise = 0.2;

# vrátí náhodné pole podle distribuce
function randomType($probabilities) {
    $prefix = array($probabilities[0]);
    for ($i = 1; $i < count($probabilities); $i++) {
        $prefix[$i] = $prefix[$i - 1] + $probabilities[$i];
    }
    $r01 = mt_rand() / mt_getrandmax();
    $r = $r01 * $prefix[count($prefix) - 1];
    $prev = 0;
    for ($i = 0; $i < count($prefix); $i++) {
        if ($prev < $r && $r < $prefix[$i]) {
            return $i;
        }
        $prev = $prefix[$i];
    }
    return 0; # to be sure
}

# vygeneruje náhodnou počáteční mapu
$map = array();
for ($y = 0; $y <= 1 << $start; $y++) {
    $map[] = array();
    for ($x = 0; $x <= 1 << $start; $x++) {
        $map[$y][$x] = randomType($probs);
    }
}

# spočítá distribuci typů v okolí políčka a přidá šum
function calculateProbs($notypes, $map, $y, $x, $diagonal = FALSE, $noise) {
    $around = array();
    for ($t = 0; $t < $notypes; $t++) {
        $c = 0;
        if ($diagonal) {
            $c += $map[$y-1][$x-1] == $t;
            $c += $map[$y-1][$x+1] == $t;
            $c += $map[$y+1][$x-1] == $t;
            $c += $map[$y+1][$x+1] == $t;
        } else {
            if ($y > 0)
                $c += $map[$y-1][$x] == $t;
            if ($y < count($map) - 1)
                $c += $map[$y+1][$x] == $t;
            if ($x > 0)
                $c += $map[$y][$x-1] == $t;
            if ($x < count($map[0]) - 1)
                $c += $map[$y][$x+1] == $t;
        }
        $r01 = mt_rand() / mt_getrandmax();
        $r = $r01 * $noise * 2 - $noise; # (-$noise, $noise)
        $around[$t] = max($c + $r, 0);
    }
    return $around;
}

# vypíše mapu
function printMap($map, $marks) {
    for ($y = 0; $y < count($map); $y++) {
        for ($x = 0; $x < count($map[$y]); $x++) {
            echo $marks[$map[$y][$x]];
        }
        echo "\n";
    }
    echo "\n";
}

printMap($map, $marks);

# postupně zjemňuje mapu
for ($l = $start + 1; $l <= $end; $l++) { # increasing level of details
    # expand map
    $map2 = array();
    for ($y = 0; $y <= 1 << $l; $y++) {
        $map2[] = array();
        for ($x = 0; $x <= 1 << $l; $x++) {
            $map2[$y][$x] = $map[(int)$y/2][(int)$x/2];
        }
    }
    $map = $map2;

    # fill centers
    for ($y = 1; $y <= 1 << $l; $y += 2) {
        for ($x = 1; $x <= 1 << $l; $x += 2) {
            $p = calculateProbs(count($types), $map, $y, $x, TRUE, $noise);
            $map[$y][$x] = randomType($p);
        }
    }

    # fill borders
    for ($y = 0; $y <= 1 << $l; $y += 2) {
        for ($x = 1; $x <= 1 << $l; $x += 2) {
            $p = calculateProbs(count($types), $map, $y, $x, FALSE, $noise);
            $map[$y][$x] = randomType($p);
        }
    }
    for ($y = 1; $y <= 1 << $l; $y += 2) {
        for ($x = 0; $x <= 1 << $l; $x += 2) {
            $p = calculateProbs(count($types), $map, $y, $x, FALSE, $noise);
            $map[$y][$x] = randomType($p);
        }
    }
    printMap($map, $marks);
}

# vypíše mapu
for ($y = 0; $y < count($map); $y++) {
    for ($x = 0; $x < count($map[$y]); $x++) {
        echo $marks[$map[$y][$x]];
    }
    echo "\n";
}

Ukázka jeho výstupu:
.#
,#

.##
..#
,.#

..###
..~##
....#
,..,,
,,..#

...~#####
...~####,
....~,###
.....#~##
.....#.##
,~...~.,#
,.....,.,
........#
,.,,...,#

......~##########
....#.~#,#####,#,
......~##~######,
..,...~.##,#####,
........~,,######
........,,,######
.........,##~##,#
..........###.###
..........#~.####
....#....##~~.###
,,~.......~~..,,#
,,~.......~.....~
,...........,,..,
...#.............
...........#....#
,...,.........,..
,...,.,......,,##

............~####################
.............~#,,################
........#..~~~##,###########,###,
...........~~~~###~########,####,
.......~...~~#####~#############,
...........~~.######,#.#######,,,
.#.,,..,...~~..#####,,#########,,
..........~~~..,,##,,,##########,
.,...#..........~~,~,,###########
..,.............~~,,,,###########
...........#...,,,,,,,###########
...............,,,,##,####..#,,#~
.................,,##~#~~~#.#,,##
................,,,####~~~~##.,##
.................,..#####~..#####
.....#...............####.#######
...,.............~.###~~..#.#####
.........##........##~~~~.#.#####
........#........##~##~~~..######
...~~..............~##~~...,,,,##
,,,,~...............~~~,...,,,,##
,,,~~~..........#...~~..,........
,#,~~~..............~...........~
,##,................~...,......~,
,#.....................,,,,,....,
.........................,,......
......#..................,.,.....
...........#..............#.....#
...#.................##.........#
....................#####........
,..#....,...................,,.#.
,......,,...,............,..,,,##
,......,,,..,,.....~.....,,,,,###

...............#.......~~####################################,###
.~~.....................~~~~~,,,,#####################,##########
.................#.#...~..~###,#,########################,#######
.................#.....~.~~~~#######~###################,,#######
.,..............#....~~~~~~~####,##############.########,#######,
,,,...................~~~~~~~######~~##################,,,#####,,
.............#.......~~~~~~~~######~~#################,########,,
...............~.....~~~~~##~#######~~###########..###########~,,
.......,......~~......~~~~##########~############.###,##########,
............#.,.......~~~~~.################..######~########,##,
......................~~~..#############,###.###############,,,,,
..#...,,,,...,........~~~~..#.##########,,,#.###############,,,,,
.##..,,,,,...,,......#~~~.....#########,,,,###################,,,
...#.,,,.....,,.....~~~~~......#.######,,,,###################,,,
.......,............~~~~~~....,#,####,,,,,,#####################,
.,,...............~~...........,,,~#,,,,,,,############~######.##
.,,,......#..#..................~~~#,~~~,,,############~#########
.,....~.........................~~~~,,,~,,,,,####################
....,,.........................~~~~~,,,,,,,######################
......~...............#.......,~~,,~~,,,,,,,#####################
.............,........##......,,,,,.,,,#,,,,#######.#############
............................,,,,,.,,,,,###,,######...##..##,,##,,
...,.........................,,,,,,,,,###,,,#######..,.##,,,,##~~
...............................,,,,#,,,###~~##~####.######,,,,,~#
...............................~.,,,,,###~~~##~~~#~~##.##,,,,##~#
.................,.............~..,,,,#####~##~~~~~~~##..#.,,####
.................,.............~,.,,,,########~~~~~~~#####.#,####
....#....................,,.......,,,.,########~~~~~~~~##.##,####
...................#..............,.....#########~~~....#.#######
..........~.......................,....##########~.......########
..........#..............................######~##.##########,###
......,........,,,...............#~~##..#######~#..##########,,##
......,..........#.....~.#........~~.#######~~~~...###.##########
.................###.#...............###~~#~,~##...#....######~##
..................####...............##~#~~~~~~~~~..##.##########
..,..~..............#............####~~~~##~~~~~~~...############
................##...............##.#~~~###~~~~~~~....###########
.........~.......#...................######~~~,,.....,##,###,####
...,..~~~~...........................#~.####~~~......,,#,#,,,####
#..,,~~~~~~.............................~~~#~~~......,,,,,,,#####
,,,,,,,,~~.......................,......~~~~~~,......,,,,,,,,####
,,,,,,~~~~......................###....~~~...~.......,,,,,,,,...#
,,,,,~~~~~~................#.~..##......~~~....~,,.......,.......
,,,,,~~~~~~~...................#.#....##.......,,,...............
,##,,,~~~~~............................#~..................,...,~
,,,##,,,,~~..............................~~...,,,.............~..
,,###,,,...............................~~~......,.............~#,
,,##.....................................~~...,,,,,....,......~~~
,,##..........................................,,,,,,,,,,.......,,
,#.#............................~..............,,,,,,,.........,,
.#.#.............................................,,,,~...........
.............##....##............................,,,,,,,.........
...~........#................................#...~,,.,,,.........
,...........#..........#..........................~..,....~....##
.......#..............#............................##...........#
.....#.#....~.........#.,...........,.....#####.....#....#.......
.....##...................................###...................#
......#....................................######..............##
...............~........................#.########.............#.
,.............,,........................#...##....~......,,,,##..
,,....#........,,........,...................#.....#....,,,,.###.
,..##............,.................,...............#..,,,,,,,.##.
,,...........,,,,~.,....,,.......#................,....~,,,,,.###
,,.............,,,,,,##,,,,.............###.....,,,..,,,,,,,,,###
,,...........,,,,,,..#.,,~,,.........~~........#..,.,,,,,,,,#####

Ostrovy občas vzniknou, ale lze je eliminovat zmenšením šumu.

Ukázka generování mapy.
Dase
Profil *
Díky tan algoritmus vypadá zajímavě, ale moc se mi nelíbí že se musí mapa procházet v tolika krocích. jestli tomu rozumím správně tak se nejprve vygenerují části náhodně a potom se pole znovu prochází a některé části co jsou blízko u sebe se spojují? Raději bych chtěl vylepšit algoritmus přímo toho generování, tak aby se nebrali náhodné prvky z okolí ale podle nějakého kritéria..
juriad
Profil
Dase:
Ne. Vezme se malinkatá náhodná mapička:
#.
~#
A ta se potom rozšíří napřed o šedé pole a pak o bílá (to jsou ty 4 cykly uvnitř toho velkého foru):
#..
###
~~#
Další krok (náhodně provedeno ručně):
#,...
##...
##.##
~~###
~~~##

O výkon se bát nemusíš, generování mapy není častá činnost. Zvládá to (bez paměťových optimalizací) mapu 513x513.
Pro výpočet každého políčka jsou potřeba v průměru: 4 přístupy do paměti pro každý typ a jelikož menší mapa je čtvrtinová, přispěje zhruba #typů přístupy. V celku můžeme řict, že generování je stejně náročné jako projít celou velkou mapu 5*počet typů, to už není tak strašné, ne? To je pro 4 typy a velikost 513x513 jen 5 263 380 operací.

Trošku jsem upravil zdroják a ukázku, aby byl zřejmý postup generování.
Dase
Profil *
Ok zkusím tvůj algoritmus použít. díky za pomoc

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0