// // 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 #include #include #include #include #include #include // 前向声明主题 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& getStageOrder() { static const std::vector stageOrder = []() { std::vector 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& getStageModelMap() const { return stage_model_map; } /** * 获取阶段音频映射表 * @return 阶段到音频路径的映射表 */ virtual const std::unordered_map& 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 getAllStages() const { std::list 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: /// stage_model_map; /// stage_audio_map; ///& getActionOrder() { static const std::vector actionOrder = []() { std::vector 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& getActionModelMap() const { return action_model_map; } /** * 获取动作音频映射表 * @return 动作到音频路径的映射表 */ virtual const std::unordered_map& 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 getAllActions() const { std::list 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: /// action_model_map; /// action_audio_map; /// observer) = 0; // 移除观察者 virtual void removeObserver(std::shared_ptr observer) = 0; // 通知动作 virtual void notifyAction(PetActionType action) = 0; // 通知阶段 virtual void notifyStageChange(PetStageType oldStage, PetStageType newStage) = 0; };