23

ERC721使用教程:实现一个打怪游戏 | 登链社区 | 深入浅出区块链技术

 4 years ago
source link: https://learnblockchain.cn/article/935?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

ERC-20 与 ERC-721 的区别

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

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

ERC-721Token 就像收藏品。 每张口袋妖怪卡与其他口袋妖怪卡都不相同。 甚至代表同一个 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/[email protected] --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 的妖怪战斗游戏:我们可以创建新的怪物并分配给某主人。 怪物的主人可以与其他人战斗以升级他们的怪物。

作者 Alex Roan原文链接

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

  • 发表于 2020-04-25 17:17
  • 阅读 ( 926 )
  • 学分 ( 135 )
  • 分类:以太坊

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK