Solidity学习(2.僵尸攻击人类)

第1章: 第二课概览

在第一课中,我们创建了一个函数用来生成僵尸,并且将它放入区块链上的僵尸数据库中。 在第二课里,我们会让我们的 app 看起来更像一个游戏: 它得支持多用户,并且采用更加有趣,而不仅仅使用随机的方式,来生成新的僵尸。

如何生成新的僵尸呢?通过让现有的僵尸猎食其他生物!

僵尸猎食

僵尸猎食的时候,僵尸病毒侵入猎物,这些病毒会将猎物变为新的僵尸,加入你的僵尸大军。系统会通过猎物和猎食者僵尸的DNA计算出新僵尸的DNA。

僵尸最喜欢猎食什么物种呢? 等你学完第二课就知道了!

实战演习

右边是一个简单的猎食演示。点击一个“人”,看看僵尸猎食的时候会发生什么? 可见,新僵尸的DNA是通过从原来的僵尸的DNA, 加上猎物的DNA计算得来的。

学完这一章,请点击“下一章”, 我们该让游戏支持多玩家模式了。

 

第2章: 映射(Mapping)和地址(Address)

我们通过给数据库中的僵尸指定“主人”, 来支持“多玩家”模式。

如此一来,我们需要引入2个新的数据类型:mapping(映射) 和 address(地址)。

Addresses (地址)

以太坊区块链由 _ account _ (账户)组成,你可以把它想象成银行账户。一个帐户的余额是 _以太_ (在以太坊区块链上使用的币种),你可以和其他帐户之间支付和接受以太币,就像你的银行帐户可以电汇资金到其他银行帐户一样。

每个帐户都有一个“地址”,你可以把它想象成银行账号。这是账户唯一的标识符,它看起来长这样:

0x0cE446255506E92DF41614C46F1d6df9Cc969183

(这是 CryptoZombies 团队的地址,如果你喜欢 CryptoZombies 的话,请打赏我们一些以太币!😉)

我们将在后面的课程中介绍地址的细节,现在你只需要了解地址属于特定用户(或智能合约)的

所以我们可以指定“地址”作为僵尸主人的 ID。当用户通过与我们的应用程序交互来创建新的僵尸时,新僵尸的所有权被设置到调用者的以太坊地址下。

Mapping(映射)

在第1课中,我们看到了 _ 结构体 _ 和 _ 数组 _ 。 _映射_ 是另一种在 Solidity 中存储有组织数据的方法。

映射是这样定义的:

//对于金融应用程序,将用户的余额保存在一个 uint类型的变量中:
mapping (address => uint) public accountBalance;
//或者可以用来通过userId 存储/查找的用户名
mapping (uint => string) userIdToName;

映射本质上是存储和查找数据所用的键-值对。在第一个例子中,键是一个 address,值是一个 uint,在第二个例子中,键是一个uint,值是一个 string

实战演习

为了存储僵尸的所有权,我们会使用到两个映射:一个记录僵尸拥有者的地址,另一个记录某地址所拥有僵尸的数量。

1.创建一个叫做 zombieToOwner 的映射。其键是一个uint(我们将根据它的 id 存储和查找僵尸),值为 address。映射属性为public

2.创建一个名为 ownerZombieCount 的映射,其中键是 address,值是 uint

 

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

第3章: Msg.sender

现在有了一套映射来记录僵尸的所有权了,我们可以修改 _createZombie 方法来运用它们。

为了做到这一点,我们要用到 msg.sender

msg.sender

在 Solidity 中,有一些全局变量可以被所有函数调用。 其中一个就是 msg.sender,它指的是当前调用者(或智能合约)的 address

注意:在 Solidity 中,功能执行始终需要从外部调用者开始。 一个合约只会在区块链上什么也不做,除非有人调用其中的函数。所以 msg.sender总是存在的。

以下是使用 msg.sender 来更新 mapping 的例子:

mapping (address => uint) favoriteNumber;

function setMyNumber(uint _myNumber) public {
  // 更新我们的 `favoriteNumber` 映射来将 `_myNumber`存储在 `msg.sender`名下
  favoriteNumber[msg.sender] = _myNumber;
  // 存储数据至映射的方法和将数据存储在数组相似
}

function whatIsMyNumber() public view returns (uint) {
  // 拿到存储在调用者地址名下的值
  // 若调用者还没调用 setMyNumber, 则值为 `0`
  return favoriteNumber[msg.sender];
}

在这个小小的例子中,任何人都可以调用 setMyNumber 在我们的合约中存下一个 uint 并且与他们的地址相绑定。 然后,他们调用 whatIsMyNumber 就会返回他们存储的 uint

使用 msg.sender 很安全,因为它具有以太坊区块链的安全保障 —— 除非窃取与以太坊地址相关联的私钥,否则是没有办法修改其他人的数据的。

实战演习

我们来修改第1课的 _createZombie 方法,将僵尸分配给函数调用者吧。

  1. 首先,在得到新的僵尸 id 后,更新 zombieToOwner 映射,在 id 下面存入 msg.sender

  2. 然后,我们为这个 msg.sender 名下的 ownerZombieCount 加 1。

跟在 JavaScript 中一样, 在 Solidity 中你也可以用 ++ 使 uint 递增。

uint number = 0;
number++;
// `number` 现在是 `1`了

修改两行代码即可。

pragma solidity ^0.4.19;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string _str) private view returns (uint) {
        uint rand = uint(keccak256(_str));
        return rand % dnaModulus;
    }

    function createRandomZombie(string _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

第4章: Require

在第一课中,我们成功让用户通过调用 createRandomZombie函数 并输入一个名字来创建新的僵尸。 但是,如果用户能持续调用这个函数来创建出无限多个僵尸加入他们的军团,这游戏就太没意思了!

于是,我们作出限定:每个玩家只能调用一次这个函数。 这样一来,新玩家可以在刚开始玩游戏时通过调用它,为其军团创建初始僵尸。

我们怎样才能限定每个玩家只调用一次这个函数呢?

答案是使用require。 require

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值