如何使用Discord.py开发21大冒险游戏 实现轮流输入与数字校验
21大冒险Discord机器人开发指南
核心功能实现
1. 玩家轮流操作机制
拿到通过反应收集的players列表后,首先过滤掉机器人自身账号,避免把机器人算入玩家队列。之后维护一个current_player_idx整数变量,初始值为0,每完成一名玩家的操作后将变量加1,再对玩家总数取模,即可循环按顺序调度玩家。
注意不要使用同步的time.sleep(10),会阻塞整个机器人的所有指令响应,替换为await asyncio.sleep(10)异步等待。
2. 玩家输入校验规则
维护两个游戏状态变量:last_num记录上一名玩家输出的最后一个数字,初始值为0;current_round记录当前轮次。
每条玩家输入需要同时满足以下条件才视为有效:
- 发送消息的用户为当前轮次指定的玩家,且消息发送频道为游戏所在频道
- 消息内容按分隔符(支持顿号、空格)拆分后可转为1-3个正整数
- 拆分后的第一个数字等于
last_num + 1 - 所有数字为连续递增序列,无跳数、无重复
- 若数字序列包含21,当前玩家直接判定为落败
修正后可运行代码
import asyncio import discord from discord.ext import commands class GameCog(commands.Cog): def __init__(self, bot): self.bot = bot @commands.command() async def tod(self, ctx, user: discord.Member = None): '''21大冒险游戏''' the_author = ctx.author channel = ctx.channel # 邀请逻辑 if user is None: embed = discord.Embed(title="21大冒险游戏", color=discord.Colour.orange(), description=f"{the_author.mention} 正在邀请大家玩21大冒险!\n\n点击下方✅反应参与游戏,10秒后自动开始。") elif user != the_author and not user.bot: embed = discord.Embed(title="21大冒险游戏", color=discord.Colour.orange(), description=f"{the_author.mention} 邀请 {user.mention} 玩21大冒险!\n\n点击下方✅反应参与游戏,10秒后自动开始。") else: await ctx.send(embed=discord.Embed(title="错误:不能邀请自己或者机器人参与游戏!")) return msg = await channel.send(embed=embed) await msg.add_reaction("✅") # 异步等待10秒收集参与者 await asyncio.sleep(10) # 刷新消息状态,获取最新反应用户 msg = await channel.fetch_message(msg.id) reaction = msg.reactions[0] players = [user async for user in reaction.users() if not user.bot] if len(players) < 2: await channel.send(embed=discord.Embed(title="参与人数不足2人,游戏取消")) return # 初始化游戏状态 current_player_idx = 0 last_num = 0 game_over = False while not game_over: current_player = players[current_player_idx] await channel.send(f"轮到 {current_player.mention} 输入数字,请输入1-3个连续数字,首个数字需为 {last_num + 1}") # 校验函数 def check(message): if message.author != current_player or message.channel != channel: return False # 尝试拆分转数字 try: # 支持顿号、空格分隔 parts = message.content.replace('、', ' ').split() nums = [int(p) for p in parts] except: return False # 校验数量 if len(nums) <1 or len(nums)>3: return False # 校验首数字 if nums[0] != last_num + 1: return False # 校验连续 for i in range(len(nums)-1): if nums[i+1] != nums[i] +1: return False # 校验是否超过21 if nums[-1] >21: return False return True try: resp = await self.bot.wait_for('message', check=check, timeout=30) except asyncio.TimeoutError: await channel.send(f"{current_player.mention} 超时未作答,判负!") game_over = True break # 处理有效输入 nums = [int(p) for p in resp.content.replace('、', ' ').split()] # 判断是否踩中21 if 21 in nums: await channel.send(f"{current_player.mention} 说出了21,本轮游戏落败!请接受大冒险惩罚~") game_over = True break # 更新状态 last_num = nums[-1] # 切换到下一名玩家 current_player_idx = (current_player_idx + 1) % len(players)
Discord.py开发小技巧
- 所有等待、延迟操作都使用asyncio提供的异步方法,避免使用同步阻塞方法导致机器人无响应
- 收集反应用户时,discord.py v2.0以上版本需要用异步迭代方式获取用户,不要用已经废弃的
flatten()方法 - 校验函数要同时校验发送者、频道两个维度,避免其他频道的消息或者其他玩家的消息干扰游戏流程
- 游戏相关的状态变量都定义在游戏函数内部,不要用全局变量,避免同时开多局游戏时状态冲突
内容的提问来源于stack exchange,提问作者Dhravya Shah
相关产品推荐
相关产品推荐

