Solidity实现智能合约——Solidity高级理论(三)

Solidity实现智能合约——Solidity高级理论(三)

在上一节当中我们实现了一个可以初始化创建宠物,并让它进食生成一个新宠物的功能,接下来我们继续对这个系统功能进行完善。

在这一节当中我们将会完成以下的功能:为宠物添加俩个新属性等级和冷却时间,不知道大家有没有发现上一节宠物可以不限制的进食,在这一节当中我们来给它们做一个进食时间的限制,此外我们可以花费gas让我们的宠物升级,对于达到一定等级的宠物我们可以增加一些新权限:对自己宠物进行更改名字或者是更改DNA。
话不多说,我们直接开始吧。

Ownable 合约:

1,合约创建,构造函数先行,将其 owner 设置为msg.sender(其部署者)

2,为它加上一个修饰符 onlyOwner,它会限制陌生人的访问,将访问某些函数的权限锁定在 owner 上。

3,允许将合约所有权转让给他人。

contract Ownable {
  address public owner;

  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);


  function Ownable() public {
    owner = msg.sender;
  }


  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }



  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }

}

首先,我们新建一个 ownable.sol文件,将上面的代码复制到这个文件当中,然后修改 AnimalFactory 合约, 让它继承自 Ownable,记得要将 ownable.sol导入 AnimalFactory.sol文件当中。

回到AnimalFactory.sol,为 Animal 结构体 添加两个属性:level(uint32)和readyTime(uint32)。因为希望同类型数据打成一个包,所以把它们放在结构体的末尾。

 struct Animal{
        
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
        
    }

声明一个名为 cooldownTime 的uint,并将其设置为30 seconds,我们给 Animal 结构体中添加 level 和 readyTime 两个参数,所以现在创建一个新的 Animal 结构体时,需要修改 _createAnimal(),在其中把新旧参数都初始化一下。

修改 animals.push那一行, 添加2个参数:1(表示当前的 level )和uint32(now + cooldownTime)(现在+冷却时间,表示下次允许攻击的时间 readyTime)。

注意:必须使用 uint32(…) 进行强制类型转换,因为now返回类型 uint256。所以我们需要明确将它转换成一个uint32 类型的变量。

uint dnaDigits = 16;   //宠物DNA位数
uint dnaLength = 10**dnaDigits;
uint cooldownTime = 30 seconds;
 function _createAnimal(string _name,uint _dna) internal{
        uint animalId = animals.push(Animal(_name,_dna,1,uint32(now + cooldownTime)))-1; 
         //  将当前地址对应此时的id 
        AnimalToOwner[animalId] = msg.sender;
        //  这个地址下的宠物数量加一 
        ownerAnimalCount[msg.sender]++;
        NewAnimal(animalId, _name, _dna);
    }

接下来我们回到AnimalFeeding.sol文件当中
先定义一个 _triggerCooldown 函数。它要求一个参数, _Animal,表示一某个宠物的存储指针。这个函数可见性设置为 internal。

在函数中,把 _Animal.readyTime设置为 uint32(now + cooldownTime)。

接下来,创建一个名为 _isReady 的函数。这个函数的参数也是名为 _Animal 的Animal storage。这个功能只具有 internal 可见性,并返回一个 bool 值。

函数计算返回(_Animal.readyTime <= now),值为 true 或 false。这个功能的目的是判断下次允许猎食的时间是否已经到了。

 // 重新计算冷却时间 
     function _triggerCooldown(Animal storage _Animal) internal {
       _Animal.readyTime = uint32(now + cooldownTime);
    }
    
    // 是否到了宠物的冷却时间 
    function _isReady(Animal storage _Animal) internal view returns (bool) {
      return (_Animal.readyTime <= now);
    }

feedAndGrow 过程需要参考 cooldownTime。首先,在找到 myAnimal 之后,添加一个 require 语句来检查 _isReady() 并将 myAnimal 传递给它。这样用户必须等到宠物的冷却周期结束后才能执行feedAndGrow功能。

在函数结束时,调用 _triggerCooldown( myAnimal),标明进食行为触发了宠物新的冷却周期。

  //  实现进食功能           宠物   食物DNA 
    function feedAndGrow(uint _AnimalId,uint _targetDna) internal {
        // 确保当前的宠物是自己的  
        require(msg.sender == AnimalToOwner[_AnimalId]);
        //  获取这个宠物的DNA
        Animal storage myAnimal = animals[_AnimalId];
        
        //必须等到宠物的冷却周期 
         require(_isReady(myAnimal));
         
         _targetDna = _targetDna % dnaLength;
         uint newDna = (myAnimal.dna + _targetDna) / 2;
         newDna = newDna - newDna % 100 + 99;
         _createAnimal("No-one", newDna);
         
         // 触发了宠物新的冷却周期
          _triggerCooldown(myAnimal);
    }

在AnimalHelper中,创建一个名为 aboveLevel 的modifier,它接收2个参数,_level(uint类型) 以及_AnimalId(uint类型),运用函数逻辑确保宠物animals[AnimalId].level大于或等于_level。修饰符的最后一行为“;”表示修饰符调用结束后返回,并执行调用函数剩下的部分。

modifier aboveLevel(uint _level, uint _AnimalId) {
         require(animals[_AnimalId].level >= _level);
         _;
    }

添加一些使用aboveLevel修饰符的函数,作为达到level的奖励。激励玩家们去升级他们的宠物。

  //  当宠物等级达到2级时就可以自己改名 
    function changeName(uint _AnimalId, string _newName) external aboveLevel(2,_AnimalId){
        require(msg.sender ==  AnimalToOwner[_AnimalId]);
        animals[_AnimalId].name = _newName;
        
    }
    
     //  当宠物等级达到4级时就可以自己改DNA 
    function changeDna(uint _AnimalId, uint _newDna) external aboveLevel(4,_AnimalId) {
        require(msg.sender ==  AnimalToOwner[_AnimalId]);
        animals[_AnimalId].dna = _newDna;
    }

创建一个名为getAnimalsByOwner 的新函数。它有一个名为 _owner 的 address 类型的参数。将其申明为 external view 函数,这样当玩家从 web3.js 中调用它时,不需要花费任何 gas,函数需要返回一个uint []声明一个result的uint[] memory(内存变量数组),将其设置为一个新的 uint 类型数组。数组的长度为该 _owner 所拥有的宠物数量,这可通过调用 ownerAnimalCount [_owner] 来获取。

  function getAnimalsByOwner(address _owner) external view returns(uint[]) {
        
         uint[] memory result = new uint[](ownerAnimalCount[_owner]);
         
         uint counter = 0;
         for (uint i = 0; i < animals.length; i++) {
         if (AnimalToOwner[i] == _owner) {
               result[counter] = i;
                counter++;
      }
    }
         return result;

    }

最后我们需要实现的效果:展示宠物的冷却周期,成功实现getAnimalsByOwner函数

接下来我将完整的AnimalHelper.sol中的合约代码贴在下方

pragma solidity ^0.4.19;

import "./AnimalFeeding.sol";

contract AnimalHelper is AnimalFeeding {
        
    modifier aboveLevel(uint _level, uint _AnimalId) {
         require(animals[_AnimalId].level >= _level);
         _;
    }
     
     //  当宠物等级达到2级时就可以自己改名 
    function changeName(uint _AnimalId, string _newName) external aboveLevel(2,_AnimalId){
        require(msg.sender ==  AnimalToOwner[_AnimalId]);
        animals[_AnimalId].name = _newName;
        
    }
    
     //  当宠物等级达到4级时就可以自己改DNA 
    function changeDna(uint _AnimalId, uint _newDna) external aboveLevel(4,_AnimalId) {
        require(msg.sender ==  AnimalToOwner[_AnimalId]);
        animals[_AnimalId].dna = _newDna;
    }
       
    function getAnimalsByOwner(address _owner) external view returns(uint[]) {
        
         uint[] memory result = new uint[](ownerAnimalCount[_owner]);
         
         uint counter = 0;
         for (uint i = 0; i < animals.length; i++) {
         if (AnimalToOwner[i] == _owner) {
               result[counter] = i;
                counter++;
      }
    }
         return result;

    }

 
}

AnimalFeeding.sol的完整代码

pragma solidity ^0.4.19;
import "./AnimalIncubators.sol";

contract AnimalFeeding is AnimalFactory{
   
   // 重新计算冷却时间 
    function _triggerCooldown(Animal storage _Animal) internal {
      _Animal.readyTime = uint32(now + cooldownTime);
   }
   
   // 是否到了宠物的冷却时间 
   function _isReady(Animal storage _Animal) internal view returns (bool) {
     return (_Animal.readyTime <= now);
   }
   
   
    //  实现进食功能           宠物   食物DNA 
   function feedAndGrow(uint _AnimalId,uint _targetDna) internal {
       // 确保当前的宠物是自己的  
       require(msg.sender == AnimalToOwner[_AnimalId]);
       //  获取这个宠物的DNA
       Animal storage myAnimal = animals[_AnimalId];
       
       //必须等到宠物的冷却周期 
        require(_isReady(myAnimal));
        
        _targetDna = _targetDna % dnaLength;
        uint newDna = (myAnimal.dna + _targetDna) / 2;
        newDna = newDna - newDna % 100 + 99;
        _createAnimal("No-one", newDna);
        
        // 触发了宠物新的冷却周期
         _triggerCooldown(myAnimal);
   }
   
   function _catchFood(uint _name) internal pure returns (uint){
       uint rand = uint(keccak256(_name));
       return rand;
   }
   
   function feedOnFood(uint _AnimalId,uint _FoodId) public{
       uint foodDna = _catchFood(_FoodId);
       feedAndGrow(_AnimalId,foodDna);
   }
}

AnimalFactory.sol中的完整代码

pragma solidity ^0.4.19;

import "./ownable.sol";

contract AnimalFactory is  Ownable  {
    
    uint dnaDigits = 16;   //宠物DNA位数
    uint dnaLength = 10**dnaDigits;
    uint cooldownTime = 30 seconds;
    
    struct Animal{
        
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
        
    }
    
    Animal [] public animals;
    
    mapping (uint => address) public AnimalToOwner;
    mapping (address => uint) ownerAnimalCount;
    
    
    //  孵化宠物函数 
    // function hatchAnimal(string name,uint dna) public{
    //     animals.push(Animal(name,dna));
        
    // }
    
    event NewAnimal(uint AnimalId,string name,uint dna);
    
    function _createAnimal(string _name,uint _dna) internal{
        uint animalId = animals.push(Animal(_name,_dna,1,uint32(now + cooldownTime)))-1; 
         //  animals.push(Animal(_name,_dna));
         
         //  将当前地址对应此时的id 
        AnimalToOwner[animalId] = msg.sender;
        //  这个地址下的宠物数量加一 
        ownerAnimalCount[msg.sender]++;
        NewAnimal(animalId, _name, _dna);
    }
    
    function _generateRandomDna(string _str) private view returns(uint){
        uint rand = uint(keccak256(_str));
        return rand % dnaLength;
    }
    
    function createRandomAnimal(string _name) public {
        //  用户只能创建一次初始宠物  
        require(ownerAnimalCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createAnimal(_name, randDna);
    }
}

最后给大家介绍一下学习solidity一个非常好玩的网站,我做的这三个合约也是从中学习而来,希望能帮助到大家: 编游戏的同时学习以太坊 DApp 开发.

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>