Создание игр без canvas: Matreshka.js

Добра всем хаброчитателям! В прошлой статье мы рассматривали способ создания карточных игр с помощью манипуляций с DOM, без использования canvas, на подобии HeartStone.

Сегодня мы продолжим эту тему, подключив к нашему делу полезнейшую в данном случае библиотеку Matreshka.js.image

ВведениеКратко напомню, к чему мы пришли в прошлый раз. Общение с сервером осуществляется по 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: '

  • ', // Как рендрится каждый элемент 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 = '' } }); 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: '

    ' +'
    ' +'
    ' + $b ('#icons #heart')[0].innerHTML + '
    ' +'
    ' + $b ('#icons #attack')[0].innerHTML + '
    ' +'
    ' + $b ('#icons #diamond')[0].innerHTML + '
    ' +'
    ' +'
    ', 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(){}).Пример (косяков море, цель — показать технологию) Github

    © Habrahabr.ru