Продолжаю на досуге изучать 2D движек для работы с графикой на javascript. Решил написать игру «жизнь» со следующими правилами:
У каждого жителя есть возраст,пол и профессия.
- Жители двигаются по всему полю хаотично
- При достижении возраста 120 лет житель умирает
- Если два жителя сталкиваются между собой и они разного пола от 18 до 50 лет, тогда они рожают ребенка
Сначала определим объекты карты:
// Люди
class People {
/**
* Конструктор
* @param {type} gender - пол
* @param {type} age - возраст
* @param {type} health - здоровье
* @param {type} profession - профессия из map professions
* @returns {People}
*/
constructor(gender,age,health,profession) {
this.gender=gender; // пол 0/1 м/ж
this.age=age; // возраст
this.health=health; // здоровье
this.profession=profession; // профессия
}
}
class Citizen {
constructor(name,pers,color) {
this.name=name;
this.pers=pers; // изначальныый процент от общего колиества
this.color=color;
}
}
// Зданиям
class Building {
/**
* Конструктор
* @param {type} name название
* @param {type} cnt количество зданий на 1000 жителей
* @returns {Building}
*/
constructor(name,color,cnt) {
this.name=name;
this.color=color;
this.cnt=cnt;
}
/**
* Установить начальные координаты
* @param {type} x
* @param {type} y
* @param {type} height
* @param {type} width
* @returns {undefined}
*/
setSpawn(x,y,height,width){
this.x=x;
this.y=y;
this.height=height;
this.width=width;
}
}
Зададим переменные для игры:
/**
* Профессии, они же модели поведения
* @type Map
*/
professions=new Map(
[
// индекс, название, % от всего населения
[1,new Citizen("Детсадовец",10,0xFFFF0B)], // ночует дома, днём ходит в детский сад
[2,new Citizen("Школьник",10,0xAA0000)], // ночует дома, днем до 13:00 в школе, потом идёт домой через магазин
[3,new Citizen("Студент",10,0xFFFFFF)], // ночует дома, днем до 17:00 в институте, потом идёт домой через магазин
[4,new Citizen("Продавец",20,0xFF0000)], // ночует дома, днём в магазине, потом идёт домой через магазин
[5,new Citizen("Офисный клерк",20,0xFF3300)], // ночует дома, днём в офисе, потом идёт домой через магазин
[6,new Citizen("Рабочий",20,0x0000FF)], // ночует дома, днём на заводе, потом идёт домой через магазин
[7,new Citizen("Пенсионер",10,0xDE3249)] // ночует дома, днём шляется по магазинам или по улицам
]
);
buldings_types=new Map(
[
[1,new Building("Жилище",0xDE3249,10)], // индекс, название, количество на 1000 жителей
[2,new Building("Школа",0xAA0000,1)],
[3,new Building("Университет",0xFFFFFF,1)],
[4,new Building("Магазин",0xFF0000,2)],
[5,new Building("Офис",0xFF3300,3)],
[6,new Building("Завод",0x0000FF,1)],
[7,new Building("Детсад",0xFFFF0B,1)]
]
);
// ну пусть размеры пока в экран, потом можно поиграться
var scrollWidth=GetScrollWidth();
var screen_width = window.innerWidth-4; //получаем ширину экрана
var screen_height = window.innerHeight-4; // получаем высоту экрана
residents_count=1000; // начальное количество жителей
residents=[]; // массив жителей
buldings=[]; // массив зданий
cur_seconds=0; // текущая секунда
cur_minuts=0; // текущая минута
cur_hour=0; // текущая час
cur_day=0; // текущая день
cur_month=0; // текущая месяц
cur_year=0; // текущая год
max_FPS=60;
Напишем процедуры генерации зданий и жителей:
function GenerateNewCitizen(){
x=randomIntFromInterval(1,screen_width-width);
y=randomIntFromInterval(1,screen_height-height);
resident=new People(randomIntFromInterval(0,1),1,randomIntFromInterval(1,100),professions.get(1));
graphics = new PIXI.Graphics();
graphics.beginFill(professions.get(1).color);
graphics.lineStyle(2, professions.get(1).color, 1);
graphics.beginFill(professions.get(1).color, 1);
graphics.drawCircle(0,0, 1);
graphics.position.set(x, y);
graphics.direction=randomIntFromInterval(0,360);
graphics.endFill();
graphics.resident=resident;
residents.push(graphics);
app.stage.addChild(residents[residents.length-1]);
}
/**
* Начальная генерация зданий
* @returns {undefined}
*/
function InitGenerateBuilding(){
for (let i=1;i<= buldings_types.size;i++){
cnt=buldings_types.get(i).cnt/1000*residents_count;
yet_cnt=1;
while (yet_cnt<=cnt){
// случайным образом генерируем координаты и размеры здания
width=randomIntFromInterval(20,60);
height=randomIntFromInterval(20,60);
x=randomIntFromInterval(1,screen_width-width);
y=randomIntFromInterval(1,screen_height-height);
// если наложений нет, то добавляю здание в массив
if (CheckingOverlaysRect(x,y,width,height,buldings)==false){
console.log("Добавляю "+buldings_types.get(i).name+" №"+yet_cnt);
graphics = new PIXI.Graphics();
graphics.lineStyle(2, buldings_types.get(i).color, 1);
graphics.drawRect(x, y, width, height);
buldings.push(graphics);
app.stage.addChild(buldings[buldings.length-1]);
FPSText = new PIXI.Text(buldings_types.get(i).name,new PIXI.TextStyle({fontFamily: 'Arial',fontSize: 11,}));
FPSText.x = x;
FPSText.y = y;
app.stage.addChild(FPSText);
yet_cnt++;
};
}
};
}
/**
* Проверка наложения квадратов друг на друга
* @param {type} x
* @param {type} y
* @param {type} width
* @param {type} height
* @param {type} rects
* @returns {res|Boolean}
*/
function CheckingOverlaysRect(x,y,width,height,rects){
res=false;
for (let bi=1;bi<= rects.length;bi++){
rect_height=rects[bi-1].getBounds().height;
rect_width=rects[bi-1].getBounds().width;
rect_x=rects[bi-1].getBounds().x;
rect_y=rects[bi-1].getBounds().y;
//console.log(rect_height,rect_width,rect_x,rect_y);
if ((x>=rect_x)&(x<=rect_x+rect_width)){res=true;};
if ((y>=rect_y)&(y<=rect_y+rect_height)){res=true;};
if ((x+width>=rect_x)&(x+width<=rect_x+rect_width)){res=true;};
if ((y+height>=rect_y)&(y+height<=rect_y+rect_height)){res=true;};
};
return res;
}
/**
* Начальная генерация жителей
* @returns {undefined}
*/
function InitGeneratePeoples(){
for (let i=1;i<= professions.size;i++){ //перебираем все профессии
console.log("-генерирую "+professions.get(i).name);
for (let cnt=1;cnt<= professions.get(i).pers*residents_count/100;cnt++){
x=randomIntFromInterval(1,screen_width-width);
y=randomIntFromInterval(1,screen_height-height);
resident=new People(randomIntFromInterval(0,1),randomIntFromInterval(1,100),randomIntFromInterval(1,100),professions.get(i));
graphics = new PIXI.Graphics();
graphics.beginFill(professions.get(i).color);
graphics.lineStyle(2, professions.get(i).color, 1);
graphics.beginFill(professions.get(i).color, 1);
graphics.drawCircle(0,0, 1);
graphics.position.set(x, y);
graphics.direction=randomIntFromInterval(0,360);
graphics.endFill();
graphics.resident=resident;
residents.push(graphics);
app.stage.addChild(residents[residents.length-1]);
}
}
}
Создадим сцену и запустим время:
const app = new PIXI.Application({
width:screen_width,
height:screen_height,
background: '#1099bb' ,
antialias: true
});
document.body.appendChild(app.view);
app.stage.hitArea = app.screen;
InitGenerateBuilding(); // генерируем здания
InitGeneratePeoples(); // генерируем жителей
let ticker = PIXI.Ticker.shared;
ticker.autoStart = false;
ticker.maxFPS=max_FPS;
ticker.start();
const style = new PIXI.TextStyle({
fontSize: 20
});
FPSText = new PIXI.Text('',style);
FPSText.x = 10;
FPSText.y = 10;
app.stage.addChild(FPSText);
StatText = new PIXI.Text(residents.length,style);
StatText.x = 10;
StatText.y = 30;
app.stage.addChild(StatText);
// пусть жители оживут!
ticker.add(function (time) {
cur_seconds++;
if (cur_seconds==61){
cur_seconds=0;cur_minuts++;
// прибавляем всем жителям по году жизни..
for (let i = 0; i < residents.length; i++) {
residents[i].resident.age++;
if (residents[i].resident.age==120){
residents.pop(residents[i]); // в 120 лет жизненный путь завершается..
};
};
};
if (cur_minuts==61){cur_minuts=0;cur_hour++;};
if (cur_hour==25){cur_hour=0;cur_day++;};
if (cur_day==366){cur_day=0;cur_year++;};
FPSText.text="С начала эпохи прошел: "+cur_minuts+" год"; // выводим FPS
StatText.text="Жителей:"+residents.length;
// перебираю каждого человека и двигаем его
for (let i = 0; i < residents.length; i++) {
step=false;
while (step==false){
pre_y=residents[i].position.y+Math.sin(residents[i].direction);
pre_x=residents[i].position.x+Math.cos(residents[i].direction);
if ((pre_x>=0)&& (pre_x<=screen_width) && (pre_y>=0) && (pre_y<=screen_height)) {
step=true;
} else {
residents[i].direction=randomIntFromInterval(0,360);
};
};
residents[i].position.y=residents[i].position.y+Math.sin(residents[i].direction);
residents[i].position.x=residents[i].position.x+Math.cos(residents[i].direction);
// проверим, есть ли совпадение точек?
// если возраст от 18..50
// если полы противоположные
// то считаем что это "лябовь" и размножаемся
for (let j = 0; j < residents.length; j++) {
if (Math.round(residents[i].position.y)==Math.round(residents[j].position.y) &&
Math.round(residents[i].position.x)==Math.round(residents[j].position.x) && i!=j){
if (residents[i].resident.age>=18 && residents[i].resident.age<=50){
if (residents[j].resident.age>=18 && residents[j].resident.age<=50){
if (residents[j].resident.gender==0 && residents[i].resident.gender==1){
console.log("-это лябофь!");
GenerateNewCitizen();
}
}
}
};
};
}
});