📜 ⬆️ ⬇️

Creating games without canvas: Matreshka.js

Good to all the readers!

In the last article, we looked at how to create card games using DOM manipulations, without using canvas, like HeartStone.

Today we will continue this topic by connecting the most useful Matreshka.js library in our case to our case.
image

Introduction


')
Let me briefly remind you of what we came up with last time. Communication with the server is carried out via WebSockets, we transfer JSON objects of the form: {“method”: method, “args”: arguments}.
The server side is implemented using php, the script was launched as a daemon (infinite loop) into the null stream.
The client accepts the same kind of JSON string, we call the methods of the Actions object (in more detail in the last article).

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); }; } 



We start to introduce the nested doll


The very first place where you can embed a matryoshka is a menu with a list of players. In general, the lists in the matryoshka are made very conveniently, in my opinion.

So, the task: we get a list of players in JSON, when we connect, we need to draw them and assign events.
We do not bother with the design.

The lists in the nested doll consist of a model and a class (well, and an object of a class).
In our case
List model
 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); }); }); } }); 



Let's see what happened here, what does player bindim mean?
For matryoshka design
 this.bindNode('name',':sandbox .name',Matreshka.binders.innerHTML()); 

means that we associate the name property, which is then available as an object property (Obj.name) and some html entity, in this case, an entity with the selector ': sandbox .name', where the sandbox is a sandbox, that is, the very element that we just rented. Let me remind you that this event is a render of one particular list item.
As the third argument, passes the type of dependency. That is how they (property and essence) are interconnected.
In the matryoshka there is a standard set of binders, and in this case Matreshka.binders.innerHTML () makes the value of the property and the contents of the html-container ': sandbox .name' dependent.
What is the specific relationship between them? The most obvious: change the property of the object - the contents of the container html change.

The basics of the model are dismantled, we move on to the class
 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'); //    } }); 


In class it is worthwhile to focus on two things, albeit very simple ones. The itemRenderer property shows how each item in the list will be rendered. In the above example, /> is: sandbox, from which we count other selekotry.

Note
  constructor: function(){ this.bindNode('sandbox','#players'); //    } 

says that all elements of the list will be drawn inside the '#players' container.

Matryoshka in battle mode


When the players connected and started the game, what we have (logically):


It remains to implement these lists with the help of a nesting doll and ask them some events.

Cards in my hand


Cards in my hand
 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; //    



A similar list, we will not repeat, consider how binds are applied here.
  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()); 


As we discussed above, these strings link the contents of the html node and the properties of the object.
By linking them in the above way, we can easily create a map by simply pushing into our list:
 var Actions = { ......... cardToHand: function(card){ myCards.push({ name: card.name, attack: card.attack, health: card.health, picture: card.picture, mana: card.mana }); } ......... } 


Extremely simple. But even easier is how we can change these properties:
 this.health = 0; 

Not only will set the health indicator equal to zero, but also draw it in html in the desired object.
But that's not all, we need to track changes in health, and if it becomes less than one, initiate the death of the unit. To do this, associate the object's health property with the map itself:
  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); }; } }); 

The third argument, as I said, sets the communication logic. In this example, the logic is as follows:
When the value of the object's health has changed (established), we run the function
  function(v){ if (v < 1){ this.className += ' die'; var iot = myArenaCards.indexOf(this); setTimeout(function(){ myArenaCards.splice(iot,1); },2000); }; } 

This points to the entire map, to the sandbox (second argument: ': sandbox').

Conclusion


In complex applications, where you really need two-way and multiple binding, the nested doll perfectly makes life easier and creates comfort during development.
After all, you can link as you like, in one case we set processing only to the received value (setValue), in the other to change the property by event (on: 'click', getValue: function () {}).

Source: https://habr.com/ru/post/259907/


All Articles