Zacznę tutaj od algorytmu tworzenia terenu. Który powstał już dawno temu i bardzo dobrze spełniał swoje zadanie. Przygotowałem go do tworzenia mapy dla gry strategicznej w stylu general. Gdzie podstawowym założeniem było różne umiejscowienie państw i wody...
Założeniem algorytmu było podzielenie mapy na ameby(prowincje).
Mapa była reprezentowana przez tablice kwadratową klasy Pole. A ameby/prowincje zawierały w sobie ich listę. Z tej listy można było utworzyć baseny wodne a także początkowe pozycje graczy - nie mniej z tych dodatkowych rzeczy poruszymy tylko zagadnienie tworzenia obszarów wodnych z mniejszych prowincji. Tak by wszystkie prowincje przedstawiały mniej więcej podobną wielkość.
Klasy użyte w algorytmie można znaleźć w tym pliku. Znajduje się tam także algorytm i jego robocza wersja czyli przed moim opracowaniem dla tego postu.
Tutaj umieszczę kod algorytmu a pod nim kod klas wykorzystywanych w nim:
Algorytm:
// Główna funkcja generująca mapę
public void
GenerujNowąMape( int ileGraczy)
{
//zmienna ustala liczbę prowincji
const int
liczbaProwincji = 2300;
Random random = new Random();
Vertices = new
List<Triangulator.Geometry.Point>();
// lista
ameb
List<AMEBA> ameby = new
List<AMEBA>();
map.pole = new
Pole[mapX, mapY];
for(int x = 0; x < mapX; x++)
{
for(int y = 0; y < mapY; y++)
{
map.pole[x,y] = new Pole();
}
}
for
(int x = 0; x < liczbaProwincji; ) // optimum koło 500
{
bool
losój = true;
Triangulator.Geometry.Point
pNew;
while (true)
{
pNew = new Triangulator.Geometry.Point(random.Next(0, mapX), random.Next(0, mapY));
// losujemy pozycje kolejnej ameby
losój = false; //
przyjmujemy że punkt się nie powtarza
foreach
(Triangulator.Geometry.Point vertices in Vertices) // ale i tak
to sprawdzamy:)
{
if (vertices.X == pNew.X && vertices.Y == pNew.Y) // jeśli już jest w tym miejscu jakaś inna ameba
{
losój = true; // ustaw na dalsze
losowanie
break; // wyjdz z tej pętli
}
}
//Jesli losój jest na true to znaczy że dany
punkt się dubluje - losujemy kolejny raz jeśli nie kończymy
if (!losój) //
jesli się punkty nie powtarzają to zrób tak
{
Vertices.Add(pNew);
ameby.Add(new AMEBA(x,
pNew));
break; //
zakończ losowanie
}
}
x++;
}
int LiczbaNieaktywnychAmeb = 0;
while(LiczbaNieaktywnychAmeb != ameby.Count)
{
//dla każdej z ameb po jednym ruchu
for (int x = 0; x < ameby.Count; x++)
{
if (ameby[x].AKTYWNA)
{
bool RuchWykonany = false;
//ameby szukają w pierwszej kolejności wolnych przestrzeni
for (int y = 0; y <
ameby[x].pozycje.Count && !RuchWykonany; y++)
{
//lewo
if (ameby[x].pozycje[y].X - 1 >= 0 &&
map.pole[(int)(ameby[x].pozycje[y].X - 1), (int)(ameby[x].pozycje[y].Y)].Typ != Typ_Pola.Ziemia && !RuchWykonany)
{
ameby[x].pozycje.Add(new
Triangulator.Geometry.Point((int)(ameby[x].pozycje[y].X - 1), (int)(ameby[x].pozycje[y].Y))); //dodajemy pozycje ameby
ameby[x].ZwiększRozmiarAmeby();
map.pole[(int)(ameby[x].pozycje[y].X - 1), (int)(ameby[x].pozycje[y].Y)].Typ = Typ_Pola.Ziemia;
RuchWykonany = true;
}
//prawo
if (ameby[x].pozycje[y].X + 1 < mapX &&
map.pole[(int)(ameby[x].pozycje[y].X + 1), (int)(ameby[x].pozycje[y].Y)].Typ != Typ_Pola.Ziemia && !RuchWykonany)
{
ameby[x].pozycje.Add(new
Triangulator.Geometry.Point((int)(ameby[x].pozycje[y].X + 1), (int)(ameby[x].pozycje[y].Y))); //dodajemy pozycje ameby
ameby[x].ZwiększRozmiarAmeby();
map.pole[(int)(ameby[x].pozycje[y].X + 1), (int)(ameby[x].pozycje[y].Y)].Typ = Typ_Pola.Ziemia;
RuchWykonany = true;
}
//góra
if (ameby[x].pozycje[y].Y - 1 >= 0 &&
map.pole[(int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y) - 1].Typ != Typ_Pola.Ziemia && !RuchWykonany)
{
ameby[x].pozycje.Add(new
Triangulator.Geometry.Point((int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y - 1))); //dodajemy
pozycje ameby
ameby[x].ZwiększRozmiarAmeby();
map.pole[(int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y - 1)].Typ = Typ_Pola.Ziemia;
RuchWykonany = true;
}
//dół
if (ameby[x].pozycje[y].Y + 1 < mapY &&
map.pole[(int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y) + 1].Typ != Typ_Pola.Ziemia && !RuchWykonany)
{
ameby[x].pozycje.Add(new
Triangulator.Geometry.Point((int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y + 1))); //dodajemy
pozycje ameby
ameby[x].ZwiększRozmiarAmeby();
map.pole[(int)(ameby[x].pozycje[y].X), (int)(ameby[x].pozycje[y].Y) + 1].Typ = Typ_Pola.Ziemia;
RuchWykonany = true;
}
//Jeśli ameba nadal może się ruszać, czyli nie rozmnożyła
się na pole boczne bo nie ma już wolnego koło niej to ją wyłącz
if (RuchWykonany == false
&& y + 1 == ameby[x].pozycje.Count)
{
ameby[x].ZdeaktywujAmebe();
LiczbaNieaktywnychAmeb++;
}
}
}
}
/*
//jeśli jednak nie mają co jeść
wgryzają się w inne ameby ale tylko jeśli tamte są większe od nich - narazie
bez tego
*/
}
Klasa AMEBA:
public
class AMEBA
{
public
List<Triangulator.Geometry.Point> pozycje;
public int WielkośćAmeby;
public bool AKTYWNA;
int ID_AMEBY;
public
AMEBA(int ID,Triangulator.Geometry.Point poz)
{
ID_AMEBY = ID;
WielkośćAmeby = 1;
pozycje = new List<Triangulator.Geometry.Point>();
pozycje.Add(poz); //dodanie pierwszej pozycji
AKTYWNA = true;
}
public void ZwiększRozmiarAmeby()
{
WielkośćAmeby++;
}
public
int WeźID()
{
return
ID_AMEBY;
}
public void
ZdeaktywujAmebe()
{
AKTYWNA = false;
}
}
Klasa Poin
/// <summary>
/// 2D Point with double precision
/// </summary>
public class Point
{
/// <summary>
/// X component of point
/// </summary>
protected
double _X;
/// <summary>
/// Y component of point
/// </summary>
protected
double _Y;
/// <summary>
/// Initializes a new instance of a point
/// </summary>
/// <param
name="x"></param>
/// <param
name="y"></param>
public
Point(double x, double
y)
{
_X = x;
_Y = y;
}
/// <summary>
/// Gets or sets the X component of the point
/// </summary>
public
double X
{
get
{ return _X; }
set
{ _X = value; }
}
/// <summary>
/// Gets or sets the Y component of the point
/// </summary>
public
double Y
{
get
{ return _Y; }
set
{ _Y = value; }
}
/// <summary>
/// Makes a planar checks for if the points is spatially equal
to another point.
/// </summary>
/// <param
name="other">Point to check
against</param>
/// <returns>True if X and Y values are the same</returns>
public
bool Equals2D(Point
other)
{
return
(X == other.X && Y == other.Y);
}
}
Wszystkie powyższe pliki można znaleźć w paczce z tym algorytmem. Link u góry.
Poniżej screeny przedstawiające algorytm w działaniu: