@@ -0,0 +1 @@ | |||
К |
@@ -0,0 +1,16 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |||
<title>КОЛОБОК 0.1.0</title> | |||
<script src="https://cdn.jsdelivr.net/npm/phaser@3.54.0/dist/phaser.min.js"></script> | |||
</head> | |||
<body> | |||
<script src="игра/001.мир.js"></script> | |||
<script src="игра/011.phaser.js"></script> | |||
<script src="игра/109.управление.js"></script> | |||
<script src="игра/110.колобок.js"></script> | |||
<script src="игра/111.изба.js"></script> | |||
<script src="игра/499.пуск.js"></script> | |||
</body> | |||
</html> |
@@ -0,0 +1,190 @@ | |||
/* | |||
* | |||
* Реализация шаблона "издатель-подписчик" | |||
* | |||
*/ | |||
function Уведомитель() | |||
{ | |||
function Подписка(id, отклик, уведомитель) | |||
{ | |||
this.id = id; | |||
this.отклик = отклик; | |||
this.уведомитель = уведомитель; | |||
}; | |||
this.уведомить = function() | |||
{ | |||
// Попутно собираем подписки без отклика. | |||
var безотклика = []; | |||
for (var номер in this.подписки) | |||
{ | |||
var подписка = this.подписки[номер]; | |||
if (подписка.отклик) | |||
{ | |||
подписка.отклик(); | |||
} | |||
else | |||
{ | |||
безотклика.push(подписка); | |||
} | |||
} | |||
// И удаляем их. | |||
if (безотклика.length) | |||
{ | |||
this._удалитьПодпискиБезОтклика(безотклика); | |||
} | |||
}; | |||
this.подписать = function(отклик) | |||
{ | |||
var id = this._сгенерироватьUUID(); | |||
var подписка = new Подписка(id, отклик, this); | |||
this.подписки.push(подписка); | |||
return подписка; | |||
}; | |||
this.отписать = function(подписка) | |||
{ | |||
подписка.отклик = null; | |||
}; | |||
this._удалитьПодпискиБезОтклика = function(удалить) | |||
{ | |||
var подписка = удалить.shift() | |||
while (подписка) | |||
{ | |||
var индекс = this.подписки.indexOf(подписка); | |||
if (индекс !== -1) | |||
{ | |||
this.подписки.splice(индекс, 1); | |||
} | |||
var подписка = удалить.shift() | |||
} | |||
}; | |||
this._сгенерироватьUUID = function() | |||
{ | |||
// https://stackoverflow.com/a/2117523 | |||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( | |||
/[xy]/g, | |||
function(c) | |||
{ | |||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); | |||
return v.toString(16); | |||
} | |||
); | |||
}; | |||
// Конструктор. | |||
this.подписки = []; | |||
} | |||
/* | |||
* | |||
* Связь событий и реакций в виде последовательности (череды) | |||
* | |||
*/ | |||
function Мир() | |||
{ | |||
// Разобрать события и реакции, выраженные в тексте. | |||
this.разобрать = function(череда) | |||
{ | |||
var соответствия = this._событияРеакции(череда); | |||
for (var событие in соответствия) | |||
{ | |||
if (!(событие in this.события)) | |||
{ | |||
this.события[событие] = new Уведомитель(); | |||
} | |||
var реакции = соответствия[событие]; | |||
for (var номер in реакции) | |||
{ | |||
const реакция = реакции[номер]; | |||
const название = this._имяФункцииИзРеакции(реакция); | |||
const функция = eval(название); | |||
var тут = this; | |||
this.события[событие].подписать(function(){ | |||
функция(тут); | |||
}); | |||
} | |||
} | |||
}; | |||
// Уведомить о событии при его наличии. | |||
this.уведомить = function(событие) | |||
{ | |||
if (событие in this.события) | |||
{ | |||
this.события[событие].уведомить(); | |||
} | |||
}; | |||
// Разобрать текст с событиями и реакциями, вернуть словарь их соответствия. | |||
this._событияРеакции = function(текст) | |||
{ | |||
var соответствие = {}; | |||
var элементы = текст.split("\n"); | |||
var имяСобытия = null; | |||
for (var номер in элементы) | |||
{ | |||
var элемент = элементы[номер]; | |||
// Пропускаем комментарии. | |||
if (элемент.charAt(0) == "#") | |||
{ | |||
continue; | |||
} | |||
var имя = элемент.trim(); | |||
// Пропускаем пустые строки. | |||
if (!имя.length) | |||
{ | |||
continue; | |||
} | |||
// Событие. | |||
if (имя == элемент) | |||
{ | |||
if (!(имя in соответствие)) | |||
{ | |||
имяСобытия = имя; | |||
соответствие[имя] = []; | |||
} | |||
} | |||
// Реакция. | |||
else | |||
{ | |||
соответствие[имяСобытия].push(имя); | |||
} | |||
} | |||
return соответствие; | |||
}; | |||
// Преобразовать имя реакции в название функции. | |||
this._имяФункцииИзРеакции = function(реакция) | |||
{ | |||
var имя = ""; | |||
var части = реакция.split(" "); | |||
for (var номер in части) | |||
{ | |||
var часть = части[номер]; | |||
имя += часть.charAt(0).toUpperCase() + часть.slice(1); | |||
} | |||
return имя; | |||
}; | |||
// Конструктор. | |||
this.события = {}; | |||
} | |||
/* | |||
* | |||
* Создание глобального мира. | |||
* | |||
*/ | |||
мир = new Мир(); |
@@ -0,0 +1,88 @@ | |||
/* | |||
* | |||
* Реакции | |||
* | |||
*/ | |||
ПодготовитьОбластьОтрисовки = мир => | |||
{ | |||
var родитель = document.createElement("div"); | |||
родитель.style = ` | |||
position: absolute; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
top: 0; | |||
display: flex; | |||
align-items: center; | |||
`; | |||
var ребёнок = document.createElement("div"); | |||
ребёнок.style = `margin: auto;`; | |||
родитель.appendChild(ребёнок); | |||
document.body.appendChild(родитель); | |||
мир.областьОтрисовки = ребёнок; | |||
}; | |||
// // // // | |||
НастроитьИгру = мир => | |||
{ | |||
мир.настройки = { | |||
type: Phaser.AUTO, | |||
width: 800, | |||
height: 600, | |||
parent: мир.областьОтрисовки, | |||
physics: { | |||
default: "arcade", | |||
arcade: { | |||
gravity: { | |||
y: 1300, | |||
}, | |||
debug: false, | |||
}, | |||
}, | |||
scene: { | |||
preload: function() { | |||
мир.сцена = this; | |||
мир.уведомить("загрузить ресурсы сцены"); | |||
}, | |||
create: function() { | |||
мир.сцена = this; | |||
мир.уведомить("создать сцену"); | |||
}, | |||
update: function() { | |||
мир.сцена = this; | |||
мир.уведомить("обновить сцену"); | |||
}, | |||
}, | |||
}; | |||
}; | |||
// // // // | |||
ЗапуститьИгру = мир => | |||
{ | |||
мир.игра = new Phaser.Game(мир.настройки); | |||
}; | |||
/* | |||
* | |||
* Последовательность | |||
* | |||
*/ | |||
мир.разобрать(` | |||
пуск | |||
подготовить область отрисовки | |||
настроить игру | |||
запустить игру | |||
`); | |||
@@ -0,0 +1,104 @@ | |||
/* | |||
* | |||
* Реакции | |||
* | |||
*/ | |||
НастроитьМышь = мир => | |||
{ | |||
мир.сцена.input.mouse.disableContextMenu(); | |||
}; | |||
// // // // | |||
УведомитьОМыши = мир => | |||
{ | |||
var мышь = мир.сцена.input.activePointer; | |||
const новое = { | |||
"x": мышь.x, | |||
"y": мышь.y, | |||
"нажата": мышь.isDown, | |||
}; | |||
if ( | |||
мир.мышь && | |||
мир.мышь.x == новое.x && | |||
мир.мышь.y == новое.y && | |||
мир.мышь.нажата == новое.нажата | |||
) { | |||
// Ничего не делаем. | |||
} | |||
else | |||
{ | |||
мир.мышь = новое; | |||
мир.уведомить("изменили мышь"); | |||
} | |||
}; | |||
// // // // | |||
УведомитьОбУправлении = мир => | |||
{ | |||
const камера = мир.сцена.cameras.main; | |||
const ширина = камера.width; | |||
const высота = камера.height; | |||
var новое = { | |||
"направление": 0, // Центр. | |||
"прыжок": false, | |||
}; | |||
новое.прыжок = (мир.мышь.y < высота * 0.45); | |||
if (мир.мышь.x < ширина * 0.45) | |||
{ | |||
новое.направление = -1; // Лево. | |||
} | |||
else if (мир.мышь.x > ширина * 0.55) | |||
{ | |||
новое.направление = 1; // Право. | |||
} | |||
// Начало движения. | |||
if (!мир.управление && мир.мышь.нажата) | |||
{ | |||
мир.управление = новое; | |||
мир.уведомить("изменили управление"); | |||
} | |||
// Окончание движения. | |||
else if (мир.управление && !мир.мышь.нажата) | |||
{ | |||
мир.управление = null; | |||
мир.уведомить("изменили управление"); | |||
} | |||
// Продолжение движения. | |||
else if ( | |||
(мир.управление && мир.мышь.нажата) && | |||
( | |||
(мир.управление.направление != новое.направление) || | |||
(мир.управление.прыжок != новое.прыжок) | |||
) | |||
) { | |||
мир.управление = новое; | |||
мир.уведомить("изменили управление"); | |||
} | |||
}; | |||
/* | |||
* | |||
* Последовательность | |||
* | |||
*/ | |||
мир.разобрать(` | |||
создать сцену | |||
настроить мышь | |||
обновить сцену | |||
уведомить о мыши | |||
изменили мышь | |||
уведомить об управлении | |||
`); | |||
@@ -0,0 +1,95 @@ | |||
/* | |||
* | |||
* Реакции | |||
* | |||
*/ | |||
ЗагрузитьРесурсыКолобка= мир => | |||
{ | |||
мир.сцена.load.image("колобок", "ресурсы/колобок/колобок_неподвижен.png"); | |||
}; | |||
// // // // | |||
СоздатьКаркас = мир => | |||
{ | |||
мир.каркас = мир.сцена.physics.add.staticGroup(); | |||
}; | |||
// // // // | |||
СоздатьКолобка = мир => | |||
{ | |||
мир.колобок = мир.сцена.physics.add.sprite(100, 100, "колобок"); | |||
мир.колобок.depth = 10; | |||
//мир.колобок.setBounce(0.1); | |||
// Следить за колобком камерой. | |||
const камера = мир.сцена.cameras.main; | |||
камера.startFollow(мир.колобок, false, 0.1, 0.1); | |||
}; | |||
// // // // | |||
СоздатьСтолкновениеКолобкаСКаркасом = мир => | |||
{ | |||
мир.сцена.physics.add.collider(мир.колобок, мир.каркас); | |||
}; | |||
// // // // | |||
ПереместитьКолобка = мир => | |||
{ | |||
if (!мир.управление) | |||
{ | |||
мир.колобок.setVelocityX(0); | |||
return; | |||
} | |||
var x = 0; | |||
if (мир.управление.направление == -1) | |||
{ | |||
x = -200; | |||
} | |||
else if (мир.управление.направление == 1) | |||
{ | |||
x = 200; | |||
} | |||
мир.колобок.setVelocityX(x); | |||
if (мир.управление.прыжок && мир.колобок.body.touching.down) | |||
{ | |||
мир.колобок.setVelocityY(-500); | |||
} | |||
}; | |||
/* | |||
* | |||
* Последовательность | |||
* | |||
*/ | |||
мир.разобрать(` | |||
загрузить ресурсы сцены | |||
загрузить ресурсы колобка | |||
создать сцену | |||
создать каркас | |||
создать колобка | |||
создать столкновение колобка с каркасом | |||
изменили управление | |||
переместить колобка | |||
`); | |||
@@ -0,0 +1,57 @@ | |||
/* | |||
* | |||
* Реакции | |||
* | |||
*/ | |||
ЗагрузитьРесурсыИзбы = мир => | |||
{ | |||
мир.сцена.load.image("изба", "ресурсы/сцены/изба.jpg"); | |||
мир.сцена.load.image("основа", "ресурсы/отладка/основа.jpg"); | |||
}; | |||
// // // // | |||
СоздатьИзбу = мир => | |||
{ | |||
const камера = мир.сцена.cameras.main; | |||
камера.backgroundColor = Phaser.Display.Color.HexStringToColor("#fbfbfb"); | |||
мир.сцена.add.image(0, 0, "изба").setOrigin(0, 0); | |||
var полСлева = мир.каркас.create(0, 585, "основа").setOrigin(0, 0); | |||
полСлева.setScale(43, 1).refreshBody(); | |||
полСлева.setVisible(false); | |||
var полСправа = мир.каркас.create(510, 585, "основа").setOrigin(0, 0); | |||
полСправа.setScale(200, 1).refreshBody(); | |||
полСправа.setVisible(false); | |||
мир.полКрышки = мир.каркас.create(300, 585, "основа").setOrigin(0, 0); | |||
мир.полКрышки.setScale(30, 1).refreshBody(); | |||
мир.полКрышки.setVisible(false); | |||
var стенаСлева = мир.каркас.create(0, 385, "основа"); | |||
стенаСлева.setScale(1, 50).refreshBody(); | |||
стенаСлева.setVisible(false); | |||
var стенаСправа = мир.каркас.create(2000, 385, "основа"); | |||
стенаСправа.setScale(1, 50).refreshBody(); | |||
стенаСправа.setVisible(false); | |||
}; | |||
/* | |||
* | |||
* Последовательность | |||
* | |||
*/ | |||
мир.разобрать(` | |||
загрузить ресурсы сцены | |||
загрузить ресурсы избы | |||
создать сцену | |||
создать избу | |||
`); | |||
@@ -0,0 +1 @@ | |||
мир.уведомить("пуск"); |