一、概述
设计一个基于以太坊 ERC721 合约的 DAPP 游戏。 源码:https://github.com/hello2mao/CryptoKitties
二、UI
三、设计目标
(1)交易系统:用户可使用帐号在商店里对产品进行买卖交易。
(2)繁育系统:用户可以使用已有的产品与繁殖中心的产品进行繁殖,产生新的产品。
(3)对战系统:用户可以使用已有的产品与对战心中的产品进行对战,赢了将升级并产生一个新产品,输了失败次数将加一。
(4)喂养系统:用户可以对已有的产品喂养以太坊公链上的以太猫,从而产生新的带以太猫基因的杂交品种。
(5)升级系统:用户可以对已有的产品花 ETH 进行升级,2 级以后可以改名,20 级后可以定制 DNA,从而用户激励升级。
四、游戏系统设计
4.1 交易系统
4.1.1 帐号
注:所有帐号都默认设置好,不提供注册新账号功能。
(1)1 个交易中心管理员帐号:对用户不可见,用于初始化交易中心内的默认摆放产品
(2)1 个繁育中心管理员帐号:对用户不可见,用于初始化繁殖中心内的默认摆放产品
(3)1 个对战中心管理员帐号:对用户不可见,用于初始化对战中心内的默认摆放产品
(4)几个测试用户帐号
4.1.2 产品
产品有名字、ID、价格、等级、代数、技能冷却、战斗记录和基因属性。所有数据都入链。
(1)名字:随机字符串
(2)ID:唯一 ID
(3)价格:默认价格由出生时的基因决定
(4)等级:默认 1 级,在升级系统进行升级,需花费 ETH
(5)代数:繁育代数,新生的为 0 代
(6)技能冷却:技能冷却剩余时间,所有技能默认冷却时间是 1 天
(7)战斗记录:战斗中心进行战斗后的记录
(8)属性:分为头部、眼部、皮肤、上身、下身五个部分,最终决定产品的外形
4.1.3 买卖交易
(1)用户默认拥有大约 10000ETH
(2)用户花费 ETH 对产品进行买卖。
(3)买卖需以太坊确认后才入链生效。
4.2 繁育系统
(1)用户可以使用已有的产品与繁育中心的产品进行繁殖,产生新的产品。
(2)默认为第 0 代,繁育后代数加一。
(3)技能冷却时间为 1 天。
(4)繁育立马产生新产品。
4.3 对战系统
用户可以使用已有的产品与对战心中的产品进行对战,赢了将升级并产生一个新产品,输了失败次数将加一。
对战流程如下:
(1)选择一个自己的产品,然后选择一个对战中心的产品去攻击。
(2)如果你是攻击方,你将有 70%的几率获胜,防守方将有 30%的几率获胜。
(3)所有的产品(攻守双方)都将有一个 winCount 和一个 lossCount,这两个值都将根据战斗结果增长。
(4)若攻击方获胜,这个产品将升级并产生一个新产品。
(5)如果攻击方失败,除了失败次数将加一外,什么都不会发生。
(6)无论输赢,当前产品的冷却时间都将被激活。
4.4 喂养系统
(1)用户可以对已有的产品喂养以太坊公链上的以太猫,从而产生新的带以太猫基因的杂交品种。
(2)用户只需输入公链上以太猫的 ID
(3)产生的杂交品种有特殊的标记,且基因也很特殊。
4.5 升级系统
(1)用户可以对已有的产品花 ETH 进行升级
(2)2 级以后可以改名,20 级后可以定制 DNA,从而用户激励升级。
五、合约设计
5.1 产品数据结构
struct Thing {
string name; // 名字
uint price; // 价格
uint dna; // DNA
uint32 level; // 等级
uint32 readyTime; // 技能冷却
uint32 generation; // 代数
uint16 winCount; // 战斗胜利次数
uint16 lossCount; // 战斗失败次数
}
5.2 DNA 属性
DNA 一共 16 位,会映射到 UI 上。
0 到 1 位:头部基因
2 到 3 位:眼部基因
4 到 5 位:皮肤基因
6 到 7 位:上身基因
8 到 9 位:下身基因
10 到 13:空
14 到 15 位:喂养后的杂交基因
5.3 繁育&喂养
繁育:两产品的 DNA 求和作为最终 DNA
喂养:使用公链上的以太猫的 id 作为合成 DNA 来源,与目标产品的 DNA 求平均作为最终的 DNA,并且设置最后两位为 99 来标记。
如下:
function feedAndMultiply(uint _thingId, uint _targetDna, string _species) internal onlyOwnerOf(_thingId) {
Thing storage myThing = things[_thingId];
require(_isReady(myThing));
_targetDna = _targetDna % dnaModulus;
uint newDna = (myThing.dna + _targetDna) / 2;
// 吃了Kitty后,dna最后两个数字设定为99
// 例如:7290459416715799
if (keccak256(_species) == keccak256("kitty")) {
newDna = newDna - newDna % 100 + 99;
}
_createThing(strConcat("n",myThing.name, "", "", ""), newDna, myThing.generation + 1);
_triggerCooldown(myThing);
}
// 喂养
function feedOnKitty(uint _thingId, uint _kittyId) public {
// 使用_kittyId作为kittyDna
uint kittyDna = _kittyId;
// (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
feedAndMultiply(_thingId, kittyDna, "kitty");
}
// 繁育
function breed(uint _thingId, uint _targetThingId) public {
uint _targetDna = things[_targetThingId].dna;
feedAndMultiply(_thingId, _targetDna, "thing");
}
5.4 对战
对战流程详见:4.3 对战系统
function attack(uint _thingId, uint _targetId) external onlyOwnerOf(_thingId) {
Thing storage myThing = things[_thingId];
Thing storage enemyThing = things[_targetId];
uint rand = randMod(100);
if (rand <= attackVictoryProbability) {
myThing.winCount++;
myThing.level++;
enemyThing.lossCount++;
feedAndMultiply(_thingId, enemyThing.dna, "thing");
} else {
myThing.lossCount++;
enemyThing.winCount++;
_triggerCooldown(myThing);
}
}
5.5 升级
消耗升级费用后升级产品
function levelUp(uint _thingId) external payable {
require(msg.value == levelUpFee);
things[_thingId].level++;
}