@@ -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 @@ | |||||
мир.уведомить("пуск"); |