Files
Bionic_sphere/Bionic_Core/PetBaseClass/PetInterface.h
T
Misaki dc420c3b7a 1. 历时两天,完整且完美的设计了宠物类,使用到了多种设计模式,完成了低耦合,高内聚的完美代码,测试也完美通过。
2. 顺便完善了底层通信类的封装,基于websocket,基准测试通过,但存在一点很小的线程bug,似乎是来自于esp32 idf底层的问题,待解决
2025-09-15 01:49:09 +08:00

390 lines
14 KiB
C++

//
// Created by misaki on 2025/9/8.
//
/**
* 需要实现的最终效果
1. 可以选择多种宠物,如雪豹,卡皮巴拉,守宫,每种宠物有独特音频和动作,通过联网更新切换。
2. 每种宠物有可以编辑的身份设定,类似小智AI的身份
3. 宠物三个阶段,幼年,青年,成年,阶段切换载入新模型即可。
4. 语音互动可以实现喂食,开心,生气,沮丧几种动作
5. 宠物具有生命值hp和亲密度,当发生喂食等动作会对生命值和亲密度产生影响,亲密度达到一定程度宠物可以进化到新的阶段。
6. 宠物的各种信息是可以保存和读取的。
分析一下具体设计思路:
1. 首先抽象出所有的宠物的共同点为一个基类,包括:名称,生命值,亲密度,身份设定
对于阶段,动作,不同的宠物之间存在不同的实现差异,可以为其抽象出不同的接口,组合到抽象出来的宠物基类当中
这样当创建新的宠物时,只需要实现对应的接口即可(在此使用策略模式),而在创建不同的宠物的过程中,又使用到了抽象工厂模式
对于音频,由于阶段变化或是产生动作,都会触发响应的音频,因此音频需要作为一个观察者,当宠物产生阶段变化或是产生动作,就会触发相应的音频播放
2. 接着要考虑到与宠物相关的一些事件,例如外部通知网络更新,语音输入互动,喂食这些事件,需要及时做出响应,在此使用依赖注入来解决这一类事件处理问题
向外部类当中组合一个依赖注入类,将宠物类当中的回调函数注入到其中,当发生某些事件时,就会触发相应的回调函数,并执行相应的操作
这样在增加新的事件时,就不需要频繁修改外部事件类,只需要新增新的依赖注入项(如果C++17支持反射的话还有更好的实现方法)
3. 再者是对于宠物的信息保存与读取,在此处增加一个DAO层,专门用于数据持久化,信息以json的方式存储与读取
既然存在了宠物的数据信息,那么就需要在DAO层之上增加一个可以从json数据中得到新的宠物对象的方法或者是类,以此组合出所需的宠物对象
4. 还需要考虑到一些极端事件的情况,例如设备断电,用户强制关机,网络中断等,最好的处理方式就是及时做好数据持久化和数据备份
设置bak备份防止在写文件的时候断电导致写文件错误
*/
#pragma once
#include <string>
#include <memory>
#include <utility>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
// 前向声明主题
class PetSubject;
// 某个宠物不同阶段的抽象接口 宠物的不同阶段对应于不同的模型文件路径
// 如果需要添加新的阶段,要在PetStageType当中增加新的阶段,并将新的类继承自PetStage类
// 由于使用的是C++17,并不支持反射,只能通过宏定义统一管理,以此实现松耦合和开闭原则
#define PET_STAGE_TYPES \
X(PET_STAGE_YOUNG, "幼年") \
X(PET_STAGE_ADULT, "青年") \
X(PET_STAGE_OLD, "成年")
enum class PetStageType {
#define X(name, desc) name,
PET_STAGE_TYPES
#undef X
};
// 某个宠物所有阶段的接口集合,该接口集合会被组合到抽象出来的宠物基类当中,该接口还需要抽象出宠物不同阶段可能具备的行为
class PetStageStrategy {
public:
virtual ~PetStageStrategy() = default;
// 定义阶段顺序的静态常量
static const std::vector<PetStageType>& getStageOrder() {
static const std::vector<PetStageType> stageOrder = []() {
std::vector<PetStageType> order;
#define X(name, desc) order.push_back(PetStageType::name);
PET_STAGE_TYPES
#undef X
return order;
}();
return stageOrder;
}
/**
* 添加阶段及其模型路径
* @param stage 阶段类型
* @param model_path 模型路径
*/
virtual void addStage(PetStageType stage, const std::string& model_path) {
stage_model_map[stage] = model_path;
// 如果这是第一个阶段,设置为当前阶段
if (stage_model_map.size() == 1) {
current_stage = stage;
}
}
/**
* 添加阶段音频映射
* @param stage 阶段类型
* @param audio_path 音频文件路径
*/
virtual void addStageAudio(PetStageType stage, const std::string& audio_path) {
stage_audio_map[stage] = audio_path;
}
/**
* 获取阶段模型映射表
* @return 阶段到模型路径的映射表
*/
virtual const std::unordered_map<PetStageType, std::string>& getStageModelMap() const {
return stage_model_map;
}
/**
* 获取阶段音频映射表
* @return 阶段到音频路径的映射表
*/
virtual const std::unordered_map<PetStageType, std::string>& getStageAudioMap() const {
return stage_audio_map;
}
/**
* 获取特定阶段的模型路径
* @param stage 阶段类型
* @return 模型路径,如果不存在则返回空字符串
*/
virtual std::string getStageModelPath(PetStageType stage) const {
auto it = stage_model_map.find(stage);
if (it != stage_model_map.end()) {
return it->second;
}
return "";
}
/**
* 获取特定阶段的音频路径
* @param stage 阶段类型
* @return 音频路径,如果不存在则返回空字符串
*/
virtual std::string getStageAudioPath(PetStageType stage) const {
auto it = stage_audio_map.find(stage);
if (it != stage_audio_map.end()) {
return it->second;
}
return "";
}
/**
* 切换到指定阶段
* @param newStage 要切换到的阶段类型
* @return 是否成功切换
*/
virtual bool switchToStage(PetStageType newStage) {
if (stage_model_map.find(newStage) != stage_model_map.end()) {
current_stage = newStage;
return true;
}
return false;
}
/**
* 进化到下一阶段(基于当前阶段)
* @return 是否成功进化
*/
virtual bool evolveToNextStage() {
const auto& stageOrder = getStageOrder();
auto it = std::find(stageOrder.begin(), stageOrder.end(), current_stage);
if (it == stageOrder.end() || it == stageOrder.end() - 1) {
return false;
}
// 查找下一个可用阶段
for (auto iter = it + 1; iter != stageOrder.end(); ++iter) {
if (stage_model_map.find(*iter) != stage_model_map.end()) {
current_stage = *iter;
return true;
}
}
return false;
}
/**
* 获取当前阶段
* @return 当前阶段类型
*/
virtual PetStageType getCurrentStageType() const {
return current_stage;
}
/**
* 获取当前阶段的模型路径
* @return 当前阶段模型路径
*/
virtual std::string getCurrentStageModelPath() const {
return getStageModelPath(current_stage);
}
/**
* 获取所有可用阶段
* @return 所有阶段的列表
*/
virtual std::list<PetStageType> getAllStages() const {
std::list<PetStageType> stages;
for (const auto& stage : stage_model_map) {
stages.push_back(stage.first);
}
return stages;
}
/**
* 移除阶段
* @param stage 要移除的阶段类型
*/
virtual void removeStage(PetStageType stage) {
if (current_stage == stage && stage_model_map.size() > 1) {
// 找到另一个阶段作为当前阶段
for (const auto& s : stage_model_map) {
if (s.first != stage) {
current_stage = s.first;
break;
}
}
}
stage_model_map.erase(stage);
stage_audio_map.erase(stage);
}
public:
///<! 阶段到模型路径的映射表
std::unordered_map<PetStageType, std::string> stage_model_map;
///<! 阶段到音频路径的映射表
std::unordered_map<PetStageType, std::string> stage_audio_map;
///<! 当前阶段
PetStageType current_stage;
};
// 宠物动作接口
// 一般而言,对于所有的动物,其动作行为基本相同,不同的地方在与发生动作时,所触发的模型不同
// 因此,宠物的动作设计,和阶段设计基本类似
#define PET_ACTION_TYPES \
X(PET_ACTION_SLEEP, "睡觉") \
X(PET_ACTION_EAT, "喂食") \
X(PET_ACTION_HAPPY, "开心") \
X(PET_ACTION_ANGRY, "生气") \
X(PET_ACTION_SAD, "沮丧") \
X(PET_ACTION_EVOLVE, "进化") \
X(PET_ACTION_TOUCH, "被触摸")
enum class PetActionType {
#define X(name, desc) name,
PET_ACTION_TYPES
#undef X
};
// 宠物动作策略类
class PetActionStrategy {
public:
virtual ~PetActionStrategy() = default;
// 定义动作顺序的静态常量
static const std::vector<PetActionType>& getActionOrder() {
static const std::vector<PetActionType> actionOrder = []() {
std::vector<PetActionType> order;
#define X(name, desc) order.push_back(PetActionType::name);
PET_ACTION_TYPES
#undef X
return order;
}();
return actionOrder;
}
/**
* 添加动作及其模型路径
* @param action 动作类型
* @param model_path 模型路径
*/
virtual void addAction(PetActionType action, const std::string& model_path) {
action_model_map[action] = model_path;
}
/**
* 添加动作音频映射
* @param action 动作类型
* @param audio_path 音频文件路径
*/
virtual void addActionAudio(PetActionType action, const std::string& audio_path) {
action_audio_map[action] = audio_path;
}
/**
* 获取动作模型映射表
* @return 动作到模型路径的映射表
*/
virtual const std::unordered_map<PetActionType, std::string>& getActionModelMap() const {
return action_model_map;
}
/**
* 获取动作音频映射表
* @return 动作到音频路径的映射表
*/
virtual const std::unordered_map<PetActionType, std::string>& getActionAudioMap() const {
return action_audio_map;
}
/**
* 获取特定动作的模型路径
* @param action 动作类型
* @return 模型路径,如果不存在则返回空字符串
*/
virtual std::string getActionModelPath(PetActionType action) const {
auto it = action_model_map.find(action);
if (it != action_model_map.end()) {
return it->second;
}
return "";
}
/**
* 获取特定动作的音频路径
* @param action 动作类型
* @return 音频路径,如果不存在则返回空字符串
*/
virtual std::string getActionAudioPath(PetActionType action) const {
auto it = action_audio_map.find(action);
if (it != action_audio_map.end()) {
return it->second;
}
return "";
}
/**
* 执行指定动作
* @param action 要执行的动作类型
* @return 是否成功执行
*/
virtual bool performAction(PetActionType action) {
if (action_model_map.find(action) != action_model_map.end()) {
current_action = action;
return true;
}
return false;
}
/**
* 获取当前动作
* @return 当前动作类型
*/
virtual PetActionType getCurrentActionType() const {
return current_action;
}
/**
* 获取当前动作的模型路径
* @return 当前动作模型路径
*/
virtual std::string getCurrentActionModelPath() const {
return getActionModelPath(current_action);
}
/**
* 获取所有可用动作
* @return 所有动作的列表
*/
virtual std::list<PetActionType> getAllActions() const {
std::list<PetActionType> actions;
for (const auto& action : action_model_map) {
actions.push_back(action.first);
}
return actions;
}
/**
* 获取动作的描述信息
* @param action 动作类型
* @return 动作描述
*/
virtual std::string getActionDescription(PetActionType action) const {
switch (action) {
#define X(name, desc) case PetActionType::name: return desc;
PET_ACTION_TYPES
#undef X
default: return "未知动作";
}
}
/**
* 获取当前动作的描述信息
* @return 当前动作描述
*/
virtual std::string getCurrentActionDescription() const {
return getActionDescription(current_action);
}
/**
* 移除动作
* @param action 要移除的动作类型
*/
virtual void removeAction(PetActionType action) {
action_model_map.erase(action);
action_audio_map.erase(action);
if (current_action == action) {
current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作
}
}
public:
///<! 动作到模型路径的映射表
std::unordered_map<PetActionType, std::string> action_model_map;
///<! 动作到音频路径的映射表
std::unordered_map<PetActionType, std::string> action_audio_map;
///<! 当前动作
PetActionType current_action = PetActionType::PET_ACTION_SLEEP; // 默认动作
};
// 观察者接口
class PetObserver {
public:
virtual ~PetObserver() = default;
// 宠物动作
virtual void onPetAction(PetActionType action) = 0;
// 宠物阶段
virtual void onPetStageChange(PetStageType oldStage, PetStageType newStage) = 0;
};
// 主题接口
class PetSubject {
public:
virtual ~PetSubject() = default;
// 添加观察者
virtual void addObserver(std::shared_ptr<PetObserver> observer) = 0;
// 移除观察者
virtual void removeObserver(std::shared_ptr<PetObserver> observer) = 0;
// 通知动作
virtual void notifyAction(PetActionType action) = 0;
// 通知阶段
virtual void notifyStageChange(PetStageType oldStage, PetStageType newStage) = 0;
};