链游开发(二):僵尸大战

本项目来源 https://cryptozombies.io/

0.前言

上一节中,我们只实现了僵尸的生成,但是并没有给每个僵尸指定特定的主人,今天主要给项目里面加入了用户认证功能,这样就支持多用户了。同时加入了攻击融合功能,能够将两个僵尸进行融合形成新的僵尸,同时调用加密猫的链码,完成加密猫和僵尸的融合。

1.重点知识

  • 映射(Mapping)和地址(Address)地址是Solidity语言里面特有的数据类型,它标记了一个以太坊用户(可以是普通用户也可以是合约用户)。映射本质上是存储和查找数据所用的键-值对,声明形式如下:
 //声明了一个地址到uint类型的映射。
mapping (address => uint) public accountBalance;
//声明了一个uint类型到字符串类型的映射。
mapping (uint => string) userIdToName;
  • msg.sender:在 Solidity 中,有一些全局变量可以被所有函数调用。 其中一个就是 msg.sender,它指的是当前调用者(或智能合约)的 address。以下是使用 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];
}
  • require:使得函数在执行过程中,当不满足某些条件时抛出错误,并停止执行,展示如下:
function sayHiToVitalik(string _name) public returns (string) {
  // 比较 _name 是否等于 "Vitalik". 如果不成立,抛出异常并终止程序
  // (敲黑板: Solidity 并不支持原生的字符串比较, 我们只能通过比较
  // 两字符串的 keccak256 哈希值来进行判断)
  require(keccak256(_name) == keccak256("Vitalik"));
  // 如果返回 true, 运行如下语句
  return "Hi!";
}
  • 支持继承和引入,继承父类后,可以使用父类的函数,同样的,引用后同样可以使用里面的合约函数,合理的使用继承和引入可以让代码更加的简洁。
  • 存储变量的两种形式:storage 或 memory。Storage 变量是指永久存储在区块链中的变量。 Memory 变量则是临时的,当外部函数对某合约调用完成时,内存型变量即被移除。
  • internal 和 external修饰词:internal 和 private 类似,不过, 如果某个合约继承自其父合约,这个合约即可以访问父合约中定义的“内部”函数。。
    external 与public 类似,只不过这些函数只能在合约之外调用 - 它们不能被合约内的其他函数调用。稍后我们将讨论什么时候使用 external 和 public。
  • 与其他合约进行交互: 需要先定义一个接口.如果区块链上有这个合约:
contract LuckyNumber {
  mapping(address => uint) numbers;
  function setNum(uint _num) public {
    numbers[msg.sender] = _num;
  }
  function getNum(address _myAddress) public view returns (uint) {
    return numbers[_myAddress];
  }
}

我们可以声明一个接口:

contract NumberInterface {
  function getNum(address _myAddress) public view returns (uint);
}

我们使用的时候可以这样:

contract MyContract {
  address NumberInterfaceAddress = 0xab38...;
  // ^ 这是FavoriteNumber合约在以太坊上的地址
  NumberInterface numberContract = NumberInterface(NumberInterfaceAddress);
  // 现在变量 `numberContract` 指向另一个合约对象
  function someFunction() public {
    // 现在我们可以调用在那个合约中声明的 `getNum`函数:
    uint num = numberContract.getNum(msg.sender);
    // ...在这儿使用 `num`变量做些什么
  }
}

2.代码

代码里面涉及的编程知识都在以上。

//zombiefeeding.sol
pragma solidity ^0.4.19;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}
//继承僵尸工厂
contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

  function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(_species) == keccak256("kitty")) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

猜你喜欢

转载自blog.csdn.net/doreen211/article/details/129170456
今日推荐