Dappsでゲームを作ってみる(Part.1) コントラクトの定義
基礎的な勉強ばかりやっていてもつまらないので、ここは階段を数段飛ばして、実際にゲームを作っていきたいと思います。
なんとか形になるといいなw
どんなゲームを作るのか
コントラクトの定義に入る前に、まず、「どんなゲームを作るのか」を決めないと進むに進めません。
可能な限りシンプルなゲームにしたいので、「3Dボックスを自由に積めるゲーム」にしようと思います。

要件を抜き出すと
- ボックスには「所有者」の概念がある
- ボックスには「色」の概念がある
- ボックスには「座標(x,y,z)」の概念がある
といったあたりでしょうか?
まずは、このあたりの要件をコントラクトで表現してみる事にします。
プロジェクトの作成
最初の一歩として、まずはtruffleのプロジェクトを作成します。
$ truffle init
Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
ネットワーク設定
dockerで立ち上げているganacheに対してデプロイされるように、ネットワーク設定を追加しておきます。
ganacheは、以前の記事 で用意したものと同じものが立ち上がっているとします。
module.exports = {
networks: {
development: {
host: "localhost",
port: 8545,
network_id: "*",
},
},
};
これで、コントラクトを書く準備が整いました!
truffle.jsとtruffle-config.jsの違い
余談ですが、truffle init
コマンドでは、設定ファイルとして truffle.js
と truffle-config.js
の二つが作成されます。
これって、何がどう違うんだろう?って思ったら、公式ドキュメントに書いてありました。
どうも、Windowsだと、truffle.jsを実行ファイルと勘違いされる事があるようで、それを回避するための一つの方法として truffle-config.js にリネームしろ、とそういう事のようです。
Windows使わないので、truffle-config.js
は削除とします(笑)
コントラクトの定義
BoxFactoryコントラクトの作成
まずは、truffleコマンドを通して、コントラクトの箱形を作成します。
$ truffle create contract BoxFactory
作成されたコントラクトは内容が空なので、今回の要件を満たせるように実装を加えます。
pragma solidity ^0.4.22;
contract BoxFactory {
struct Box {
address owner;
uint32 color; // FFFFFF = 16777215 なので、32あれば十分
int8 x; // +-127程度を確保
int8 y; // z座標は、まだ持たせない
}
// 全てのボックスを格納
Box[] public boxes;
/**
* boxを生成する
*/
function _createBox(address _owner, uint32 _color, int8 _x, int8 _y) private {
require(_checkCoordinate(_owner, _x, _y));
boxes.push(Box({
owner: _owner,
color: _color,
x: _x,
y: _y
}));
}
/**
* boxの生成機能を外部に公開
*/
function createBox(uint32 _color, int8 _x, int8 _y) external {
_createBox(msg.sender, _color, _x, _y);
}
/**
* 同じ所有者で、同座標のボックスがない判定
*/
function _checkCoordinate(address _owner, int8 _x, int8 _y) private view returns(bool) {
for (uint i = 0; i < boxes.length; i++) {
Box memory box = boxes[i];
if (box.owner == _owner && box.x == _x && box.y == _y) {
return false;
}
}
return true;
}
/**
* 自分が所有するボックスを全て取得
* structのarrayで返却する事ができないので、返し方を工夫するしかない...
*/
function getBoxes() external view returns(address[], uint32[], int8[], int8[]) {
uint boxCount = _getBoxCount(msg.sender);
address[] memory owners = new address[](boxCount);
uint32[] memory colors = new uint32[](boxCount);
int8[] memory x = new int8[](boxCount);
int8[] memory y = new int8[](boxCount);
uint counter = 0;
for (uint i = 0; i < boxes.length; i++) {
Box memory box = boxes[i];
if (box.owner == msg.sender) {
owners[counter] = box.owner;
colors[counter] = box.color;
x[counter] = box.x;
y[counter] = box.y;
counter++;
}
}
return (owners, colors, x, y);
}
/**
* 自分が所有するボックスの個数を取得
*/
function _getBoxCount(address _owner) private view returns(uint count) {
for (uint i = 0; i < boxes.length; i++) {
if (boxes[i].owner == _owner) {
count++;
}
}
}
/**
* 自分のボックス数を取得する機能を外部公開
*/
function getBoxCount() external view returns(uint count) {
return _getBoxCount(msg.sender);
}
}
必要そうな機能を、難しい事は考えずにざっと実装してみました!
z座標に関して
本当はz座標に関しても定義するべきなんですが、この後のクライアント実装がちょっと複雑になりそうだったので、 一旦ステイしています。
Solidityは、構造体を返却できない
どうも、現段階のsolidityでは構造体を返却する事ができないようです。
なので、getBoxes
の返却内容が非常に残念な事になっています。
https://medium.com/coinmonks/solidity-tutorial-returning-structs-from-public-functions-e78e48efb378
このあたりの記事が参考になるのですが、これは、今後のバージョンアップでサポートされる事を期待したい…。
振り返り
今回から、truffle
コマンドはdockerを通さずに実行するようにしました。
truffleコマンドは常に最新を使う、という精神で問題なさそうに感じたというのと、
truffleが何かプロジェクト固有のデータをストアする事がないからです。
次の記事では、今回定義したコントラクトに関するテストを書いてみたいと思います。