一、服務(wù)器我選用的是阿里云服務(wù)器,按量付費,方便隨時不想用的時候直接釋放掉就可以了
首先選擇配置,按實際情況來選

選擇Ubuntu 18.04 64位系統(tǒng)

創(chuàng)建密碼,填寫主機名

其他保留默認即可!
后面需要用到Xftp傳輸文件,以及Unity與Loom節(jié)點通信所以需要把服務(wù)器相關(guān)的端口開放
打開服務(wù)器實例里的管理,左邊列表的安全組進入,點擊配置規(guī)則,添加安全組規(guī)則


二、安裝服務(wù)器上需要用到的一些插件
我本地使用的是Win7筆記本來開發(fā),為了方便操作服務(wù)器,推薦安裝Xshell和Xftp軟件來連接服務(wù)器

使用Xshell比較方便的可以同時開多個窗口
輸入命令更新源
apt-get update


安裝nodejs
sudo apt-get install nodejs
安裝npm
sudo apt-get install npm
然后測試nodejs的版本和包管理器npm的版本node -v、npm -v


三、安裝Loom以及開發(fā)合約需要用到的相關(guān)插件
安裝Loom運行Dapp鏈:
curl https://raw.githubusercontent.com/loomnetwork/loom-sdk-documentation/master/scripts/get_loom.sh | sh
./loom init
./loom run

LoomNetwork官方開發(fā)文檔:https://loomx.io/developers/zh-CN/how-to-develop-locally.html#introduction
安裝truffle
npm install truffle -g
npm install solc -g
安裝完成會顯示truffle版本
初始化一個新項目
mkdir truffle-dappchain
cd truffle-dappchain
truffle init


cd contracts
touch SimpleStore.sol
vi SimpleStore.sol
編寫SimpleStore合約
pragma solidity ^0.5.0;
contract SimpleStore {
uint value;
event NewValueSet(uint _value);
function set(uint _value) public {
value = _value;
emit NewValueSet(value);
}
function get() public view returns (uint) {
return value;
}
}
編寫完按esc :wq 保存退出
編譯合約
root@LoomNet:~/truffle-dappchain# truffle compile
Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/SimpleStore.sol
> Artifacts written to /root/truffle-dappchain/build/contracts
> Compiled successfully using:
- solc: 0.5.8+commit.23d335f2.Emscripten.clang
root@LoomNet:~/truffle-dappchain# ls
build contracts migrations test truffle-config.js
root@LoomNet:~/truffle-dappchain#
合約部署需要創(chuàng)建一個遷移文件,進入migrations文件夾
root@LoomNet:~/truffle-dappchain# cd migrations
root@LoomNet:~/truffle-dappchain/migrations# ls
1_initial_migration.js
root@LoomNet:~/truffle-dappchain/migrations# cp 1_initial_migration.js 2_simple_store.js
root@LoomNet:~/truffle-dappchain/migrations# ls
1_initial_migration.js 2_simple_store.js
root@LoomNet:~/truffle-dappchain/migrations# vi 2_simple_store.js
root@LoomNet:~/truffle-dappchain/migrations#
var SimpleStore = artifacts.require("./SimpleStore.sol");
module.exports = function(deployer) {
deployer.deploy(SimpleStore);
};
修改truffle-config.js里的網(wǎng)絡(luò)配置
const { readFileSync } = require('fs')
const path = require('path')
const { join } = require('path')
const LoomTruffleProvider = require('loom-truffle-provider')
module.exports = {
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
local_loom: {
provider: function() {
const privateKey = readFileSync(path.join(__dirname, 'loom_private_key'), 'utf-8')
const chainId = 'default'
const writeUrl = 'http://127.0.0.1:46658/rpc'
const readUrl = 'http://127.0.0.1:46658/query'
const loomTruffleProvider = new LoomTruffleProvider(chainId, writeUrl, readUrl, privateKey)
loomTruffleProvider.createExtraAccountsFromMnemonic("gravity top burden flip student usage spell purchase hundred improve check genre", 10)
return loomTruffleProvider
},
network_id: '*'
}
}
}
安裝loom-truffle-provider
npm install loom-truffle-provider --save
創(chuàng)建一個名為'loom_private_key'的私鑰
由于我的loom鏈?zhǔn)窃诟?jié)點,私鑰需要放到truffle-dappchain的根目錄,所以我這樣執(zhí)行
root@LoomNet:~/truffle-dappchain# ../loom genkey -a loom_public_key -k loom_private_key
local address: 0x02b40aAaF26f97f3ea0CA550a4C24F8b5C8d1324
local address base64: ArQKqvJvl/PqDKVQpMJPi1yNEyQ=
root@LoomNet:~/truffle-dappchain# ls
build chaindata contracts loom_private_key loom_public_key migrations test truffle-config.js
root@LoomNet:~/truffle-dappchain#
接下來打開另一個Xshell窗口,啟動LoomDapp鏈
./loom run
在剛才的窗口將合約部署到Loom鏈上
truffle migrate --network local_loom
這個時候合約就開始部署了
root@LoomNet:~/truffle-dappchain#
root@LoomNet:~/truffle-dappchain#
root@LoomNet:~/truffle-dappchain# truffle migrate --network local_loom
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Starting migrations...
======================
> Network name: 'local_loom'
> Network id: 13654820909954
> Block gas limit: 0x0
1_initial_migration.js
======================
Deploying 'Migrations'
----------------------
> transaction hash: 0x434fee0b8efcdec81dbf30520bc7d476b038cd3e44441b1655f66a93fc6dd833
> Blocks: 0 Seconds: 0
> contract address: 0x04B755D415BF2D528AfC562866B3DD52c1717893
> block number: 141
> block timestamp: 1569744222
> account: 0x02B40AAAF26F97f3Ea0CA550a4C24f8B5c8D1324
> balance: 0
> gas used: 0
> gas price: 0 gwei
> value sent: 0 ETH
> total cost: 0 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
2_simple_store.js
=================
Deploying 'SimpleStore'
-----------------------
> transaction hash: 0x072f26c332cc19b59c3c37c9f0cfe9ea55122169b2308161828271e17010e520
> Blocks: 0 Seconds: 0
> contract address: 0xda98B323AC5c6c4E5DE2C6B71fc908744f14E7d4
> block number: 147
> block timestamp: 1569744228
> account: 0x02B40AAAF26F97f3Ea0CA550a4C24f8B5c8D1324
> balance: 0
> gas used: 0
> gas price: 0 gwei
> value sent: 0 ETH
> total cost: 0 ETH
> Saving migration to chain.
> Saving artifacts
-------------------------------------
> Total cost: 0 ETH
Summary
=======
> Total deployments: 2
> Final cost: 0 ETH
root@LoomNet:~/truffle-dappchain#
部署成功會在build/contracts目錄里找到SimpleStore.json文件,里面就包含我們的合約abi
root@LoomNet:~/truffle-dappchain# cd build
root@LoomNet:~/truffle-dappchain/build# ls
contracts
root@LoomNet:~/truffle-dappchain/build# cd contracts
root@LoomNet:~/truffle-dappchain/build/contracts# ls
Migrations.json SimpleStore.json
root@LoomNet:~/truffle-dappchain/build/contracts# vi SimpleStore.json
{
"contractName": "SimpleStore",
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "_value",
"type": "uint256"
}
],
"name": "NewValueSet",
"type": "event"
},
{
"constant": false,
"inputs": [
{
"name": "_value",
"type": "uint256"
}
],
"name": "set",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "get",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
],
我們只需要"abi":[]大括號的部分一會會用到,
記住我們部署的SimpleStore合約地址,等會在Unity工程當(dāng)中會用到,你的地址部署出來可能跟我的不一樣
contract address: 0xda98B323AC5c6c4E5DE2C6B71fc908744f14E7d4
接下來我們在test目錄創(chuàng)建一個測試合約腳本
root@LoomNet:~/truffle-dappchain# cd test
root@LoomNet:~/truffle-dappchain/test# ls
root@LoomNet:~/truffle-dappchain/test# touch SimpleStore.js
root@LoomNet:~/truffle-dappchain/test# ls
SimpleStore.js
root@LoomNet:~/truffle-dappchain/test# vi SimpleStore.js
root@LoomNet:~/truffle-dappchain/test#
測試合約內(nèi)容
const SimpleStore = artifacts.require('SimpleStore')
contract('Test', (accounts) => {
let [alice] = accounts
let contractInstance
beforeEach(async () => {
contractInstance = await SimpleStore.new()
});
it('should be able to set a value', async () => {
let value = 4
const result = await contractInstance.set(value, {from: alice})
assert.equal(result.receipt.status, true)
assert.equal(result.logs[0].args._value.toString(), value.toString())
})
it('should be able to set and then get a value', async () => {
let value = 5
await contractInstance.set(value, {from: alice})
const result = await contractInstance.get({from: alice})
assert.equal(result.toString(), value.toString())
})
})
使用命令測試合約
root@LoomNet:~/truffle-dappchain/test# cd ../
root@LoomNet:~/truffle-dappchain# truffle test --network local_loom
Using network 'local_loom'.
Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.
Contract: Test
? should be able to set a value (3018ms)
? should be able to set and then get a value (3021ms)
2 passing (12s)
root@LoomNet:~/truffle-dappchain#
測試成功!
四、在Unity工程中調(diào)用Loom合約
這里是官方的Unity教程:
https://loomx.io/developers/zh-CN/unity-truffle-loom-sample.html
首先去github下載LoomSDK的Unity工程包:
https://github.com/zandk/CryptoRealmClient
SimpleStoreHandler腳本代碼:
using System;
using System.Threading.Tasks;
using UnityEngine;
using Loom.Unity3d;
using Loom.Unity3d.Samples;
using Loom.Nethereum.ABI.FunctionEncoding.Attributes;
public class LoomQuickStartSample : MonoBehaviour {
// 部署合約之后返回的abi
// 合約部署的地址
public TextAsset simpleStoreABI;
public TextAsset simpleStoreAddress;
async Task<EvmContract> GetContract(byte[] privateKey, byte[] publicKey)
{
var writer = RPCClientFactory.Configure()
.WithLogger(Debug.unityLogger)
.WithHTTP("http://127.0.0.1:46658/rpc")//替換自己部署到云服務(wù)器上的LoomDapp鏈的服務(wù)器IP
//.WithWebSocket("ws://127.0.0.1:46657/websocket")
.Create();
var reader = RPCClientFactory.Configure()
.WithLogger(Debug.unityLogger)
.WithHTTP("http://127.0.0.1:46658/query")//替換自己部署到云服務(wù)器上的LoomDapp鏈的服務(wù)器IP
//.WithWebSocket("ws://127.0.0.1:9999/queryws")
.Create();
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
string abi = simpleStoreABI.ToString();
//string abi = "[{\"anonymous\": false,\"inputs\": [{\"indexed\": false,\"name\": \"_value\",\"type\": \"uint256\"}],\"name\": \"NewValueSet\",\"type\": \"event\"},{\"constant\": false,\"inputs\": [{\"name\": \"_value\",\"type\": \"uint256\"}],\"name\": \"set\",\"outputs\": [],\"payable\": false,\"stateMutability\": \"nonpayable\",\"type\": \"function\"},{\"constant\": true,\"inputs\": [],\"name\": \"get\",\"outputs\": [{\"name\": \"\",\"type\": \"uint256\"}],\"payable\": false,\"stateMutability\": \"view\",\"type\": \"function\"}]\r\n";
//合約地址
var contractAddr = Address.FromHexString(simpleStoreAddress.ToString());
var callerAddr = Address.FromPublicKey(publicKey);
return new EvmContract(client, contractAddr, callerAddr, abi);
}
/// <summary>
/// 調(diào)用合約的set方法
/// </summary>
/// <param name="contract"></param>
/// <returns></returns>
async Task CallContract(EvmContract contract,int value)
{
await contract.CallAsync("set", value);
}
/// <summary>
/// 調(diào)用合約的get方法
/// </summary>
/// <param name="contract"></param>
/// <returns></returns>
async Task StaticCallContract(EvmContract contract)
{
if (contract == null)
{
throw new Exception("Not signed in!");
}
var result = await contract.StaticCallSimpleTypeOutputAsync<int>("get");
if (result!=null)
{
Debug.Log("Smart contract returned: " + result.ToString());
}
else
{
throw new Exception("Smart contract didn't return anything!");
}
}
// Use this for initialization
async void Start()
{
// The private key is used to sign transactions sent to the DAppChain.
// Usually you'd generate one private key per player, or let them provide their own.
// In this sample we just generate a new key every time.
var privateKey = CryptoUtils.GeneratePrivateKey();
var publicKey = CryptoUtils.PublicKeyFromPrivateKey(privateKey);
var contract = await GetContract(privateKey, publicKey);
/**
由于合約方法執(zhí)行需要有一定的時間,
所以這里調(diào)用set方法的時候不能直接就調(diào)用get方法,
有可能返回的是之前保存的值,不是最新的
為了看到正確結(jié)果,可以分兩次執(zhí)行
*/
await CallContract(contract, 123);//調(diào)用合約set方法保存一個值
//await StaticCallContract(contract);//調(diào)用合約get方法獲取合約中保存的值
}
}
如果你在調(diào)用get方法之后得到了你之前設(shè)置的值,那就說明你通過Unity與LoomSDK通信成功了!
接下來我們就可以編寫更復(fù)雜的合約了,我會繼續(xù)更新Unity游戲方面的教程,敬請期待!
下面是我的個人公眾號,我是一名Unity游戲開發(fā)工程師,也擅長小游戲開發(fā),如果有感興趣的請關(guān)注我!

Loom Network官方發(fā)布了一個賞金贏取Loom代幣獎勵的活動,推薦大家參加!
活動詳情:賞金和獎勵 —— 參與網(wǎng)絡(luò),贏取 LOOM 代幣
Loom Network官方的免費學(xué)習(xí)智能合約游戲編寫 DApp:https://cryptozombies.io/zh,一步一步教你如何編寫Solidity智能合約!
如果有阿里云服務(wù)器的新用戶推薦大家使用阿里云的券來體驗:高性能云服務(wù)器2折起
