Добра всем хаброчитателям!
В прошлой статье мы рассматривали способ создания карточных игр с помощью манипуляций с DOM, без использования canvas, на подобии HeartStone.
Сегодня мы продолжим эту тему, подключив к нашему делу полезнейшую в данном случае библиотеку Matreshka.js.
Кратко напомню, к чему мы пришли в прошлый раз. Общение с сервером осуществляется по WebSocket'ам, передаем JSON объекты вида: { «method»: метод, «args»: аргументы}.
Серверная сторона реализована с помощью php, скрипт запустили как демона (бесконечный цикл) в поток null.
Клиент принимает такого же вида JSON строки, вызываем методы объекта Actions (подробнее в прошлой статье).
Самое первое, где можно внедрить матрешку — это меню со списком игроков. Вообще списки в матрешке сделаны очень удобно, на мой взгляд.
Итак, задача: мы получаем список игроков в JSON, когда подключаемся, необходимо их отрисовать и назначить события.
Над дизайном не заморачиваемся.
Списки в матрешке состоят из модели и класса (ну и объекта класса).
В нашем случае
Давайте разберемся, что же тут произошло, что значит «биндим игрока»?
Для матрешки конструкция
означает, что мы связываем свойство name, доступное потом как свойство объекта (Obj.name) и некоторую html-сущность, в данном случае сущность с селектором ':sandbox .name', где sandbox — песочница, то есть тот самый элемент, который мы только что отрендрили. Напомню, что мы это событие рендера одного конкретного элемента списка.
В качестве третьего аргумента передает тип зависимости. То есть то как они (свойство и сущность) между собой связаны.
В матрешке есть стандартны набор биндеров, и в данном случае Matreshka.binders.innerHTML() ставит в зависимость значение свойства и содержимое html-контейнера ':sandbox .name'.
Какая меж ними конкретно зависимость? Самая очевидная: изменяем свойство объекта — изменяется содержимое html контейнера.
Основы модели разобрали, идем дальше к классу
В классе стоит заострить внимание на двух вещах, хоть и весьма несложных. Свойство itemRenderer показывает, как будет отрисовываться каждый элемент списка. В приведенном примере /> и есть :sandbox, от которого отсчитываем прочие селекотры.
Указание
говорит о том, что все элементы списка будут отрисовываться внутри контейнера '#players'.
Когда игроки соединились и начали игру, что мы имеем (чисто логически):
Осталось реализовать эти списки с помощью матрешки и задать им некоторые события.
Аналогичный список, не будем повторяться, рассмотрим, как здесь применены бинды.
Как мы рассматривали выше, эти строки связывают содержимое html узла и свойства объекта.
Связав их вышеуказанным способом мы легко можем создать карту, просто сделав push в наш список:
Крайне просто. Но еще проще то, как мы можем менять эти свойства:
Не только задаст показатель здоровья равным нулю, но и отрисует это в html в нужном объекте.
Но и это еще не все, нам же надо отслеживать изменения здоровья, и если оно станет меньшим единицы, инициировать смерть юнита. Для этого свяжем свойство health объекта с самой картой:
Третий аргумент, как я говорил, задает логику связи. В данном примере логика следующая:
Когда поменялось (установилось) значение health объекта, запускаем функцию
This указывает на карту целиком, на песочницу (второй аргумент: ':sandbox').
В сложных приложениях, где действительно нужно двусторонее и множественное связывание, матрешка великолепно облегчает жизнь и создает комфорт при разработке.
Ведь связывать можно как угодно, в одном случае ставим обработку только на принимаемое значание (setValue), в другом на изменение свойства по событию (on: 'click', getValue: function(){}).
В прошлой статье мы рассматривали способ создания карточных игр с помощью манипуляций с DOM, без использования canvas, на подобии HeartStone.
Сегодня мы продолжим эту тему, подключив к нашему делу полезнейшую в данном случае библиотеку Matreshka.js.
Введение
Кратко напомню, к чему мы пришли в прошлый раз. Общение с сервером осуществляется по WebSocket'ам, передаем JSON объекты вида: { «method»: метод, «args»: аргументы}.
Серверная сторона реализована с помощью php, скрипт запустили как демона (бесконечный цикл) в поток null.
Клиент принимает такого же вида JSON строки, вызываем методы объекта Actions (подробнее в прошлой статье).
socket.onmessage
socket.onmessage = function (e){
if (typeof e.data === "string"){
var request = JSON.parse(e.data);
console.log('Response: ' + request.function);
Actions[request.function](request.args);
};
}
Начинаем внедрять матрешку
Самое первое, где можно внедрить матрешку — это меню со списком игроков. Вообще списки в матрешке сделаны очень удобно, на мой взгляд.
Итак, задача: мы получаем список игроков в JSON, когда подключаемся, необходимо их отрисовать и назначить события.
Над дизайном не заморачиваемся.
Списки в матрешке состоят из модели и класса (ну и объекта класса).
В нашем случае
Модель списка
var listModel = Matreshka.Class({ // Модель списка
'extends': Matreshka.Object, // Наследуется от Matreshka.Object всегда
constructor: function(data){
this.jset(data);
this.on('render',function(){ // Что происходит после отрисовки
this.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML()); // Биндим имя игрока
this.bindNode('letsFight',':sandbox .fightButton'); // Биндим кнопку вызова на бой
this.on('click::letsFight',function(){
Actions.figthRequest(this.name);
});
});
}
});
Давайте разберемся, что же тут произошло, что значит «биндим игрока»?
Для матрешки конструкция
this.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML());
означает, что мы связываем свойство name, доступное потом как свойство объекта (Obj.name) и некоторую html-сущность, в данном случае сущность с селектором ':sandbox .name', где sandbox — песочница, то есть тот самый элемент, который мы только что отрендрили. Напомню, что мы это событие рендера одного конкретного элемента списка.
В качестве третьего аргумента передает тип зависимости. То есть то как они (свойство и сущность) между собой связаны.
В матрешке есть стандартны набор биндеров, и в данном случае Matreshka.binders.innerHTML() ставит в зависимость значение свойства и содержимое html-контейнера ':sandbox .name'.
Какая меж ними конкретно зависимость? Самая очевидная: изменяем свойство объекта — изменяется содержимое html контейнера.
Основы модели разобрали, идем дальше к классу
var listArray = Matreshka.Class({ // Класс списка
'extends': Matreshka.Array,
Model: listModel, // Наша модель
itemRenderer: '<li class="player"><span class="name"></span><span class="fightButton"></span></li>', // Как рендрится каждый элемент
constructor: function(){
this.bindNode('sandbox','#players'); // Засовываем в песочницу
}
});
В классе стоит заострить внимание на двух вещах, хоть и весьма несложных. Свойство itemRenderer показывает, как будет отрисовываться каждый элемент списка. В приведенном примере /> и есть :sandbox, от которого отсчитываем прочие селекотры.
Указание
constructor: function(){
this.bindNode('sandbox','#players'); // Засовываем в песочницу
}
говорит о том, что все элементы списка будут отрисовываться внутри контейнера '#players'.
Матрешка в режиме сражения
Когда игроки соединились и начали игру, что мы имеем (чисто логически):
- Список карт в моей руке
- Список карт в руке соперника
- Список моих карт на игровом поле
- Список карт противника на игровом поле
Осталось реализовать эти списки с помощью матрешки и задать им некоторые события.
Карты в моей руке
Карты в моей руке
var myCardsModel = Matreshka.Class({ // Модель списка
'extends': Matreshka.Object,
constructor: function(data){
this.jset(data);
this.on('render',function(){
this.bindNode('name',':sandbox .title',Matreshka.binders.innerHTML());
this.bindNode('attack',':sandbox .attack .value',Matreshka.binders.innerHTML());
this.bindNode('health',':sandbox .health .value',Matreshka.binders.innerHTML());
this.bindNode('mana',':sandbox .mana .value',Matreshka.binders.innerHTML());
this.bindNode('picture',':sandbox .picture',{
setValue: function(v){
this.innerHTML = '<img src="img/' + v + '">'
}
});
this.on('click::sandbox',function(){
myArenaCards.push(this);
myCards.splice(myCards.indexOf(this),1);
Actions.send('putCard',this.toJSON());
});
});
}
});
var myCardsArray = Matreshka.Class({ // Класс списка
'extends': Matreshka.Array,
Model: myCardsModel,
itemRenderer: '<div class="card">'
+'<div class="title"></div>'
+'<div class="health"><div class="svg">' + $b('#icons #heart')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="attack"><div class="svg">' + $b('#icons #attack')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="mana"><div class="svg">' + $b('#icons #diamond')[0].innerHTML + '</div><div class="value"></div></div>'
+'<div class="picture"></div>'
+'</div>',
constructor: function(){
this.bindNode('sandbox','#myhand'); // Засовываем в песочницу
}
});
var myCards = new myCardsArray; // Экземпляр класса списка
Аналогичный список, не будем повторяться, рассмотрим, как здесь применены бинды.
this.bindNode('name',':sandbox .title',Matreshka.binders.innerHTML());
this.bindNode('attack',':sandbox .attack .value',Matreshka.binders.innerHTML());
this.bindNode('health',':sandbox .health .value',Matreshka.binders.innerHTML());
this.bindNode('mana',':sandbox .mana .value',Matreshka.binders.innerHTML());
Как мы рассматривали выше, эти строки связывают содержимое html узла и свойства объекта.
Связав их вышеуказанным способом мы легко можем создать карту, просто сделав push в наш список:
var Actions = {
.........
cardToHand: function(card){
myCards.push({
name: card.name,
attack: card.attack,
health: card.health,
picture: card.picture,
mana: card.mana
});
}
.........
}
Крайне просто. Но еще проще то, как мы можем менять эти свойства:
this.health = 0;
Не только задаст показатель здоровья равным нулю, но и отрисует это в html в нужном объекте.
Но и это еще не все, нам же надо отслеживать изменения здоровья, и если оно станет меньшим единицы, инициировать смерть юнита. Для этого свяжем свойство health объекта с самой картой:
this.bindNode('health',':sandbox',{
setValue: function(v){
if (v < 1){
this.className += ' die';
var iot = myArenaCards.indexOf(this);
setTimeout(function(){
myArenaCards.splice(iot,1);
},2000);
};
}
});
Третий аргумент, как я говорил, задает логику связи. В данном примере логика следующая:
Когда поменялось (установилось) значение health объекта, запускаем функцию
function(v){
if (v < 1){
this.className += ' die';
var iot = myArenaCards.indexOf(this);
setTimeout(function(){
myArenaCards.splice(iot,1);
},2000);
};
}
This указывает на карту целиком, на песочницу (второй аргумент: ':sandbox').
Заключение
В сложных приложениях, где действительно нужно двусторонее и множественное связывание, матрешка великолепно облегчает жизнь и создает комфорт при разработке.
Ведь связывать можно как угодно, в одном случае ставим обработку только на принимаемое значание (setValue), в другом на изменение свойства по событию (on: 'click', getValue: function(){}).