Skip to content

Commit 931275f

Browse files
committed
✨ feat: 添加 7 的倍数示例游戏
1 parent 110238b commit 931275f

4 files changed

Lines changed: 118 additions & 36 deletions

File tree

docs/game/deploy.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ npm run dev:frontend # 仅启动前端
2727

2828
VSCode 按下 F5 可直接启动调试后端。
2929

30+
本地在登录时可以使用任意用户名模拟登录,如果是需要积分的游戏,可以使用摸鱼派已存在的用户名登录。
31+
32+
![模拟登录](/login.png)
33+
3034
## 生产构建
3135

3236
```bash

docs/game/getting-started.md

Lines changed: 106 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,55 @@
44

55
## 1. 创建后端逻辑
66

7-
`game/backend/src/games` 目录下创建 `mygame.ts` 文件。
7+
`game/backend/src/games` 目录下创建 `click.ts` 文件。
88

99
```typescript
1010
import { GameRoom, IGameCommand } from '.';
11+
import { RoomPlayer, PlayerRole } from 'tiaoom';
1112

1213
// 定义游戏基本属性
13-
export const name = '我的游戏'; // 游戏名称
14+
export const name = '7 的倍数'; // 游戏名称
1415
export const minSize = 2; // 最小玩家数
1516
export 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
80127
const props = defineProps<{
81-
roomPlayer: RoomPlayer & { room: Room }
82-
game: GameCore
128+
roomPlayer: RoomPlayer & { room: Room };
129+
game: GameCore;
83130
}>();
84131
85132
const count = ref(0);
133+
const clickNumber = ref(1);
86134
87135
function 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
```

docs/public/login.png

4.77 KB
Loading

game/frontend/src/components/common/PlayerList.vue

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
@click="$router.push(`/u/${p.attributes.username}`)"
99
>
1010
<slot :player="p">
11-
<span>[{{ p.isReady ? '已准备' : '未准备' }}]</span>
11+
<span>[{{ getStatus(p) }}]</span>
1212
<span>{{ p.name }}</span>
1313
</slot>
1414
</li>
@@ -31,4 +31,11 @@ import { PlayerStatus, RoomPlayer } from 'tiaoom/client'
3131
defineProps<{
3232
players: RoomPlayer[]
3333
}>()
34+
35+
function getStatus(p: RoomPlayer): string {
36+
if (p.role !== 'player') return '围观中';
37+
if (p.status === PlayerStatus.offline) return '已离线';
38+
if (p.status === PlayerStatus.playing) return '游戏中';
39+
return p.isReady ? '已准备' : '未准备'
40+
}
3441
</script>

0 commit comments

Comments
 (0)