🎮 WxBot Enhanced 游戏插件开发指南¶
 **打造互动性强、用户体验佳的Enhanced游戏插件**
📋 目录导航¶
🎯 游戏插件概述¶
什么是Enhanced游戏插件¶
Enhanced游戏插件是WxBot Enhanced的核心特色,提供:
- 🎮 互动游戏体验 - 丰富的游戏玩法和用户交互
- 📈 用户成长系统 - 等级、积分、成就解锁
- 🏆 排行榜机制 - 激励用户持续参与
- 🎨 智能UI生成 - 自动生成游戏界面图片
- 📊 数据分析 - 游戏统计和用户画像
现有游戏插件生态¶
| 插件名称 | 类型 | 特色功能 |
|---|---|---|
| 猜成语Enhanced | 知识问答 | 图片生成、智能提示、成语知识库 |
| 成语接龙 | 文字游戏 | 多人参与、防重复检测、超时控制 |
| 音乐竞猜 | 娱乐竞技 | 音频播放、分阶段提示、积分奖励 |
| 歇后语游戏 | 传统文化 | 文化教育、随机题库、答案解析 |
| 签到系统 | 日常任务 | 连续签到、积分累积、排行统计 |
🏗️ 游戏架构设计¶
基础游戏框架¶
package mygame
import (
"sync"
"time"
"github.com/ruk1ng001/wxbot/engine/control"
"github.com/ruk1ng001/wxbot/engine/robot"
)
// 游戏状态枚举
type GameStatus int
const (
GameStatusIdle GameStatus = iota
GameStatusWaiting
GameStatusPlaying
GameStatusPaused
GameStatusEnded
)
// 游戏实例结构
type GameInstance struct {
GameID string `json:"game_id"`
GroupID string `json:"group_id"`
PlayerID string `json:"player_id"`
Status GameStatus `json:"status"`
Score int `json:"score"`
Level int `json:"level"`
StartTime time.Time `json:"start_time"`
LastUpdate time.Time `json:"last_update"`
GameData map[string]interface{} `json:"game_data"`
mutex sync.RWMutex
}
// 游戏管理器
type GameManager struct {
games map[string]*GameInstance
mutex sync.RWMutex
}
var globalGameManager = &GameManager{
games: make(map[string]*GameInstance),
}
插件注册模式¶
func init() {
engine := control.Register("mygame", &control.Options{
Alias: "我的游戏",
Help: "一个示例游戏插件",
DataFolder: "mygame",
DisableOnDefault: false,
})
// 游戏开始命令
engine.OnPrefix("开始游戏").SetBlock(true).Handle(startGameHandler)
// 游戏答题
engine.OnPrefix("答").Handle(answerHandler)
// 游戏状态查询
engine.OnFullMatch("游戏状态").Handle(statusHandler)
// 排行榜
engine.OnFullMatch("排行榜").Handle(leaderboardHandler)
// 管理员命令
engine.OnCommand("gameadmin", robot.AdminPermission).Handle(adminHandler)
}
🎲 游戏状态管理¶
状态管理核心代码¶
// 创建游戏实例
func (gm *GameManager) CreateGame(groupID, playerID string) *GameInstance {
gm.mutex.Lock()
defer gm.mutex.Unlock()
gameID := fmt.Sprintf("%s_%s_%d", groupID, playerID, time.Now().Unix())
game := &GameInstance{
GameID: gameID,
GroupID: groupID,
PlayerID: playerID,
Status: GameStatusWaiting,
Score: 0,
Level: 1,
StartTime: time.Now(),
LastUpdate: time.Now(),
GameData: make(map[string]interface{}),
}
gm.games[gameID] = game
return game
}
// 获取游戏实例
func (gm *GameManager) GetGame(groupID, playerID string) *GameInstance {
gm.mutex.RLock()
defer gm.mutex.RUnlock()
for _, game := range gm.games {
if game.GroupID == groupID && game.PlayerID == playerID &&
game.Status != GameStatusEnded {
return game
}
}
return nil
}
// 更新游戏状态
func (gi *GameInstance) UpdateStatus(status GameStatus) {
gi.mutex.Lock()
defer gi.mutex.Unlock()
gi.Status = status
gi.LastUpdate = time.Now()
}
// 增加分数
func (gi *GameInstance) AddScore(points int) {
gi.mutex.Lock()
defer gi.mutex.Unlock()
gi.Score += points
// 等级升级逻辑
newLevel := gi.Score/100 + 1
if newLevel > gi.Level {
gi.Level = newLevel
// 触发升级事件
gi.GameData["level_up"] = true
}
}
游戏数据持久化¶
import (
"gorm.io/gorm"
"github.com/ruk1ng001/wxbot/engine/pkg/sqlite"
)
// 游戏记录数据模型
type GameRecord struct {
ID uint `gorm:"primaryKey"`
GameID string `gorm:"column:game_id;uniqueIndex"`
GroupID string `gorm:"column:group_id;index"`
PlayerID string `gorm:"column:player_id;index"`
PlayerName string `gorm:"column:player_name"`
Score int `gorm:"column:score"`
Level int `gorm:"column:level"`
Duration int `gorm:"column:duration"` // 游戏时长(秒)
CompletedAt time.Time `gorm:"column:completed_at"`
CreatedAt time.Time `gorm:"column:created_at"`
}
// 保存游戏记录
func (gi *GameInstance) SaveRecord() error {
db := sqlite.GetDB()
record := &GameRecord{
GameID: gi.GameID,
GroupID: gi.GroupID,
PlayerID: gi.PlayerID,
Score: gi.Score,
Level: gi.Level,
Duration: int(time.Since(gi.StartTime).Seconds()),
CompletedAt: time.Now(),
CreatedAt: gi.StartTime,
}
return db.Create(record).Error
}
// 获取用户统计
func GetPlayerStats(playerID string) (*PlayerStats, error) {
db := sqlite.GetDB()
var stats PlayerStats
err := db.Model(&GameRecord{}).
Select("player_id, COUNT(*) as total_games, MAX(score) as best_score, AVG(score) as avg_score, SUM(duration) as total_time").
Where("player_id = ?", playerID).
Group("player_id").
Scan(&stats).Error
return &stats, err
}
👥 多人游戏支持¶
房间管理系统¶
// 游戏房间结构
type GameRoom struct {
RoomID string `json:"room_id"`
GroupID string `json:"group_id"`
OwnerID string `json:"owner_id"`
Players map[string]*Player `json:"players"`
Status GameStatus `json:"status"`
MaxPlayers int `json:"max_players"`
CurrentRound int `json:"current_round"`
RoomData map[string]interface{} `json:"room_data"`
CreatedAt time.Time `json:"created_at"`
mutex sync.RWMutex
}
// 玩家结构
type Player struct {
PlayerID string `json:"player_id"`
PlayerName string `json:"player_name"`
Score int `json:"score"`
Status string `json:"status"` // waiting/playing/finished
JoinedAt time.Time `json:"joined_at"`
}
// 房间管理器
type RoomManager struct {
rooms map[string]*GameRoom
mutex sync.RWMutex
}
var globalRoomManager = &RoomManager{
rooms: make(map[string]*GameRoom),
}
// 创建房间
func (rm *RoomManager) CreateRoom(groupID, ownerID string, maxPlayers int) *GameRoom {
rm.mutex.Lock()
defer rm.mutex.Unlock()
roomID := fmt.Sprintf("room_%s_%d", groupID, time.Now().Unix())
room := &GameRoom{
RoomID: roomID,
GroupID: groupID,
OwnerID: ownerID,
Players: make(map[string]*Player),
Status: GameStatusWaiting,
MaxPlayers: maxPlayers,
CurrentRound: 0,
RoomData: make(map[string]interface{}),
CreatedAt: time.Now(),
}
// 房主自动加入
room.AddPlayer(ownerID, "房主")
rm.rooms[roomID] = room
return room
}
// 加入房间
func (gr *GameRoom) AddPlayer(playerID, playerName string) bool {
gr.mutex.Lock()
defer gr.mutex.Unlock()
if len(gr.Players) >= gr.MaxPlayers {
return false
}
if _, exists := gr.Players[playerID]; exists {
return false
}
gr.Players[playerID] = &Player{
PlayerID: playerID,
PlayerName: playerName,
Score: 0,
Status: "waiting",
JoinedAt: time.Now(),
}
return true
}
多人游戏逻辑¶
// 多人游戏处理器
func multiPlayerGameHandler(ctx *robot.Ctx) {
groupID := ctx.Event.FromWxId
playerID := ctx.Event.FinalFromWxId
message := ctx.MessageString()
room := globalRoomManager.GetRoomByGroup(groupID)
if room == nil {
ctx.ReplyText("当前没有进行中的游戏,发送'创建房间'开始新游戏")
return
}
player, exists := room.Players[playerID]
if !exists {
ctx.ReplyText("你还没有加入游戏,发送'加入游戏'参与")
return
}
// 处理玩家回答
result := processPlayerAnswer(room, player, message)
// 发送结果
ctx.ReplyText(result.Message)
// 检查游戏是否结束
if result.GameEnd {
endMultiPlayerGame(ctx, room)
}
}
// 结束多人游戏
func endMultiPlayerGame(ctx *robot.Ctx, room *GameRoom) {
// 计算排名
rankings := calculateRankings(room)
// 发送结果
resultMsg := "🏆 游戏结束!最终排名:\\n"
for i, player := range rankings {
medal := "🥇"
if i == 1 {
medal = "🥈"
} else if i == 2 {
medal = "🥉"
} else {
medal = fmt.Sprintf("%d.", i+1)
}
resultMsg += fmt.Sprintf("%s %s - %d分\\n", medal, player.PlayerName, player.Score)
}
ctx.ReplyText(resultMsg)
// 保存游戏记录
saveMultiPlayerGameRecord(room, rankings)
// 清理房间
globalRoomManager.RemoveRoom(room.RoomID)
}
📊 用户成长体系¶
等级系统设计¶
// 用户成长数据模型
type UserGrowth struct {
ID uint `gorm:"primaryKey"`
UserID string `gorm:"column:user_id;uniqueIndex"`
UserName string `gorm:"column:user_name"`
Level int `gorm:"column:level;default:1"`
Experience int `gorm:"column:experience;default:0"`
TotalGames int `gorm:"column:total_games;default:0"`
TotalWins int `gorm:"column:total_wins;default:0"`
TotalScore int `gorm:"column:total_score;default:0"`
Achievements []string `gorm:"column:achievements;type:json"`
LastPlayed time.Time `gorm:"column:last_played"`
CreatedAt time.Time `gorm:"column:created_at"`
UpdatedAt time.Time `gorm:"column:updated_at"`
}
// 经验值配置
var expConfig = map[int]int{
1: 100, // 升到1级需要100经验
2: 300, // 升到2级需要300经验
3: 600, // 升到3级需要600经验
4: 1000, // 升到4级需要1000经验
5: 1500, // 升到5级需要1500经验
}
// 增加经验值
func (ug *UserGrowth) AddExperience(exp int) bool {
ug.Experience += exp
// 检查是否升级
oldLevel := ug.Level
for level, requiredExp := range expConfig {
if ug.Experience >= requiredExp && level > ug.Level {
ug.Level = level
}
}
return ug.Level > oldLevel // 返回是否升级
}
// 更新游戏统计
func UpdateUserStats(userID, userName string, gameResult GameResult) {
db := sqlite.GetDB()
var userGrowth UserGrowth
err := db.Where("user_id = ?", userID).First(&userGrowth).Error
if err != nil {
// 创建新用户
userGrowth = UserGrowth{
UserID: userID,
UserName: userName,
Level: 1,
Experience: 0,
}
}
// 更新统计
userGrowth.TotalGames++
userGrowth.TotalScore += gameResult.Score
userGrowth.LastPlayed = time.Now()
if gameResult.IsWin {
userGrowth.TotalWins++
}
// 增加经验
expGained := calculateExperience(gameResult)
levelUp := userGrowth.AddExperience(expGained)
// 检查成就
newAchievements := checkAchievements(&userGrowth, gameResult)
userGrowth.Achievements = append(userGrowth.Achievements, newAchievements...)
// 保存到数据库
if userGrowth.ID == 0 {
db.Create(&userGrowth)
} else {
db.Save(&userGrowth)
}
// 发送升级通知
if levelUp {
sendLevelUpNotification(userID, userGrowth.Level)
}
// 发送成就通知
for _, achievement := range newAchievements {
sendAchievementNotification(userID, achievement)
}
}
成就系统¶
// 成就定义
type Achievement struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Icon string `json:"icon"`
Condition func(*UserGrowth, GameResult) bool `json:"-"`
}
var achievements = []Achievement{
{
ID: "first_game",
Name: "初试身手",
Description: "完成第一场游戏",
Icon: "🎮",
Condition: func(ug *UserGrowth, gr GameResult) bool {
return ug.TotalGames == 1
},
},
{
ID: "win_streak_5",
Name: "连胜达人",
Description: "连续获胜5场",
Icon: "🔥",
Condition: func(ug *UserGrowth, gr GameResult) bool {
return gr.WinStreak >= 5
},
},
{
ID: "high_score_1000",
Name: "高分选手",
Description: "单局得分超过1000",
Icon: "⭐",
Condition: func(ug *UserGrowth, gr GameResult) bool {
return gr.Score >= 1000
},
},
}
// 检查成就
func checkAchievements(userGrowth *UserGrowth, gameResult GameResult) []string {
var newAchievements []string
for _, achievement := range achievements {
// 检查是否已获得
hasAchievement := false
for _, existingAchievement := range userGrowth.Achievements {
if existingAchievement == achievement.ID {
hasAchievement = true
break
}
}
// 检查条件
if !hasAchievement && achievement.Condition(userGrowth, gameResult) {
newAchievements = append(newAchievements, achievement.ID)
}
}
return newAchievements
}
🎨 游戏UI设计¶
图片生成系统¶
import (
"image"
"image/color"
"image/draw"
"image/png"
"golang.org/x/image/font"
"golang.org/x/image/font/basicfont"
"golang.org/x/image/math/fixed"
)
// 游戏UI生成器
type GameUIGenerator struct {
width int
height int
bgColor color.RGBA
font font.Face
}
// 创建游戏界面图片
func (g *GameUIGenerator) GenerateGameUI(gameData GameUIData) ([]byte, error) {
// 创建图片
img := image.NewRGBA(image.Rect(0, 0, g.width, g.height))
// 填充背景
draw.Draw(img, img.Bounds(), &image.Uniform{g.bgColor}, image.ZP, draw.Src)
// 绘制标题
g.drawText(img, gameData.Title, 50, 50, color.RGBA{0, 0, 0, 255})
// 绘制分数
scoreText := fmt.Sprintf("分数: %d", gameData.Score)
g.drawText(img, scoreText, 50, 100, color.RGBA{0, 100, 0, 255})
// 绘制等级
levelText := fmt.Sprintf("等级: %d", gameData.Level)
g.drawText(img, levelText, 200, 100, color.RGBA{0, 0, 100, 255})
// 绘制游戏内容
g.drawGameContent(img, gameData)
// 转换为PNG
var buf bytes.Buffer
err := png.Encode(&buf, img)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// 绘制文本
func (g *GameUIGenerator) drawText(img *image.RGBA, text string, x, y int, col color.RGBA) {
point := fixed.Point26_6{
X: fixed.Int26_6(x * 64),
Y: fixed.Int26_6(y * 64),
}
d := &font.Drawer{
Dst: img,
Src: &image.Uniform{col},
Face: g.font,
Dot: point,
}
d.DrawString(text)
}
响应式界面设计¶
// 自适应UI数据
type ResponsiveUIData struct {
DeviceType string // mobile/desktop
ScreenSize string // small/medium/large
Theme string // light/dark
Language string // zh/en
}
// 生成响应式UI
func GenerateResponsiveGameUI(gameData GameUIData, uiConfig ResponsiveUIData) ([]byte, error) {
var generator *GameUIGenerator
switch uiConfig.ScreenSize {
case "small":
generator = &GameUIGenerator{width: 400, height: 300}
case "medium":
generator = &GameUIGenerator{width: 600, height: 450}
case "large":
generator = &GameUIGenerator{width: 800, height: 600}
default:
generator = &GameUIGenerator{width: 600, height: 450}
}
// 设置主题色彩
if uiConfig.Theme == "dark" {
generator.bgColor = color.RGBA{30, 30, 30, 255}
} else {
generator.bgColor = color.RGBA{240, 240, 240, 255}
}
return generator.GenerateGameUI(gameData)
}
⚡ 性能优化¶
游戏数据缓存¶
import (
"sync"
"time"
)
// 游戏缓存管理器
type GameCache struct {
data map[string]interface{}
mutex sync.RWMutex
ttl map[string]time.Time
}
var gameCache = &GameCache{
data: make(map[string]interface{}),
ttl: make(map[string]time.Time),
}
// 设置缓存
func (gc *GameCache) Set(key string, value interface{}, duration time.Duration) {
gc.mutex.Lock()
defer gc.mutex.Unlock()
gc.data[key] = value
gc.ttl[key] = time.Now().Add(duration)
}
// 获取缓存
func (gc *GameCache) Get(key string) (interface{}, bool) {
gc.mutex.RLock()
defer gc.mutex.RUnlock()
// 检查是否过期
if expireTime, exists := gc.ttl[key]; exists {
if time.Now().After(expireTime) {
delete(gc.data, key)
delete(gc.ttl, key)
return nil, false
}
}
value, exists := gc.data[key]
return value, exists
}
// 清理过期缓存
func (gc *GameCache) cleanup() {
gc.mutex.Lock()
defer gc.mutex.Unlock()
now := time.Now()
for key, expireTime := range gc.ttl {
if now.After(expireTime) {
delete(gc.data, key)
delete(gc.ttl, key)
}
}
}
// 启动清理协程
func init() {
go func() {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()
for range ticker.C {
gameCache.cleanup()
}
}()
}
数据库连接池优化¶
// 优化的数据库操作
func GetUserStatsOptimized(userID string) (*UserGrowth, error) {
// 先检查缓存
cacheKey := fmt.Sprintf("user_stats_%s", userID)
if cached, exists := gameCache.Get(cacheKey); exists {
return cached.(*UserGrowth), nil
}
// 查询数据库
db := sqlite.GetDB()
var userGrowth UserGrowth
err := db.Where("user_id = ?", userID).First(&userGrowth).Error
if err != nil {
return nil, err
}
// 缓存结果(10分钟)
gameCache.Set(cacheKey, &userGrowth, 10*time.Minute)
return &userGrowth, nil
}
🧪 游戏测试¶
单元测试框架¶
package mygame
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/ruk1ng001/wxbot/engine/robot"
)
// 测试游戏创建
func TestCreateGame(t *testing.T) {
manager := &GameManager{
games: make(map[string]*GameInstance),
}
game := manager.CreateGame("group123", "user456")
assert.NotNil(t, game)
assert.Equal(t, "group123", game.GroupID)
assert.Equal(t, "user456", game.PlayerID)
assert.Equal(t, GameStatusWaiting, game.Status)
}
// 测试游戏逻辑
func TestGameLogic(t *testing.T) {
game := &GameInstance{
GameID: "test_game",
GroupID: "group123",
PlayerID: "user456",
Status: GameStatusPlaying,
Score: 0,
Level: 1,
}
// 测试答对题目
result := processAnswer(game, "正确答案")
assert.True(t, result.Correct)
assert.True(t, game.Score > 0)
}
// 测试经验值系统
func TestExperienceSystem(t *testing.T) {
userGrowth := &UserGrowth{
Level: 1,
Experience: 50,
}
levelUp := userGrowth.AddExperience(100)
assert.True(t, levelUp)
assert.Equal(t, 2, userGrowth.Level)
assert.Equal(t, 150, userGrowth.Experience)
}
集成测试¶
// 集成测试:完整游戏流程
func TestCompleteGameFlow(t *testing.T) {
// 模拟机器人上下文
ctx := &robot.Ctx{
Event: &robot.Event{
FromWxId: "group123",
FinalFromWxId: "user456",
Message: &robot.Message{
Content: "开始游戏",
},
},
}
// 测试开始游戏
startGameHandler(ctx)
game := globalGameManager.GetGame("group123", "user456")
assert.NotNil(t, game)
assert.Equal(t, GameStatusPlaying, game.Status)
// 测试回答问题
ctx.Event.Message.Content = "答案A"
answerHandler(ctx)
// 验证分数更新
assert.True(t, game.Score > 0)
}
性能测试¶
// 并发性能测试
func TestConcurrentGames(t *testing.T) {
const numGames = 100
const numPlayers = 10
var wg sync.WaitGroup
for i := 0; i < numGames; i++ {
wg.Add(1)
go func(gameIndex int) {
defer wg.Done()
groupID := fmt.Sprintf("group%d", gameIndex)
for j := 0; j < numPlayers; j++ {
playerID := fmt.Sprintf("user%d_%d", gameIndex, j)
game := globalGameManager.CreateGame(groupID, playerID)
assert.NotNil(t, game)
// 模拟游戏过程
for k := 0; k < 10; k++ {
game.AddScore(10)
}
game.UpdateStatus(GameStatusEnded)
}
}(i)
}
wg.Wait()
// 验证所有游戏都正确创建
assert.Equal(t, numGames*numPlayers, len(globalGameManager.games))
}
📚 相关文档¶
- 🛠️ 插件开发指南 - 基础插件开发
- ⭐ Enhanced模式开发 - 高级插件特性
- 📊 监控集成 - 监控系统集成
- 🔒 安全最佳实践 - 安全开发指南
**🎮 创造有趣的游戏,让用户爱不释手!** **让WxBot Enhanced成为最受欢迎的游戏平台!**