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]);// 获取这个宠物的DNAAnimal 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]);// 获取这个宠物的DNAAnimal 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实现智能合约——宠物战斗(四).
最后给大家介绍一下学习solidity一个非常好玩的网站,我做的这三个合约也是从中学习而来,希望能帮助到大家: 编游戏的同时学习以太坊 DApp 开发.
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
