{"content":{"title":"Sui 卡牌对战小游戏","body":"## 1. 背景\r\n\r\n学习 Move 基础语法以来，自我感觉需要一个自行设计的小项目练手。\r\n\r\n[点击试玩](https://sui-card-battle.pages.dev/)\r\n\r\n现设计一个卡牌对战小游戏功能如下：\r\n\r\n\t- [ ] 玩家每次参与对战获取一定对血量和行动值，回合制对战，直到一方血量归 0，游戏结束\r\n\t- [ ] 卡牌拥有攻击力和防御力，卡牌参与对战后不可再作他用\r\n\t- [ ] 每回合结束，双方卡牌随机变换攻击力与防御力，全靠运气\r\n\t- [ ] 玩家每次行动消耗行动值，防守增加行动值\r\n\t- [ ] 其他\r\n\r\n[合约部分](https://github.com/inscriptiontek/sui-card-battle/tree/main/contracts/game)\r\n[页面部分](https://github.com/inscriptiontek/sui-card-battle/tree/main/website)\r\n\r\n## 2. 合约部分\r\n\r\n### 2.1 初始化函数\r\n\r\n需要两个共享对象记录卡牌与所有的对局，分别命名为`CardRecord`, `BattleRecord`\r\n\r\n```rust\r\nfun init(ctx: &mut TxContext) {\r\n  let battle_record = BattleRecord {\r\n    id: object::new(ctx),\r\n    battles: vector::empty<ID>(),\r\n  };\r\n  let card_record = CardRecord {\r\n    id: object::new(ctx),\r\n    cards: vector::empty<ID>(),\r\n  };\r\n\r\n  share_object(battle_record);\r\n  share_object(card_record)\r\n}\r\n```\r\n\r\n### 2.2 铸造卡牌\r\n\r\n```rust\r\nfun mint_card(clock:&Clock,ctx: &mut TxContext) : Card {\r\n  let (attack, defense) = random_strength_clock(clock);\r\n\r\n  Card {\r\n    id: object::new(ctx),\r\n    attack,\r\n    defense,\r\n  }\r\n}\r\n```\r\n\r\n这里用到随机数，有几种生成方式：\r\n\r\n1. 使用 `CLOCK`或者交易摘要生成；\r\n2. 使用天气预言机；\r\n\r\n简单点就使用`CLOCK`获取时间戳作为随机数。\r\n\r\n```rust\r\nfun random_strength_clock(clock: &Clock) :(u64, u64){\r\n  let ms = clock::timestamp_ms(clock);\r\n  let random_attack = ms % MAX_ATTACK_DEFEND_STRENGTH;\r\n  if (random_attack == 0) {\r\n    random_attack = MAX_ATTACK_DEFEND_STRENGTH / 2;\r\n  };\r\n\r\n  (random_attack, MAX_ATTACK_DEFEND_STRENGTH - random_attack)\r\n}\r\n```\r\n\r\n### 2.3 创建对局\r\n\r\n结构体定义\r\n\r\n```rust\r\nstruct Battle has key,store {\r\n  id: UID,\r\n  name: String,\r\n  status: u64, // 记录对局状态\r\n  cards: Table<address, Card>, // 对战人卡片\r\n  players: vector<address>, // 玩家\r\n  player_status: Table<address, PlayerStatus>, // 双方状态\r\n  moves: Table<address, u64>, // 当前回合行动，判断是否回合结束\r\n  winer: Option<address>,\r\n}\r\n```\r\n\r\n### 2.4 行动\r\n\r\n对应函数`public entry fun move_choice(choice: u64, battle: &mut Battle, ctx: &mut TxContext)`\r\n\r\n当双方行动结束时结算当前玩家状态，对应函数`fun resolve_battle(battle:&mut Battle,ctx: &TxContext)`\r\n\r\n### 2.5 部署到测试网\r\n\r\n记录以下三个值\r\n\r\n```bash\r\nTESTNET_CARD_PACKAGE_ID = \"0x43c21577794c10f7f36d3269d50736c45f9dc4d0c77e3c7d0f219b0a3fd0ae4d\";\r\n\r\nCARD_RECORD: string = \"0x7d6444bffd59a7b348613637422a1f335305204e4fea5be1d86d325c3eaf108b\";\r\n\r\nBATTLE_RECORD = \"0x2b1b5fc902bf675dc6acf48b61a13a27989383c4f9fba460c03c103c41faf800\";\r\n```\r\n\r\n## 3. 页面部分\r\n\r\n### 3.1 如何获取到对局中的玩家状态\r\n思路：全剧共享对象`BattleRecord`获取到`Battle_id`，拿到当前对象。\r\n\r\n接下来要用到动态字段获取才能知道玩家状态数据，这个过程需要多次查询请求。\r\n\r\n```jsx title=123\r\n// 获取对局对象\r\nconst { battleData } = useSuiClientQuery(\"getObject\", {\r\n    id: battleId ? battleId : \"\",\r\n    options: {\r\n      showContent: true,\r\n      showOwner: true,\r\n    },\r\n});\r\n\r\n// 通过 table_id 获取内容\r\nconst { players } = client.getDynamicFields({\r\n    parentId: battleData.player_status.fields.id.id,\r\n})\r\n\r\n```\r\n\r\n## 4. 试玩\r\n### 4.1 铸造卡牌\r\n打开页面，首先连接钱包登陆。\r\n点击`Mint Card For Free`铸造卡牌，稍等一会即可在`All Your Cards`区域看到你的卡牌。\r\n\r\n![first_step.png](https://img.learnblockchain.cn/attachments/2024/03/SOkOTDhZ65f9434111202.png)\r\n\r\n### 4.2 创建对局\r\n\r\n点击`Begin Game`跳转到新页面，输入房间名（仅英文）创建对局，此时默认使用你的第一张卡牌放入对局中（此卡牌已转移所有权，不再属于你）。\r\n\r\n![create_battle.png](https://img.learnblockchain.cn/attachments/2024/03/tEM4HyU065f94385ec774.png)\r\n\r\n点击`Join`可以进入房间\r\n\r\n![join.png](https://img.learnblockchain.cn/attachments/2024/03/kGz69UWz65f943d5c7cdc.png)\r\n\r\n下方显示自己的行动力和血条，中间是自己的卡牌信息。等待另一玩家加入战斗。\r\n### 4.2 另一玩家加入对局\r\n\r\n另准备一个钱包地址，`Mint Card` --> `Begin Game` --> `Join`，即可加入目标房间进行战斗，默认使用你的第一张卡牌。\r\n\r\n![battled.png](https://img.learnblockchain.cn/attachments/2024/03/kSwTDuce65f9442be8fd3.png)\r\n\r\n上方显示对方的血量和行动力，以及对方卡牌信息。\r\n\r\n### 4.3 行动\r\n\r\n接下来双方分别进行攻击和防御操作，直到一方血量归零，决出胜负为止。\r\n\r\n---\r\n\r\n星航计划 QQ群：79489587"},"author":{"user":"https://learnblockchain.cn/people/18893","address":null},"history":null,"timestamp":1710834847,"version":1}