ERC721使用教程:实现一个打怪游戏

栏目: IT技术 · 发布时间: 4年前

内容简介:通过本文了解: 什么是ERC-721? ERC-721如何实现? 如何使用?本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

通过本文了解: 什么是ERC-721? ERC-721如何实现? 如何使用?

ERC-20 与 ERC-721的区别

在2017年ICO泡沫最严重的时候,ERC-20代币无处不在。 科技公司将其用作众筹的一种形式,其中一些公司声称将来会在其平台上使用这些代币。

ERC-20代币就像货币。 每美元的价值都与其他美元相同。 将一美元的钞票换成另一美元的钞票,本质上是一样的。 这就是所谓的“可替代Token”。

ERC-721 Token就像收藏品 。 每张口袋妖怪卡与其他口袋妖怪卡都不相同。 甚至代表同一个Pokemon的卡的价值也有所不同。 有些卡的状态比其他卡好,有些是限量版或特别版等等。一张皮卡丘卡并不总是等于另一张皮卡丘卡。 这就是所谓的“不可替代Token”。

ERC721 有什么用呢 ?

ERC-721标准描述了任何不可替代令牌都必须遵守的接口才能被视为ERC-721。

幸运的是,我们每次创建ERC-721时都不需要创建新代码来满足该标准。 使用社区维护如[OpenZeppelin对我们来说是一个捷径。

让我们看一下如何使用OpenZeppelin创建简单数字化模仿Pocket Monsters的游戏。 我们将其称为“Ethermon”游戏。

ERC-721 Ethermon 游戏

首先,让我们对每个Ethermon做一些假设(游戏逻辑描述):

  • 每个Ethermon都归某人所有。

  • 他们从第一级开始。

  • 你和其他口袋妖怪战斗。

  • 通过战斗获得升级。

我们还需要围绕战斗的一些逻辑。 为简单起见,假设如果一个Ethermon攻击另一个,则级别更高Ethermon的将获胜 。 如果级别相同,则攻击者获胜。 战斗的胜利者上升两级,失败者升一级。

创建ERC721项目

使用Truffle开发框架创建这个基于ERC721的Pokemon游戏项目

首先创建一个新的文件夹,然后初始化Truffle项目:

mkdir ethermon
cd ethermon/
truffle init

使用OpenZeppelin

为了使用OpenZepplin,我们需要利用npm导入这个库。让我们先初始化npm, 然后获取正确版本的OpenZeppelin。我们使用最新的稳定版, 2.5.0 版本的OpenZeppelin, 确保你需要使用的是 0.5.5 版本的Solidity编译器:

npm init
npm install @openzeppelin/contracts@2.5.0 --save

扩展 ERC-721

在我们的 contracts/ 文件夹,先创建一个新的名为 Ethermon.sol 文件。要使用 OpenZeppelin 代码,我们需要引入并扩展 ERC721.sol

当前 Ethermon.sol 代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {
    
}

首先使用 truffle compile 检查确保我们的合约可以正确编译。 接下来,我们编写迁移脚本以便将合约部署到本地区块链。在 migrations/ 目录下创建一个新的迁移文件 2_deploy_contracts.js ,代码如下:

const Ethermon = artifacts.require("Ethermon");

module.exports = function(deployer) {
	deployer.deploy(Ethermon);
};

确保 truffle-config.js 配置可以正确连接本地区块链,你可以使用 truffle test 先测试一下。

编写 Ethermon 逻辑

Ethermon合约需要实现如下功能:

  1. 创建新的妖怪
  2. 将妖怪分配给主人
  3. 主人可以安排妖怪与其他妖怪的战斗

让我们先实现第一步。我们需要在 Ethermon 合约中用一个数组保存所有的妖怪。需要保存的妖怪相关的数据包括名字、级别等。因此我们使用一个结构体。

现在 Ethermon 合约的代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

Monster 结构体在第7行定义,数组在第12行定义。我们也添加了一个 gameOwner 变量来保存 Ethermon 合约的部署账户。第19行开始是 createNewMonster() 函数的实现, 该函数负责创建新的妖怪。

首先,它会检查这个函数是否是由合约的部署账号调用的。然后为新妖怪生成一个ID,并将新妖怪存入数组,最后使用 _safeMint() 函数将这个新创建的妖怪分配给其主人,这就完成了第一二步。

_safeMint() 是我们继承的ERC721合约中实现的函数。它可以安全地将一个 ID 分配给指定的账号,在分配之前会检查ID是否已经存在。

好了,现在我们已经可以创建新的妖怪并将其分配给指定的账号。该进行 第三步了:战斗逻辑。

战斗逻辑

正如之前所述,我们的战斗逻辑决定了一个妖怪可以升多少等级。较高等级的妖怪可以获胜并升两级,失败的妖怪升一级。如果两个妖怪处于同一等级,那么进攻者获胜。下面的代码展示了合约中战斗逻辑的实现:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    function battle(uint _attackingMonster, uint _defendingMonster) public {
        Monster storage attacker = monsters[_attackingMonster];
        Monster storage defender = monsters[_defendingMonster];

        if (attacker.level >= defender.level) {
            attacker.level += 2;
            defender.level += 1;
        }
        else{
            attacker.level += 1;
            attacker.level += 2;
        }
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

第19行开始展示了妖怪的战斗逻辑。目前任何账号都可以调用battle()方法。我们需要对此加以限制,只允许发起进攻的妖怪的主人调用该方法。为此,我们可以添加一个修饰器(参考 Solidity 文档 - 函数修饰器 ),该修饰器 onlyOwnerOf 利用 ERC721.sol 合约中的 ownerOf() 函数来检查调用者账号。

修改后的代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    modifier onlyOwnerOf(uint _monsterId) {
        require(ownerOf(_monsterId) == msg.sender, "Must be owner of monster to battle");
        _;
    }

    function battle(uint _attackingMonster, uint _defendingMonster) public onlyOwnerOf(_attackingMonster) {
        Monster storage attacker = monsters[_attackingMonster];
        Monster storage defender = monsters[_defendingMonster];

        if (attacker.level >= defender.level) {
            attacker.level += 2;
            defender.level += 1;
        }
        else{
            attacker.level += 1;
            attacker.level += 2;
        }
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

这样就完成了一个使用ERC721的妖怪战斗游戏:我们可以创建新的怪物并分配给某主人。 怪物的主人可以与其他人战斗以升级他们的怪物。

接下来

  1. 修复代码中 上下溢出漏洞
  2. 添加测试用例
  3. 扩展代码,为游戏 创建 React 前端

作者 Alex Roan原文链接

ERC-20 与 ERC-721的区别

在2017年ICO泡沫最严重的时候,ERC-20代币无处不在。 科技公司将其用作众筹的一种形式,其中一些公司声称将来会在其平台上使用这些代币。

ERC-20代币就像货币。 每美元的价值都与其他美元相同。 将一美元的钞票换成另一美元的钞票,本质上是一样的。 这就是所谓的“可替代Token”。

ERC-721 Token就像收藏品 。 每张口袋妖怪卡与其他口袋妖怪卡都不相同。 甚至代表同一个Pokemon的卡的价值也有所不同。 有些卡的状态比其他卡好,有些是限量版或特别版等等。一张皮卡丘卡并不总是等于另一张皮卡丘卡。 这就是所谓的“不可替代Token”。

ERC721 有什么用呢 ?

ERC-721标准描述了任何不可替代令牌都必须遵守的接口才能被视为ERC-721。

幸运的是,我们每次创建ERC-721时都不需要创建新代码来满足该标准。 使用社区维护如[OpenZeppelin对我们来说是一个捷径。

让我们看一下如何使用OpenZeppelin创建简单数字化模仿Pocket Monsters的游戏。 我们将其称为“Ethermon”游戏。

ERC-721 Ethermon 游戏

首先,让我们对每个Ethermon做一些假设(游戏逻辑描述):

  • 每个Ethermon都归某人所有。

  • 他们从第一级开始。

  • 你和其他口袋妖怪战斗。

  • 通过战斗获得升级。

我们还需要围绕战斗的一些逻辑。 为简单起见,假设如果一个Ethermon攻击另一个,则级别更高Ethermon的将获胜 。 如果级别相同,则攻击者获胜。 战斗的胜利者上升两级,失败者升一级。

创建ERC721项目

使用Truffle开发框架创建这个基于ERC721的Pokemon游戏项目

首先创建一个新的文件夹,然后初始化Truffle项目:

mkdir ethermon
cd ethermon/
truffle init

使用OpenZeppelin

为了使用OpenZepplin,我们需要利用npm导入这个库。让我们先初始化npm, 然后获取正确版本的OpenZeppelin。我们使用最新的稳定版, 2.5.0 版本的OpenZeppelin, 确保你需要使用的是 0.5.5 版本的Solidity编译器:

npm init
npm install @openzeppelin/contracts@2.5.0 --save

扩展 ERC-721

在我们的 contracts/ 文件夹,先创建一个新的名为 Ethermon.sol 文件。要使用 OpenZeppelin 代码,我们需要引入并扩展 ERC721.sol

当前 Ethermon.sol 代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

}

首先使用 truffle compile 检查确保我们的合约可以正确编译。 接下来,我们编写迁移脚本以便将合约部署到本地区块链。在 migrations/ 目录下创建一个新的迁移文件 2_deploy_contracts.js ,代码如下:

const Ethermon = artifacts.require("Ethermon");

module.exports = function(deployer) {
    deployer.deploy(Ethermon);
};

确保 truffle-config.js 配置可以正确连接本地区块链,你可以使用 truffle test 先测试一下。

编写 Ethermon 逻辑

Ethermon合约需要实现如下功能:

  1. 创建新的妖怪
  2. 将妖怪分配给主人
  3. 主人可以安排妖怪与其他妖怪的战斗

让我们先实现第一步。我们需要在 Ethermon 合约中用一个数组保存所有的妖怪。需要保存的妖怪相关的数据包括名字、级别等。因此我们使用一个结构体。

现在 Ethermon 合约的代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

Monster 结构体在第7行定义,数组在第12行定义。我们也添加了一个 gameOwner 变量来保存 Ethermon 合约的部署账户。第19行开始是 createNewMonster() 函数的实现, 该函数负责创建新的妖怪。

首先,它会检查这个函数是否是由合约的部署账号调用的。然后为新妖怪生成一个ID,并将新妖怪存入数组,最后使用 _safeMint() 函数将这个新创建的妖怪分配给其主人,这就完成了第一二步。

_safeMint() 是我们继承的ERC721合约中实现的函数。它可以安全地将一个 ID 分配给指定的账号,在分配之前会检查ID是否已经存在。

好了,现在我们已经可以创建新的妖怪并将其分配给指定的账号。该进行 第三步了:战斗逻辑。

战斗逻辑

正如之前所述,我们的战斗逻辑决定了一个妖怪可以升多少等级。较高等级的妖怪可以获胜并升两级,失败的妖怪升一级。如果两个妖怪处于同一等级,那么进攻者获胜。下面的代码展示了合约中战斗逻辑的实现:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    function battle(uint _attackingMonster, uint _defendingMonster) public {
        Monster storage attacker = monsters[_attackingMonster];
        Monster storage defender = monsters[_defendingMonster];

        if (attacker.level >= defender.level) {
            attacker.level += 2;
            defender.level += 1;
        }
        else{
            attacker.level += 1;
            attacker.level += 2;
        }
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

第19行开始展示了妖怪的战斗逻辑。目前任何账号都可以调用battle()方法。我们需要对此加以限制,只允许发起进攻的妖怪的主人调用该方法。为此,我们可以添加一个修饰器(参考 Solidity 文档 - 函数修饰器 ),该修饰器 onlyOwnerOf 利用 ERC721.sol 合约中的 ownerOf() 函数来检查调用者账号。

修改后的代码如下:

pragma solidity ^0.5.5;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Ethermon is ERC721 {

    struct Monster {
        string name;
        uint level;
    }

    Monster[] public monsters;
    address public gameOwner;

    constructor() public {
        gameOwner = msg.sender;
    }

    modifier onlyOwnerOf(uint _monsterId) {
        require(ownerOf(_monsterId) == msg.sender, "Must be owner of monster to battle");
        _;
    }

    function battle(uint _attackingMonster, uint _defendingMonster) public onlyOwnerOf(_attackingMonster) {
        Monster storage attacker = monsters[_attackingMonster];
        Monster storage defender = monsters[_defendingMonster];

        if (attacker.level >= defender.level) {
            attacker.level += 2;
            defender.level += 1;
        }
        else{
            attacker.level += 1;
            attacker.level += 2;
        }
    }

    function createNewMonster(string memory _name, address _to) public {
        require(msg.sender == gameOwner, "Only game owner can create new monsters");
        uint id = monsters.length;
        monsters.push(Monster(_name, 1));
        _safeMint(_to, id);
    }
}

这样就完成了一个使用ERC721的妖怪战斗游戏:我们可以创建新的怪物并分配给某主人。 怪物的主人可以与其他人战斗以升级他们的怪物。

接下来

  1. 修复代码中 上下溢出漏洞
  2. 添加测试用例
  3. 扩展代码,为游戏 创建 React 前端

作者 Alex Roan原文链接

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 2分钟前
  • 阅读 ( 5 )
  • 学分 ( 0 )
  • 分类:以太坊

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Building Social Web Applications

Building Social Web Applications

Gavin Bell / O'Reilly Media / 2009-10-1 / USD 34.99

Building a social web application that attracts and retains regular visitors, and gets them to interact, isn't easy to do. This book walks you through the tough questions you'll face if you're to crea......一起来看看 《Building Social Web Applications》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试