44
55## 1. 创建后端逻辑
66
7- 在 ` game/backend/src/games ` 目录下创建 ` mygame .ts` 文件。
7+ 在 ` game/backend/src/games ` 目录下创建 ` click .ts` 文件。
88
99``` typescript
1010import { GameRoom , IGameCommand } from ' .' ;
11+ import { RoomPlayer , PlayerRole } from ' tiaoom' ;
1112
1213// 定义游戏基本属性
13- export const name = ' 我的游戏 ' ; // 游戏名称
14+ export const name = ' 7 的倍数 ' ; // 游戏名称
1415export const minSize = 2 ; // 最小玩家数
1516export const maxSize = 2 ; // 最大玩家数
16- export const description = ' 这是一个示例游戏 ' ; // 游戏描述
17+ export const description = ' 玩家轮流点击按钮增加计数,谁让计数变成 7 的倍数谁就获胜,计数超过 50 则失败。 ' ; // 游戏描述
1718
18- export default class MyGameRoom extends GameRoom {
19+ export default class ClickRoom extends GameRoom {
1920 count = 0 ;
21+ currentPlayer: RoomPlayer | undefined ;
2022
2123 // 游戏开始时调用
2224 onStart() {
2325 this .count = 0 ;
2426 // 广播初始状态
27+ this .currentPlayer = this .room .validPlayers [0 ];
2528 this .room .emit (' command' , { type: ' update' , data: { count: this .count } });
29+ this .room .emit (' command' , { type: ' click' , data: { player: this .currentPlayer } });
30+ this .save (); // 保存初始状态
2631 }
2732
2833 // 处理玩家指令
2934 onCommand(message : IGameCommand ) {
3035 super .onCommand (message ); // 处理通用指令
3136 if (message .type === ' click' ) {
32- this .count ++ ;
37+ this .count += Number ( message . data - 1 ) % 5 + 1 ; // 增加计数,确保增加量在 1-5 之间
3338 // 广播新状态
3439 this .room .emit (' command' , { type: ' update' , data: { count: this .count } });
3540 // 保存状态
3641 this .save ();
42+ if (this .count % 7 === 0 ) {
43+ this .saveAchievements (this .room .validPlayers .filter (p => p .id === message .sender .id ));
44+ this .say (` ${message .sender .name } 计数达到 ${this .count },是 7 的倍数,获胜! ` );
45+ } else if (this .count > 50 ) {
46+ // 先大于 50 分算输
47+ this .saveAchievements (this .room .validPlayers .filter (p => p .id !== message .sender .id ));
48+ this .say (` ${message .sender .name } 计数达到 ${this .count },超过 50 分,惜败! ` );
49+ } else {
50+ // 未大于 50 且不是 7 的倍数继续游戏
51+ this .currentPlayer = this .room .validPlayers .find (p => p .id !== message .sender .id );
52+ this .room .emit (' command' , { type: ' click' , data: { player: this .currentPlayer } });
53+ return ;
54+ }
55+ this .room .end ();
3756 }
3857 }
3958
@@ -42,59 +61,111 @@ export default class MyGameRoom extends GameRoom {
4261 return {
4362 ... super .getStatus (sender ),
4463 count: this .count ,
64+ currentPlayer: this .currentPlayer ,
4565 };
4666 }
4767}
4868```
4969
5070## 2. 创建前端组件
5171
52- 在 ` game/frontend/src/components/games ` 目录下创建 ` MygameRoom .vue` 文件。
72+ 在 ` game/frontend/src/components/games ` 目录下创建 ` ClickRoom .vue` 文件。
5373
5474``` vue
5575<template>
56- <div class="flex flex-col items-center justify-center h-full gap-4">
57- <h1 class="text-4xl font-bold">{{ count }}</h1>
58- <button class="btn btn-primary" @click="handleClick">点击 +1</button>
59-
60- <!-- 玩家列表 -->
61- <PlayerList :players="roomPlayer.room.players" />
62-
63- <!-- 聊天窗口与游戏规则 -->
64- <GameChat>
65- <template #rules>
66- <ul class="space-y-2 text-sm">
67- <li>1. 游戏规则1</li>
68- <li>2. 游戏规则2</li>
69- </ul>
70- </template>
71- </GameChat>
72- </div>
76+ <section class="flex flex-col md:flex-row gap-4 md:h-full">
77+ <!-- 左侧:游戏区域 -->
78+ <div class="flex-1 flex flex-col items-center justify-center">
79+ <h1 class="text-[50px] font-bold p-4">{{ count }}</h1>
80+ <!-- 操作 -->
81+ <div class="join">
82+ <input
83+ type="number"
84+ v-model.number="clickNumber"
85+ class="input input-bordered w-24 text-center join-item"
86+ min="1"
87+ max="5"
88+ />
89+ <button
90+ class="btn btn-primary join-item"
91+ @click="handleClick"
92+ :disabled="!isPlaying"
93+ >
94+ 点击 +{{ clickNumber }}
95+ </button>
96+ </div>
97+ </div>
98+
99+ <!-- 右侧:侧边栏 -->
100+ <aside
101+ class="w-full md:w-96 flex-none border-t md:border-t-0 md:border-l border-base-content/20 pt-4 md:pt-0 md:pl-4 space-y-4 md:h-full flex flex-col"
102+ >
103+
104+ <!-- 玩家列表 -->
105+ <PlayerList :players="roomPlayer.room.players" />
106+
107+ <!-- 聊天窗口与游戏规则 -->
108+ <GameChat>
109+ <template #rules>
110+ <ul class="space-y-2 text-sm">
111+ <li>1. 双方轮流点击按钮</li>
112+ <li>2. 当计数达到 7 的倍数时,当前玩家获胜</li>
113+ <li>3. 当计数大于 50 时,非当前玩家获胜</li>
114+ </ul>
115+ </template>
116+ </GameChat>
117+ </aside>
118+ </section>
73119</template>
74120
75121<script setup lang="ts">
76- import { ref, onMounted } from 'vue';
77- import { RoomPlayer, Room } from 'tiaoom/client';
78- import { GameCore } from '@/core/game';
122+ import { computed, ref } from "vue";
123+ import { RoomPlayer, Room } from "tiaoom/client";
124+ import { GameCore } from "@/core/game";
125+ import { useGameEvents } from "@/hook/useGameEvents";
79126
80127const props = defineProps<{
81- roomPlayer: RoomPlayer & { room: Room }
82- game: GameCore
128+ roomPlayer: RoomPlayer & { room: Room };
129+ game: GameCore;
83130}>();
84131
85132const count = ref(0);
133+ const clickNumber = ref(1);
86134
87135function handleClick() {
88- props.game.command({ type: ' click' });
136+ props.game.command(props.roomPlayer.room.id, { type: " click", data: clickNumber.value });
89137}
90138
91- onMounted(() => {
92- // 监听后端消息
93- props.game.on('command', (msg) => {
94- if (msg.type === 'update') {
139+ const currentPlayer = ref<RoomPlayer | null>(null);
140+ useGameEvents(props.game, {
141+ "player.command": onCommand,
142+ "room.command": onCommand,
143+ });
144+
145+ function onCommand(msg: any) {
146+ switch(msg.type) {
147+ case "click":
148+ if (msg.data.player) {
149+ currentPlayer.value = msg.data.player;
150+ }
151+ break;
152+ case "update":
95153 count.value = msg.data.count;
96- }
97- });
154+ break;
155+ case "status":
156+ if (msg.data.currentPlayer) {
157+ currentPlayer.value = msg.data.currentPlayer;
158+ }
159+ break;
160+ }
161+ }
162+
163+ const isPlaying = computed(() => {
164+ return (
165+ props.roomPlayer.role === "player" &&
166+ props.roomPlayer.room.status === "playing" &&
167+ currentPlayer.value?.id === props.roomPlayer.id
168+ );
98169});
99170</script>
100171```
0 commit comments