本文来自我们社区里的开发者Zac Holland,原文链接: Getting started with Loom, Truffle and Unity

我未来几个星期的目标是用Loom编写一个区块链游戏。我心目中的最佳组合是用Truffle来做合约开发,用Unity来为客户端赋予血肉。 我刚刚花了8个小时来确定好怎么做。从Loom的团队和社区里获得了很多帮助,于是我决定把自己的开发进程记录在这里,好帮助那些有可能在卡在这个地方的人。

第一步:安装loom

这个部分非常简单。只需在 这里选择适合你操作系统的教程 ,一步步做就可以了。安装好以后启动Loom即可。(loom run

第二步: 配置好你的项目目录结构

我的项目目录看起来像这样:

第三步:在dappchain目录配置好Truffle

dappchain 目录里,我跟着 这个教程 完成了一个truffle项目的架构配置。

只需要进入那个目录,然后运行yarn deploy, 就可以把你的合约部署到你的 Loom 链上了。

注意: 如果部署报错不成功,原因可能有这些: 1: 可能无法连接到你的Loom 节点。如果你在另外一台电脑或者虚拟机中运行Loom节点,确保在 truffle.js 或者truffle-config.js 中的IP地址设置对了。 2: 一些Truffle和Solc的版本与Loom不是很兼容。确保你的Truffle版本是 v4.1.8, Solc的版本是 v0.4.23

第四步: 在unityclient文件夹里配置好Unity项目

在unityclient文件夹里,我下载好了 Unity 3D SDK 直接Clone仓库放进去即可,目前还没有可用的Unity包)。

第五步:让Truffle 给你导出合约的ABI和地址

首先,进入你的Unity项目,创建Assets/Resources/contracts/abiAssets/Resources/contracts/address 目录。 你的合约数据将导出到这两个文件夹。

然后回到你的Truffle项目,在你的migrations 目录添加一个名为99_export_abis.js 的文件。现在你的项目应该看起来像这样

你每次的部署和迁移都会在你的 migrations 目录里面调用,而这个 99 文件将确保它是最后一步。这些文件看起来应该像这样:

// 2_simple_storage.js
const fs = require('fs')
const path = require('path')

var SimpleStore = artifacts.require("./SimpleStore.sol");

const unityAddresses = path.resolve(__dirname, '../../unityclient/Assets/Resources/contracts/address')

module.exports = function(deployer) {
  deployer.deploy(SimpleStore).then(() => {
    // Write the abi to a new file in the unityAbis directory
    fs.writeFileSync(path.resolve(unityAddresses, "SimpleStore.txt"), SimpleStore.address)
  });

};
// 99_export_abis.js
const fs = require('fs')
const path = require('path')
const contracts = path.resolve(__dirname, '../build/contracts/')
const unityAbis = path.resolve(__dirname, '../../unityclient/Assets/Resources/contracts/abi')

module.exports = async function(deployer, network, accounts) {
    let builtContracts = fs.readdirSync(contracts)
    // loop over every contract
    builtContracts.forEach(contract => {
        // Get the JSON for a specific contract
        let json = JSON.parse(fs.readFileSync(path.resolve(contracts, contract)))
        // Extract just the abi
        let { abi } = json
        // Write the abi to a new file in the unityAbis directory
        fs.writeFileSync(path.resolve(unityAbis, contract), JSON.stringify(json.abi))
    });
}

如果你的项目配置得和我的一模一样,那么这样就会很好运行。但是如果你的文件名和目录名不一样。请确保你相应修改了上面的文件。

另外,也要确保你运行了 yarn deploy 或者 yarn deploy:reset 来确保这些 ABI和地址文件放进了 Unity 项目的目录。

第六步:配置好你的Unity场景

新建一个场景,在新场景里面创建一个空的游戏对象,给其添加一个新脚本,起名为SimpleStoreHandler。看起来应该是这样:

你的SimpleStoreHandler脚本代码应该如下:

using System;
using System.Threading.Tasks;
using UnityEngine;
using Loom.Unity3d;
using Loom.Nethereum.ABI.FunctionEncoding.Attributes;

public class SimpleStoreHandler : MonoBehaviour {

    // Select an ABI from our project resources
    // We got these from the migration script in Truffle
    public TextAsset simpleStoreABI;
    public TextAsset simpleStoreAddress;

    // Use this for initialization
    public async void Start () {
        // Generate new keys for this user
        // TODO - Either store these or let the user enter a private key
        var privateKey = CryptoUtils.GeneratePrivateKey();
        var publicKey = CryptoUtils.PublicKeyFromPrivateKey(privateKey);

        // Get the contract
        var contract = await GetContract(privateKey, publicKey);
        // Make a call
        await StaticCallContract(contract);
    }

    // Get's the contract as an object
    async Task<EvmContract> GetContract(byte[] privateKey, byte[] publicKey)
    {
        // Get the writer and reader for the Loom node
        var writer = RPCClientFactory.Configure()
            .WithLogger(Debug.unityLogger)
            .WithWebSocket("ws://127.0.0.1:46657/websocket")
            .Create();
        var reader = RPCClientFactory.Configure()
            .WithLogger(Debug.unityLogger)
            .WithWebSocket("ws://127.0.0.1:9999/queryws")
            .Create();

        // Create a client object from them
        var client = new DAppChainClient(writer, reader)
            { Logger = Debug.unityLogger };

        // required middleware
        client.TxMiddleware = new TxMiddleware(new ITxMiddlewareHandler[]
        {
            new NonceTxMiddleware
            {
                PublicKey = publicKey,
                Client = client
            },
            new SignedTxMiddleware(privateKey)
        });

        // ABI of the Solidity contract
        string abi = simpleStoreABI.ToString();
        // Address of the Solidity contract
        var contractAddr = Address.FromHexString(simpleStoreAddress.ToString());
        // Address of the user
        var callerAddr = Address.FromPublicKey(publicKey);
        // Return the Contract object
        return new EvmContract(client, contractAddr, callerAddr, abi);
    }

    public async Task StaticCallContract(EvmContract contract)
    {
        if (contract == null)
        {
            throw new Exception("Not signed in!");
        }

        Debug.Log("Calling smart contract...");

        int result = await contract.StaticCallSimpleTypeOutputAsync<int>("get");
        if (result != null)
        {
            Debug.Log("Smart contract returned: " + result);
        } else
        {
            Debug.LogError("Smart contract didn't return anything!");
        }
    }
}

代码已经注释得很好了,如果你有任何问题,可以问我们。

总结:测试 试着运行你的Unity项目。你应该能看到来自Loom的消息和数据。 如果你遇到任何问题,欢迎来问我们。我们将收集大家的问题并尝试作出解答。希望这些步骤不是很难。

一些错误信息和解决方法:

Exception: JSON-RPC Error -32603 (Internal error): name is not registered

如果你在Unity里面遇到这个问题,意味着你在尝试用名字来访问你的合约,请尝试使用它的地址来访问。 只有Go-loom 插件可以用名字访问, Solidity虚拟机合约只能用地址来访问。在这里你只需要用第六步里面的代码第58行来用你的合约地址来实例化你的EvmContract

源代码:

CryptoRealmChain

CryptoRealmClient