Pixi.js: игра «жизнь»
Продолжаю на досуге изучать 2D движек для работы с графикой на javascript. Решил написать игру «жизнь» со следующими правилами:
У каждого жителя есть возраст,пол и профессия.
- Жители двигаются по всему полю хаотично
- При достижении возраста 120 лет житель умирает
- Если два жителя сталкиваются между собой и они разного пола от 18 до 50 лет, тогда они рожают ребенка
Сначала определим объекты карты:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// Люди 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; } } |
Зададим переменные для игры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/** * Профессии, они же модели поведения * @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; |
Напишем процедуры генерации зданий и жителей:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
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]); } } } |
Создадим сцену и запустим время:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
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(); } } } }; }; } }); |