准备城堡游戏数据
在学习了 Move 和 Sui 的基础知识后,本章将引导我们完成 Move Castle 游戏。
在深入了解游戏机制核心逻辑的数学之前,我们首先需要为 Move Castle 游戏准备游戏数据。
我们在之前的课程中已经介绍了 GameStore
和 CastleData
,现在,我们只需要在建造城堡时初始化它们。
要确定城堡的初始游戏数据(例如攻击力和防御力),需要依赖于城堡的大小和种族。在 core.move
模块中添加一个 get_initial_attack_defense_power
函数:
/// 通过种族获取初始攻击力和防御力
fun get_initial_attack_defense_power(race: u64): (u64, u64) {
let (attack, defense);
if (race == 0) {
(attack, defense) = (1000, 1000);
} else if (race == 1) {
(attack, defense) = (500, 1500);
} else if (race == 2) {
(attack, defense) = (1500, 500);
} else if (race == 3) {
(attack, defense) = (1200, 800);
} else if (race == 4) {
(attack, defense) = (800, 1200);
} else {
abort 0
};
(attack, defense)
}
代码中的数字源自游戏数据设计。然而,很明显,这段代码的实现充满了“魔法数字”,使审阅者或项目维护者在阅读代码时难以理解其含义。
一个广泛采用的良好实践是将这些数字定义为常量
:
/// 通过种族获取初始攻击力和防御力
fun get_initial_attack_defense_power(race: u64): (u64, u64) {
let (attack, defense);
if (race == CASTLE_RACE_HUMAN) {
(attack, defense) = (INITIAL_ATTCK_POWER_HUMAN, INITIAL_DEFENSE_POWER_HUMAN);
} else if (race == CASTLE_RACE_ELF) {
(attack, defense) = (INITIAL_ATTCK_POWER_ELF, INITIAL_DEFENSE_POWER_ELF);
} else if (race == CASTLE_RACE_ORCS) {
(attack, defense) = (INITIAL_ATTCK_POWER_ORCS, INITIAL_DEFENSE_POWER_ORCS);
} else if (race == CASTLE_RACE_GOBLIN) {
(attack, defense) = (INITIAL_ATTCK_POWER_GOBLIN, INITIAL_DEFENSE_POWER_GOBLIN);
} else if (race == CASTLE_RACE_UNDEAD) {
(attack, defense) = (INITIAL_ATTCK_POWER_UNDEAD, INITIAL_DEFENSE_POWER_UNDEAD);
} else {
abort 0
};
(attack, defense)
}
/// 城堡种族 - 人类
const CASTLE_RACE_HUMAN : u64 = 0;
/// 城堡种族 - 精灵
const CASTLE_RACE_ELF : u64 = 1;
/// 城堡种族 - 兽人
const CASTLE_RACE_ORCS : u64 = 2;
/// 城堡种族 - 哥布林
const CASTLE_RACE_GOBLIN : u64 = 3;
/// 城堡种族 - 僵尸
const CASTLE_RACE_UNDEAD : u64 = 4;
/// 初始攻击力 - 人类城堡
const INITIAL_ATTCK_POWER_HUMAN : u64 = 1000;
/// 初始攻击力 - 精灵城堡
const INITIAL_ATTCK_POWER_ELF : u64 = 500;
/// 初始攻击力 - 兽人城堡
const INITIAL_ATTCK_POWER_ORCS : u64 = 1500;
/// 初始攻击力 - 哥布林城堡
const INITIAL_ATTCK_POWER_GOBLIN : u64 = 1200;
/// 初始攻击力 - 僵尸城堡
const INITIAL_ATTCK_POWER_UNDEAD : u64 = 800;
/// 初始防御力 - 人类城堡
const INITIAL_DEFENSE_POWER_HUMAN : u64 = 1000;
/// 初始防御力 - 精灵城堡
const INITIAL_DEFENSE_POWER_ELF : u64 = 1500;
/// 初始防御力 - 兽人城堡
const INITIAL_DEFENSE_POWER_ORCS : u64 = 500;
/// 初始防御力 - 哥布林城堡
const INITIAL_DEFENSE_POWER_GOBLIN : u64 = 800;
/// 初始防御力 - 僵尸城堡
const INITIAL_DEFENSE_POWER_UNDEAD : u64 = 1200;
同样,初始经济实力依赖于城堡的大小,在同一个模块中添加 get_initial_economic_power
函数:
// 通过城堡大小获取初始经济实力
fun get_initial_economic_power(size: u64): u64 {
let power;
if (size == CASTLE_SIZE_SMALL) {
power = INITIAL_ECONOMIC_POWER_SMALL_CASTLE;
} else if (size == CASTLE_SIZE_MIDDLE) {
power = INITIAL_ECONOMIC_POWER_MIDDLE_CASTLE;
} else if (size == CASTLE_SIZE_BIG) {
power = INITIAL_ECONOMIC_POWER_BIG_CASTLE;
} else {
abort 0
};
power
}
/// 城堡大小 - 小型
const CASTLE_SIZE_SMALL : u64 = 1;
/// 城堡大小 - 中型
const CASTLE_SIZE_MIDDLE : u64 = 2;
/// 城堡大小 - 大型
const CASTLE_SIZE_BIG : u64 = 3;
/// 初始经济实力 - 小型城堡
const INITIAL_ECONOMIC_POWER_SMALL_CASTLE : u64 = 100;
/// 初始经济实力 - 中型城堡
const INITIAL_ECONOMIC_POWER_MIDDLE_CASTLE : u64 = 150;
/// 初始经济实力 - 大型城堡
const INITIAL_ECONOMIC_POWER_BIG_CASTLE : u64 = 250;
别忘了初始化城堡的总攻击/防御力,它由基础力量和士兵的力量组成。士兵的攻击力和防御力取决于他们的种族,要获得单个士兵的力量,在 core.move
中添加这个函数:
/// 城堡单个士兵的攻击力和防御力
public(package) fun get_castle_soldier_attack_defense_power(race: u64): (u64, u64) {
let soldier_attack_power;
let soldier_defense_power;
if (race == CASTLE_RACE_HUMAN) {
soldier_attack_power = SOLDIER_ATTACK_POWER_HUMAN;
soldier_defense_power = SOLDIER_DEFENSE_POWER_HUMAN;
} else if (race == CASTLE_RACE_ELF) {
soldier_attack_power = SOLDIER_ATTACK_POWER_ELF;
soldier_defense_power = SOLDIER_DEFENSE_POWER_ELF;
} else if (race == CASTLE_RACE_ORCS) {
soldier_attack_power = SOLDIER_ATTACK_POWER_ORCS;
soldier_defense_power = SOLDIER_DEFENSE_POWER_ORCS;
} else if (race == CASTLE_RACE_GOBLIN) {
soldier_attack_power = SOLDIER_ATTACK_POWER_GOBLIN;
soldier_defense_power = SOLDIER_DEFENSE_POWER_GOBLIN;
} else if (race == CASTLE_RACE_UNDEAD) {
soldier_attack_power = SOLDIER_ATTACK_POWER_UNDEAD;
soldier_defense_power = SOLDIER_DEFENSE_POWER_UNDEAD;
} else {
abort 0
};
(soldier_attack_power, soldier_defense_power)
}
/// 通过种族和士兵数量获取初始士兵的攻击力和防御力
fun get_initial_soldiers_attack_defense_power(race: u64, soldiers: u64): (u64, u64) {
let (attack, defense) = get_castle_soldier_attack_defense_power(race);
(attack * soldiers, defense * soldiers)
}
/// 士兵攻击力 - 人类
const SOLDIER_ATTACK_POWER_HUMAN : u64 = 100;
/// 士兵防御力 - 人类
const SOLDIER_DEFENSE_POWER_HUMAN : u64 = 100;
/// 士兵攻击力 - 精灵
const SOLDIER_ATTACK_POWER_ELF : u64 = 50;
/// 士兵防御力 - 精灵
const SOLDIER_DEFENSE_POWER_ELF : u64 = 150;
/// 士兵攻击力 - 兽人
const SOLDIER_ATTACK_POWER_ORCS : u64 = 150;
/// 士兵防御力 - 兽人
const SOLDIER_DEFENSE_POWER_ORCS : u64 = 50;
/// 士兵攻击力 - 哥布林
const SOLDIER_ATTACK_POWER_GOBLIN : u64 = 120;
/// 士兵防御力 - 哥布林
const SOLDIER_DEFENSE_POWER_GOBLIN : u64 = 80;
/// 士兵攻击力 - 僵尸
const SOLDIER_ATTACK_POWER_UNDEAD : u64 = 120;
/// 士兵防御力 - 僵尸
const SOLDIER_DEFENSE_POWER_UNDEAD : u64 = 80;
然后我们在 core.move
模块中添加一个 init_castle_data
函数,并初始化 CastleData
对象:
/// 初始化城堡数据
public(package) fun init_castle_data(id: ID,
size: u64,
race: u64,
current_timestamp: u64,
game_store: &mut GameStore) {
// 1. 获取初始力量并初始化城堡数据
let (attack_power, defense_power) = get_initial_attack_defense_power(race);
let (soldiers_attack_power, soldiers_defense_power) = get_initial_soldiers_attack_defense_power(race, INITIAL_SOLDIERS);
let castle_data = CastleData {
id: id,
size: size,
race: race,
level: 1,
experience_pool: 0,
economy: Economy {
treasury: 0,
base_power: get_initial_economic_power(size),
settle_time: current_timestamp,
soldier_buff: EconomicBuff {
debuff: false,
power: SOLDIER_ECONOMIC_POWER * INITIAL_SOLDIERS,
start: current_timestamp,
end: 0
},
battle_buff: vector::empty<EconomicBuff>()
},
millitary: Millitary {
attack_power: attack_power,
defense_power: defense_power,
total_attack_power: attack_power + soldiers_attack_power,
total_defense_power: defense_power + soldiers_defense_power,
soldiers: INITIAL_SOLDIERS,
battle_cooldown: current_timestamp
}
};
}
/// 初始士兵数量
const INITIAL_SOLDIERS : u64 = 10;
/// 士兵经济力量
const SOLDIER_ECONOMIC_POWER : u64 = 1;
还记得我们之前学过的动态字段吗?让我们通过使用 动态字段
将城堡数据存储在游戏存储对象下面来实践它:
use sui::dynamic_field;
// 2. 存储城堡数据
dynamic_field::add(&mut game_store.id, id, castle_data);
还需要更新其他一些游戏数据:
// 3. 更新城堡 ID 和城堡数量
vector::push_back(&mut game_store.castle_ids, id);
if (size == CASTLE_SIZE_SMALL) {
game_store.small_castle_count = game_store.small_castle_count + 1;
} else if (size == CASTLE_SIZE_MIDDLE) {
game_store.middle_castle_count = game_store.middle_castle_count + 1;
} else if (size == CASTLE_SIZE_BIG) {
game_store.big_castle_count = game_store.big_castle_count + 1;
} else {
abort 0
};
因此,整个 init_castle_data
函数如下:
/// 初始化城堡数据
public(package) fun init_castle_data(id: ID,
size: u64,
race: u64,
current_timestamp: u64,
game_store: &mut GameStore) {
// 1. 获取初始力量并初始化城堡数据
let (attack_power, defense_power) = get_initial_attack_defense_power(race);
let (soldiers_attack_power, soldiers_defense_power) = get_initial_soldiers_attack_defense_power(race, INITIAL_SOLDIERS);
let castle_data = CastleData {
id: id,
size: size,
race: race,
level: 1,
experience_pool: 0,
economy: Economy {
treasury: 0,
base_power: get_initial_economic_power(size),
settle_time: current_timestamp,
soldier_buff: EconomicBuff {
debuff: false,
power: SOLDIER_ECONOMIC_POWER * INITIAL_SOLDIERS,
start: current_timestamp,
end: 0
},
battle_buff: vector::empty()
},
millitary: Millitary {
attack_power: attack_power,
defense_power: defense_power,
total_attack_power: attack_power + soldiers_attack_power,
total_defense_power: defense_power + soldiers_defense_power,
soldiers: INITIAL_SOLDIERS,
battle_cooldown: current_timestamp
}
};
// 2. 存储城堡数据
dynamic_field::add(&mut game_store.id, id, castle_data);
// 3. 更新城堡 ID 和城堡数量
vector::push_back(&mut game_store.castle_ids, id);
if (size == CASTLE_SIZE_SMALL) {
game_store.small_castle_count = game_store.small_castle_count + 1;
} else if (size == CASTLE_SIZE_MIDDLE) {
game_store.middle_castle_count = game_store.middle_castle_count + 1;
} else if (size == CASTLE_SIZE_BIG) {
game_store.big_castle_count = game_store.big_castle_count + 1;
} else {
abort 0
};
}
在建造城堡时初始化城堡数据,在 castle.move
中的 build_castle
函数中添加以下逻辑:
use sui::clock::{Self, Clock};
use move_castle::core::{Self, GameStore};
/// 建造城堡
entry fun build_castle(size: u64, name_bytes: vector<u8>, desc_bytes: vector<u8>, clock: &Clock, game_store: &mut GameStore, ctx: &mut TxContext) {
...
// 新的城堡对象
let castle = Castle {...
// 新的城堡游戏数据
let id = object::uid_to_inner(&castle.id);
let race = get_castle_race(serial_number);
core::init_castle_data(
id,
size,
race,
clock::timestamp_ms(clock),
game_store
);
// 将城堡对象转移给所有者
...
}
/// 获取城堡种族
public fun get_castle_race(serial_number: u64): u64 {
let mut race_number = serial_number % 10;
if (race_number >= 5) {
race_number = race_number - 5;
};
race_number
}