Sui 卡牌对战小游戏

  • sycute
  • 更新于 2024-03-19 15:54
  • 阅读 714

学习 Move 基础语法之后,设计的小项目练手。 记录实现过程中遇到的核心问题,同时试用 Sui-SDK 实现配套页面功能。

1. 背景

学习 Move 基础语法以来,自我感觉需要一个自行设计的小项目练手。

点击试玩

现设计一个卡牌对战小游戏功能如下:

- [ ] 玩家每次参与对战获取一定对血量和行动值,回合制对战,直到一方血量归 0,游戏结束
- [ ] 卡牌拥有攻击力和防御力,卡牌参与对战后不可再作他用
- [ ] 每回合结束,双方卡牌随机变换攻击力与防御力,全靠运气
- [ ] 玩家每次行动消耗行动值,防守增加行动值
- [ ] 其他

合约部分 页面部分

2. 合约部分

2.1 初始化函数

需要两个共享对象记录卡牌与所有的对局,分别命名为CardRecord, BattleRecord

fun init(ctx: &mut TxContext) {
  let battle_record = BattleRecord {
    id: object::new(ctx),
    battles: vector::empty<ID>(),
  };
  let card_record = CardRecord {
    id: object::new(ctx),
    cards: vector::empty<ID>(),
  };

  share_object(battle_record);
  share_object(card_record)
}

2.2 铸造卡牌

fun mint_card(clock:&Clock,ctx: &mut TxContext) : Card {
  let (attack, defense) = random_strength_clock(clock);

  Card {
    id: object::new(ctx),
    attack,
    defense,
  }
}

这里用到随机数,有几种生成方式:

  1. 使用 CLOCK或者交易摘要生成;
  2. 使用天气预言机;

简单点就使用CLOCK获取时间戳作为随机数。

fun random_strength_clock(clock: &Clock) :(u64, u64){
  let ms = clock::timestamp_ms(clock);
  let random_attack = ms % MAX_ATTACK_DEFEND_STRENGTH;
  if (random_attack == 0) {
    random_attack = MAX_ATTACK_DEFEND_STRENGTH / 2;
  };

  (random_attack, MAX_ATTACK_DEFEND_STRENGTH - random_attack)
}

2.3 创建对局

结构体定义

struct Battle has key,store {
  id: UID,
  name: String,
  status: u64, // 记录对局状态
  cards: Table<address, Card>, // 对战人卡片
  players: vector<address>, // 玩家
  player_status: Table<address, PlayerStatus>, // 双方状态
  moves: Table<address, u64>, // 当前回合行动,判断是否回合结束
  winer: Option<address>,
}

2.4 行动

对应函数public entry fun move_choice(choice: u64, battle: &mut Battle, ctx: &mut TxContext)

当双方行动结束时结算当前玩家状态,对应函数fun resolve_battle(battle:&mut Battle,ctx: &TxContext)

2.5 部署到测试网

记录以下三个值

TESTNET_CARD_PACKAGE_ID = "0x43c21577794c10f7f36d3269d50736c45f9dc4d0c77e3c7d0f219b0a3fd0ae4d";

CARD_RECORD: string = "0x7d6444bffd59a7b348613637422a1f335305204e4fea5be1d86d325c3eaf108b";

BATTLE_RECORD = "0x2b1b5fc902bf675dc6acf48b61a13a27989383c4f9fba460c03c103c41faf800";

3. 页面部分

3.1 如何获取到对局中的玩家状态

思路:全剧共享对象BattleRecord获取到Battle_id,拿到当前对象。

接下来要用到动态字段获取才能知道玩家状态数据,这个过程需要多次查询请求。

// 获取对局对象
const { battleData } = useSuiClientQuery("getObject", {
    id: battleId ? battleId : "",
    options: {
      showContent: true,
      showOwner: true,
    },
});

// 通过 table_id 获取内容
const { players } = client.getDynamicFields({
    parentId: battleData.player_status.fields.id.id,
})

4. 试玩

4.1 铸造卡牌

打开页面,首先连接钱包登陆。 点击Mint Card For Free铸造卡牌,稍等一会即可在All Your Cards区域看到你的卡牌。

first_step.png

4.2 创建对局

点击Begin Game跳转到新页面,输入房间名(仅英文)创建对局,此时默认使用你的第一张卡牌放入对局中(此卡牌已转移所有权,不再属于你)。

create_battle.png

点击Join可以进入房间

join.png

下方显示自己的行动力和血条,中间是自己的卡牌信息。等待另一玩家加入战斗。

4.2 另一玩家加入对局

另准备一个钱包地址,Mint Card --> Begin Game --> Join,即可加入目标房间进行战斗,默认使用你的第一张卡牌。

battled.png

上方显示对方的血量和行动力,以及对方卡牌信息。

4.3 行动

接下来双方分别进行攻击和防御操作,直到一方血量归零,决出胜负为止。


星航计划 QQ群:79489587

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
sycute
sycute
0x80d6...FdC9
电报联系 @luosanzhang