Как я уже упоминал в комментарии, алмаз и квадрат намного легче с хорошими результатами. Таким образом, алгоритм:
zed[][]
. Для этого вам нужно немного модифицировать алгоритм Diamond & amp; Square . Проблема заключается в том, что этот алгоритм создает «внутренний» ландшафт. Чтобы настроить его так, чтобы он создавал островные ландшафты, вам необходимо инициализировать его с минимальным возвышением в углах. Также вам нужно проигнорировать первый шаг алмаза и инициализировать среднюю точку с помощью некоторого случайного значения (не в среднем по углам). И последний раз после каждой квадратной итерации правильная граница указывает на минимальную (подводную) высоту (или некоторое случайное значение рядом с ней). Для достижения хорошего результата я использую примерно диапазон <-2^15 , 2^16>
во время генерации. После этого я нахожу минимальное и максимальное возвышение в сгенерированном ландшафте и масштабируюсь до заданных диапазонов высоты. Не забывайте, что карта с диамантом и квадратом с разрешением (2^n)+1
!!! typ[][]
. Когда карта местности закончена, вы можете добавить такие функции, как в порядке возрастания: watter, sand, тип растительности, горные породы, снег. Затем добавьте параметры, основанные на наклоне рельефных пород. Затем вы можете добавить дополнительные вещи, например (по некоторым правилам): реки, ручьи, водовороты, строительство, дороги, ... Я делаю это в C ++ следующим образом:
void map_random(int _xs,int _ys)
{
// config
int h0=-1000,h1=3000; // [m] terrain elevation range
int h_water= 0; // [m] sea level
int h_sand=15; // [m] sand level
int h_evergreen=1500; // [m] evergreen level
int h_snow=2000; // [m] snow level
int h_rock=1800; // [m] mountine rock level
float a_rock=60.0; // [deg] mountine rock slope
float d_pixel=15.0; // [m] pixel size
bool _island=true;
// types
enum _cover_enum
{
_cover_none=0,
_cover_water,
_cover_snow,
_covers,
_cover_shift=0,
_cover_mask=15,
};
DWORD _cover[_covers]=
{
// RRGGBB
0x00000000, // none
0x00004080, // water
0x008F8F8F, // snow
};
enum _terrain_enum
{
_terrain_enum_beg=-1,
_terrain_dirt,
_terrain_sand,
_terrain_rock,
_terrains,
_terrain_shift=4,
_terrain_mask=15,
};
DWORD _terrain[_terrains]=
{
// RRGGBB
0x00301510, // dirt
0x00EEC49A, // sand
0x00777777, // rock
};
enum _flora_enum
{
_flora_enum_beg=-1,
_flora_none,
_flora_grass,
_flora_hardwood,
_flora_evergreen,
_flora_deadwood,
_floras,
_flora_shift=8,
_flora_mask=15,
};
DWORD _flora[_floras]=
{
// RRGGBB
0x00000000, // none
0x007F7F3F, // grass
0x001FFF1F, // hardwood
0x00007F00, // evergreen
0x007F3F1F, // deadwood
};
// variables
float a,b; int c,t,f;
int x,y,z,xx,yy,mxs,mys,dx,dy,dx2,dy2,r,r2;
int **ter=NULL,**typ=NULL;
Randomize();
// align resolution to power of 2
for (mxs=1;mxs+1<_xs;mxs<<=1); if (mxs<3) mxs=3;
for (mys=1;mys+1<_ys;mys<<=1); if (mys<3) mys=3;
ter=new int*[mys+1]; for (y=0;y<=mys;y++) ter[y]=new int[mxs+1];
typ=new int*[mys+1]; for (y=0;y<=mys;y++) typ[y]=new int[mxs+1];
// [Terrain]
// diamond & square random height map -> ter[][]
dx=mxs; dx2=dx>>1; r=1<<16; // init step,half step and randomness
dy=mys; dy2=dy>>1; r2=r>>1;
// set corners values
if (_island)
{
t=-r2;
ter[ 0][ 0]=t;
ter[ 0][mxs]=t;
ter[mys][ 0]=t;
ter[mys][mxs]=t;
ter[dy2][dx2]=r2;
}
else{
ter[ 0][ 0]=Random(r);
ter[ 0][mxs]=Random(r);
ter[mys][ 0]=Random(r);
ter[mys][mxs]=Random(r);
}
for (;dx2|dy2;dx=dx2,dx2>>=1,dy=dy2,dy2>>=1) // subdivide step until full image is filled
{
if (!dx) dx=1;
if (!dy) dy=1;
// diamond (skip first one for islands)
if ((!_island)||(dx!=mxs))
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y-dy2][x-dx2]+ter[y-dy2][x+dx2]+ter[y+dy2][x-dx2]+ter[y+dy2][x+dx2])>>2)+Random(r)-r2;
// square
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
for (x=dx ,xx=mxs-dx ;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (y=dy ,yy=mys-dy ;y<=yy;y+=dy)
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])>>2)+Random(r)-r2;
for (x=dx2,xx=mxs-dx2;x<=xx;x+=dx)
{
y= 0; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y+dy2][x])/3)+Random(r)-r2;
y=mys; ter[y][x]=((ter[y][x-dx2]+ter[y][x+dx2]+ter[y-dy2][x])/3)+Random(r)-r2;
}
for (y=dy2,yy=mys-dy2;y<=yy;y+=dy)
{
x= 0; ter[y][x]=((ter[y][x+dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
x=mxs; ter[y][x]=((ter[y][x-dx2]+ter[y-dy2][x]+ter[y+dy2][x])/3)+Random(r)-r2;
}
// adjust border
if (_island)
{
for (y=0;y<=mys;y+=dy2) { ter[y][0]=t; ter[y][mxs]=t; }
for (x=0;x<=mxs;x+=dx2) { ter[0][x]=t; ter[mys][x]=t; }
}
// adjust randomness
// r=(r*100)>>8; if (r<2) r=2; r2=r>>1;
r>>=1; if (r<2) r=2; r2=r>>1;
}
// rescale to
xx=ter[0][0]; yy=xx;
for (y=0;yz) xx=z;
if (yy=h_snow ) c=_cover_snow;
t=_terrain_dirt;
if (z<=h_sand) t=_terrain_sand;
if (z>=h_rock) t=_terrain_rock;
if (a>=a_rock) t=_terrain_rock;
f=_flora_none;
if (t==_terrain_dirt)
{
r=Random(100);
if (r>10) f=_flora_grass;
if (r>50)
{
if (z>h_evergreen) f=_flora_evergreen;
else{
r=Random(h_evergreen);
if (r<=z) f=_flora_evergreen;
else f=_flora_hardwood;
}
}
if (r<5) f=_flora_deadwood;
}
typ[y][x]=(c<<_cover_shift)|(t<<_terrain_shift)|(f<<_flora_shift);
}
// [copy data] rewrite this part to suite your needs it just compute color based on type of terrain and height
// ter[][] is elevation in meters
// typ[][] is surface type
/*
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
pic.p[y][x].dd=(((ter[y][x]-h0)*255)/(h1-h0))*0x00010101;
for (y=0;y<_ys;y++)
for (x=0;x<_xs;x++)
{
r=typ[y][x];
c=(r>> _cover_shift)& _cover_mask;
t=(r>>_terrain_shift)&_terrain_mask;
f=(r>> _flora_shift)& _flora_mask;
r=_terrain[t];
if (c) r= _cover[c];
if (c==_cover_water)
{
xx=256-((ter[y][x]<<7)/h0);
yy=int(r>>16)&255; yy=(yy*xx)>>8; r=(r&0x0000FFFF)|(yy<<16);
yy=int(r>> 8)&255; yy=(yy*xx)>>8; r=(r&0x00FF00FF)|(yy<< 8);
yy=int(r )&255; yy=(yy*xx)>>8; r=(r&0x00FFFF00)|(yy );
}
if (f){ if (c) r|=_flora[f]; else r=_flora[f]; };
pic.p[y][x+_xs].dd=r;
}
*/
// free ter[][],typ[][]
for (y=0;y<=mys;y++) delete[] ter[y]; delete[] ter; ter=NULL;
for (y=0;y<=mys;y++) delete[] typ[y]; delete[] typ; typ=NULL;
}
Выход с текущими настройками выглядит так:
[Примечания]
Этот подход обычно создает только один большой холм на острове. (Inland генерируется OK) Если вы хотите больше из них, вы можете создать больше карт местности и усреднить их вместе.
Вместо этого я выполняю следующие действия: я установил среднюю точку на максимальную высоту и проигнорировал первый прогон алмаза. После первого квадратного прохода я вернул среднюю точку к некоторому случайному значению. Это добавляет возможность более центральных холмов, а затем только одного. Используя этот подход и добавляя освещение (окружающее + нормальное затенение) для предварительного просмотра и небольшой настройки размера пикселя (35m
), я получил этот результат:
В редких случаях это может генерировать внутреннюю карту (если центральная область слишком мала). Чтобы справиться с ней, вы можете сканировать углы для watter. Если есть земля, сгенерируйте ее снова или добавьте некоторое смещение для случайности в центральных точках на первом проходе.
Вы можете играть с кодом, например, добавить реки:
От документация Django :
MAYBECHOICE = (
('y', 'Yes'),
('n', 'No'),
('u', 'Unknown'),
)
И Вы определяете charfield в своей модели:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
можно сделать то же с целочисленными полями, если Вам не нравится иметь буквы в Вашем дб.
В этом случае, перепишите свой выбор:
MAYBECHOICE = (
(0, 'Yes'),
(1, 'No'),
(2, 'Unknown'),
)
Используя choices
параметр не будет использовать ПЕРЕЧИСЛИМЫЙ тип дб; это просто создаст VARCHAR или ЦЕЛОЕ ЧИСЛО, в зависимости от того, используете ли Вы choices
с CharField или IntegerField. Обычно это очень хорошо. Если для Вас важно, чтобы ПЕРЕЧИСЛИМЫЙ тип использовался на уровне базы данных, у Вас есть три опции:
С любой из этих опций, это была бы Ваша обязанность иметь дело с последствиями для мобильности перекрестной базы данных. В опции 2 Вы могли использовать database-backend-specific пользовательский SQL, чтобы гарантировать, что Ваш ALTER TABLE только выполняется на MySQL. В опции 3 Ваш db_type метод должен был бы проверить механизм базы данных и установить тип столбца дб на тип, который на самом деле существует в той базе данных.
ОБНОВЛЕНИЕ : Так как платформа миграций была добавлена в Django 1.7, опции 1 и 2 выше являются совершенно устаревшими. Опция 3 всегда была наилучшим вариантом так или иначе. Новая версия опций 1/2 включила бы сложную пользовательскую миграцию с помощью SeparateDatabaseAndState
- но действительно Вы хотите опцию 3.
Если Вы действительно хотите использовать свой тип баз данных ENUM:
Удачи!
from django.db import models
class EnumField(models.Field):
"""
A field class that maps to MySQL's ENUM type.
Usage:
class Card(models.Model):
suit = EnumField(values=('Clubs', 'Diamonds', 'Spades', 'Hearts'))
c = Card()
c.suit = 'Clubs'
c.save()
"""
def __init__(self, *args, **kwargs):
self.values = kwargs.pop('values')
kwargs['choices'] = [(v, v) for v in self.values]
kwargs['default'] = self.values[0]
super(EnumField, self).__init__(*args, **kwargs)
def db_type(self):
return "enum({0})".format( ','.join("'%s'" % v for v in self.values) )