LINUX.ORG.RU
ФорумTalks

Расчет зомби-апокалипсиса


0

2

Тут недавно всплывала тема про зомби, так что я решил смоделировать зомби-апокалипсис.

Исходное положение:

В круглой комнате находится 1 зомби и много людей. Все они бегают с постоянной скоростью. Скорость зомби и человека разная. Если зомби оказывается достаточно близок к человеку, то тот превращается в зомби.

Зомби притягивает к людям, людей отталкивает от зомби. Когда два одинаковых существа оказываются слишком близко, их тоже начинает отталкивать.

Стены моделируются как отталкивающая поверхность.

Результаты таковы: если стены отталкивают не очень сильно, то оставшиеся люди углубляются в них, отталкиваясь от большого количества зомби. А зомби не могут до них дотянуться, т.к. притяжение мало ввиду малого количества оставшихся людей.

Текущий код: http://pastebin.com/xYZy5L53

--------

При некоторых параметрах отталкивания, люди быстро разбегаются к стенам и зомби оказывается в центре в положении буриданова осла.

★★★★★

Последнее исправление: cvs-255 (всего исправлений: 1)

При некоторых параметрах отталкивания, люди быстро разбегаются к стенам и зомби оказывается в центре в положении буриданова осла.

Приделай видимую область для существ. Тогда зомби пойдет на тех людей, которых видит, даже если за спиной есть люди ближе. А люди могут не заметить зомби, который идет за их спиной.

vurdalak ★★★★★
()

Не забывай про рапторов, они ведь научились открывать двери!

Lighting ★★★★★
()
Ответ на: комментарий от vurdalak

Но получается интересная проблема, что человек, убегающий от зомби перестает его видеть и останавливается.

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от cvs-255

Оглядываться каждые N секунд. N тем меньше, чем больше человек напуган. Степень испуга зависит от того, насколько близко был виден зомби в последний раз.

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Странно оно работает. Все зомби дергаются кольцом диаметром в полокна, а 2 оставшихся человека бегают ближе к краям невпопад.

vurdalak ★★★★★
()
Ответ на: комментарий от vurdalak

Вот я чуть улучшил.

Теперь людям требуется отдых, и у людей и зомби ограничена область зрения

pastebin перегружен, так что тут:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <SDL/SDL.h>

typedef struct
{
	double x, y, ang;
	double tx, ty, tang;
	
	double tired, rest;
	int t, r;
	int z;
} per;

int n;
per *mass;
double rad;
double ra;
double vh, vz;

double tmin=10, nrest=2;

double anm_z = 3.141*0.6;
double anm_h = 3.141*0.9;


void zombie_field(int k, double *fx, double *fy)
{
	int i;
	double ffx, ffy;
	double l;
	double x = mass[k].x;
	double y = mass[k].y;
	double a = mass[k].ang;
	*fx = 0;
	*fy = 0;
	for (i = 0; i < n; i++)
	{
		if (mass[k].z == 0 && mass[i].z == 1)
		{
			double ang;
			ffx = mass[i].x - x;
			ffy = mass[i].y - y;
			l = sqrt(ffx*ffx + ffy*ffy);
			if (l > 1e-10)
			{
				ffx /= l;
				ffy /= l;
				
				ang = atan2(ffy, ffx);
				ang -= a;
				while (ang > 3.14159)
					ang -= 2*3.14159;
				while (ang < -3.14159)
					ang += 2*3.14159;
				if (ang > anm_h || ang < -anm_h)
				{
					ffx = 0;		
					ffy = 0;
				}
			}
			*fx -= ffx;
			*fy -= ffy;
		}
	}
}

void human_field(int k, double *fx, double *fy)
{
	int i;
	double l;
	double ffx, ffy;
	double x = mass[k].x;
	double y = mass[k].y;
	double a = mass[k].ang;
	*fx = 0;
	*fy = 0;
	for (i = 0; i < n; i++)
	{
		if (mass[i].z == 0 && mass[k].z == 1)
		{
			ffx = mass[i].x - x;
			ffy = mass[i].y - y;
			l = sqrt(ffx*ffx + ffy*ffy);

			if (l > 1e-6)
			{
				double ang;
				ffx /= l*l;
				ffy /= l*l;
			
				ang = atan2(ffy, ffx);
				ang -= a;
				while (ang > 3.14159)
					ang -= 2*3.14159;
				while (ang < -3.14159)
					ang += 2*3.14159;
				if (ang > anm_z || ang < -anm_z)
				{
					ffx = 0;		
					ffy = 0;
				}
			}
			*fx += ffx;
			*fy += ffy;
		}
	}
}

void space_field(int k, double *fx, double *fy)
{
	int i;
	double l;
	double ffx, ffy;
	double x = mass[k].x;
	double y = mass[k].y;
	*fx = 0;
	*fy = 0;
	for (i = 0; i < n; i++)
	{
		if (mass[i].z == mass[k].z)
		{
			ffx = mass[i].x - x;
			ffy = mass[i].y - y;
			l = sqrt(ffx*ffx + ffy*ffy);
			if (l > 1e-6)
			{
				ffx /= -l*l*l*l;
				ffy /= -l*l*l*l;
			}
			if (l > ra)
			{
				ffx = 0;
				ffy = 0;
			}
			*fx += ffx;
			*fy += ffy;
		}
	}
}

void extra_field(int k, double *fx, double *fy)
{
	int i;
	double l;
	double ffx, ffy;
	double x = mass[k].x;
	double y = mass[k].y;

	l = sqrt(x*x+y*y);
	if (l > rad)
	{
		*fx = -x/4;
		*fy = -y/4;
	}
	
		
}

void random_field(int k, double *fx, double *fy)
{
	double fm = mass[k].z?0:0.2;
	double f = (rand()%RAND_MAX)/((double)RAND_MAX) * fm;
	double an = (rand()%RAND_MAX)/((double)RAND_MAX) * 2*3.14159;
	*fx = f*cos(an);
	*fy = f*sin(an);
}

int if_victim(int k)
{
	int i;
	for (i = 0; i < n; i++)
	{
		if (mass[i].z == 1)
		{
			double x, y, zx, zy, l;
			x = mass[k].x;
			y = mass[k].y;
			zx = mass[i].x;
			zy = mass[i].y;
			l = sqrt((x-zx)*(x-zx) + (y-zy)*(y-zy));
			if (l < ra)
				return 1;
		}
	}
	return 0;
}

void go(double dt)
{
	int i;
	for (i = 0; i < n; i++)
	{
		double fx, fy, sx=0, sy=0, l;
		if (mass[i].z == 0)
		{
			zombie_field(i, &fx, &fy);
		}	
		else
		{
			human_field(i, &fx, &fy);
		}

		sx += fx;
		sy += fy;
		
		space_field(i, &fx, &fy);
		
		sx += fx;
		sy += fy;
		
		extra_field(i, &fx, &fy);
		
		sx += fx;
		sy += fy;

		random_field(i, &fx, &fy);
		
		sx += fx;
		sy += fy;


		l = sqrt(sx*sx+sy*sy);
		if (l > 1e-6)
		{
			sx /= l;
			sy /= l;
			mass[i].tang = atan2(sy,sx);
		}
		
		if (mass[i].z == 0)
		{
			sx *= vh*dt;
			sy *= vh*dt;
		}
		else
		{
			sx *= vz*dt;
			sy *= vz*dt;
		}

		if (mass[i].z == 0)
		{

			if (mass[i].t)
				mass[i].tired += dt;
			if (mass[i].r)
				mass[i].rest += dt;	
		
			if (mass[i].t && mass[i].tired > tmin)
			{
				mass[i].rest = 0;
				mass[i].t = 0;
				mass[i].r = 1;
			}
			if (mass[i].r && mass[i].rest > nrest)
			{
				mass[i].tired = 0;	
				mass[i].r = 0;
				mass[i].t = 1;
			}
		
		
		}
		if (mass[i].t || mass[i].z == 1)
		{
			mass[i].tx = mass[i].x + sx;
			mass[i].ty = mass[i].y + sy;
		}
		

	}
	for (i = 0; i < n; i++)
	{
		double l;
		mass[i].x = mass[i].tx;
		mass[i].y = mass[i].ty;
		mass[i].ang = mass[i].tang;
	}
	for (i = 0; i < n; i++)
	{
		if (mass[i].z == 0)
			mass[i].z = if_victim(i);
	}
}


Uint8 colour_h, colour_z;  

void draw_pixel(int X, int Y, Uint8 color, SDL_Surface *screen)
{
	Uint8 *pixmem;
	if (X < 10)
		X = 10;
	if (Y < 10)
		Y = 10;
	if (X >= 790)
		X = 789;
	if (Y >= 790)
		Y = 789;

	pixmem = (Uint8 *)screen->pixels + Y * screen->pitch + X * screen->format->BytesPerPixel;		
	*pixmem = color;
}

void draw_per(int i, SDL_Surface *screen)
{
	Uint8 color;
	int X, Y;
	double x, y;
	
	if (mass[i].z)
		color = colour_z;
	else	
		color = colour_h;
	X = mass[i].x/(rad*2)*400 + 400;
	Y = mass[i].y/(rad*2)*400 + 400;
	
	draw_pixel(X-1, Y-1, color, screen);
	draw_pixel(X-1, Y, color, screen);
	draw_pixel(X-1, Y+1, color, screen);
	draw_pixel(X, Y-1, color, screen);
	draw_pixel(X, Y, color, screen);
	draw_pixel(X, Y+1, color, screen);
	draw_pixel(X+1, Y-1, color, screen);
	draw_pixel(X+1, Y, color, screen);
	draw_pixel(X+1, Y+1, color, screen);
	
	int dx = 8*cos(mass[i].ang);
	int dy = 8*sin(mass[i].ang);	
	draw_pixel(X+dx, Y+dy, color, screen);
}

int main(void)
{
	double t, t1;
	double dt = 0.1;
	int i;
	int keypress = 0;
	SDL_Surface *screen;
	SDL_Event event;

	Uint8 *pixmem;
	
	srand(time(NULL));

	n = 20;
	rad = 10;
	ra = 0.5;

	vh = 1;
	vz = 0.7;

	t1 = 10;	

	SDL_Init(SDL_INIT_VIDEO);
	screen = SDL_SetVideoMode(800, 800,8,SDL_HWSURFACE|SDL_DOUBLEBUF);

	colour_h = SDL_MapRGB( screen->format, 0, 255, 0 );
	colour_z = SDL_MapRGB( screen->format, 255, 0, 0 );
	

	mass = malloc(sizeof(per)*n);

	for (i = 0; i < n; i++)
	{
		double l, a;
		
		l = (rand()%RAND_MAX)/((double)RAND_MAX)*rad;
		a = (rand()%RAND_MAX)/((double)RAND_MAX)*2*3.141;
		mass[i].x = l*cos(a);
		mass[i].y = l*sin(a);
		mass[i].ang = (rand()%RAND_MAX)/((double)RAND_MAX)*2*3.141;
		mass[i].z = 0;
		mass[i].t = 1;	
		mass[i].r = 0;
		mass[i].tired = 0;
		mass[i].rest = 0;
	}
	
	mass[0].z = 1;


	while(!keypress) 
	{
		go(dt);
	        usleep(dt*1e6);
		printf("t = %lf\n", t);

		SDL_FillRect(screen, NULL, 0x000000); 
		for (i = 0; i < n; i++)
		{
			draw_per(i, screen);
			printf("%s %lf %lf\n", mass[i].z?"z":"h", mass[i].x, mass[i].y);
		}

		SDL_Flip(screen); 

		while(SDL_PollEvent(&event)) 
        	{      
		        switch (event.type) 
              		{	
				case SDL_QUIT:
					keypress = 1;
					break;
				case SDL_KEYDOWN:
					keypress = 1;
					break;
			}
		}
 		t+= dt;
	}


	SDL_Quit();	
	free(mass);
	return 0;
}

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от cvs-255

Все равно в кольце застревают. Да еще и мотают головой туда-сюда с бешеной скоростью, уменьши этот таймаут в несколько раз. А еще пусть замедляются во время оглядывания назад, чтобы не повадно было.

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Да, и еще: люди почему-то двигаются синхронно. Зомби далеко - все стоят. Зомби подошли к одной из групп людей - двигаться начали все, даже те, кто вообще в другом конце карты.

vurdalak ★★★★★
()
Ответ на: комментарий от vurdalak

То, что они крутят головой - это потому, что у них к направлению движения рандом добавляется. Голова всего по движению направлена.

Сейчас я сделал так, что для зомбы стенка в полтора раза дальше от центра, картинка стала несколько интереснее

cvs-255 ★★★★★
() автор топика
Ответ на: комментарий от cvs-255

То, что они крутят головой - это потому, что у них к направлению движения рандом добавляется. Голова всего по движению направлена.

Плавности добавь. Хотя бы на 180 градусов резко не надо поворачивать.

Сейчас я сделал так, что для зомбы стенка в полтора раза дальше от центра, картинка стала несколько интереснее.

Стенка круглая или квадратная? Там какая-то НЁХ с границами, за кольцо рандомно или выходят, или нет.

И да, запили уже нормальный репозиторий вместо пасты.

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Ну вот, отдыхают синхронно. А должны по отдельности, ибо уровень усталости у каждого свой.

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Я бы сделал так.

Есть усталость. Увеличивается от бега, уменьшается от стояния. По мере увеличения усталости скорость падает. Отдыхает человек, если в некотором радиусе от него нет зомби.

Можно повышать предел усталости и максимальную скорость, если человек много бегал (что-то вроде прокачки).

Все аналогичное сделать зомби, т.к. они тоже люди, только кушать хотят :3

vurdalak ★★★★★
()
Ответ на: комментарий от vurdalak

Стенка круглая или квадратная?

круглая. Но когда рядом много зомби, человек может несколько углубляться в нее.

cvs-255 ★★★★★
() автор топика

В круглой комнате находится 1 зомби

если люди правильно мотивированы, они его просто порвут на кусочки. И бегать от зомби не понадобится. А в реальной жизни скорей всего он их всех постепенно покусает

Harald ★★★★★
()

сделай убежище на ограниченное число человек, чтобы люди убивали более слабых за место в нём. картина ещё немного оживится. и заюзай хоть константу для пи, это же ужас, что сейчас.

dmfd
()

Класс!
Можно еще сделать, чтобы область, в которой скопилось много людей, была в большем радиусе видна для зомби.

То есть радиус видимости человека увеличивается пропорционально количеству людей рядом с ним с небольшим коэффициентом.

user_2190
()

Да, можно еще добавить припасы для людей, чтобы они старались их достать, но в то же время убежать от зомби.

А еще лучше сделать универсальный класс существа со списком еды и списком тех, от кого нужно убегать. Тогда можно будет легко добавить еще животных, зомби-животных, роботов и прочие сущности.

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Ну выставь силу поля в 9000 раз сильнее текущего :3

vurdalak ★★★★★
()
Ответ на: комментарий от cvs-255

Хотя лучше все-таки сделать поддержку стен, чтобы можно было создавать лабиринты и здания для пущего веселья.

vurdalak ★★★★★
()

Не нужно, ведь есть Zombies! 7HRL на православном лиспе.

buddhist ★★★★★
()
Ответ на: комментарий от vurdalak

Хотя лучше все-таки сделать поддержку стен, чтобы можно было создавать лабиринты и здания для пущего веселья.

Поле можно любой формы задать

cvs-255 ★★★★★
() автор топика

У меня люди равномерно распределились вдоль стенки в виде буквы С и теперь бегают вдоль нее. А 5 зомбаков ходят по кругу за этим сектором. И так уже минут 15. У меня скоро голова кружиться начнет от наблюдений за этим хороводом!

der_looser ★★
()
Ответ на: комментарий от cvs-255

а человек в состоянии алкогольного опьянения сойдет за прототип зомби?

Harald ★★★★★
()
Ответ на: комментарий от cvs-255

При дефолтных настройках получается 4 зомби, а дальше они тупо бегают по кругу и никто никого не может догнать. Так не интересно.

KivApple ★★★★★
()
Ответ на: комментарий от cvs-255

Все теперь смотрят только в одну сторону, и бешенно бегают по кругу. При движении налево вообще все это напоминает какое-то движение сперматозоидов под микроскопом (и поле зрения круглое, ога). И превращение людей в зомби не логичное, просто при приблежении, даже не касании.

darth-nerd
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.