We can build and edit static UI interfaces with Scene panel easily, but in real world game project it's not enough. We'll need dynamically generated UI elements with data, such as character selection, inventory and level selection.
Let's take an inventory interface as example, we need following data structure to generate an item dynamically:
- Item id
- Icon id, we can put up a icon id to spriteFrame reference dictionary or array
- Item name
- Item price
- ...
We will introduce how to define a data class and generate those data in Properties panel. If you're not familiar with Cocos Creator's component system, please start with Workflow of script development chapter.
For most game project, you can get inventory data from game server. For the simplicity let's define a data class for data structure and input. Let's add a new script ItemList.js
and add the following properties:
var Item = cc.Class({
name: 'Item',
properties: {
id: 0,
itemName: '',
itemPrice: 0,
iconSF: cc.SpriteFrame
}
});
cc.Class({
extends: cc.Component,
properties: {
items: {
default: [],
type: Item
}
},
});
We defined an Item
class at the top of the script for storing and easily updating data needed by item. Please notice this class does not extends cc.Component
, so it can be defined as a property type for any component. You can learn more details about data class here Declare class.
After the Item
class definition, we defined a component class. Each script file can only contains one component definition and the component name will be the same as the file name. So the component we define is ItemList
. In this component we have a list property which type is Item
. This way we can populate the list with data input in Properties panel.
Now let's create an empty node and add ItemList
component. We can find Items
property in Properties panel. To populate data, let's give the list a length. Type 3
in the field and you can input data like these:
We have our data ready for now, you can also type in more data entries as you wish. If you're making a game with lots of data, please consider using more specialized data source like Excel and database. It's easy to convert such data sources to JSON for the engine.
Let's now move on to the 'view' to visualize data, we can use Prefab to do this job. Let's create a prefab that looks like this:
The child nodes icon
, name
, price
will be used to display icon, item name and price from data.
You can freely customize the prefab as you need, the picture above only shows an example. Now we need a component script to bind the data to the components that show them. Create a new script ItemTemplate.js
and add it to the prefab root node. The script contains:
cc.Class({
extends: cc.Component,
properties: {
id: 0,
icon: cc.Sprite,
itemName: cc.Label,
itemPrice: cc.Label
}
});
Let's drag all those nodes onto the property fields of ItemTemplate
component.
Note: We will assign value for id
property through script, no data binding needed.
Let's modify ItemTemplate.js
script to add function to update the render components with input data. Let's add the following to the end of script (inside class definition):
properties: {
...
},
// data: {id,iconSF,itemName,itemPrice}
init: function (data) {
this.id = data.id;
this.icon.spriteFrame = data.iconSF;
this.itemName.string = data.itemName;
this.itemPrice.string = data.itemPrice;
}
init
method takes a data object and use the data to update each render component on bound nodes. Now we can save Item
node as a Prefab asset and use it as the template of our item entries.
Let's go back to ItemList.js
script, and add reference to our Prefab and then instantiate it with data.
properties: {
//...
itemPrefab: cc.Prefab
},
onLoad () {
for (var i = 0; i < this.items.length; ++i) {
var item = cc.instantiate(this.itemPrefab);
var data = this.items[i];
this.node.addChild(item);
item.getComponent('ItemTemplate').init({
id: data.id,
itemName: data.itemName,
itemPrice: data.itemPrice,
iconSF: data.iconSF
});
}
}
In the onLoad
callback method, we traverse each data stored in items
in turn, instantiate itemPrefab
to generate a new node and add it to the node where ItemList.js
is. Then call the init
method in ItemTemplate.js
to update its display.
Now we can add a Layout component to the node that holds ItemList.js
through Add Component -> UI Component -> Layout under the Properties panel, and set the following properties:
Type
:HORIZONTAL
Resize Mode
:CONTAINER
Don't forget to drag and drop item
Prefab to itemPrefab
property field of ItemList
component. You can also add a Sprite component to the node as the background.
We have completed all the work. Now itemList
node should look like this:
Running preview of the scene will get the result like this (the acutal look depends on how your template was setup and your data):
The Layout component added in previous step is not necessary. We can use it to help putting multiple items in a container in order but you can also use the script program to do that. You can also add a ScrollView component together to display a large amount of content in a limited space. For details of those layout method please read Auto Layout Container and ScrollView Component.