From 8b88904ef7cf12d487ff38d1af098d65683b0c63 Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Sun, 8 Feb 2026 16:20:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 4 +- CMakeLists.txt | 18 +- folder-alias.json | 21 + source/Asset/Asset_Audio.hpp | 306 ++ source/Asset/Asset_SoundPack.cpp | 233 ++ source/Asset/Asset_SoundPack.h | 88 + source/Asset/rapidxml/rapidxml.hpp | 2596 +++++++++++++++++ source/Asset/rapidxml/rapidxml_iterators.hpp | 174 ++ source/Asset/rapidxml/rapidxml_print.hpp | 421 +++ source/Asset/rapidxml/rapidxml_utils.hpp | 122 + source/EngineCore/Game.cpp | 30 +- source/EngineCore/Game.h | 10 +- source/EngineFrame/Base/Actor.cpp | 32 +- source/EngineFrame/Base/Actor.h | 29 +- source/EngineFrame/Base/BaseNode.cpp | 368 --- source/EngineFrame/Base/BaseNode.h | 147 - source/EngineFrame/Base/Node.cpp | 440 +-- source/EngineFrame/Base/Node.h | 355 ++- source/EngineFrame/Component/Animation.cpp | 63 +- source/EngineFrame/Component/Animation.h | 35 +- .../Component/AnimationManager.cpp | 72 - .../EngineFrame/Component/AnimationManager.h | 21 - source/EngineFrame/Component/AnimationMap.cpp | 132 + source/EngineFrame/Component/AnimationMap.h | 36 + source/EngineFrame/Component/Canvas.cpp | 157 +- source/EngineFrame/Component/Canvas.h | 38 +- source/EngineFrame/Component/NumberText.h | 2 +- source/EngineFrame/Component/RenderBase.cpp | 63 - source/EngineFrame/Component/RenderBase.h | 45 - source/EngineFrame/Component/Sprite.cpp | 161 +- source/EngineFrame/Component/Sprite.h | 26 +- source/EngineFrame/Render/RenderManager.cpp | 354 ++- source/EngineFrame/Render/RenderManager.h | 58 +- source/EngineFrame/Scene/Scene.cpp | 5 +- source/EngineFrame/Scene/Scene.h | 6 +- source/Tool/Ifstream_NPK.cpp | 26 +- source/Tool/Ifstream_NPK.h | 7 +- source_game/Actor/Map/GameMap.cpp | 190 +- source_game/Actor/Map/GameMap.h | 31 +- source_game/Actor/Map/GameMapLayer.cpp | 48 +- source_game/Actor/Map/GameMapLayer.h | 6 +- source_game/Actor/Map/GameTown.cpp | 67 + source_game/Actor/Map/GameTown.h | 54 + source_game/Actor/Map/GameWorld.cpp | 100 + source_game/Actor/Map/GameWorld.h | 31 + source_game/Actor/Map/Tile.cpp | 143 +- source_game/Actor/Map/Tile.h | 51 +- source_game/Actor/Object/ActiveObject.cpp | 4 +- source_game/Actor/Object/ActiveObject.h | 5 +- source_game/Actor/Object/BaseObject.cpp | 33 +- source_game/Actor/Object/BaseObject.h | 2 - source_game/Actor/Object/CharacterObject.cpp | 39 +- source_game/Actor/Object/CharacterObject.h | 11 +- source_game/Asset/Character/Chr_Animation.cpp | 52 +- source_game/Asset/Character/Chr_Animation.h | 7 +- .../Asset/Character/Chr_Controller.cpp | 1 - source_game/Asset/Character/Chr_Controller.h | 4 +- source_game/Asset/Character/Chr_Shadow.cpp | 44 - source_game/Asset/Character/Chr_Shadow.h | 29 - .../Asset/Character/Chr_StateMachine.cpp | 1 - .../Asset/Character/Chr_StateMachine.h | 4 +- source_game/Asset/Monster/Mon_Animation.cpp | 1 - source_game/Asset/Monster/Mon_Animation.h | 2 +- source_game/Asset/Squirrel/Sqr_UI.hpp | 30 +- source_game/Global/Global_Game.cpp | 29 +- source_game/Global/Global_Game.h | 7 + source_game/Global/Script/TownConfig.cpp | 58 + source_game/Global/Script/TownConfig.h | 41 + source_game/Scene/Scene_Loading_UI.cpp | 18 +- source_game/Scene/Scene_Test.cpp | 122 - source_game/Scene/Scene_Test.h | 28 - source_game/main.cpp | 7 +- 72 files changed, 5963 insertions(+), 2038 deletions(-) create mode 100644 source/Asset/Asset_Audio.hpp create mode 100644 source/Asset/Asset_SoundPack.cpp create mode 100644 source/Asset/Asset_SoundPack.h create mode 100644 source/Asset/rapidxml/rapidxml.hpp create mode 100644 source/Asset/rapidxml/rapidxml_iterators.hpp create mode 100644 source/Asset/rapidxml/rapidxml_print.hpp create mode 100644 source/Asset/rapidxml/rapidxml_utils.hpp delete mode 100644 source/EngineFrame/Base/BaseNode.cpp delete mode 100644 source/EngineFrame/Base/BaseNode.h delete mode 100644 source/EngineFrame/Component/AnimationManager.cpp delete mode 100644 source/EngineFrame/Component/AnimationManager.h create mode 100644 source/EngineFrame/Component/AnimationMap.cpp create mode 100644 source/EngineFrame/Component/AnimationMap.h delete mode 100644 source/EngineFrame/Component/RenderBase.cpp delete mode 100644 source/EngineFrame/Component/RenderBase.h create mode 100644 source_game/Actor/Map/GameTown.cpp create mode 100644 source_game/Actor/Map/GameTown.h create mode 100644 source_game/Actor/Map/GameWorld.cpp create mode 100644 source_game/Actor/Map/GameWorld.h delete mode 100644 source_game/Asset/Character/Chr_Shadow.cpp delete mode 100644 source_game/Asset/Character/Chr_Shadow.h create mode 100644 source_game/Global/Script/TownConfig.cpp create mode 100644 source_game/Global/Script/TownConfig.h delete mode 100644 source_game/Scene/Scene_Test.cpp delete mode 100644 source_game/Scene/Scene_Test.h diff --git a/.vscode/settings.json b/.vscode/settings.json index fc04630..cadd5b0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -95,7 +95,9 @@ "numbers": "cpp", "semaphore": "cpp", "cinttypes": "cpp", - "netfwd": "cpp" + "netfwd": "cpp", + "coroutine": "cpp", + "cfenv": "cpp" }, "Codegeex.RepoIndex": true } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 620739b..0620aba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,16 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/DNF) set(CMAKE_CXX_STANDARD 17) +if(MINGW) # 你的环境是 MinGW(Strawberry Perl 自带) + # 保留 -g2 精简调试信息(避免体积过大),同时移除可能的优化选项 + string(REPLACE "-O2" "-O0" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "-O2" "-O0" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + add_compile_options(-g2) # 明确保留调试信息 + add_compile_options(-Wa,-mbig-obj) # 解决 MinGW 大文件限制(之前的问题) + add_compile_options(-fno-omit-frame-pointer) # 帮助调试器定位栈帧 + +endif() + # 添加子目录,引用squirrel项目 add_subdirectory(Library/squirrel) # 添加zlib子项目 @@ -32,6 +42,8 @@ set(SDL2MIXER_DEPS_SHARED OFF CACHE BOOL "Compile vendored deps as static libs" set(SDL2MIXER_BUILD_SHARED OFF CACHE BOOL "Disable SDL2_mixer shared library" FORCE) set(SDL2MIXER_BUILD_STATIC ON CACHE BOOL "Enable SDL2_mixer static library" FORCE) set(SDL2MIXER_SAMPLES OFF CACHE BOOL "Disable SDL2_mixer examples (playmus/playwave)" FORCE) +set(SDL2MIXER_VORBIS "VORBISFILE" CACHE STRING "Enable OGG via vorbisfile" FORCE) +set(SDL2MIXER_VORBIS_VORBISFILE_SHARED OFF CACHE BOOL "Link vorbisfile statically" FORCE) add_subdirectory(Library/SDL_mixer) set(SDL2NET_INSTALL OFF CACHE BOOL "Disable SDL2_net installation" FORCE) @@ -74,7 +86,7 @@ file(GLOB_RECURSE SOURCES ) add_executable(${PROJECT_NAME} ${SOURCES}) -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -O1 -g" CACHE STRING "" FORCE) + if (MINGW) target_link_options(${PROJECT_NAME} PRIVATE @@ -112,8 +124,8 @@ target_link_libraries(${PROJECT_NAME} PRIVATE SDL2_ttf SDL2_net - zlibstatic # zlib库 - squirrel_static # squirrel库(根据实际目标名调整) + zlibstatic + squirrel_static sqstdlib_static mingwex diff --git a/folder-alias.json b/folder-alias.json index 4f7d50f..90083b2 100644 --- a/folder-alias.json +++ b/folder-alias.json @@ -217,5 +217,26 @@ }, "source/EngineFrame/Attribute": { "description": "属性" + }, + "source_game/Actor/Map/GameTown.h": { + "description": "城镇类" + }, + "source_game/Global/Script/TownConfig.h": { + "description": "城镇配置信息" + }, + "source_game/Actor/Map/GameWorld.h": { + "description": "游戏世界类" + }, + "source/EngineFrame/Component/AnimationMap.h": { + "description": "动画组类" + }, + "source/EngineFrame/Component/Animation.h": { + "description": "动画类" + }, + "source/EngineFrame/Component/Animation.cpp": { + "description": "动画类" + }, + "source/EngineFrame/Component/AnimationMap.cpp": { + "description": "动画组类" } } \ No newline at end of file diff --git a/source/Asset/Asset_Audio.hpp b/source/Asset/Asset_Audio.hpp new file mode 100644 index 0000000..c0ed5a2 --- /dev/null +++ b/source/Asset/Asset_Audio.hpp @@ -0,0 +1,306 @@ +#pragma once +#include +#include +#include +#include +#include +#include "rapidxml/rapidxml.hpp" + +class AudioConfigReader +{ +public: + // -------------------------- + // 1. 结构体定义:放在类内public区,外部可通过 AudioConfigReader::SoundEffect 访问 + // -------------------------- + struct SoundEffect + { + std::string file_path; // 文件路径(FILE属性) + int volume_adjust = 100; // 音量调整(默认100) + int priority = 0; // 优先级(默认0) + }; + + struct RandomItem + { + std::string tag; // 关联的音效ID(TAG属性) + int prob = 0; // 概率(PROB属性) + }; + + struct RandomSoundGroup + { + std::vector items; // 组内所有项 + int delay = 0; // 延迟(DELAY属性) + int loop_delay = 0; // 循环延迟 + int loop_delay_range = 0; // 循环延迟范围 + }; + + struct Music + { + std::string file_path; // 文件路径(FILE属性) + int loop_delay = 0; // 循环延迟(LOOP_DELAY属性) + }; + +public: + // 构造函数:初始化解析器 + AudioConfigReader() = default; + + // 析构函数:清理资源 + ~AudioConfigReader() = default; + + // 核心函数:解析XML文件(返回是否成功) + bool LoadFromFile(const std::string &xml_path) + { + // 1. 读取XML文件内容 + std::ifstream file(xml_path, std::ios::binary); + if (!file.is_open()) + { + throw std::runtime_error("无法打开XML文件: " + xml_path); + } + std::string xml_content((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + + // 2. 解析XML + rapidxml::xml_document<> doc; + try + { + doc.parse(&xml_content[0]); + } + catch (const rapidxml::parse_error &e) + { + throw std::runtime_error("XML解析错误: " + std::string(e.what()) + + "(位置: " + std::string(e.where()) + ")"); + } + + // 3. 获取根节点 + rapidxml::xml_node<> *root = doc.first_node("AudioTagDatabase"); + if (!root) + { + throw std::runtime_error("XML根节点错误: 找不到AudioTagDatabase"); + } + + // 4. 解析所有节点 + ParseEffects(root); // 解析 + ParseRandomGroups(root); // 解析 + ParseMusics(root); // 解析 + + return true; + } + + // ------------------------------ + // 接口1:获取音效配置() + // ------------------------------ + bool HasEffect(const std::string &effect_id) const + { + return m_effects.find(effect_id) != m_effects.end(); + } + + const SoundEffect &GetEffect(const std::string &effect_id) const + { + auto it = m_effects.find(effect_id); + if (it == m_effects.end()) + { + throw std::out_of_range("音效ID不存在: " + effect_id); + } + return it->second; + } + + // 获取所有音效ID列表 + std::vector GetAllEffectIds() const + { + return GetAllKeys(m_effects); + } + + // ------------------------------ + // 接口2:获取随机音效组配置() + // ------------------------------ + bool HasRandomGroup(const std::string &group_id) const + { + return m_random_groups.find(group_id) != m_random_groups.end(); + } + + const RandomSoundGroup &GetRandomGroup(const std::string &group_id) const + { + auto it = m_random_groups.find(group_id); + if (it == m_random_groups.end()) + { + throw std::out_of_range("随机组ID不存在: " + group_id); + } + return it->second; + } + + // 获取所有随机组ID列表 + std::vector GetAllRandomGroupIds() const + { + return GetAllKeys(m_random_groups); + } + + // ------------------------------ + // 接口3:获取背景音乐配置() + // ------------------------------ + bool HasMusic(const std::string &music_id) const + { + return m_musics.find(music_id) != m_musics.end(); + } + + const Music &GetMusic(const std::string &music_id) const + { + auto it = m_musics.find(music_id); + if (it == m_musics.end()) + { + throw std::out_of_range("背景音乐ID不存在: " + music_id); + } + return it->second; + } + + // 获取所有背景音乐ID列表 + std::vector GetAllMusicIds() const + { + return GetAllKeys(m_musics); + } + +private: + // 辅助函数:解析节点 + void ParseEffects(rapidxml::xml_node<> *root) + { + for (rapidxml::xml_node<> *node = root->first_node("EFFECT"); + node; node = node->next_sibling("EFFECT")) + { + + // 必选属性:ID和FILE + rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); + rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE"); + if (!id_attr || !file_attr) + { + continue; // 跳过无效节点 + } + + SoundEffect effect; + effect.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符 + + // 可选属性:VOLUME_ADJUST + if (auto attr = node->first_attribute("VOLUME_ADJUST")) + { + effect.volume_adjust = std::stoi(attr->value()); + } + + // 可选属性:PRIORITY + if (auto attr = node->first_attribute("PRIORITY")) + { + effect.priority = std::stoi(attr->value()); + } + + m_effects[id_attr->value()] = effect; + } + } + + // 辅助函数:解析节点 + void ParseRandomGroups(rapidxml::xml_node<> *root) + { + for (rapidxml::xml_node<> *node = root->first_node("RANDOM"); + node; node = node->next_sibling("RANDOM")) + { + + // 必选属性:ID + rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); + if (!id_attr) + { + continue; // 跳过无效节点 + } + + RandomSoundGroup group; + + // 可选属性:延迟相关 + if (auto attr = node->first_attribute("DELAY")) + { + group.delay = std::stoi(attr->value()); + } + if (auto attr = node->first_attribute("LOOP_DELAY")) + { + group.loop_delay = std::stoi(attr->value()); + } + if (auto attr = node->first_attribute("LOOP_DELAY_RANGE")) + { + group.loop_delay_range = std::stoi(attr->value()); + } + + // 解析子节点 + for (rapidxml::xml_node<> *item_node = node->first_node("ITEM"); + item_node; item_node = item_node->next_sibling("ITEM")) + { + + rapidxml::xml_attribute<> *tag_attr = item_node->first_attribute("TAG"); + rapidxml::xml_attribute<> *prob_attr = item_node->first_attribute("PROB"); + if (!tag_attr || !prob_attr) + { + continue; // 跳过无效项 + } + + group.items.push_back({tag_attr->value(), + std::stoi(prob_attr->value())}); + } + + m_random_groups[id_attr->value()] = group; + } + } + + // 辅助函数:解析节点 + void ParseMusics(rapidxml::xml_node<> *root) + { + for (rapidxml::xml_node<> *node = root->first_node("MUSIC"); + node; node = node->next_sibling("MUSIC")) + { + + // 必选属性:ID和FILE + rapidxml::xml_attribute<> *id_attr = node->first_attribute("ID"); + rapidxml::xml_attribute<> *file_attr = node->first_attribute("FILE"); + if (!id_attr || !file_attr) + { + continue; // 跳过无效节点 + } + + Music music; + music.file_path = NormalizePath(file_attr->value()); // 统一路径分隔符 + + // 可选属性:LOOP_DELAY + if (auto attr = node->first_attribute("LOOP_DELAY")) + { + music.loop_delay = std::stoi(attr->value()); + } + + m_musics[id_attr->value()] = music; + } + } + + // 辅助函数:统一路径分隔符(将\转为/,兼容跨平台) + std::string NormalizePath(const std::string &path) + { + std::string normalized = path; + for (char &c : normalized) + { + if (c == '\\') + { + c = '/'; + } + } + return normalized; + } + + // 辅助函数:获取哈希表中所有key + template + std::vector GetAllKeys(const Map &map) const + { + std::vector keys; + keys.reserve(map.size()); + for (const auto &pair : map) + { + keys.push_back(pair.first); + } + return keys; + } + +private: + // 存储解析后的数据 + std::unordered_map m_effects; // 音效映射(ID -> 音效) + std::unordered_map m_random_groups; // 随机组映射(ID -> 随机组) + std::unordered_map m_musics; // 背景音乐映射(ID -> 音乐) +}; \ No newline at end of file diff --git a/source/Asset/Asset_SoundPack.cpp b/source/Asset/Asset_SoundPack.cpp new file mode 100644 index 0000000..54b6bb9 --- /dev/null +++ b/source/Asset/Asset_SoundPack.cpp @@ -0,0 +1,233 @@ +#include "Asset_SoundPack.h" +Asset_SoundPack::Asset_SoundPack() +{ + int device = -1; // 使用默认设备 + unsigned long freq = 44100; // 采样频率,PCM常用的采样率之一,可以按需调整 + + // 构造NPK信息 + InitNpkList(); + // 解析audio.xml + XmlConfig.LoadFromFile("audio.xml"); + + // 初始化SDL_mixer,支持OGG格式 + // 44100: 采样率, MIX_DEFAULT_FORMAT: 音频格式, 2: 声道数(立体声), 4096: 缓冲区大小 + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 8192) < 0) + { + SDL_LogError(0, "SDL_mixer初始化失败! Mix_Error: %s\n", Mix_GetError()); + } + if (Mix_Init(MIX_INIT_MP3 | MIX_INIT_OGG) < 0) + { + SDL_LogError(0, "Mix_Init failed: %s\n", Mix_GetError()); + } + // 分配通道 + ChannelCount = Mix_AllocateChannels(64); + SetAllChannelVolume(0); +} + +Asset_SoundPack::~Asset_SoundPack() +{ +} + +void Asset_SoundPack::InitNpkList() +{ + DIR *dir; + struct dirent *ent; + std::string path = "SoundPacks/"; + dir = opendir(path.c_str()); + + while ((ent = readdir(dir))) + { + // 跳过.和..目录 + if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) + continue; + std::string RealPath = path + ent->d_name; + Ifstream_NPK Fs; + Fs.open(RealPath.c_str(), std::ios::in | std::ios::binary); + if (Fs.is_open()) + { + std::string Header = Fs.ReadString(); + // 如果是NPK + if (Header == "NeoplePack_Bill") + { + // 读取img数量 + int ImageCount = Fs.ReadInt(); + + for (int i = 0; i < ImageCount; ++i) + { + // 直接读取当前项的偏移、长度和路径 + auto offset = Fs.ReadUnsignedInt(); + auto length = Fs.ReadUnsignedInt(); + auto path = Fs.ReadInfo(); + + // 构造 WAV 并插入映射表 + lp_arr[path] = WAV{ + offset, // offset + length, // size + path, // lpWavName(直接复用读取的path) + RealPath, // lpBelongsFile + i // img_index + }; + } + } + } + } + + closedir(dir); +} + +void Asset_SoundPack::Reclaims() +{ +} + +Mix_Chunk *Asset_SoundPack::GetOgg(const std::string &lpWavName) +{ + + if (map_sound.find(lpWavName) != map_sound.end()) + { + map_sound[lpWavName].LastUseTime = SDL_GetTicks64(); + return map_sound[lpWavName].lpMusic; + } + // 在NPK映射表中找到 + if (lp_arr.find(lpWavName) != lp_arr.end()) + { + WAV &wav = lp_arr[lpWavName]; + if (wav.lpBelongsFile.empty()) + return nullptr; + Ifstream_NPK Fs; + Fs.open(wav.lpBelongsFile.c_str(), std::ios::in | std::ios::binary); + if (Fs.is_open()) + { + Fs.seekg(wav.offset, std::ios::beg); + auto buffer = Fs.ReadCustomSize(wav.size); + SDL_RWops *rw = SDL_RWFromConstMem(buffer, wav.size); + if (!rw) + { + SDL_Log("创建内存流失败: %s\n", SDL_GetError()); + Mix_CloseAudio(); + SDL_Quit(); + return nullptr; + } + Mix_Chunk *music = Mix_LoadWAV_RW(rw, 0); + if (!music) + { + SDL_Log("加载OGG失败: %s\n", Mix_GetError()); + SDL_RWclose(rw); // 手动关闭流 + Mix_CloseAudio(); + SDL_Quit(); + return nullptr; + } + map_sound[lpWavName].lpMusic = music; + map_sound[lpWavName].LastUseTime = SDL_GetTicks64(); + return music; + } + } + // 去Music文件夹中寻找 + else + { + Mix_Chunk *music = Mix_LoadWAV(lpWavName.c_str()); + if (!music) + { + SDL_Log("加载OGG失败: %s\n", Mix_GetError()); + Mix_CloseAudio(); + SDL_Quit(); + return nullptr; + } + map_sound[lpWavName].lpMusic = music; + map_sound[lpWavName].LastUseTime = SDL_GetTicks64(); + return music; + } +} + +void Asset_SoundPack::PlayAudio(const std::string Key) +{ + std::string RealKey = Key; + // 先判断是不是随机组 如果是随机组去里面调出真正要播放的Key + if (XmlConfig.HasRandomGroup(Key)) + { + auto Group = XmlConfig.GetRandomGroup(Key); + int RandNum = rand() % 100; + for (auto &item : Group.items) + { + if (RandNum <= item.prob) + { + RealKey = item.tag; + } + } + } + + // 如果是特效音频 + if (XmlConfig.HasEffect(Key)) + { + std::string oggPath = XmlConfig.GetEffect(Key).file_path; + int volume = XmlConfig.GetEffect(Key).volume_adjust; + PlayEffect(oggPath, volume); + } + // 如果是背景音乐 + else if (XmlConfig.HasMusic(Key)) + { + std::string oggPath = XmlConfig.GetMusic(Key).file_path; + PlayMusic(Key, oggPath); + } + else + { + SDL_LogError(0, "找不到音频: %s\n", Key.c_str()); + } +} + +void Asset_SoundPack::StopAudio(const std::string Key) +{ + StopChannel(backgroud_music_map[Key]); + backgroud_music_map.erase(Key); +} + +void Asset_SoundPack::PlayMusic(const std::string Key, const std::string oggpath) +{ + auto ogg = GetOgg(oggpath); + if (ogg) + { + int channel = FindFreeChannelInRange(0, 4); + backgroud_music_map[Key] = Mix_PlayChannel(channel, ogg, -1); + } +} + +void Asset_SoundPack::PlayEffect(const std::string Key, int Volume) +{ + auto ogg = GetOgg(Key); + if (ogg) + { + int channel = FindFreeChannelInRange(4, ChannelCount - 1); + Mix_VolumeChunk(ogg, Volume); + Mix_PlayChannel(channel, ogg, 0); + } +} + +void Asset_SoundPack::StopChannel(int Channel) +{ + Mix_HaltChannel(Channel); // 停止指定通道的音效 +} + +void Asset_SoundPack::SetAllChannelVolume(int Volume) +{ + Mix_Volume(-1, Volume); +} + +std::map Asset_SoundPack::GetBackgroudMusicVector() +{ + return backgroud_music_map; +} + +int Asset_SoundPack::FindFreeChannelInRange(int startChannel, int endChannel) +{ + // 遍历通道(若总通道数小于32,则只检查到最后一个通道) + for (int channel = startChannel; channel <= endChannel; channel++) + { + // 检查通道是否空闲(Mix_Playing返回0表示空闲) + if (Mix_Playing(channel) == 0) + { + return channel; // 返回第一个空闲通道 + } + } + + // 若所有通道都在使用中 + return -1; +} diff --git a/source/Asset/Asset_SoundPack.h b/source/Asset/Asset_SoundPack.h new file mode 100644 index 0000000..01e0ca0 --- /dev/null +++ b/source/Asset/Asset_SoundPack.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "Tool/Ifstream_NPK.h" +#include "Asset/Asset_Audio.hpp" + +class Asset_SoundPack +{ +public: + struct WAV + { + unsigned int offset; // 偏移 + unsigned int size; // 大小 + std::string lpWavName; // img文件的路径 + std::string lpBelongsFile; // 这个img属于哪个npk文件 + int img_index; // img文件在npk文件里的序号 + }; + + struct NpkInfo + { + int Offset; + int Length; + std::string Path; + }; + + struct MusicInfo + { + /**上次使用时间 */ + Uint64 LastUseTime; + /**音频指针 */ + Mix_Chunk *lpMusic; + }; + +public: + Asset_SoundPack(const Asset_SoundPack &) = delete; + Asset_SoundPack &operator=(const Asset_SoundPack &) = delete; + Asset_SoundPack(Asset_SoundPack &&) = delete; + Asset_SoundPack &operator=(Asset_SoundPack &&) = delete; + // 全局访问点 + static Asset_SoundPack &GetInstance() + { + static Asset_SoundPack instance; // 局部静态变量,保证只初始化一次 + return instance; + } + + /**音频NPK信息表 */ + std::map lp_arr; + /**音频储存表 */ + std::map map_sound; + /**audio信息表 */ + AudioConfigReader XmlConfig; + /**正在播放的背景音乐表 */ + std::map backgroud_music_map; + /**总通道数 */ + int ChannelCount; + +public: + Asset_SoundPack(/* args */); + ~Asset_SoundPack(); + + void InitNpkList(); + + void Reclaims(); + + Mix_Chunk *GetOgg(const std::string &lpWavName); + + /**播放音频 */ + void PlayAudio(const std::string Key); + /**停止音频 */ + void StopAudio(const std::string Key); + + void PlayMusic(const std::string Key, const std::string oggpath); + void PlayEffect(const std::string Key, int Volume); + + /**停止通道播放音频 */ + void StopChannel(int Channel); + /**设置所有通道音量 */ + void SetAllChannelVolume(int Volume); + /**获取正在播放的背景音乐 */ + std::map GetBackgroudMusicVector(); + + /**获取空闲的通道 */ + int FindFreeChannelInRange(int Start, int End); +}; diff --git a/source/Asset/rapidxml/rapidxml.hpp b/source/Asset/rapidxml/rapidxml.hpp new file mode 100644 index 0000000..ae91e08 --- /dev/null +++ b/source/Asset/rapidxml/rapidxml.hpp @@ -0,0 +1,2596 @@ +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

+ //! This function cannot return. If it does, the results are undefined. + //!

+ //! A very simple definition might look like that: + //!
+    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
+    //! {
+    //!     std::cout << "Parse error: " << what << "\n";
+    //!     std::abort();
+    //! }
+    //! 
+ //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

+ //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

+ //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

+ //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

+ //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

+ //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
    + //!
  • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
  • + //!
  • entities will not be translated
  • + //!
  • whitespace will not be normalized
  • + //!
+ //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

+ //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

+ //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

+ //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

+ //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

+ //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

+ //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

+ //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
(align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

+ //! User defined allocation functions must have the following forms: + //!
+ //!
void *allocate(std::size_t size); + //!
void free(void *pointer); + //!

+ //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
(pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

+ //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

+ //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

+ //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

+ //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

+ //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

+ //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

+ //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/source/Asset/rapidxml/rapidxml_iterators.hpp b/source/Asset/rapidxml/rapidxml_iterators.hpp new file mode 100644 index 0000000..52ebc29 --- /dev/null +++ b/source/Asset/rapidxml/rapidxml_iterators.hpp @@ -0,0 +1,174 @@ +#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED +#define RAPIDXML_ITERATORS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_iterators.hpp This file contains rapidxml iterators + +#include "rapidxml.hpp" + +namespace rapidxml +{ + + //! Iterator of child nodes of xml_node + template + class node_iterator + { + + public: + + typedef typename xml_node value_type; + typedef typename xml_node &reference; + typedef typename xml_node *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + node_iterator() + : m_node(0) + { + } + + node_iterator(xml_node *node) + : m_node(node->first_node()) + { + } + + reference operator *() const + { + assert(m_node); + return *m_node; + } + + pointer operator->() const + { + assert(m_node); + return m_node; + } + + node_iterator& operator++() + { + assert(m_node); + m_node = m_node->next_sibling(); + return *this; + } + + node_iterator operator++(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + node_iterator& operator--() + { + assert(m_node && m_node->previous_sibling()); + m_node = m_node->previous_sibling(); + return *this; + } + + node_iterator operator--(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const node_iterator &rhs) + { + return m_node == rhs.m_node; + } + + bool operator !=(const node_iterator &rhs) + { + return m_node != rhs.m_node; + } + + private: + + xml_node *m_node; + + }; + + //! Iterator of child attributes of xml_node + template + class attribute_iterator + { + + public: + + typedef typename xml_attribute value_type; + typedef typename xml_attribute &reference; + typedef typename xml_attribute *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + attribute_iterator() + : m_attribute(0) + { + } + + attribute_iterator(xml_node *node) + : m_attribute(node->first_attribute()) + { + } + + reference operator *() const + { + assert(m_attribute); + return *m_attribute; + } + + pointer operator->() const + { + assert(m_attribute); + return m_attribute; + } + + attribute_iterator& operator++() + { + assert(m_attribute); + m_attribute = m_attribute->next_attribute(); + return *this; + } + + attribute_iterator operator++(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + attribute_iterator& operator--() + { + assert(m_attribute && m_attribute->previous_attribute()); + m_attribute = m_attribute->previous_attribute(); + return *this; + } + + attribute_iterator operator--(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const attribute_iterator &rhs) + { + return m_attribute == rhs.m_attribute; + } + + bool operator !=(const attribute_iterator &rhs) + { + return m_attribute != rhs.m_attribute; + } + + private: + + xml_attribute *m_attribute; + + }; + +} + +#endif diff --git a/source/Asset/rapidxml/rapidxml_print.hpp b/source/Asset/rapidxml/rapidxml_print.hpp new file mode 100644 index 0000000..0ae2b14 --- /dev/null +++ b/source/Asset/rapidxml/rapidxml_print.hpp @@ -0,0 +1,421 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif diff --git a/source/Asset/rapidxml/rapidxml_utils.hpp b/source/Asset/rapidxml/rapidxml_utils.hpp new file mode 100644 index 0000000..37c2953 --- /dev/null +++ b/source/Asset/rapidxml/rapidxml_utils.hpp @@ -0,0 +1,122 @@ +#ifndef RAPIDXML_UTILS_HPP_INCLUDED +#define RAPIDXML_UTILS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "rapidxml.hpp" +#include +#include +#include +#include + +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} + +#endif diff --git a/source/EngineCore/Game.cpp b/source/EngineCore/Game.cpp index c022a30..f968da3 100644 --- a/source/EngineCore/Game.cpp +++ b/source/EngineCore/Game.cpp @@ -3,6 +3,7 @@ #include "EngineFrame/Component/Sprite.h" #include "EngineFrame/Base/Actor.h" #include "EngineFrame/Component/Text.h" +#include "Asset/Asset_SoundPack.h" Game::Game() { @@ -67,13 +68,6 @@ void Game::Init(std::function CallBack) IMG_Init(IMG_INIT_PNG); - // 初始化SDL_mixer,支持OGG格式 - // 44100: 采样率, MIX_DEFAULT_FORMAT: 音频格式, 2: 声道数(立体声), 4096: 缓冲区大小 - if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) - { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "SDL_mixer初始化失败! Mix_Error: %s\n", Mix_GetError()); - m_isRunning = false; - } // 初始化 TTF if (TTF_Init() == -1) @@ -157,9 +151,16 @@ void Game::HandleEvents(SDL_Event *e) void Game::Update(float deltaTime) { - //调用松鼠的更新 + // 内存回收机制 + m_lastMemoryPrintTime += deltaTime; + if (m_lastMemoryPrintTime >= m_memoryPrintInterval) + { + m_lastMemoryPrintTime = 0; + MemoryReclaims(); + } + // 调用松鼠的更新 SquirrelEx::GetInstance().Update(deltaTime); - if (m_scene != nullptr) + if (m_scene != nullptr) m_scene->Update(deltaTime); if (m_uiScene != nullptr) m_uiScene->Update(deltaTime); @@ -205,6 +206,12 @@ void Game::Clear() SDL_Quit(); } +void Game::MemoryReclaims() +{ + // 音频内存回收 + Asset_SoundPack::GetInstance().Reclaims(); +} + void Game::ChangeScene(RefPtr scene) { if (m_scene != nullptr) @@ -225,6 +232,11 @@ void Game::ChangeUIScene(RefPtr scene) m_uiScene->Enter(); } +RefPtr Game::GetScene() +{ + return m_scene; +} + RenderManager *Game::GetRenderer() { return m_renderer; diff --git a/source/EngineCore/Game.h b/source/EngineCore/Game.h index 7ee5edf..e71ac46 100644 --- a/source/EngineCore/Game.h +++ b/source/EngineCore/Game.h @@ -51,10 +51,14 @@ public: void Render(); void Clear(); + void MemoryReclaims(); + // 切换场景 void ChangeScene(RefPtr scene); // 设定UI层场景对象 void ChangeUIScene(RefPtr scene); + // 获取当前游戏层场景 + RefPtr GetScene(); RenderManager *GetRenderer(); @@ -79,7 +83,7 @@ private: #ifdef __SWITCH__ float m_Settingfps = 5000.0; #else - float m_Settingfps = 5000.0; + float m_Settingfps = 144.0; #endif // 单帧时间 float m_frameTime = 0.f; @@ -88,6 +92,10 @@ private: // 每秒内的帧数计数器 u32 m_frameCounter; u32 m_lastFpsPrintTime; + // 内存回收计时器 + u32 m_lastMemoryPrintTime = 0; + // 内存回收间隔 + u32 m_memoryPrintInterval = 5000; public: // 屏幕宽高 diff --git a/source/EngineFrame/Base/Actor.cpp b/source/EngineFrame/Base/Actor.cpp index fe9db83..a1323e9 100644 --- a/source/EngineFrame/Base/Actor.cpp +++ b/source/EngineFrame/Base/Actor.cpp @@ -1,45 +1,41 @@ #include "Actor.h" #include "EngineFrame/Scene/Scene.h" #include "EngineCore/Game.h" -#include "EngineFrame/Component/RenderBase.h" #include "EngineFrame/Render/RenderManager.h" #include -Actor::Actor() -{ - Init(); -} - void Actor::Init() { - addTag(Tag::ACTOR); - addTag(Tag::RENDER); - addTag(Tag::TRANSFORM); - addTag(Tag::UPDATE); } -void Actor::Update(float deltaTime) +void Actor::OnAdded(Actor *node) { - if (!Visible) - return; - BaseNode::Update(deltaTime); + +} + +void Actor::SetBlendMode(LE_BlEND_MODE mode) +{ + this->_BlendMode = mode; +} + +LE_BlEND_MODE Actor::GetBlendMode() +{ + return this->_BlendMode; } void Actor::Render() { - if (!Visible) - return; // 如果有裁切视口 if (this->_CropViewportFlag) { RenderManager *renderer = Game::GetInstance().GetRenderer(); renderer->SetClipRect(&this->_CropViewport); - BaseNode::Render(); + Node::Render(); renderer->CloseClipRect(); } else { - BaseNode::Render(); + Node::Render(); } } diff --git a/source/EngineFrame/Base/Actor.h b/source/EngineFrame/Base/Actor.h index cce1872..5858dea 100644 --- a/source/EngineFrame/Base/Actor.h +++ b/source/EngineFrame/Base/Actor.h @@ -1,5 +1,5 @@ #pragma once -#include "EngineFrame/Base/BaseNode.h" +#include "EngineFrame/Base/Node.h" #include "Tool/IntrusiveList.hpp" class Scene; /** @@ -7,24 +7,31 @@ class Scene; * * Actor类是一个基础的游戏对象类,可以添加到场景中 */ -class Actor : public BaseNode +class Actor : public Node { -private: - //裁切视口Flag +protected: + /**裁切视口Flag */ bool _CropViewportFlag = false; - //裁切视口 + /**裁切视口 */ SDL_Rect _CropViewport = {0, 0, 0, 0}; + /**混合模式 */ + LE_BlEND_MODE _BlendMode = NONE; public: - Actor(); - + // 初始化 + virtual void Init(); + // 被添加时 + virtual void OnAdded(Actor *node); + // 设置混合模式 + void SetBlendMode(LE_BlEND_MODE mode); + // 获取混合模式 + LE_BlEND_MODE GetBlendMode(); public: - void Init() override; - void Update(float deltaTime) override; + /**重载渲染函数 */ void Render() override; - // 设置裁切视口(放在Actor里 他与他的子对象都会被裁切) + /**设置裁切视口(放在Actor里 他与他的子对象都会被裁切) */ void SetCropViewport(SDL_Rect CropViewport); - // 获取裁切视口 + /**获取裁切视口 */ SDL_Rect GetCropViewport(); }; diff --git a/source/EngineFrame/Base/BaseNode.cpp b/source/EngineFrame/Base/BaseNode.cpp deleted file mode 100644 index 6e0288f..0000000 --- a/source/EngineFrame/Base/BaseNode.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#include "BaseNode.h" -#include "EngineCore/Game.h" -BaseNode::BaseNode() -{ - Game::GetInstance().m_nodeCount++; -} - -BaseNode::~BaseNode() -{ - Game::GetInstance().m_nodeCount--; -} - -void BaseNode::Init() -{ -} -void BaseNode::HandleEvents(SDL_Event *e) -{ - // 如果有子节点并含有事件标签,则处理事件 - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - if (child->hasTag(Tag::ACTOR) || child->hasTag(Tag::HANDEL_EVENT)) - child->HandleEvents(e); - child = child->GetNext(); - } -} - -void BaseNode::Update(float deltaTime) -{ - // 如果有子节点并含有刷新标签,则更新子节点 - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - if (child->hasTag(Tag::UPDATE)) - child->Update(deltaTime); - child = child->GetNext(); - } - // 如果有回调函数,则调用回调函数 - if (cb_update_.size() > 0) - { - for (auto &cb : cb_update_) - { - cb.second(deltaTime); - } - } -} - -void BaseNode::PreRender() -{ - // 如果有子节点并含有渲染标签,则渲染子节点 - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - if (child->hasTag(Tag::RENDER)) - child->PreRender(); - child = child->GetNext(); - } -} -void BaseNode::Render() -{ - // 如果有子节点并含有渲染标签,则渲染子节点 - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - if (child->hasTag(Tag::RENDER)) - child->Render(); - child = child->GetNext(); - } -} - -void BaseNode::Clear() -{ -} - -void BaseNode::SetCallbackOnUpdate(std::string Key, const UpdateCallback &cb) -{ - cb_update_[Key] = cb; -} - -void BaseNode::RemoveCallbackOnUpdate(std::string Key) -{ - cb_update_.erase(Key); -} - -void BaseNode::SetChildIterationTransform() -{ - Transform n_transform; - n_transform.position = transform.position + transformIter.position; - n_transform.scale = transform.scale * transformIter.scale; - n_transform.rotation = transform.rotation + transformIter.rotation; - - // 如果有子节点并含有transform标签,则设置其位置 - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - if (child->hasTag(Tag::TRANSFORM)) - child->SetIterationTransform(n_transform); - child = child->GetNext(); - } -} - -void BaseNode::CalcRenderInfo() -{ -} - -void BaseNode::SetName(std::string name) -{ - m_Name = name; -} - -std::string BaseNode::GetName() -{ - return m_Name; -} - -int BaseNode::GetRenderZOrder() -{ - return m_RenderZOrder; -} - -void BaseNode::SetRenderZOrder(int zOrder) -{ - m_RenderZOrder = zOrder; - Reorder(); -} - -void BaseNode::Reorder() -{ - if (m_Parent) - { - RefPtr me = this; - m_Parent->m_BaseNodes.Remove(me); - RefPtr sibling = m_Parent->m_BaseNodes.GetLast(); - if (sibling && sibling->GetRenderZOrder() > m_RenderZOrder) - { - sibling = sibling->GetPrev(); - while (sibling) - { - if (sibling->GetRenderZOrder() <= m_RenderZOrder) - break; - sibling = sibling->GetPrev(); - } - } - if (sibling) - { - m_Parent->m_BaseNodes.InsertAfter(me, sibling); - } - else - { - m_Parent->m_BaseNodes.PushFront(me); - } - } -} - -void BaseNode::AddChild(RefPtr child) -{ - m_BaseNodes.PushBack(child); - child->OnAdded(this); - // 如果是需要渲染的子对象 - if (child->hasTag(Tag::RENDER)) - { - // 重新排序 - child->Reorder(); - // 如果组件有transform标签,则设置其位置 - if (child->hasTag(Tag::TRANSFORM)) - { - Transform n_transform; - n_transform.position = transform.position + transformIter.position; - n_transform.scale = transform.scale * transformIter.scale; - n_transform.rotation = transform.rotation + transformIter.rotation; - child->SetIterationTransform(n_transform); - } - } -} - -void BaseNode::RemoveChild(RefPtr child) -{ - child->m_Parent = nullptr; - m_BaseNodes.Remove(child); -} - -void BaseNode::RemoveAllChild() -{ - m_BaseNodes.Clear(); -} - -void BaseNode::OnAdded(BaseNode *node) -{ - m_Parent = node; -} - -void BaseNode::SetIterationTransform(Transform n_transform) -{ - if (n_transform == transformIter) - return; - transformIter = n_transform; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Transform BaseNode::GetIterationTransform() -{ - return transformIter; -} - -void BaseNode::SetTransform(Transform n_transform) -{ - if (n_transform == transform) - return; - transform = n_transform; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Transform BaseNode::GetTransform() -{ - return transform; -} - -void BaseNode::SetPos(Vec2 pos) -{ - if (pos == this->transform.position) - return; - this->transform.position = pos; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Vec2 BaseNode::GetPos() -{ - return this->transform.position; -} - -Vec2 BaseNode::GetWorldPos() -{ - return this->transform.position + this->transformIter.position; -} - -void BaseNode::SetScale(Vec2 scale) -{ - if (scale == this->transform.scale) - return; - this->transform.scale = scale; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Vec2 BaseNode::GetScale() -{ - return this->transform.scale; -} - -void BaseNode::SetRotation(float angle) -{ - if (angle == this->transform.rotation) - return; - this->transform.rotation = angle; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -float BaseNode::GetRotation() -{ - return this->transform.rotation; -} - -void BaseNode::SetAnchor(Vec2 anchor) -{ - if (anchor == this->Anchor) - return; - Anchor.x = anchor.x; - Anchor.y = anchor.y; -} - -Vec2 BaseNode::GetAnchor() -{ - Vec2 P; - P.x = Anchor.x; - P.y = Anchor.y; - return P; -} - -void BaseNode::SetSize(VecSize size) -{ - this->Size = size; -} - -VecSize BaseNode::GetSize() -{ - return this->Size; -} - -void BaseNode::SetVisible(bool visible) -{ - this->Visible = visible; -} - -bool BaseNode::GetVisible() -{ - return this->Visible; -} - -void BaseNode::SetAlpha(float alpha) -{ - this->Alpha = alpha; -} - -BaseNode *BaseNode::GetParent() -{ - return this->m_Parent; -} - -float BaseNode::GetAlpha() -{ - return this->Alpha; -} - -void BaseNode::SetIterationPos(Vec2 pos) -{ - if (pos == this->transformIter.position) - return; - this->transformIter.position = pos; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Vec2 BaseNode::GetIterationPos() -{ - return this->transformIter.position; -} - -void BaseNode::SetIterationScale(Vec2 scale) -{ - if (scale == this->transformIter.scale) - return; - this->transformIter.scale = scale; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -Vec2 BaseNode::GetIterationScale() -{ - return this->transformIter.scale; -} - -void BaseNode::SetIterationRotation(float angle) -{ - if (angle == this->transformIter.rotation) - return; - this->transformIter.rotation = angle; - CalcRenderInfo(); - SetChildIterationTransform(); -} - -float BaseNode::GetIterationRotation() -{ - return this->transformIter.rotation; -} - -void BaseNode::SetBlendMode(LE_BlEND_MODE mode) -{ - this->_BlendMode = mode; -} - -LE_BlEND_MODE BaseNode::GetBlendMode() -{ - return this->_BlendMode; -} diff --git a/source/EngineFrame/Base/BaseNode.h b/source/EngineFrame/Base/BaseNode.h deleted file mode 100644 index b627177..0000000 --- a/source/EngineFrame/Base/BaseNode.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once -#include -#include -#include -#include "Tool/RefObject.h" -#include "Tool/RefPtr.h" -#include "Tool/IntrusiveList.hpp" -#include "Tool/TagGed.h" -#include "math/Transform.hpp" -#include "math/Math.h" -using namespace ember; -class BaseNode : public RefObject, public TagGed, protected IntrusiveListValue> -{ - -public: - typedef std::function UpdateCallback; - - using IntrusiveListValue>::GetNext; - using IntrusiveListValue>::GetPrev; - -public: - // 更新时的回调函数 - std::map cb_update_; - // 节点名称 - std::string m_Name; - // 子节点列表 - IntrusiveList> m_BaseNodes; - // 指向父对象的指针 - BaseNode *m_Parent = nullptr; - // 渲染层级 - int m_RenderZOrder = 0; - // 二维仿射变换 - Transform transform; - // 迭代的二维仿射变换 - Transform transformIter; - // 锚点 - Vec2 Anchor = {0.f, 0.f}; - // 大小 - VecSize Size = {0, 0}; - // 透明度 - float Alpha = 1.f; - // 混合模式 - LE_BlEND_MODE _BlendMode = NONE; - // 是否显示 - bool Visible = true; - // 计算渲染信息Flag (为了保证每帧只计算一次) - bool CalcRenderInfoFlag = true; - -public: - BaseNode(/* args */); - ~BaseNode(); - -public: - virtual void Init(); - virtual void HandleEvents(SDL_Event *e); - virtual void Update(float deltaTime); - virtual void PreRender(); - virtual void Render(); - virtual void Clear(); - - /// \~chinese - /// @brief 设置更新时的回调函数 - void SetCallbackOnUpdate(std::string Key,const UpdateCallback &cb); - void RemoveCallbackOnUpdate(std::string Key); - - void SetChildIterationTransform(); - - // 计算渲染信息 - virtual void CalcRenderInfo(); - // 设置名字 - void SetName(std::string name); - // 获取名字 - std::string GetName(); - // 设置渲染层级 - int GetRenderZOrder(); - // 获取渲染层级 - void SetRenderZOrder(int zOrder); - // 重新排序 - void Reorder(); - // 添加子对象 - virtual void AddChild(RefPtr child); - // 移除子对象 - void RemoveChild(RefPtr child); - // 移除所有子对象 - void RemoveAllChild(); - // 被添加时 - virtual void OnAdded(BaseNode *node); - // 设置迭代的二维仿射变换 - void SetIterationTransform(Transform transform); - // 获取迭代的二维仿射变换 - Transform GetIterationTransform(); - // 设置二维仿射变换 - void SetTransform(Transform transform); - // 获取二维仿射变换 - Transform GetTransform(); - - // 设置迭代的坐标 - virtual void SetIterationPos(Vec2 pos); - // 获取迭代的坐标 - Vec2 GetIterationPos(); - // 设置迭代的缩放 - virtual void SetIterationScale(Vec2 scale); - // 获取迭代的缩放 - Vec2 GetIterationScale(); - // 设置迭代的旋转角度 - virtual void SetIterationRotation(float angle); - // 获取迭代的旋转角度 - float GetIterationRotation(); - - // 设置坐标 - virtual void SetPos(Vec2 pos); - // 获取坐标 - Vec2 GetPos(); - // 获取世界坐标 - Vec2 GetWorldPos(); - // 设置缩放 - virtual void SetScale(Vec2 scale); - // 获取缩放 - Vec2 GetScale(); - // 设置旋转角度 - virtual void SetRotation(float angle); - // 获取旋转角度 - float GetRotation(); - // 设置中心点 - virtual void SetAnchor(Vec2 anchor); - // 获取中心点 - Vec2 GetAnchor(); - // 设置大小 - virtual void SetSize(VecSize size); - // 获取大小 - VecSize GetSize(); - // 设置是否显示 - virtual void SetVisible(bool visible); - // 获取是否显示 - bool GetVisible(); - // 设置透明度 - virtual void SetAlpha(float alpha); - // 获取透明度 - float GetAlpha(); - // 设置混合模式 - void SetBlendMode(LE_BlEND_MODE mode); - // 获取混合模式 - LE_BlEND_MODE GetBlendMode(); - - //获取父对象 - BaseNode *GetParent(); -}; diff --git a/source/EngineFrame/Base/Node.cpp b/source/EngineFrame/Base/Node.cpp index 49c4f51..59e3197 100644 --- a/source/EngineFrame/Base/Node.cpp +++ b/source/EngineFrame/Base/Node.cpp @@ -5,11 +5,18 @@ void Node::Init() } void Node::HandleEvents(SDL_Event *e) { + RefPtr child = children_.GetFirst(); + while (child) + { + child->HandleEvents(e); + child = child->GetNext(); + } } inline void Node::Update(float deltaTime) { if (children_.IsEmpty()) { + UpdateSelf(deltaTime); return; } @@ -34,26 +41,118 @@ inline void Node::Update(float deltaTime) } inline void Node::PreRender() { + if (!visible_) + return; + UpdateTransform(); + UpdateOpacity(); + + if (children_.IsEmpty()) + { + if (CheckVisibility()) + { + OnPreRender(); + } + } + else + { + RefPtr child = children_.GetFirst(); + while (child) + { + if (child->GetZOrder() >= 0) + break; + + child->PreRender(); + child = child->GetNext(); + } + + if (CheckVisibility()) + { + OnPreRender(); + } + + while (child) + { + child->PreRender(); + child = child->GetNext(); + } + } } inline void Node::Render() { + if (!visible_) + return; + if (children_.IsEmpty()) + { + if (visible_in_rt_) + { + PrepareToRender(); + OnRender(); + } + } + else + { + RefPtr child = children_.GetFirst(); + while (child) + { + if (child->GetZOrder() >= 0) + break; + + child->Render(); + child = child->GetNext(); + } + + if (visible_in_rt_) + { + PrepareToRender(); + OnRender(); + } + + while (child) + { + child->Render(); + child = child->GetNext(); + } + } } void Node::Clear() { } +void Node::OnPreRender() +{ +} + +void Node::OnRender() +{ +} + +void Node::OnUpdate(float deltaTime) +{ +} + void Node::UpdateSelf(float deltaTime) { - // 如果有回调函数,则调用回调函数 - if (cb_update_.size() > 0) + if (!update_pausing_) { - for (auto &cb : cb_update_) + OnUpdate(deltaTime); + + // 如果有回调函数,则调用回调函数 + if (cb_update_.size() > 0) { - cb.second(deltaTime); + for (auto &cb : cb_update_) + { + cb.second(deltaTime); + } } } } +void Node::PrepareToRender() +{ + Game::GetInstance().GetRenderer()->SetMatrix(render_matrix_); + Game::GetInstance().GetRenderer()->SetOpacity(displayed_opacity_); +} + void Node::RemoveFromParent() { if (parent_) @@ -109,7 +208,7 @@ void Node::ShowBorder(bool show) show_border_ = show; } -Node::Node() : visible_(true), update_pausing_(false), show_border_(false), parent_(nullptr), anchor_(0.0f, 0.0f), z_order_(0), opacity_(1.f), name_("Node") +Node::Node() : visible_(true), update_pausing_(false), show_border_(false), parent_(nullptr), anchor_(0.0f, 0.0f), z_order_(0), opacity_(1.f), name_("Node"), displayed_opacity_(1.f), cascade_opacity_(true) { Game::GetInstance().m_nodeCount++; } @@ -146,6 +245,8 @@ void Node::UpdateTransform() const transform_matrix_ *= parent_->transform_matrix_; } + GenerateRenderMatrix(); + for (const auto &child : children_) child->dirty_flag_.Set(DirtyFlag::DirtyTransform); } @@ -165,6 +266,26 @@ void Node::UpdateTransformUpwards() const UpdateTransform(); } +void Node::UpdateOpacity() +{ + if (!dirty_flag_.Has(DirtyFlag::DirtyOpacity)) + return; + + dirty_flag_.Unset(DirtyFlag::DirtyOpacity); + + if (parent_ && parent_->IsCascadeOpacityEnabled()) + { + displayed_opacity_ = opacity_ * parent_->displayed_opacity_; + } + else + { + displayed_opacity_ = opacity_; + } + + for (const auto &child : children_) + child->dirty_flag_.Set(DirtyFlag::DirtyOpacity); +} + const Matrix3x2 &Node::GetTransformMatrix() const { UpdateTransformUpwards(); @@ -188,6 +309,20 @@ const Matrix3x2 &Node::GetTransformMatrixToParent() const return transform_matrix_to_parent_; } +void Node::GenerateRenderMatrix() const +{ + // 构造OpenGl渲染矩阵 + render_matrix_ = glm::mat4(1.0f); + // 填充旋转+缩放分量(2D 变换核心,对应 4x4 矩阵左上 2x2 区域) + render_matrix_[0][0] = transform_matrix_[0]; // _11 → x轴缩放+旋转 + render_matrix_[0][1] = transform_matrix_[1]; // _12 → 影响y轴方向的旋转分量 + render_matrix_[1][0] = transform_matrix_[2]; // _21 → 影响x轴方向的旋转分量 + render_matrix_[1][1] = transform_matrix_[3]; // _22 → y轴缩放+旋转 + // 填充平移分量(对应 4x4 矩阵最后一行前两列,z轴平移为0) + render_matrix_[3][0] = transform_matrix_[4]; // _31 → x方向平移(世界坐标) + render_matrix_[3][1] = transform_matrix_[5]; // _32 → y方向平移(世界坐标) +} + void Node::Reorder() { if (parent_) @@ -220,287 +355,24 @@ void Node::Reorder() } } -inline void Node::RemoveAllChildren() +bool Node::CheckVisibility() const { - RefPtr next; - for (RefPtr child = children_.GetFirst(); child; child = next) + if (dirty_flag_.Has(DirtyFlag::DirtyVisibility)) { - next = child->GetNext(); - RemoveChild(child); - } -} - -inline bool Node::IsVisible() const -{ - return visible_; -} - -inline int Node::GetZOrder() const -{ - return z_order_; -} - -inline glm::vec2 Node::GetPosition() const -{ - return transform_.position; -} - -inline float Node::GetPositionX() const -{ - return GetPosition().x; -} - -inline float Node::GetPositionY() const -{ - return GetPosition().y; -} - -inline VecSize Node::GetSize() const -{ - return size_; -} - -inline float Node::GetWidth() const -{ - return GetSize().width; -} - -inline float Node::GetHeight() const -{ - return GetSize().height; -} - -inline float Node::GetScaledWidth() const -{ - return GetWidth() * GetScaleX(); -} - -inline float Node::GetScaledHeight() const -{ - return GetHeight() * GetScaleY(); -} - -inline VecSize Node::GetScaledSize() const -{ - return VecSize{GetScaledWidth(), GetScaledHeight()}; -} - -inline glm::vec2 Node::GetAnchor() const -{ - return anchor_; -} - -inline float Node::GetAnchorX() const -{ - return GetAnchor().x; -} - -inline float Node::GetAnchorY() const -{ - return GetAnchor().y; -} - -inline float Node::GetOpacity() const -{ - return opacity_; -} - -inline float Node::GetRotation() const -{ - return transform_.rotation; -} - -inline glm::vec2 Node::GetScale() const -{ - return transform_.scale; -} - -inline float Node::GetScaleX() const -{ - return GetScale().x; -} - -inline float Node::GetScaleY() const -{ - return GetScale().y; -} - -inline glm::vec2 Node::GetSkew() const -{ - return transform_.skew; -} - -inline float Node::GetSkewX() const -{ - return GetSkew().x; -} - -inline float Node::GetSkewY() const -{ - return GetSkew().y; -} - -inline Y_Transform Node::GetTransform() const -{ - return transform_; -} - -inline Node *Node::GetParent() const -{ - return parent_; -} - -inline void Node::SetVisible(bool val) -{ - visible_ = val; -} - -inline void Node::SetName(std::string name) -{ - name_ = name; -} - -inline void Node::SetPosition(const glm::vec2 &pos) -{ - if (transform_.position == pos) - return; - - transform_.position = pos; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetPosition(float x, float y) -{ - this->SetPosition(glm::vec2(x, y)); -} - -inline void Node::SetPositionX(float x) -{ - this->SetPosition(glm::vec2(x, GetPosition().y)); -} - -inline void Node::SetPositionY(float y) -{ - this->SetPosition(glm::vec2(GetPosition().x, y)); -} - -inline void Node::MoveTo(const glm::vec2 &p) -{ - this->SetPosition(p); -} - -inline void Node::MoveTo(float x, float y) -{ - this->SetPosition(glm::vec2(x, y)); -} - -inline void Node::MoveBy(const glm::vec2 &trans) -{ - this->SetPosition(transform_.position.x + trans.x, transform_.position.y + trans.y); -} - -inline void Node::MoveBy(float trans_x, float trans_y) -{ - this->MoveBy(glm::vec2(trans_x, trans_y)); -} - -inline void Node::SetScale(const glm::vec2 &scale) -{ - if (transform_.scale == scale) - return; - - transform_.scale = scale; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetScale(float scalex, float scaley) -{ - this->SetScale(glm::vec2(scalex, scaley)); -} - -inline void Node::SetSkew(const glm::vec2 &skew) -{ - if (transform_.skew == skew) - return; - - transform_.skew = skew; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetSkew(float skewx, float skewy) -{ - this->SetSkew(glm::vec2(skewx, skewy)); -} - -inline void Node::SetRotation(float rotation) -{ - if (transform_.rotation == rotation) - return; - - transform_.rotation = rotation; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetAnchor(const glm::vec2 &anchor) -{ - if (anchor_ == anchor) - return; - - anchor_ = anchor; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetAnchor(float anchorx, float anchory) -{ - this->SetAnchor(glm::vec2(anchorx, anchory)); -} - -inline void Node::SetSize(const VecSize &size) -{ - if (size_ == size) - return; - - size_ = size; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetSize(float width, float height) -{ - this->SetSize(VecSize{width, height}); -} - -inline void Node::SetWidth(float width) -{ - this->SetSize(width, GetHeight()); -} - -inline void Node::SetHeight(float height) -{ - this->SetSize(GetWidth(), height); -} - -inline void Node::SetOpacity(float opacity) -{ - if (opacity_ == opacity) - return; - - opacity_ = std::min(std::max(opacity, 0.f), 1.f); - dirty_flag_.Set(DirtyFlag::DirtyOpacity); -} - -inline void Node::SetTransform(const Y_Transform &transform) -{ - transform_ = transform; - dirty_flag_.Set(DirtyFlag::DirtyTransform); -} - -inline void Node::SetZOrder(int zorder) -{ - if (z_order_ != zorder) - { - z_order_ = zorder; - Reorder(); + dirty_flag_.Unset(DirtyFlag::DirtyVisibility); + + if (size_.width == 0.f && size_.height == 0.f) + { + visible_in_rt_ = false; + } + else + { + // TODO + // visible_in_rt_ = ctx.CheckVisibility(GetBounds(), transform_matrix_ /* GetTransformMatrix() */); + visible_in_rt_ = true; + } } + return visible_in_rt_; } void Node::AddChild(RefPtr child) @@ -532,7 +404,7 @@ const Node::NodeList &Node::GetAllChildren() const return children_; } -inline void Node::RemoveChild(RefPtr child) +void Node::RemoveChild(RefPtr child) { if (children_.IsEmpty()) return; diff --git a/source/EngineFrame/Base/Node.h b/source/EngineFrame/Base/Node.h index 8a5fab3..7bce910 100644 --- a/source/EngineFrame/Base/Node.h +++ b/source/EngineFrame/Base/Node.h @@ -16,13 +16,19 @@ public: using IntrusiveListValue>::GetNext; using IntrusiveListValue>::GetPrev; -private: +protected: + /**渲染矩阵 */ + mutable glm::mat4 render_matrix_; /**变换属性 */ Y_Transform transform_; /**变换矩阵 */ mutable Matrix3x2 transform_matrix_; mutable Matrix3x2 transform_matrix_inverse_; mutable Matrix3x2 transform_matrix_to_parent_; + +private: + + /**更新时的回调函数 */ std::map cb_update_; /**锚点 */ @@ -33,6 +39,8 @@ private: VecSize size_; /**透明度 */ float opacity_; + /**显示透明度 */ + float displayed_opacity_; /**是否可见 */ bool visible_; @@ -40,6 +48,10 @@ private: bool update_pausing_; /**是否显示边界 */ bool show_border_; + /**是否在渲染区域中 */ + mutable bool visible_in_rt_; + /**联级透明度 */ + bool cascade_opacity_; /**名称 */ std::string name_; @@ -58,14 +70,22 @@ public: virtual void Render(); virtual void Clear(); - void UpdateSelf(float dt); + virtual void OnPreRender(); + virtual void OnRender(); + virtual void OnUpdate(float deltaTime); + void UpdateSelf(float dt); + void PrepareToRender(); public: /// \~chinese /// @brief 获取显示状态 bool IsVisible() const; + /// \~chinese + /// @brief 是否启用级联透明度 + bool IsCascadeOpacityEnabled() const; + /// \~chinese /// @brief 获取 Z 轴顺序 int GetZOrder() const; @@ -160,12 +180,18 @@ public: /// \~chinese /// @brief 设置角色是否可见 - void SetVisible(bool val); + virtual void SetVisible(bool val); + + virtual void OnSetPosition(); /// \~chinese /// @brief 设置名称 void SetName(std::string name); + /// \~chinese + /// @brief 获取名称 + std::string GetName() const; + /// \~chinese /// @brief 设置坐标 void SetPosition(const glm::vec2 &pos); @@ -246,6 +272,10 @@ public: /// @brief 设置透明度,默认为 1.0, 范围 [0, 1] void SetOpacity(float opacity); + /// \~chinese + /// @brief 启用或禁用级联透明度 + void SetCascadeOpacityEnabled(bool enabled); + /// \~chinese /// @brief 设置二维仿射变换 void SetTransform(const Y_Transform &transform); @@ -328,6 +358,10 @@ public: /// @details 对于节点树 A->B(dirty)->C->D,当对 D 执行 UpdateTransformUpwards 时会对 B、C、D 从上到下依次更新 void UpdateTransformUpwards() const; + /// \~chinese + /// @brief 更新自己和所有子角色的透明度 + void UpdateOpacity(); + /// \~chinese /// @brief 获取二维变换矩阵 const Matrix3x2 &GetTransformMatrix() const; @@ -340,10 +374,18 @@ public: /// @brief 获取变换到父角色的二维变换矩阵 const Matrix3x2 &GetTransformMatrixToParent() const; + /// \~chinese + /// @brief 生成用于OpenGL渲染的矩阵 + virtual void GenerateRenderMatrix() const; + /// \~chinese /// @brief 将所有子角色按Z轴顺序排序 void Reorder(); + /// \~chinese + /// @brief 检查是否在渲染上下文的视区内 + virtual bool CheckVisibility() const; + enum DirtyFlag : uint8_t { Clean = 0, @@ -353,3 +395,310 @@ public: DirtyVisibility = 1 << 3 }; }; + +inline void Node::RemoveAllChildren() +{ + RefPtr next; + for (RefPtr child = children_.GetFirst(); child; child = next) + { + next = child->GetNext(); + RemoveChild(child); + } +} + +inline bool Node::IsVisible() const +{ + return visible_; +} + +inline bool Node::IsCascadeOpacityEnabled() const +{ + return cascade_opacity_; +} + +inline int Node::GetZOrder() const +{ + return z_order_; +} + +inline glm::vec2 Node::GetPosition() const +{ + return transform_.position; +} + +inline float Node::GetPositionX() const +{ + return GetPosition().x; +} + +inline float Node::GetPositionY() const +{ + return GetPosition().y; +} + +inline VecSize Node::GetSize() const +{ + return size_; +} + +inline float Node::GetWidth() const +{ + return GetSize().width; +} + +inline float Node::GetHeight() const +{ + return GetSize().height; +} + +inline float Node::GetScaledWidth() const +{ + return GetWidth() * GetScaleX(); +} + +inline float Node::GetScaledHeight() const +{ + return GetHeight() * GetScaleY(); +} + +inline VecSize Node::GetScaledSize() const +{ + return VecSize{GetScaledWidth(), GetScaledHeight()}; +} + +inline glm::vec2 Node::GetAnchor() const +{ + return anchor_; +} + +inline float Node::GetAnchorX() const +{ + return GetAnchor().x; +} + +inline float Node::GetAnchorY() const +{ + return GetAnchor().y; +} + +inline float Node::GetOpacity() const +{ + return opacity_; +} + +inline float Node::GetRotation() const +{ + return transform_.rotation; +} + +inline glm::vec2 Node::GetScale() const +{ + return transform_.scale; +} + +inline float Node::GetScaleX() const +{ + return GetScale().x; +} + +inline float Node::GetScaleY() const +{ + return GetScale().y; +} + +inline glm::vec2 Node::GetSkew() const +{ + return transform_.skew; +} + +inline float Node::GetSkewX() const +{ + return GetSkew().x; +} + +inline float Node::GetSkewY() const +{ + return GetSkew().y; +} + +inline Y_Transform Node::GetTransform() const +{ + return transform_; +} + +inline Node *Node::GetParent() const +{ + return parent_; +} + +inline void Node::SetVisible(bool val) +{ + visible_ = val; +} + +inline void Node::OnSetPosition() +{ +} + +inline void Node::SetName(std::string name) +{ + name_ = name; +} + +inline std::string Node::GetName() const +{ + return name_; +} + +inline void Node::SetPosition(const glm::vec2 &pos) +{ + if (transform_.position == pos) + return; + + transform_.position = pos; + dirty_flag_.Set(DirtyFlag::DirtyTransform); + OnSetPosition(); +} + +inline void Node::SetPosition(float x, float y) +{ + this->SetPosition(glm::vec2(x, y)); +} + +inline void Node::SetPositionX(float x) +{ + this->SetPosition(glm::vec2(x, GetPosition().y)); +} + +inline void Node::SetPositionY(float y) +{ + this->SetPosition(glm::vec2(GetPosition().x, y)); +} + +inline void Node::MoveTo(const glm::vec2 &p) +{ + this->SetPosition(p); +} + +inline void Node::MoveTo(float x, float y) +{ + this->SetPosition(glm::vec2(x, y)); +} + +inline void Node::MoveBy(const glm::vec2 &trans) +{ + this->SetPosition(transform_.position.x + trans.x, transform_.position.y + trans.y); +} + +inline void Node::MoveBy(float trans_x, float trans_y) +{ + this->MoveBy(glm::vec2(trans_x, trans_y)); +} + +inline void Node::SetScale(const glm::vec2 &scale) +{ + if (transform_.scale == scale) + return; + + transform_.scale = scale; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetScale(float scalex, float scaley) +{ + this->SetScale(glm::vec2(scalex, scaley)); +} + +inline void Node::SetSkew(const glm::vec2 &skew) +{ + if (transform_.skew == skew) + return; + + transform_.skew = skew; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetSkew(float skewx, float skewy) +{ + this->SetSkew(glm::vec2(skewx, skewy)); +} + +inline void Node::SetRotation(float rotation) +{ + if (transform_.rotation == rotation) + return; + + transform_.rotation = rotation; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetAnchor(const glm::vec2 &anchor) +{ + if (anchor_ == anchor) + return; + + anchor_ = anchor; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetAnchor(float anchorx, float anchory) +{ + this->SetAnchor(glm::vec2(anchorx, anchory)); +} + +inline void Node::SetSize(const VecSize &size) +{ + if (size_ == size) + return; + + size_ = size; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetSize(float width, float height) +{ + this->SetSize(VecSize{width, height}); +} + +inline void Node::SetWidth(float width) +{ + this->SetSize(width, GetHeight()); +} + +inline void Node::SetHeight(float height) +{ + this->SetSize(GetWidth(), height); +} + +inline void Node::SetOpacity(float opacity) +{ + if (opacity_ == opacity) + return; + + opacity_ = std::min(std::max(opacity, 0.f), 1.f); + dirty_flag_.Set(DirtyFlag::DirtyOpacity); +} + +inline void Node::SetCascadeOpacityEnabled(bool enabled) +{ + if (cascade_opacity_ == enabled) + return; + + cascade_opacity_ = enabled; + dirty_flag_.Set(DirtyFlag::DirtyOpacity); +} + +inline void Node::SetTransform(const Y_Transform &transform) +{ + transform_ = transform; + dirty_flag_.Set(DirtyFlag::DirtyTransform); +} + +inline void Node::SetZOrder(int zorder) +{ + if (z_order_ != zorder) + { + z_order_ = zorder; + Reorder(); + } +} \ No newline at end of file diff --git a/source/EngineFrame/Component/Animation.cpp b/source/EngineFrame/Component/Animation.cpp index 4ccb070..2a19ede 100644 --- a/source/EngineFrame/Component/Animation.cpp +++ b/source/EngineFrame/Component/Animation.cpp @@ -27,8 +27,6 @@ Animation::~Animation() void Animation::Init(std::string AniPath) { - Actor::Init(); - AniScriptParser::AniInfo Info = AssetManager::GetInstance().StructAniInfo(AniPath); this->AniPath = AniPath; this->AnimationFlag = Info.Flag; @@ -45,17 +43,19 @@ void Animation::Init(std::string AniPath) FrameObj.Img_Path = AdditionalOptions(FrameObj.Img_Path, this->AdditionalOptionsData); } SpriteObj = new Sprite(FrameObj.Img_Path, FrameObj.Img_Index); - SpriteObj->SetAnchor(Vec2(0.5f, 0.5f)); - SpriteObj->SetPos(Vec2(FrameObj.Img_Pos.x, FrameObj.Img_Pos.y)); + SpriteObj->SetPosition(FrameObj.Img_Pos.x, FrameObj.Img_Pos.y); SpriteObj->SetVisible(false); } else { SpriteObj = new Sprite(); SpriteObj->SetVisible(false); - SDL_Log("Animation::Init() SpriteObj is nullptr"); } SpriteArr.push_back(SpriteObj); + //记录最大帧宽高 + auto SpriteSize = SpriteObj->GetSize(); + if(this->MaxSize.width < SpriteSize.width)this->MaxSize.width = SpriteSize.width; + if(this->MaxSize.height < SpriteSize.height)this->MaxSize.height = SpriteSize.height; AddChild(SpriteObj); } @@ -76,7 +76,7 @@ void Animation::Init(std::string AniPath) for (auto &Ani : Info.AniList) { RefPtr AlsAniObj = new Animation(Dir + Ani.second.path); - AlsAniObj->SetRenderZOrder(Ani.second.layer[1]); + AlsAniObj->SetZOrder(Ani.second.layer[1]); AddChild(AlsAniObj); } } @@ -84,20 +84,16 @@ void Animation::Init(std::string AniPath) FlushFrame(0); } -void Animation::HandleEvents(SDL_Event *e) -{ -} - -void Animation::Update(float deltaTime) +void Animation::OnUpdate(float deltaTime) { // 可用性检查 - if (IsUsability && Visible) + if (IsUsability && IsVisible()) { float dt_ms = deltaTime * 1000.0f; // 累加当前帧时间 CurrentIndexT += dt_ms; // 插值模式判断 - InterpolationLogic(); + // InterpolationLogic(); // 循环处理:只要当前时间超过帧延迟,就切换帧(支持一次跳过多帧) while (CurrentIndexT >= NextFrameDelay) @@ -127,32 +123,26 @@ void Animation::Update(float deltaTime) } } } - Actor::Update(deltaTime); + } + else + { + for (auto &Sp : SpriteArr) + { + Sp->SetVisible(false); + } } } -void Animation::Render() +void Animation::OnAdded(Actor *node) { - Actor::Render(); -} - -void Animation::OnAdded(BaseNode *node) -{ - Actor::OnAdded(node); FlushFrame(0); } -void Animation::Clear() -{ -} - void Animation::SetVisible(bool visible) { - // 设置启用的时候要更新一下当前帧的渲染信息 避免因为延迟造成的坐标闪烁 if (visible) { - CurrentFrame->CalcRenderInfoLogic(); - CurrentFrame->CalcRenderInfo(); + CurrentFrame->SetVisible(true); } Actor::SetVisible(visible); } @@ -192,7 +182,8 @@ void Animation::FlushFrame(int Index) if (FlagBuf.count("IMAGE_RATE")) { Vec2 Rate = std::get(FlagBuf["IMAGE_RATE"]); - CurrentFrame->SetScale(Vec2{Rate.x, Rate.y}); + CurrentFrame->SetScale(Rate.x, Rate.y); + CurrentFrame->SetPosition(FrameInfo.Img_Pos.x * Rate.x, FrameInfo.Img_Pos.y * Rate.y); } // 线性减淡 if (FlagBuf.count("GRAPHIC_EFFECT_LINEARDODGE")) @@ -202,7 +193,7 @@ void Animation::FlushFrame(int Index) // 旋转 if (FlagBuf.count("IMAGE_ROTATE")) { - CurrentFrame->SetAnchor(Vec2{0.5f, 0.5f}); + // CurrentFrame->SetAnchor(0.5f, 0.5f); CurrentFrame->SetRotation(std::get(FlagBuf["IMAGE_ROTATE"])); } // 染色 @@ -277,10 +268,10 @@ void Animation::InterpolationLogic() } // 坐标 { - Vec2 PosData = { + glm::vec2 PosData = { (OldData.Img_Pos.x + (NewData.Img_Pos.x - OldData.Img_Pos.x) * InterRate), (OldData.Img_Pos.y + (NewData.Img_Pos.y - OldData.Img_Pos.y) * InterRate)}; - CurrentFrame->SetPos(PosData); + CurrentFrame->SetPosition(PosData); } // 缩放 { @@ -294,10 +285,9 @@ void Animation::InterpolationLogic() { NewRateData = std::get(NewData.Flag["IMAGE_RATE"]); } - Vec2 RateData = { + glm::vec2 RateData = { OldRateData.x + (NewRateData.x - OldRateData.x) * InterRate, OldRateData.y + (NewRateData.y - OldRateData.y) * InterRate}; - CurrentFrame->SetAnchor(Vec2{0.5f, 0.5f}); CurrentFrame->SetScale(RateData); } // 旋转 @@ -315,3 +305,8 @@ void Animation::InterpolationLogic() CurrentFrame->SetRotation(OldAngleData + (NewAngleData - OldAngleData) * InterRate); } } + +VecSize Animation::GetMaxSize() +{ + return MaxSize; +} diff --git a/source/EngineFrame/Component/Animation.h b/source/EngineFrame/Component/Animation.h index 2f2abc3..124e936 100644 --- a/source/EngineFrame/Component/Animation.h +++ b/source/EngineFrame/Component/Animation.h @@ -24,24 +24,26 @@ public: Animation(std::string AniPath, std::function AdditionalOptions, Animation::ReplaceData); ~Animation(); + /**初始化 */ void Init(std::string AniPath); - void HandleEvents(SDL_Event *e) override; - void Update(float deltaTime) override; - void Render() override; - void OnAdded(BaseNode *node) override; - void Clear() override; + + void OnUpdate(float deltaTime) override; + void OnAdded(Actor *node) override; void SetVisible(bool visible) override; public: + /**刷新帧 */ void FlushFrame(int Index); + /**重置Ani */ void Reset(); + /**获取当前帧信息 */ AniScriptParser::AniFrame GetCurrentFrameInfo(); // 设置帧索引 void SetFrameIndex(int Index); void InterpolationLogic(); - // TODO SetOutline - // TODO SetDye - // TODO SetCrop + + /**获取最大尺寸 */ + VecSize GetMaxSize(); public: // Ani是否可用 @@ -76,20 +78,11 @@ public: std::string AniPath; // 是否描边 bool IsOutline = false; - // // 描边颜色 - // OutlineColor = null; - // // 描边对象List - // OutlineList = null; - // // 当前描边对象 - // CurrentOutline = null; - // // 染色颜色 - // DyeColor = null; - // // 染色帧List - // DyeFrameList = null; - // // 整体染色 - // DyeAllFlag = false; - // 附加选项 std::function AdditionalOptions; Animation::ReplaceData AdditionalOptionsData; + /**最大尺寸 + * 所有子精灵最大的尺寸 + */ + VecSize MaxSize = {0, 0}; }; diff --git a/source/EngineFrame/Component/AnimationManager.cpp b/source/EngineFrame/Component/AnimationManager.cpp deleted file mode 100644 index 5089769..0000000 --- a/source/EngineFrame/Component/AnimationManager.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "AnimationManager.h" -#include "EngineCore/Game.h" - -AnimationManager::AnimationManager() -{ -} - -void AnimationManager::PreRender() -{ - Actor::PreRender(); - // 标记是否是第一个子对象(用于初始化min/max值) - bool isFirst = true; - float minX = 0.0f, minY = 0.0f; // 所有子对象的最小X、Y坐标 - float maxX = 0.0f, maxY = 0.0f; // 所有子对象的最大X、Y坐标(右下角) - - RefPtr child = m_BaseNodes.GetFirst(); - while (child) - { - // 转换为Animation对象 - Animation *ani = static_cast(child.Get()); - - float width = ani->CurrentFrame->_RenderGuidanceInfo.rect.w; - float height = ani->CurrentFrame->_RenderGuidanceInfo.rect.h; - float x = ani->CurrentFrame->_RenderGuidanceInfo.rect.x; - float y = ani->CurrentFrame->_RenderGuidanceInfo.rect.y; - - // 计算子对象的右下角坐标(左上角 + 宽高) - float currentMaxX = x + width; - float currentMaxY = y + height; - // 初始化或更新min/max值 - if (isFirst) - { - minX = x; - minY = y; - maxX = currentMaxX; - maxY = currentMaxY; - isFirst = false; - } - else - { - minX = std::min(minX, x); - minY = std::min(minY, y); - maxX = std::max(maxX, currentMaxX); - maxY = std::max(maxY, currentMaxY); - } - - child = child->GetNext(); - } - // 计算最终的包围盒矩形 - if (!isFirst) // 至少有一个子对象时才计算 - { - m_RenderRect.x = minX; // 矩形左上角X - m_RenderRect.y = minY; // 矩形左上角Y - m_RenderRect.w = maxX - minX; // 矩形宽度(右下角X - 左上角X) - m_RenderRect.h = maxY - minY; // 矩形高度(右下角Y - 左上角Y) - } -} - -void AnimationManager::Render() -{ - Actor::Render(); -} - -void AnimationManager::AddAnimation(RefPtr ani) -{ - this->AddChild(ani); -} - -void AnimationManager::Clear() -{ - this->RemoveAllChild(); -} diff --git a/source/EngineFrame/Component/AnimationManager.h b/source/EngineFrame/Component/AnimationManager.h deleted file mode 100644 index b31784d..0000000 --- a/source/EngineFrame/Component/AnimationManager.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include "EngineFrame/Component/Animation.h" -#include -class AnimationManager : public Actor -{ -private: - SDL_Texture *m_Texture = nullptr; - SDL_FRect m_RenderRect = {0, 0, 0, 0}; - -public: - AnimationManager(); - // void Update(float deltaTime) override; - void PreRender() override; - void Render() override; - -public: - // 添加Ani - void AddAnimation(RefPtr ani); - // 清空Ani - void Clear(); -}; diff --git a/source/EngineFrame/Component/AnimationMap.cpp b/source/EngineFrame/Component/AnimationMap.cpp new file mode 100644 index 0000000..754d8d8 --- /dev/null +++ b/source/EngineFrame/Component/AnimationMap.cpp @@ -0,0 +1,132 @@ +#include "AnimationMap.h" +#include "EngineCore/Game.h" +#include "EngineFrame/Render/RenderManager.h" +AnimationMap::~AnimationMap() +{ +} + +void AnimationMap::CompleteConstruction() +{ + m_size = {800, 600}; + // 遍历自己的所持有的所有Ani 取最大的尺寸 + auto &ac = this->GetAllChildren(); + RefPtr child = ac.GetFirst(); + while (child) + { + Animation *anim = static_cast(child.Get()); + auto size = anim->GetMaxSize(); + if (size.width > m_size.width) + m_size.width = size.width; + if (size.height > m_size.height) + m_size.height = size.height; + child = child->GetNext(); + } + + SetSize(m_size); + // 创建指定大小的纹理 + m_texture = new Texture(); + m_texture->Init(m_size); + + // 构造FBO + glGenFramebuffers(1, &m_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + // 将目标纹理附加到FBO的颜色附着点 + glFramebufferTexture2D( + GL_FRAMEBUFFER, // 帧缓冲类型 + GL_COLOR_ATTACHMENT0, // 颜色附着点(可多个,这里用第一个) + GL_TEXTURE_2D, // 纹理类型 + m_texture->getID(), // 目标纹理ID + 0 // 多级渐远纹理级别 + ); + glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO +} + +void AnimationMap::AddAnimation(RefPtr animation) +{ + this->AddChild(animation); +} + +void AnimationMap::Reset() +{ + auto &ac = this->GetAllChildren(); + RefPtr child = ac.GetFirst(); + while (child) + { + Animation *anim = static_cast(child.Get()); + anim->Reset(); + child = child->GetNext(); + } +} + +void AnimationMap::Render() +{ + Actor::PreRender(); + Actor::PrepareToRender(); + Actor::OnRender(); +} + +void AnimationMap::OnRender() +{ + RenderManager *renderer = Game::GetInstance().GetRenderer(); + renderer->DrawTexture(m_texture); +} + +void AnimationMap::OnUpdate(float deltaTime) +{ + Actor::OnUpdate(deltaTime); + + RenderManager *renderer = Game::GetInstance().GetRenderer(); + // 绑定FBO + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + // 清屏 + glClear(GL_COLOR_BUFFER_BIT); + // 保存原始的正交矩阵 设置纹理对应的正交矩阵 和 视口 + Oom = renderer->GetOrthoMatrix(); + renderer->SetOrthoMatrix(glm::ortho(0.0f, (float)m_size.width, 0.0f, (float)m_size.height, -1.0f, 1.0f)); + Oviewport = renderer->GetViewport(); + renderer->SetViewport({0, 0, m_size.width, m_size.height}); + // 以上步骤将渲染目标调整至AniMap的纹理上 + + SDL_Rect buf; + buf.x = 0; + buf.y = 0; + buf.w = m_size.width; + buf.h = m_size.height; + glm::vec4 red = {0.0f, 0.0f, 1.0f, 0.4f}; // RGBA:红色,不透明基础色 + Game::GetInstance().GetRenderer()->DrawRect(&buf, red); + + auto &ac = this->GetAllChildren(); + RefPtr child = ac.GetFirst(); + while (child) + { + child->Render(); + child = child->GetNext(); + } + + // 渲染完毕后,将渲染目标恢复为默认 + // 解绑FBO + glBindFramebuffer(GL_FRAMEBUFFER, 0); + // 恢复原始的正交矩阵 和 视口 + renderer->SetOrthoMatrix(Oom); + renderer->SetViewport(Oviewport); + + Actor::OnUpdate(deltaTime); +} + +// void AnimationMap::SetVisible(bool visible) +// { +// if (this->IsVisible() == visible) +// return; +// Actor::SetVisible(visible); +// auto ac = this->GetAllChildren(); +// RefPtr next; +// for (RefPtr child = ac.GetFirst(); child; child = next) +// { +// next = child->GetNext(); +// child->SetVisible(visible); +// } +// } + +AnimationMap::AnimationMap() +{ +} diff --git a/source/EngineFrame/Component/AnimationMap.h b/source/EngineFrame/Component/AnimationMap.h new file mode 100644 index 0000000..a79b872 --- /dev/null +++ b/source/EngineFrame/Component/AnimationMap.h @@ -0,0 +1,36 @@ +#pragma once +#include "EngineFrame/Base/Actor.h" +#include "Animation.h" +class AnimationMap : public Actor +{ +private: + /**纹理 */ + RefPtr m_texture = nullptr; + /**FBO */ + GLuint m_fbo = 0; + /**大小 */ + VecSize m_size; + /**原始的正交矩阵 */ + glm::mat4 Oom; + /**原始的视口 */ + SDL_Rect Oviewport; + +public: + AnimationMap(/* args */); + ~AnimationMap(); + + /**完成构造 + * 必须调用完成构造以创建足够大小的画布和各项初始化 + */ + void CompleteConstruction(); + /**添加动画 */ + void AddAnimation(RefPtr animation); + + /**重置动画组 */ + void Reset(); + + void Render() override; + void OnRender() override; + void OnUpdate(float deltaTime) override; + // void SetVisible(bool visible) override; +}; diff --git a/source/EngineFrame/Component/Canvas.cpp b/source/EngineFrame/Component/Canvas.cpp index ee349cd..dd15f11 100644 --- a/source/EngineFrame/Component/Canvas.cpp +++ b/source/EngineFrame/Component/Canvas.cpp @@ -1,16 +1,29 @@ #include "Canvas.h" #include "EngineCore/Game.h" #include "EngineFrame/Render/RenderManager.h" +Canvas::Canvas() +{ + +} Canvas::Canvas(VecSize size) +{ + Init(size); +} + +Canvas::Canvas(int width, int height) +{ + Init({width, height}); +} + +void Canvas::Init(VecSize size) { m_size = size; - m_renderRect = {0, 0, size.width, size.height}; - m_rect = {0, 0, (float)size.width, (float)size.height}; + SetSize(size); + // 创建指定大小的纹理 m_texture = new Texture(); - m_texture->Init(VecSize(Game::GetInstance().Screen_W, Game::GetInstance().Screen_H)); - - Actor::Init(); + m_texture->Init(m_size); + // 构造FBO glGenFramebuffers(1, &m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // 将目标纹理附加到FBO的颜色附着点 @@ -24,92 +37,86 @@ Canvas::Canvas(VecSize size) glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO } -void Canvas::PreRender() -{ - m_rect.x = GetWorldPos().x; - m_rect.y = GetWorldPos().y; -} - -void Canvas::Render() +void Canvas::BeginDraw() { RenderManager *renderer = Game::GetInstance().GetRenderer(); - SDL_FPoint AnchorPos = {0, 0}; - if (m_dirty) - { - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // 绑定FBO - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - for (auto &info : m_drawQueue) - { - renderer->DrawTexture(m_imgMap[info.texture], nullptr, &info.rect, 0.f, &AnchorPos, SDL_FLIP_NONE); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO - m_dirty = false; - } + // 绑定FBO + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + // 清屏 + glClear(GL_COLOR_BUFFER_BIT); + // 保存原始的正交矩阵 设置纹理对应的正交矩阵 和 视口 + Oom = renderer->GetOrthoMatrix(); + renderer->SetOrthoMatrix(glm::ortho(0.0f, (float)m_size.width, 0.0f, (float)m_size.height, -1.0f, 1.0f)); + Oviewport = renderer->GetViewport(); + renderer->SetViewport({0, 0, m_size.width, m_size.height}); - renderer->SetCurrentShaderProgram("flip_y"); - // 混合 - if (this->_BlendMode != NONE) - Blend(); - renderer->DrawTexture(m_texture, &m_renderRect, &m_rect, 0.f, &AnchorPos, SDL_FLIP_NONE); - // 还原混合 - if (this->_BlendMode != NONE) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - renderer->SetCurrentShaderProgram("normal"); } -void Canvas::DrawImg(std::string img, int index, Vec2 pos) +void Canvas::EndDraw() { - m_dirty = true; - ImgKey key = {img, index}; - DrawInfo info; - info.rect.x = pos.x; - info.rect.y = pos.y; - info.texture = key; - - if (m_imgMap.find(key) == m_imgMap.end()) - { - RefPtr tex = new Texture(); - tex->Init(img, index); - m_imgMap[key] = tex; - } - - info.rect.w = m_imgMap[key]->getSize().width; - info.rect.h = m_imgMap[key]->getSize().height; - - m_drawQueue.push_back(info); + RenderManager *renderer = Game::GetInstance().GetRenderer(); + // 解绑FBO + glBindFramebuffer(GL_FRAMEBUFFER, 0); + // 恢复原始的正交矩阵 和 视口 + renderer->SetOrthoMatrix(Oom); + renderer->SetViewport(Oviewport); } -void Canvas::DrawImg(std::string img, int index, SDL_FRect rect) +void Canvas::DrawActor(RefPtr actor) { - m_dirty = true; - ImgKey key = {img, index}; - DrawInfo info; - info.rect = rect; - info.texture = key; - - if (m_imgMap.find(key) == m_imgMap.end()) - { - RefPtr tex = new Texture(); - tex->Init(img, index); - m_imgMap[key] = tex; - } - - m_drawQueue.push_back(info); + // 计算矩阵 + actor->PreRender(); + // 设定矩阵并渲染(Render中会调用OnRender) + actor->Render(); } void Canvas::Clear() { - m_drawQueue.clear(); - m_dirty = true; + m_texture = new Texture(); + m_texture->Init(m_size); } -void Canvas::Blend() +void Canvas::OnRender() { - switch (this->_BlendMode) + if (!m_texture || !IsVisible()) + return; + + RenderManager *renderer = Game::GetInstance().GetRenderer(); + if (this->_BlendMode != NONE) { - case LINEARDODGE: - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - break; + renderer->SetBlendMode(this->_BlendMode); + } + renderer->DrawTexture(m_texture); + if (this->_BlendMode != NONE) + { + renderer->SetBlendMode(NONE); } } + +// void Canvas::Render() +// { +// RenderManager *renderer = Game::GetInstance().GetRenderer(); +// SDL_FPoint AnchorPos = {0, 0}; +// if (m_dirty) +// { +// glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // 绑定FBO +// glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +// glClear(GL_COLOR_BUFFER_BIT); +// for (auto &info : m_drawQueue) +// { +// renderer->DrawTexture(m_imgMap[info.texture], nullptr, &info.rect, 0.f, &AnchorPos, SDL_FLIP_NONE); +// } +// glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO +// m_dirty = false; +// } + +// renderer->SetCurrentShaderProgram("flip_y"); +// // 混合 +// if (this->_BlendMode != NONE) +// Blend(); +// renderer->DrawTexture(m_texture, &m_renderRect, &m_rect, 0.f, &AnchorPos, SDL_FLIP_NONE); +// // 还原混合 +// if (this->_BlendMode != NONE) +// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +// renderer->SetCurrentShaderProgram("normal"); +// } diff --git a/source/EngineFrame/Component/Canvas.h b/source/EngineFrame/Component/Canvas.h index 2f63318..d291d53 100644 --- a/source/EngineFrame/Component/Canvas.h +++ b/source/EngineFrame/Component/Canvas.h @@ -18,37 +18,37 @@ class Canvas : public Actor return index < other.index; // 字符串相同则比较index } }; - struct DrawInfo - { - ImgKey texture; - SDL_FRect rect = {0, 0, 0, 0}; - }; private: /**纹理 */ RefPtr m_texture = nullptr; /**大小 */ VecSize m_size; - /**渲染大小 */ - SDL_Rect m_renderRect; - SDL_FRect m_rect; - /**脏标记 */ - bool m_dirty = true; /**FBO */ GLuint m_fbo = 0; - /**绘制纹理集 */ - std::map> m_imgMap; - /**绘制队列 */ - std::vector m_drawQueue; + + /**原始的正交矩阵 */ + glm::mat4 Oom; + /**原始的视口 */ + SDL_Rect Oviewport; public: + Canvas(); Canvas(VecSize size); + Canvas(int width, int height); - void PreRender() override; - void Render() override; - void Blend(); + /**初始化 */ + void Init(VecSize size); - void DrawImg(std::string img, int index, Vec2 pos); - void DrawImg(std::string img, int index, SDL_FRect rect); + /**开始绘制 */ + void BeginDraw(); + /**结束绘制 */ + void EndDraw(); + /**绘制角色 */ + void DrawActor(RefPtr actor); + /**清空画布 */ void Clear(); + + /**重载OnRender函数 */ + void OnRender() override; }; diff --git a/source/EngineFrame/Component/NumberText.h b/source/EngineFrame/Component/NumberText.h index 9d358dd..3bbe4c1 100644 --- a/source/EngineFrame/Component/NumberText.h +++ b/source/EngineFrame/Component/NumberText.h @@ -20,7 +20,7 @@ private: // 数字字符纹理缓存('0'-'9'和'-') std::unordered_map> m_digitTextures; // 当前数字的字符精灵列表 - std::vector m_digitSprites; + std::vector m_digitSprites; // 预加载0-9和负号的纹理 void PreloadDigits(); diff --git a/source/EngineFrame/Component/RenderBase.cpp b/source/EngineFrame/Component/RenderBase.cpp deleted file mode 100644 index 886beb5..0000000 --- a/source/EngineFrame/Component/RenderBase.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "RenderBase.h" -#include "EngineFrame/Base/Actor.h" - - -void RenderBase::CalcRenderInfo() -{ - this->CalcRenderInfoFlag = true; -} - -void RenderBase::Init() -{ - // 计算渲染信息 - CalcRenderInfo(); -} - - - - - -void RenderBase::SetIterationPos(Vec2 pos) -{ - Actor::SetIterationPos(pos); - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetIterationScale(Vec2 scale) -{ - Actor::SetIterationScale(scale); - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetIterationRotation(float angle) -{ - if (!Visible) - return; - Actor::SetIterationRotation(angle); - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetPos(Vec2 pos) -{ - Actor::SetPos(pos); - - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetScale(Vec2 scale) -{ - Actor::SetScale(scale); - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetRotation(float angle) -{ - Actor::SetRotation(angle); - CalcRenderInfo(); // 更新渲染信息 -} - -void RenderBase::SetAnchor(Vec2 anchor) -{ - Actor::SetAnchor(anchor); - CalcRenderInfo(); // 更新渲染信息 -} diff --git a/source/EngineFrame/Component/RenderBase.h b/source/EngineFrame/Component/RenderBase.h deleted file mode 100644 index 5279432..0000000 --- a/source/EngineFrame/Component/RenderBase.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once -#include "EngineFrame/Base/Actor.h" - -class RenderBase : public Actor -{ - -public: - struct RenderGuidanceInfo - { - SDL_FRect rect; - // 旋转角度 - float rotation; - // 翻转Flag - SDL_RendererFlip flip = SDL_FLIP_NONE; - // 锚点坐标 - Vec2 AnchorPos; - // 是否显示 - bool Visible = true; - // 是否在屏幕内 - bool IsInScreen = false; - }; - -public: - void Init() override; - -public: - // 设置迭代的坐标 - void SetIterationPos(Vec2 pos) override; - // 设置迭代的缩放 - void SetIterationScale(Vec2 scale) override; - // 设置迭代的旋转角度 - void SetIterationRotation(float angle) override; - - // 设置坐标 - void SetPos(Vec2 pos) override; - // 设置缩放 - void SetScale(Vec2 scale) override; - // 设置旋转角度 - void SetRotation(float angle) override; - // 设置中心点 - void SetAnchor(Vec2 anchor) override; - - // 计算渲染信息 - void CalcRenderInfo() override; -}; diff --git a/source/EngineFrame/Component/Sprite.cpp b/source/EngineFrame/Component/Sprite.cpp index 7ab2917..78da2cb 100644 --- a/source/EngineFrame/Component/Sprite.cpp +++ b/source/EngineFrame/Component/Sprite.cpp @@ -3,17 +3,31 @@ #include "Text.h" #include "EngineFrame/Render/RenderManager.h" +void Sprite::GenerateRenderMatrix() const +{ + Matrix3x2 RenderBuffer = Matrix3x2::Translation(glm::vec2(m_texture->getPos().x, m_texture->getPos().y)) * GetTransformMatrix(); + + // 构造OpenGl渲染矩阵 + render_matrix_ = glm::mat4(1.0f); + // 填充旋转+缩放分量(2D 变换核心,对应 4x4 矩阵左上 2x2 区域) + render_matrix_[0][0] = RenderBuffer[0]; // _11 → x轴缩放+旋转 + render_matrix_[0][1] = RenderBuffer[1]; // _12 → 影响y轴方向的旋转分量 + render_matrix_[1][0] = RenderBuffer[2]; // _21 → 影响x轴方向的旋转分量 + render_matrix_[1][1] = RenderBuffer[3]; // _22 → y轴缩放+旋转 + // 填充平移分量(对应 4x4 矩阵最后一行前两列,z轴平移为0) + render_matrix_[3][0] = RenderBuffer[4]; // _31 → x方向平移(世界坐标) + render_matrix_[3][1] = RenderBuffer[5]; // _32 → y方向平移(世界坐标) +} + void Sprite::Init() { - this->Size = m_texture->getSize(); - RenderBase::Init(); + SetSize(m_texture->getSize()); } void Sprite::SetTexture(RefPtr texture) { m_texture = texture; Init(); - CalcRenderInfoLogic(); // 第一次计算 } Sprite::Sprite(std::string imgPath, int Index) @@ -23,7 +37,6 @@ Sprite::Sprite(std::string imgPath, int Index) m_texture = new Texture(); m_texture->Init(imgPath, Index); Init(); - CalcRenderInfoLogic(); // 第一次计算 } Sprite::Sprite(std::string PngPath) @@ -31,7 +44,6 @@ Sprite::Sprite(std::string PngPath) m_texture = new Texture(); m_texture->Init(PngPath); Init(); - CalcRenderInfoLogic(); // 第一次计算 } Sprite::~Sprite() @@ -43,140 +55,19 @@ RefPtr Sprite::GetTexture() return m_texture; } -void Sprite::CalcRenderInfoLogic() +void Sprite::OnRender() { - // 获取至在最终的父对象检查是否显示 - BaseNode *parentNode = this->GetParent(); - while (parentNode != nullptr) - { - if (parentNode->Visible == false) - { - _RenderGuidanceInfo.Visible = false; - return; - } - parentNode = parentNode->GetParent(); - } - _RenderGuidanceInfo.Visible = true; - - // 计算缩放因子和翻转状态 - float scaleX = transformIter.scale.x * transform.scale.x; - float scaleY = transformIter.scale.y * transform.scale.y; - - // X轴和Y轴上是否翻转的标志 - bool flipX = scaleX < 0; - bool flipY = scaleY < 0; - - _RenderGuidanceInfo.flip = SDL_FLIP_NONE; - // 更新翻转状态 - if (flipX) - _RenderGuidanceInfo.flip = static_cast(SDL_FLIP_HORIZONTAL | _RenderGuidanceInfo.flip); - if (flipY) - _RenderGuidanceInfo.flip = static_cast(SDL_FLIP_VERTICAL | _RenderGuidanceInfo.flip); - - // 纹理数据里带的偏移数据 有这个偏移才能保证动画播放时视觉中心点在一个点上 - auto T_Size = m_texture->getSize(); - auto T_Pos = m_texture->getPos(); - float texturePosX = flipX ? -((float)T_Size.width + (float)T_Pos.x) + SDL_fabsf(transform.position.x * 2.0f) : (float)T_Pos.x; - float texturePosY = flipY ? -((float)T_Size.height + (float)T_Pos.y) + SDL_fabsf(transform.position.y * 2.0f) : (float)T_Pos.y; - - // 先计算Img坐标与精灵坐标合成后的真实坐标 - float RealPosX = transform.position.x + texturePosX; - float RealPosY = transform.position.y + texturePosY; - - // 计算在世界中的位置 - float baseX = transformIter.position.x + RealPosX; - float baseY = transformIter.position.y + RealPosY; - - // 获取当前帧的原始尺寸 - float frameWidth = (float)Size.width; - float frameHeight = (float)Size.height; - - // 原始锚点偏移(基于帧尺寸) - float origAnchorOffsetX = frameWidth * Anchor.x; - float origAnchorOffsetY = frameHeight * Anchor.y; - - // 缩放的绝对值 - float absScaleX = SDL_fabsf(scaleX); - float absScaleY = SDL_fabsf(scaleY); - // 缩放后的尺寸 - float scaledWidth = frameWidth * absScaleX; - float scaledHeight = frameHeight * absScaleY; - // 缩放后的锚点偏移 - float scaledAnchorOffsetX = origAnchorOffsetX * absScaleX; - float scaledAnchorOffsetY = origAnchorOffsetY * absScaleY; - - // 计算缩放后的锚点偏移与原锚点偏移的差值 - float scaleOffsetX = scaledAnchorOffsetX - origAnchorOffsetX; - float scaleOffsetY = scaledAnchorOffsetY - origAnchorOffsetY; - - // 最终位置计算:世界位置 + 缩放锚点差值 - 缩放后的锚点偏移 + 纹理数据里带的偏移数据 (计算出绘制点的左上角) - float Xpos = baseX - scaleOffsetX; - float Ypos = baseY - scaleOffsetY; - - // 更新渲染信息 - _RenderGuidanceInfo.rect = {Xpos, Ypos, scaledWidth, scaledHeight}; - _RenderGuidanceInfo.AnchorPos = {scaledAnchorOffsetX, scaledAnchorOffsetY}; - // 设置纹理透明度 - m_texture->setAlpha(this->Alpha); - - // 屏幕内检测 - float screenWidth = (float)Game::GetInstance().Screen_W; - float screenHeight = (float)Game::GetInstance().Screen_H; - bool isInScreen = (Xpos + scaledWidth >= 0 && Xpos <= screenWidth && Ypos + scaledHeight >= 0 && Ypos <= screenHeight); - _RenderGuidanceInfo.IsInScreen = isInScreen; - - // this->Size = {scaledWidth, scaledHeight}; - _RenderGuidanceInfo.rotation = transformIter.rotation + transform.rotation; - - CalcRenderInfoFlag = false; -} - -void Sprite::PreRender() -{ - if (CalcRenderInfoFlag && Visible) - CalcRenderInfoLogic(); -} - -void Sprite::Render() -{ - if (!Visible) - return; - if (!m_texture) + if (!m_texture || !IsVisible()) return; RenderManager *renderer = Game::GetInstance().GetRenderer(); - - if (_RenderGuidanceInfo.IsInScreen && _RenderGuidanceInfo.Visible) + if (this->_BlendMode != NONE) { - SDL_FPoint AnchorPos; - AnchorPos.x = _RenderGuidanceInfo.AnchorPos.x; - AnchorPos.y = _RenderGuidanceInfo.AnchorPos.y; - - // 混合 - if (this->_BlendMode != NONE) - Blend(); - - SDL_Rect srcrect = {0, 0, this->Size.width, this->Size.height}; - renderer->DrawTexture(m_texture, &srcrect, &_RenderGuidanceInfo.rect, _RenderGuidanceInfo.rotation, &AnchorPos, _RenderGuidanceInfo.flip); - - // 还原混合 - if (this->_BlendMode != NONE) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } -} - -void Sprite::Clear() -{ -} - - - -void Sprite::Blend() -{ - switch (this->_BlendMode) - { - case LINEARDODGE: - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); - break; + renderer->SetBlendMode(this->_BlendMode); + } + renderer->DrawTexture(m_texture); + if (this->_BlendMode != NONE) + { + renderer->SetBlendMode(NONE); } } diff --git a/source/EngineFrame/Component/Sprite.h b/source/EngineFrame/Component/Sprite.h index 1d3ba67..dd3d337 100644 --- a/source/EngineFrame/Component/Sprite.h +++ b/source/EngineFrame/Component/Sprite.h @@ -1,14 +1,14 @@ #pragma once #include #include "Asset/Asset_ImagePack.h" -#include "EngineFrame/Component/RenderBase.h" #include "EngineFrame/Render/Texture.h" +#include "EngineFrame/Base/Actor.h" class Game; /** * @brief Sprite类,继承自Component类,用于表示游戏中的精灵组件 */ -class Sprite : public RenderBase +class Sprite : public Actor { protected: RefPtr m_texture = nullptr; @@ -18,24 +18,18 @@ public: Sprite(std::string imgPath, int Index); Sprite(std::string PngPath); ~Sprite(); - void Render() override; - void PreRender() override; - void Clear() override; - void Init() override; - void SetTexture(RefPtr texture); + void SetTexture(RefPtr texture); RefPtr GetTexture(); -public: - // 渲染信息 - RenderGuidanceInfo _RenderGuidanceInfo; + /**重载生成渲染矩阵函数 */ + void GenerateRenderMatrix() const override; + /**重载初始化函数 */ + void Init() override; + /**重载渲染函数 */ + void OnRender() override; +public: std::string imgPath; int Index; - -public: - // 计算渲染信息 - void CalcRenderInfoLogic(); - // 混合 - void Blend(); }; diff --git a/source/EngineFrame/Render/RenderManager.cpp b/source/EngineFrame/Render/RenderManager.cpp index ad873ab..77babc2 100644 --- a/source/EngineFrame/Render/RenderManager.cpp +++ b/source/EngineFrame/Render/RenderManager.cpp @@ -22,7 +22,7 @@ RenderManager::RenderManager(SDL_Window *window) int width, height; SDL_GetWindowSize(window, &width, &height); // 设置视口 - glViewport(0, 0, width, height); + SetViewport(SDL_Rect{0, 0, width, height}); // 设置窗口尺寸 _windowWidth = width; _windowHeight = height; @@ -33,7 +33,7 @@ RenderManager::RenderManager(SDL_Window *window) _UIOrthoMatrix = glm::ortho(0.0f, (float)width, (float)height, 0.0f, -1.0f, 1.0f); // 设置清屏颜色 - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 启用混合模式 glEnable(GL_BLEND); @@ -44,6 +44,8 @@ RenderManager::RenderManager(SDL_Window *window) InitShaderProgram(); // 初始化绘制2D纹理缓冲程序 Init2DTextureProgram(); + // 初始化绘制矩形缓冲程序 + InitRectProgram(); // 设定默认使用的着色器程序和缓冲程序 SetCurrentShaderProgram("normal"); @@ -79,17 +81,17 @@ void RenderManager::InitShaderProgram() { if (!groupData.is_object()) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "着色器组 %s 格式错误,跳过\n", groupName.c_str()); + SDL_LogError(0, "着色器组 %s 格式错误,跳过\n", groupName.c_str()); continue; } if (!groupData.contains("vertex") || !groupData["vertex"].is_string()) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 vertex 路径,跳过\n", groupName.c_str()); + SDL_LogError(0, "组 %s 缺少 vertex 路径,跳过\n", groupName.c_str()); continue; } if (!groupData.contains("fragment") || !groupData["fragment"].is_string()) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 缺少 fragment 路径,跳过\n", groupName.c_str()); + SDL_LogError(0, "组 %s 缺少 fragment 路径,跳过\n", groupName.c_str()); continue; } std::string vertPath = groupData["vertex"].get(); @@ -99,7 +101,7 @@ void RenderManager::InitShaderProgram() GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragPath); if (vertexShader == 0 || fragmentShader == 0) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器编译失败,跳过\n", groupName.c_str()); + SDL_LogError(0, "组 %s 着色器编译失败,跳过\n", groupName.c_str()); continue; } @@ -113,7 +115,7 @@ void RenderManager::InitShaderProgram() { char infoLog[512]; glGetProgramInfoLog(program, 512, nullptr, infoLog); - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "组 %s 着色器链接失败,失败原因: %s\n", groupName.c_str(), infoLog); + SDL_LogError(0, "组 %s 着色器链接失败,失败原因: %s\n", groupName.c_str(), infoLog); } _shaderProgramMap[groupName] = program; @@ -142,10 +144,10 @@ void RenderManager::Init2DTextureProgram() glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0); glEnableVertexAttribArray(0); @@ -158,126 +160,71 @@ void RenderManager::Init2DTextureProgram() glUseProgram(_currentShaderProgram); // 获取uniform变量位置 - GLuint _uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel"); - GLuint _uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj"); - GLuint _uTextureLoc = glGetUniformLocation(_currentShaderProgram, "uTexture"); - if (_uModelLoc == -1 || _uViewProjLoc == -1 || _uTextureLoc == -1) + GLint _uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel"); + GLint _uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj"); + GLint _uTextureLoc = glGetUniformLocation(_currentShaderProgram, "uTexture"); + GLint _uOpacityLoc = glGetUniformLocation(_currentShaderProgram, "uOpacity"); + + if (_uModelLoc == -1 || _uViewProjLoc == -1 || _uTextureLoc == -1 || _uOpacityLoc == -1) { - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "无法获取着色器 uniform 变量位置\n"); + SDL_LogError(0, "无法获取着色器 uniform 变量位置\n"); } - std::vector uniformLocs = {_uModelLoc, _uViewProjLoc, _uTextureLoc}; + std::vector uniformLocs = {_uModelLoc, _uViewProjLoc, _uTextureLoc, _uOpacityLoc}; - DrawLogicFunc drawFunc = [this]( - RefPtr textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata) - { - GLuint texture = textureObj->getID(); - if (!texture || !dstrect) - return; - - glUseProgram(_currentShaderProgram); - - glm::mat4 model = glm::mat4(1.0f); - - // 平移 - model = glm::translate(model, glm::vec3(dstrect->x, dstrect->y, 0.0f)); - - // 计算旋转中心 - float centerX = center ? center->x : dstrect->w * 0.5f; - float centerY = center ? center->y : dstrect->h * 0.5f; - - // 旋转中心平移 - model = glm::translate(model, glm::vec3(centerX, centerY, 0.0f)); - - // 旋转 - if (angle != 0.0) - { - model = glm::rotate(model, glm::radians(static_cast(angle)), glm::vec3(0.0f, 0.0f, 1.0f)); - } - - // 旋转中心平移 - model = glm::translate(model, glm::vec3(-centerX, -centerY, 0.0f)); - - // 缩放和翻转 - float scaleX = dstrect->w; - float scaleY = dstrect->h; - - // 应用翻转(通过负缩放实现) - if (flip & SDL_FLIP_HORIZONTAL) - scaleX = -scaleX; - if (flip & SDL_FLIP_VERTICAL) - scaleY = -scaleY; - - model = glm::scale(model, glm::vec3(scaleX, scaleY, 1.0f)); - - // 翻转后的位置补偿 - if (flip & SDL_FLIP_HORIZONTAL) - { - model = glm::translate(model, glm::vec3(-1.0f, 0.0f, 0.0f)); - } - if (flip & SDL_FLIP_VERTICAL) - { - model = glm::translate(model, glm::vec3(0.0f, -1.0f, 0.0f)); - } - - // 设置着色器 uniforms - glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(model)); - glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_OrthoMatrix)); - - // 绑定纹理并设置采样器 - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - glUniform1i(_currentRenderParams.UnimLocs[2], 0); - - // 处理纹理裁剪 - float texWidth = (float)textureObj->getSize().width; - float texHeight = (float)textureObj->getSize().height; - - // 计算纹理坐标 - float minu, minv, maxu, maxv; - if (srcrect) - { - minu = (float)srcrect->x / texWidth; - minv = (float)srcrect->y / texHeight; - maxu = (float)(srcrect->x + srcrect->w) / texWidth; - maxv = (float)(srcrect->y + srcrect->h) / texHeight; - } - else - { - minu = 0.0f; - minv = 0.0f; - maxu = 1.0f; - maxv = 1.0f; - } - - // 计算顶点位置(单位矩形,通过模型变换进行缩放和平移) - float minx = 0.0f; - float miny = 0.0f; - float maxx = 1.0f; - float maxy = 1.0f; - - // 创建顶点数据(位置 + 纹理坐标) - float vertices[] = { - // 位置 // 纹理坐标 - minx, miny, minu, minv, // 左上 - maxx, miny, maxu, minv, // 右上 - maxx, maxy, maxu, maxv, // 右下 - minx, maxy, minu, maxv // 左下 - }; - - // 更新VBO数据 - glBindBuffer(GL_ARRAY_BUFFER, _currentRenderParams.VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); - - // 绘制纹理 - glBindVertexArray(_currentRenderParams.VAO); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - glBindVertexArray(0); - }; - - GL_RenderParams bufferObject = {VAO, VBO, EBO, uniformLocs, drawFunc}; + GL_RenderParams bufferObject = {VAO, VBO, EBO, uniformLocs, nullptr}; AddBufferObject("2DTexture", bufferObject); } +void RenderManager::InitRectProgram() +{ + // 单位矩形顶点(仅位置,左下角(0,0),右上角(1,1)) + float vertices[] = { + 0.0f, 0.0f, // 左下 + 1.0f, 0.0f, // 右下 + 1.0f, 1.0f, // 右上 + 0.0f, 1.0f // 左上 + }; + unsigned int indices[] = {0, 1, 2, 2, 3, 0}; // 固定索引 + + GLuint VAO, VBO, EBO; + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + // 绑定并上传数据(GL_STATIC_DRAW:数据仅初始化一次,后续不修改) + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 静态数据 + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 静态数据 + + // 配置顶点属性(仅位置,2个float,步长2*sizeof(float)) + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); + glEnableVertexAttribArray(0); + + // 解绑(恢复默认状态) + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + // 获取矩形着色器的Uniform位置(需提前准备矩形专用着色器) + SetCurrentShaderProgram("rect"); // 假设着色器配置中“rect”为矩形专用着色器 + glUseProgram(_currentShaderProgram); + GLint uModelLoc = glGetUniformLocation(_currentShaderProgram, "uModel"); + GLint uViewProjLoc = glGetUniformLocation(_currentShaderProgram, "uViewProj"); + GLint uColorLoc = glGetUniformLocation(_currentShaderProgram, "uColor"); + if (uModelLoc == -1 || uViewProjLoc == -1 || uColorLoc == -1) + { + SDL_LogError(0, "矩形着色器Uniform变量获取失败\n uModelLoc: %d, uViewProjLoc: %d, uColorLoc: %d\n", uModelLoc, uViewProjLoc, uColorLoc); + return; + } + std::vector uniformLocs = {uModelLoc, uViewProjLoc, uColorLoc}; + + // 将矩形渲染参数加入管理器 + GL_RenderParams rectBuffer = {VAO, VBO, EBO, uniformLocs, nullptr}; + AddBufferObject("Rect", rectBuffer); +} + void RenderManager::SetCurrentShaderProgram(std::string name) { if (_shaderProgramMap.find(name) != _shaderProgramMap.end()) @@ -285,7 +232,7 @@ void RenderManager::SetCurrentShaderProgram(std::string name) this->_currentShaderProgram = _shaderProgramMap[name]; } else - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到着色器程序: %s\n", name.c_str()); + SDL_LogError(0, "找不到着色器程序: %s\n", name.c_str()); } void RenderManager::SetCurrentBufferObject(std::string name) @@ -294,8 +241,8 @@ void RenderManager::SetCurrentBufferObject(std::string name) { this->_currentRenderParams = _RenderParamsMap[name]; } - else - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染参数: %s\n", name.c_str()); + // else + // SDL_LogError(0, "找不到渲染参数: %s\n", name.c_str()); } void RenderManager::ClearScreen() @@ -320,13 +267,147 @@ void RenderManager::CloseClipRect() glDisable(GL_SCISSOR_TEST); } -void RenderManager::DrawTexture(RefPtr textureObj, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata) +void RenderManager::SetOpacity(float opacity) { - _frameRenderCount++; - if (_currentRenderParams.DrawFunc != nullptr) - _currentRenderParams.DrawFunc(textureObj, srcrect, dstrect, angle, center, flip, userdata); + this->opacity_ = opacity; +} + +float RenderManager::GetOpacity() +{ + return this->opacity_; +} + +void RenderManager::SetMatrix(glm::mat4 matrix) +{ + this->_currentMatrix = matrix; +} + +glm::mat4 RenderManager::GetMatrix() +{ + return this->_currentMatrix; +} + +void RenderManager::SetBlendMode(LE_BlEND_MODE blendMode) +{ + switch (blendMode) + { + /**常规混合 */ + case NONE: + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + /**线性减淡 */ + case LINEARDODGE: + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_ZERO, GL_ONE); + break; + } +} + +void RenderManager::DrawTexture(RefPtr textureObj) +{ + _frameRenderCount++; // 统计渲染次数 + glUseProgram(_currentShaderProgram); + + GLuint texture = textureObj->getID(); + float texWidth = (float)textureObj->getSize().width; + float texHeight = (float)textureObj->getSize().height; + + glm::mat4 modelMat = _currentMatrix; + + modelMat = glm::scale(modelMat, glm::vec3(texWidth, texHeight, 1.0f)); + + // 设置着色器 uniforms + glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(modelMat)); + glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_OrthoMatrix)); + + // 绑定纹理并设置采样器 + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(_currentRenderParams.UnimLocs[2], 0); + glUniform1f(_currentRenderParams.UnimLocs[3], opacity_); + + // 计算纹理坐标 + float minu, minv, maxu, maxv; + if (false) + { + // minu = (float)srcrect->x / texWidth; + // minv = (float)srcrect->y / texHeight; + // maxu = (float)(srcrect->x + srcrect->w) / texWidth; + // maxv = (float)(srcrect->y + srcrect->h) / texHeight; + } else - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "找不到渲染逻辑函数\n"); + { + minu = 0.0f; + minv = 0.0f; + maxu = 1.0f; + maxv = 1.0f; + } + + // 计算顶点位置(单位矩形,通过模型变换进行缩放和平移) + float minx = 0.0f; + float miny = 0.0f; + float maxx = 1.0f; + float maxy = 1.0f; + + // 创建顶点数据(位置 + 纹理坐标) + float vertices[] = { + // 位置 // 纹理坐标 + minx, miny, minu, minv, // 左上 + maxx, miny, maxu, minv, // 右上 + maxx, maxy, maxu, maxv, // 右下 + minx, maxy, minu, maxv // 左下 + }; + + // 更新VBO数据 + glBindBuffer(GL_ARRAY_BUFFER, _currentRenderParams.VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); + + // 绘制纹理 + glBindVertexArray(_currentRenderParams.VAO); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); // 解绑纹理 +} + +void RenderManager::DrawRect(const SDL_Rect *rect, glm::vec4 color) +{ + // 1. 合法性检查(补充VAO/EBO存在性检查) + if (!rect || _currentShaderProgram == 0 || _currentRenderParams.VAO == 0 || _currentRenderParams.EBO == 0) + { + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "DrawRect:参数无效或缓冲未初始化\n"); + return; + } + SetCurrentShaderProgram("rect"); + SetCurrentBufferObject("Rect"); + _frameRenderCount++; + + // 绑定VAO和EBO(GPU获取顶点数据和格式的关键) + glBindVertexArray(_currentRenderParams.VAO); + + // 3. 计算模型矩阵(通过缩放+平移,将单位矩形变换到目标位置和大小) + glm::mat4 model = glm::mat4(1.0f); + // 先平移到rect的左上角(OpenGL正交投影下Y轴向下,与SDL_Rect一致) + model = glm::translate(model, glm::vec3((float)rect->x, (float)rect->y, 0.0f)); + // 再缩放到rect的宽高 + model = glm::scale(model, glm::vec3((float)rect->w, (float)rect->h, 1.0f)); + + // 4. 激活着色器并设置Uniform + glUseProgram(_currentShaderProgram); + // 4.1 模型矩阵(传递变换后的矩阵) + glUniformMatrix4fv(_currentRenderParams.UnimLocs[0], 1, GL_FALSE, glm::value_ptr(model)); + // 4.2 投影矩阵(复用当前正交矩阵) + glUniformMatrix4fv(_currentRenderParams.UnimLocs[1], 1, GL_FALSE, glm::value_ptr(_OrthoMatrix)); + // 4.3 颜色 + glUniform4fv(_currentRenderParams.UnimLocs[2], 1, glm::value_ptr(color)); + + // 5. 绘制矩形(6个索引对应2个三角形) + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + + // 6. 解绑资源(避免影响后续绘制) + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + SetCurrentShaderProgram("normal"); + SetCurrentBufferObject("2DTexture"); } void RenderManager::SetOrthoMatrixType(int type) @@ -347,6 +428,17 @@ void RenderManager::SetOrthoMatrix(glm::mat4 matrix) _OrthoMatrix = matrix; } +void RenderManager::SetViewport(SDL_Rect viewport) +{ + _viewport = viewport; + glViewport(viewport.x, viewport.y, viewport.w, viewport.h); +} + +SDL_Rect RenderManager::GetViewport() +{ + return _viewport; +} + GLuint RenderManager::CompileShader(GLenum type, std::string Path) { std::ifstream Source("shader/" + Path); diff --git a/source/EngineFrame/Render/RenderManager.h b/source/EngineFrame/Render/RenderManager.h index 60b5a30..727bb53 100644 --- a/source/EngineFrame/Render/RenderManager.h +++ b/source/EngineFrame/Render/RenderManager.h @@ -22,7 +22,7 @@ public: GLuint VAO; GLuint VBO; GLuint EBO; - std::vector UnimLocs; + std::vector UnimLocs; DrawLogicFunc DrawFunc = nullptr; }; @@ -37,6 +37,8 @@ private: glm::mat4 _UIOrthoMatrix; // 渲染的正交投影矩阵 glm::mat4 _OrthoMatrix; + /**视口 */ + SDL_Rect _viewport; // 渲染器上下文 SDL_GLContext _ctx; @@ -50,40 +52,64 @@ private: // 当前使用渲染参数 GL_RenderParams _currentRenderParams; + // 渲染透明度 + float opacity_ = 1.0f; + // 渲染矩阵 + glm::mat4 _currentMatrix; + public: // 单帧渲染调用次数 int _frameRenderCount = 0; + public: RenderManager(SDL_Window *window); ~RenderManager(); - // 初始化着色器程序 + /**初始化着色器程序 */ void InitShaderProgram(); - // 初始化绘制2D纹理缓冲程序 + /**初始化绘制2D纹理缓冲程序 */ void Init2DTextureProgram(); - // 设定当前使用的着色器程序 + /**初始化绘制矩形缓冲程序 */ + void InitRectProgram(); + /**设定当前使用的着色器程序 */ void SetCurrentShaderProgram(std::string name); - // 设定当前使用的缓冲对象 + /**设定当前使用的缓冲对象 */ void SetCurrentBufferObject(std::string name); - // 清空屏幕 + /**清空屏幕 */ void ClearScreen(); - // 交换缓冲区 + /**交换缓冲区 */ void SwapBuffer(); - // 设置裁切区域 + /**设置裁切区域 */ void SetClipRect(const SDL_Rect *rect); - // 关闭裁切区域 + /**关闭裁切区域 */ void CloseClipRect(); - // 绘制纹理 - void DrawTexture(RefPtr texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect, const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, void *userdata = nullptr); - // 设置正交投影矩阵类型 + /**设置渲染透明度 */ + void SetOpacity(float opacity); + /**获取渲染透明度 */ + float GetOpacity(); + /**设置渲染矩阵 */ + void SetMatrix(glm::mat4 matrix); + /**获取渲染矩阵 */ + glm::mat4 GetMatrix(); + /**设置混合模式 */ + void SetBlendMode(LE_BlEND_MODE blendMode); + + /**绘制纹理 */ + void DrawTexture(RefPtr texture); + /**绘制矩形 */ + void DrawRect(const SDL_Rect *rect, glm::vec4 color); + + /**设置正交投影矩阵类型 */ void SetOrthoMatrixType(int type); - // 获取渲染的正交投影矩阵 + /**获取渲染的正交投影矩阵 */ glm::mat4 GetOrthoMatrix(); - // 设置渲染的正交投影矩阵 + /**设置渲染的正交投影矩阵 */ void SetOrthoMatrix(glm::mat4 matrix); - - + /**设置视口 */ + void SetViewport(SDL_Rect viewport); + /**获取视口 */ + SDL_Rect GetViewport(); private: // 编译着色器 diff --git a/source/EngineFrame/Scene/Scene.cpp b/source/EngineFrame/Scene/Scene.cpp index f2172b7..5b2f942 100644 --- a/source/EngineFrame/Scene/Scene.cpp +++ b/source/EngineFrame/Scene/Scene.cpp @@ -13,15 +13,14 @@ void Scene::Enter() void Scene::Init() { - BaseNode::Init(); - addTag(Tag::SCENE); + } void Scene::Exit() { } -RefPtr Scene::GetCamera() +RefPtr Scene::GetCamera() { return nullptr; } diff --git a/source/EngineFrame/Scene/Scene.h b/source/EngineFrame/Scene/Scene.h index 7bc1ee6..5ed7a85 100644 --- a/source/EngineFrame/Scene/Scene.h +++ b/source/EngineFrame/Scene/Scene.h @@ -1,11 +1,11 @@ #pragma once -#include "EngineFrame/Base/BaseNode.h" -class Scene : public BaseNode +#include "EngineFrame/Base/Node.h" +class Scene : public Node { public: Scene(); virtual void Enter(); virtual void Exit(); - virtual RefPtr GetCamera(); + virtual RefPtr GetCamera(); void Init(); }; \ No newline at end of file diff --git a/source/Tool/Ifstream_NPK.cpp b/source/Tool/Ifstream_NPK.cpp index 1734543..1651a0c 100644 --- a/source/Tool/Ifstream_NPK.cpp +++ b/source/Tool/Ifstream_NPK.cpp @@ -31,6 +31,18 @@ int Ifstream_NPK::ReadInt() return Count; } +unsigned int Ifstream_NPK::ReadUnsignedInt() +{ + char *CountBuffer = new char[4]; + for (int i = 0; i < 4; i++) + { + this->get(CountBuffer[i]); + } + unsigned int Count = *(unsigned int *)CountBuffer; + delete[] CountBuffer; + return Count; +} + // 读字符串 std::string Ifstream_NPK::ReadString() { @@ -60,7 +72,7 @@ std::string Ifstream_NPK::ReadInfo() } // 读LONG -int Ifstream_NPK::ReadLong() +long Ifstream_NPK::ReadLong() { char *CountBuffer = new char[8]; for (int i = 0; i < 8; i++) @@ -72,6 +84,18 @@ int Ifstream_NPK::ReadLong() return Count; } +unsigned long Ifstream_NPK::ReadUnsignedLong() +{ + char *CountBuffer = new char[8]; + for (int i = 0; i < 8; i++) + { + this->get(CountBuffer[i]); + } + unsigned long Count = *(unsigned long *)CountBuffer; + delete[] CountBuffer; + return Count; +} + // 读指定长度数据 BYTE *Ifstream_NPK::ReadCustomSize(int Size) { diff --git a/source/Tool/Ifstream_NPK.h b/source/Tool/Ifstream_NPK.h index 8380387..4ad8f29 100644 --- a/source/Tool/Ifstream_NPK.h +++ b/source/Tool/Ifstream_NPK.h @@ -22,6 +22,9 @@ public: // 读整数 int ReadInt(); + // 读无符号整数 + unsigned int ReadUnsignedInt(); + // 读字符串 std::string ReadString(); @@ -29,7 +32,9 @@ public: std::string ReadInfo(); // 读LONG - int ReadLong(); + long ReadLong(); + // 读取无符号long + unsigned long ReadUnsignedLong(); // 读指定长度数据 BYTE *ReadCustomSize(int Size); diff --git a/source_game/Actor/Map/GameMap.cpp b/source_game/Actor/Map/GameMap.cpp index 19498a8..e777acb 100644 --- a/source_game/Actor/Map/GameMap.cpp +++ b/source_game/Actor/Map/GameMap.cpp @@ -1,5 +1,6 @@ #include "GameMap.h" #include "Asset/AssetManager.h" +#include "Asset/Asset_SoundPack.h" #include "EngineCore/Game.h" #include "EngineFrame/Scene/Scene.h" #include "EngineFrame/Component/Animation.h" @@ -19,15 +20,25 @@ GameMap::GameMap() _LayerMap["max"] = new GameMapLayer(); // 设置图层渲染顺序 - _LayerMap["contact"]->SetRenderZOrder(10000); - _LayerMap["distantback"]->SetRenderZOrder(50000); - _LayerMap["middleback"]->SetRenderZOrder(100000); - _LayerMap["bottom"]->SetRenderZOrder(150000); - _LayerMap["closeback"]->SetRenderZOrder(200000); - _LayerMap["normal"]->SetRenderZOrder(250000); - _LayerMap["close"]->SetRenderZOrder(300000); - _LayerMap["cover"]->SetRenderZOrder(350000); - _LayerMap["max"]->SetRenderZOrder(400000); + _LayerMap["contact"]->SetZOrder(10000); + _LayerMap["distantback"]->SetZOrder(50000); + _LayerMap["middleback"]->SetZOrder(100000); + _LayerMap["bottom"]->SetZOrder(150000); + _LayerMap["closeback"]->SetZOrder(200000); + _LayerMap["normal"]->SetZOrder(250000); + _LayerMap["close"]->SetZOrder(300000); + _LayerMap["cover"]->SetZOrder(350000); + _LayerMap["max"]->SetZOrder(400000); + + AddChild(_LayerMap["contact"]); + AddChild(_LayerMap["distantback"]); + AddChild(_LayerMap["middleback"]); + AddChild(_LayerMap["bottom"]); + AddChild(_LayerMap["closeback"]); + AddChild(_LayerMap["normal"]); + AddChild(_LayerMap["close"]); + AddChild(_LayerMap["cover"]); + AddChild(_LayerMap["max"]); } GameMap::~GameMap() @@ -107,7 +118,7 @@ void GameMap::InitConfiguration(std::string mapName) { BackGroundAni ani; Data.Get(); - ani.filename = _MapDir + Tool_toLowerCase(Data.Get()); + ani.filename = Tool_RegRealPath(_MapDir + Tool_toLowerCase(Data.Get())); Data.Get(); ani.layer = Data.Get(); Data.Get(); @@ -119,13 +130,13 @@ void GameMap::InitConfiguration(std::string mapName) } else if (Segment == "[sound]") { - std::vector soundArry; + std::map soundArry; while (true) { std::string sounddata = Data.Get(); if (sounddata == "[/sound]") break; - soundArry.push_back(sounddata); + soundArry[sounddata] = true; } _MapInfo["sound"] = soundArry; } @@ -174,13 +185,17 @@ void GameMap::InitConfiguration(std::string mapName) std::string areadata = Data.Get(); if (areadata == "[/town movable area]") break; - town_movable_area.push_back(std::stoi(areadata)); - town_movable_area.push_back(std::stoi(areadata)); - town_movable_area.push_back(std::stoi(areadata)); - town_movable_area.push_back(std::stoi(areadata)); + int x1 = std::stoi(areadata); + int y1 = std::stoi(Data.Get()); + int x2 = std::stoi(Data.Get()); + int y2 = std::stoi(Data.Get()); + town_movable_area.push_back(x1); + town_movable_area.push_back(y1); + town_movable_area.push_back(x2); + town_movable_area.push_back(y2); MapMoveArea T; - T.town = std::stoi(areadata); - T.area = std::stoi(areadata); + T.town = std::stoi(Data.Get()); + T.area = std::stoi(Data.Get()); town_movable_area_info.push_back(T); } _MapInfo["town_movable_area"] = town_movable_area; @@ -212,25 +227,19 @@ void GameMap::InitTile() int NormalTileCount = 0; // 普通地板高度 int NormalTileHeight = 560; - std::vector tileArr = std::get>(_MapInfo["tile"]); - if (tileArr.size() > 0) - { - NormalTileCount = tileArr.size(); - _MapLength = NormalTileCount * 224; - _MapHeight = 560; - } - if (!_MapInfo.count("extended_tile")) - return; + std::vector tileArr; + std::vector extileArr; - std::vector extileArr = std::get>(_MapInfo["extended_tile"]); - if (extileArr.size() > 0) + tileArr = std::get>(_MapInfo["tile"]); + + // 拓展地板不一定有 + if (_MapInfo.count("extended_tile")) { - int ExTileCount = extileArr.size(); - int Buffer = (ExTileCount / NormalTileCount); - _MapHeight += (Buffer < 1 ? 1 : Buffer) * 40; + extileArr = std::get>(_MapInfo["extended_tile"]); } _Tile = new Tile(this, tileArr, extileArr); + _Tile->SetPosition(0, -200 - std::get(_MapInfo["background_pos"])); _LayerMap["bottom"]->AddChild(_Tile); } @@ -256,8 +265,8 @@ void GameMap::InitBackgroundAnimation() } for (int i = 0; i < (int)AniList.size(); i++) { - AniList[i]->SetPos(Vec2{i * width, -120}); - AniList[i]->SetRenderZOrder(-1000000); + AniList[i]->SetPosition(i * width, -120); + AniList[i]->SetZOrder(-1000000); std::string layer = ani.layer; layer = layer.substr(1, layer.length() - 2); if (_LayerMap.count(layer)) @@ -281,8 +290,8 @@ void GameMap::InitMapAnimation() { std::string path = ani.filename; RefPtr AniObj = new Animation(path); - AniObj->SetPos(Vec2{ani.XPos, ani.YPos - ani.ZPos}); - AniObj->SetRenderZOrder(ani.YPos); + AniObj->SetPosition(ani.XPos, ani.YPos - ani.ZPos); + AniObj->SetZOrder(ani.YPos); std::string layer = ani.layer; layer = layer.substr(1, layer.length() - 2); if (_LayerMap.count(layer)) @@ -305,12 +314,30 @@ void GameMap::InitVirtualMovableArea() float w = Info[i + 2]; float h = Info[i + 3]; if (_DebugMode) - _LayerMap["max"]->AddDebugFeasibleAreaInfo(Vec2(x, y), VecSize(w, h)); + _LayerMap["max"]->AddDebugFeasibleAreaInfo(Vec2(x, y), VecSize(w, h), 0); _MovableArea.push_back(SDL_FRect{x, y, w, h}); } } } +void GameMap::InitMoveArea() +{ + std::vector Info = std::get>(_MapInfo["town_movable_area"]); + if (Info.size() > 0) + { + for (int i = 0; i < Info.size(); i += 4) + { + float x = Info[i]; + float y = Info[i + 1]; + float w = Info[i + 2]; + float h = Info[i + 3]; + if (_DebugMode) + _LayerMap["max"]->AddDebugFeasibleAreaInfo(Vec2(x, y), VecSize(w, h), 1); + _MoveArea.push_back(SDL_FRect{x, y, w, h}); + } + } +} + void GameMap::LoadMap(std::string mapName) { // 读取脚本配置 @@ -323,24 +350,41 @@ void GameMap::LoadMap(std::string mapName) InitMapAnimation(); // 初始化可行区域 InitVirtualMovableArea(); + // 初始化传送区域 + InitMoveArea(); } -void GameMap::Enter(Scene *scene) +void GameMap::Enter() { - AddChild(_LayerMap["contact"]); - AddChild(_LayerMap["distantback"]); - AddChild(_LayerMap["middleback"]); - AddChild(_LayerMap["bottom"]); - AddChild(_LayerMap["closeback"]); - AddChild(_LayerMap["normal"]); - AddChild(_LayerMap["close"]); - AddChild(_LayerMap["cover"]); - AddChild(_LayerMap["max"]); + auto &Audio = Asset_SoundPack::GetInstance(); + // 获取背景音乐 + auto bgmIt = _MapInfo.find("sound"); + if (bgmIt != _MapInfo.end()) + { + std::map bgmlist = std::get>(_MapInfo["sound"]); + // 获取正在播放的背景音乐 + std::map curmap = Audio.GetBackgroudMusicVector(); + for (auto &it : curmap) + { + std::string name = it.first; + // 判断正在播放的背景音乐是否与当前地图背景音乐相同 + if (!bgmlist.count(name)) + { + Audio.StopAudio(name); + } + } + // 播放当前地图背景音乐 + for (auto &it : bgmlist) + { + std::string name = it.first; + if (!curmap.count(name)) + Audio.PlayAudio(name); + } + } } -void GameMap::Update(float deltaTime) +void GameMap::OnUpdate(float deltaTime) { - Actor::Update(deltaTime); RefPtr Cam = Global_Game::GetInstance().GetCamera(); if (Cam == nullptr) @@ -384,8 +428,11 @@ void GameMap::Update(float deltaTime) posX *= BackgroundMoveSpeed; posX /= 100.0f; } - Layer.second->SetPos(Vec2(posX, posY)); + Layer.second->SetPosition(posX, posY); } + + Actor::OnUpdate(deltaTime); + } void GameMap::AddObject(RefPtr object) @@ -397,9 +444,6 @@ void GameMap::AddObject(RefPtr object) { CharacterObject *chr = (CharacterObject *)(object.Get()); Global_Game::GetInstance().GetCamera()->SetFromActor(chr); - - if (chr->_Shadow) - _LayerMap["bottom"]->AddChild(chr->_Shadow); } } @@ -459,4 +503,42 @@ VecFPos3 GameMap::CheckIsItMovable(VecFPos3 CurPos, VecFPos3 PosOffset) result.z += PosOffset.z; return result; -} \ No newline at end of file +} + +GameMap::MapMoveArea GameMap::CheckIsItMoveArea(VecFPos3 ChrPos) +{ + + MapMoveArea invalidArea; + invalidArea.town = -2; + invalidArea.area = -2; + + if (_MoveArea.size() && _MapInfo.count("town_movable_area_info")) + { + SDL_FPoint CurPos = {ChrPos.x, ChrPos.y}; + + for (int i = 0; i < _MoveArea.size(); i++) + { + if (SDL_PointInFRect(&CurPos, &_MoveArea[i])) + { + const std::vector &tma = std::get>(_MapInfo["town_movable_area_info"]); + return tma[i]; + } + } + } + + // 所有矩形都不命中,返回无效值 + return invalidArea; +} + +std::vector &GameMap::GetMoveAreaInfo() +{ + return std::get>(_MapInfo["town_movable_area_info"]); +} + +SDL_Rect GameMap::GetMovablePositionArea(int Index) +{ + auto Vertex = std::get>(_MapInfo["town_movable_area"]); + int FindIndex = Index * 4; + SDL_Rect Buf = {Vertex[FindIndex], Vertex[FindIndex + 1], Vertex[FindIndex + 2], Vertex[FindIndex + 3]}; + return Buf; +} diff --git a/source_game/Actor/Map/GameMap.h b/source_game/Actor/Map/GameMap.h index aff7d3c..fe3b036 100644 --- a/source_game/Actor/Map/GameMap.h +++ b/source_game/Actor/Map/GameMap.h @@ -10,6 +10,7 @@ class BaseObject; class GameMapCamera; class GameMap : public Actor { +public: struct BackGroundAni { std::string filename; @@ -37,13 +38,14 @@ class GameMap : public Actor struct MapMoveArea { - int town; - int area; + int town = 0; + int area = 0; }; using MapInfoBody = std::variant< int, std::string, + std::map, std::vector, std::vector, std::vector, @@ -66,6 +68,8 @@ public: int _MapHeight = 0; // 可行区域 std::vector _MovableArea; + // 传送区域 + std::vector _MoveArea; // 调试模式 bool _DebugMode = false; @@ -81,17 +85,36 @@ public: GameMap(/* args */); ~GameMap(); + /**加载地图 (地图路径) */ void LoadMap(std::string mapName); + /**进入地图执行的操作 */ + void Enter(); + /**重载OnUpdate函数 */ + void OnUpdate(float deltaTime) override; + +public: + /**读取脚本配置 */ void InitConfiguration(std::string mapName); + /**初始化地板 */ void InitTile(); + /**初始化背景动画 */ void InitBackgroundAnimation(); + /**初始化地图动画 */ void InitMapAnimation(); + /**初始化移动区域 */ void InitVirtualMovableArea(); - void Enter(Scene *scene); - void Update(float deltaTime) override; + /**初始化传送区域 */ + void InitMoveArea(); + /**添加对象 */ void AddObject(RefPtr object); public: // 检查是否可移动 VecFPos3 CheckIsItMovable(VecFPos3 CurPos, VecFPos3 PosOffset); + // 检查是否进入传送区域 + MapMoveArea CheckIsItMoveArea(VecFPos3 CurPos); + /**获取移动区域信息 */ + std::vector &GetMoveAreaInfo(); + /**获取移动区域坐标信息 */ + SDL_Rect GetMovablePositionArea(int Index); }; diff --git a/source_game/Actor/Map/GameMapLayer.cpp b/source_game/Actor/Map/GameMapLayer.cpp index 63c7b24..17222d7 100644 --- a/source_game/Actor/Map/GameMapLayer.cpp +++ b/source_game/Actor/Map/GameMapLayer.cpp @@ -1,35 +1,47 @@ #include "GameMapLayer.h" #include "EngineCore/Game.h" - void GameMapLayer::Render() { - Actor::Render(); - // RenderManager *renderer = Game::GetInstance().GetRenderer(); - - // // 自身坐标 - // float Xpos = GetIterationPos().x + GetPos().x; - // float Ypos = GetIterationPos().y + GetPos().y; - // for (auto &info : this->FeasibleAreaInfoList) - // { - // SDL_Rect buf; - // buf.x = info.x + Xpos; - // buf.y = info.y + Ypos; - // buf.w = info.w; - // buf.h = info.h; - // //TODO: 渲染可行区域 - // } + // 自身坐标 + glm::vec2 Pos = this->ConvertToWorld({0, 0}); + // 可行区域 + for (auto &info : this->FeasibleAreaInfoList) + { + SDL_Rect buf; + buf.x = info.x + Pos.x; + buf.y = info.y + Pos.y; + buf.w = info.w; + buf.h = info.h; + glm::vec4 red = {1.0f, 0.0f, 0.0f, 0.4f}; // RGBA:红色,不透明基础色 + Game::GetInstance().GetRenderer()->DrawRect(&buf, red); + } + // 移动区域 + for (auto &info : this->MoveAreaInfoList) + { + SDL_Rect buf; + buf.x = info.x + Pos.x; + buf.y = info.y + Pos.y; + buf.w = info.w; + buf.h = info.h; + glm::vec4 red = {0.0f, 0.0f, 1.0f, 0.4f}; // RGBA:红色,不透明基础色 + Game::GetInstance().GetRenderer()->DrawRect(&buf, red); + } } -void GameMapLayer::AddDebugFeasibleAreaInfo(Vec2 pos, VecSize size) +void GameMapLayer::AddDebugFeasibleAreaInfo(Vec2 pos, VecSize size, int Type) { SDL_Rect info; info.x = pos.x; info.y = pos.y; info.w = size.width; info.h = size.height; - this->FeasibleAreaInfoList.push_back(info); + + if (Type == 0) + this->FeasibleAreaInfoList.push_back(info); + else if (Type == 1) + this->MoveAreaInfoList.push_back(info); } void GameMapLayer::AddObject(RefPtr obj) diff --git a/source_game/Actor/Map/GameMapLayer.h b/source_game/Actor/Map/GameMapLayer.h index 60351bc..6e2141a 100644 --- a/source_game/Actor/Map/GameMapLayer.h +++ b/source_game/Actor/Map/GameMapLayer.h @@ -9,12 +9,14 @@ class GameMapLayer : public Actor private: // 可行区域信息 std::vector FeasibleAreaInfoList; + // 移动区域信息 + std::vector MoveAreaInfoList; public: - // 重载Render以实现绘制可行区域 + // 重载OnRender以实现绘制可行区域 void Render() override; // 添加调试可行区域信息 - void AddDebugFeasibleAreaInfo(Vec2 pos, VecSize size); + void AddDebugFeasibleAreaInfo(Vec2 pos, VecSize size, int Type); public: void AddObject(RefPtr obj); // 添加对象 diff --git a/source_game/Actor/Map/GameTown.cpp b/source_game/Actor/Map/GameTown.cpp new file mode 100644 index 0000000..52c6de4 --- /dev/null +++ b/source_game/Actor/Map/GameTown.cpp @@ -0,0 +1,67 @@ +#include "GameTown.h" +#include "Global/Global_Game.h" +GameTown::GameTown() +{ +} + +GameTown::~GameTown() +{ +} + +void GameTown::Init(int Index) +{ + GlobalTownScript::TownConfig *Config = Global_Game::GetInstance().GetTownInfo(Index); + if (Config) + { + this->Name_ = Config->TownName; + this->Entering_Title = Config->Entering_Title; + this->Entering_Cutscene = Config->Entering_Cutscene; + this->NeedLevel = Config->NeedLevel; + this->NeedQuestID = Config->NeedQuestID; + // 构造全部地图 + for (auto &it : Config->AreaLst) + { + MapInfo info; + info.Map = new GameMap(); + info.Map->LoadMap(it.second.MapPath); + info.Type = it.second.MapType; + MapList_.push_back(info); + if (info.Type == "gate") + { + SariaRoomID = it.first; + SariaRoomPos = {it.second.GenerateXPos, it.second.GenerateYPos, 0}; + } + } + } +} + +void GameTown::AddCharacter(RefPtr Chr, int Index) +{ + int TargetMapIndex = Index; + + if (Index == -2) + { + TargetMapIndex = SariaRoomID; + } + // 如果有当前调用中的地图 + if (CurMapIndex != -1) + { + RemoveChild(MapList_[CurMapIndex].Map); + } + // 如果角色已有父对象 + if (Chr->GetParent()) + { + Chr->RemoveFromParent(); + } + // 调用赛利亚房间地图 + AddChild(MapList_[TargetMapIndex].Map); + if(Index == -2)Chr->SetPosition(SariaRoomPos); + MapList_[TargetMapIndex].Map->Enter(); + MapList_[TargetMapIndex].Map->AddObject(Chr); + CurMapIndex = TargetMapIndex; +} + +void GameTown::OnUpdate(float DeltaTime) +{ + Actor::OnUpdate(DeltaTime); +} diff --git a/source_game/Actor/Map/GameTown.h b/source_game/Actor/Map/GameTown.h new file mode 100644 index 0000000..d8fd5b9 --- /dev/null +++ b/source_game/Actor/Map/GameTown.h @@ -0,0 +1,54 @@ +#pragma once +#include "EngineFrame/Base/Actor.h" +#include "Actor/Map/GameMap.h" +#include "Actor/Object/CharacterObject.h" +class GameTown : public Actor +{ +public: + struct MapInfo + { + /**地图对象 */ + RefPtr Map; + /**地图类型 */ + std::string Type; + }; + +private: + /**城镇名称 */ + std::string Name_; + /**进入城镇时的标题Img */ + std::string Entering_Title; + /**进入城镇时的过场Img */ + std::string Entering_Cutscene; + /**需要的任务进入条件 */ + int NeedQuestID; + /**需要的等级进入条件 */ + int NeedLevel; + /**赛利亚房间编号 */ + int SariaRoomID = -1; + /**赛丽亚房间生成坐标 */ + VecFPos3 SariaRoomPos; + + /**地图集合 */ + std::vector MapList_; + + /**当前所在地图 */ + int CurMapIndex = -1; + +public: + GameTown(/* args */); + ~GameTown(); + + /**通过城镇ID初始化 */ + void Init(int Index); + +public: + /**获取当前所在区域 */ + int GetCurAreaIndex() { return CurMapIndex; }; + /**获取区域对象 */ + RefPtr GetArea(int Index) { return MapList_[Index].Map; }; + /**添加角色 (无区域编号 上线进入城镇 或 使用传送时进入城镇 有区域编号 移动进入的)*/ + void AddCharacter(RefPtr Chr, int AreaIndex = -2); + + void OnUpdate(float DeltaTime) override; +}; diff --git a/source_game/Actor/Map/GameWorld.cpp b/source_game/Actor/Map/GameWorld.cpp new file mode 100644 index 0000000..c6159fa --- /dev/null +++ b/source_game/Actor/Map/GameWorld.cpp @@ -0,0 +1,100 @@ +#include "GameWorld.h" +#include "Global/Global_Game.h" +#include "EngineCore/Game.h" + +#include "EngineFrame/Component/AnimationMap.h" +#include "EngineFrame/Component/Animation.h" +GameWorld::GameWorld() +{ +} + +GameWorld::~GameWorld() +{ +} + +void GameWorld::Enter() +{ + SetScale(1.2f, 1.2f); + Init(); +} + +void GameWorld::Exit() +{ +} + +void GameWorld::Init() +{ + // RefPtr ani = new Animation("common/tuguan/maineff.ani"); + RefPtr sp = new Sprite("sprite/live/else/chn/2022/0510_danjin_jar/22_danjineft/0510_danjin_body.img",0); + RefPtr am = new AnimationMap(); + // am->AddAnimation(ani); + am->AddChild(sp); + am->CompleteConstruction(); + AddChild(am); + + // // 构造所有城镇 + // auto &GlobalGame = Global_Game::GetInstance(); + // for (auto obj : GlobalGame.GetTownMap()) + // { + // RefPtr town = new GameTown; + // town->Init(obj.first); + // m_TownMap[obj.first] = town; + // } + + // // 构造角色 + // RefPtr obj = new CharacterObject(); + // obj->Construction(0); + // AddCharacter(obj, 1); +} + +void GameWorld::AddCharacter(RefPtr Chr, int TownId) +{ + // 游戏世界类赋值我的角色 + m_MyCharacter = Chr; + // 记录当前城镇 + m_CurTown = TownId; + // 进入城镇 + m_TownMap[TownId]->AddCharacter(Chr); + // 渲染并更新城镇 + AddChild(m_TownMap[TownId]); +} + +void GameWorld::MoveCharacter(RefPtr Chr, int TownId, int Area) +{ + // 当前所在城镇 + int curTown = m_CurTown; + // 当前所在区域 + int curArea = m_TownMap[curTown]->GetCurAreaIndex(); + // 获取目标城镇移动信息表 + auto MapObj = m_TownMap[TownId]->GetArea(Area); + auto MoveInfo = MapObj->GetMoveAreaInfo(); + // 传送后的目标区域 + SDL_Rect destArea = {0, 0, 0, 0}; + // 遍历移动信息表 + for (size_t i = 0; i < MoveInfo.size(); ++i) + { + auto info = MoveInfo[i]; + + if (info.town == curTown && info.area == curArea) + { + destArea = MapObj->GetMovablePositionArea(i); + } + } + // 读取出应该存在的坐标以后设置角色的坐标 + float Xpos = destArea.x + destArea.w / 2; + float Ypos = destArea.y + destArea.h / 2; + Chr->SetPosition({Xpos, Ypos, 0}); + // 将角色添加到目标城镇 + m_TownMap[TownId]->AddCharacter(Chr, Area); + // 停止渲染更新旧城镇 + RemoveChild(m_TownMap[curTown]); + // 记录当前城镇 + m_CurTown = TownId; + // 渲染并更新城镇 + AddChild(m_TownMap[TownId]); +} + +GameWorld *GameWorld::GetWorld() +{ + return (GameWorld *)Game::GetInstance().GetScene().Get(); +} diff --git a/source_game/Actor/Map/GameWorld.h b/source_game/Actor/Map/GameWorld.h new file mode 100644 index 0000000..162452f --- /dev/null +++ b/source_game/Actor/Map/GameWorld.h @@ -0,0 +1,31 @@ +#pragma once +#include "EngineFrame/Scene/Scene.h" +#include "Actor/Map/GameTown.h" +class GameWorld : public Scene +{ +private: + /**城镇Map */ + std::map> m_TownMap; + /**我的角色 */ + RefPtr m_MyCharacter; + + /**当前城镇 */ + int m_CurTown; + +public: + GameWorld(); + ~GameWorld(); + + void Enter() override; + void Exit() override; + + /**初始化 */ + void Init() override; + /**加入角色 */ + void AddCharacter(RefPtr Chr, int TownId); + /**移动角色 */ + void MoveCharacter(RefPtr Chr, int TownId,int Area); + +public: + static GameWorld *GetWorld(); +}; diff --git a/source_game/Actor/Map/Tile.cpp b/source_game/Actor/Map/Tile.cpp index 2f4ef4a..7e90cf3 100644 --- a/source_game/Actor/Map/Tile.cpp +++ b/source_game/Actor/Map/Tile.cpp @@ -4,56 +4,29 @@ #include "Actor/Map/GameMap.h" #include "Global/Global_Game.h" -// Tile::Tile(std::string Path) : Sprite() -// { -// InitInfo(Path); -// if (std::get(m_data["path"]) == "") -// m_data["path"] = "sprite/character/common/circlecooltime.img"; - -// m_texture = new Texture(); -// m_texture->Init(std::get(m_data["path"]), std::get(m_data["idx"])); -// Sprite::Init(); -// this->imgPath = Path; -// } - Tile::Tile(GameMap *parentMap, std::vector normal, std::vector ex) { m_parentMap = parentMap; - m_texture = new Texture(); - - // 通过父对象获取地图大小 - int Width = m_parentMap->_MapLength; - int Height = m_parentMap->_MapHeight; - - // 通过大小纹理大小 - m_texture->Init(VecSize(Width, Height)); - m_rect = {0, 0, (float)Width, (float)Height}; - - Actor::Init(); - - glGenFramebuffers(1, &m_fbo); - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); - // 将目标纹理附加到FBO的颜色附着点 - glFramebufferTexture2D( - GL_FRAMEBUFFER, // 帧缓冲类型 - GL_COLOR_ATTACHMENT0, // 颜色附着点(可多个,这里用第一个) - GL_TEXTURE_2D, // 纹理类型 - m_texture->getID(), // 目标纹理ID - 0 // 多级渐远纹理级别 - ); - glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO for (size_t i = 0; i < normal.size(); i++) { InitTile(normal[i], i); } - m_tileX = normal.size(); for (size_t i = 0; i < ex.size(); i++) { InitExTile(ex[i], i); } - SetPos(Vec2(0, 80 - 200 - std::get(m_parentMap->_MapInfo["background_pos"]))); + // 有可能没写地板 + if (m_BasicTileWidth == 0) + m_BasicTileWidth = 224 * normal.size(); + + // 通过父对象获取地图大小 + m_parentMap->_MapLength = m_BasicTileWidth; + m_parentMap->_MapHeight = m_BasicTileHeight + m_ExTileHeight; + + Canvas::Init({(int)m_BasicTileWidth, (int)m_BasicTileHeight + (int)m_ExTileHeight + (int)m_tileY}); + DrawTile(); } Tile::~Tile() @@ -66,6 +39,9 @@ void Tile::InitTile(std::string Path, int Index) TileInfo data; InitInfo(Path, data); // 读取信息 + float ImgOffset = std::get(data["pos"]); + if (ImgOffset > m_tileY) + m_tileY = ImgOffset; std::string path = std::get(data["path"]); int index = std::get(data["idx"]); // 如果是个空地板 给他默认赋值 @@ -77,18 +53,20 @@ void Tile::InitTile(std::string Path, int Index) // 判断是否存在标识符对应的纹理,如果不存在则创建 if (m_imgMap.find(key) == m_imgMap.end()) { - RefPtr tex = new Texture(); - tex->Init(path, index); + RefPtr tex = new Sprite(path, index); m_imgMap[key] = tex; } - // 构造绘制信息 + // 横向地板数量增加 + m_Tile_X_Count++; + // 基础地板宽度增加 + m_BasicTileWidth += m_imgMap[key]->GetTexture()->getFrameSize().width; + // 取基础地板最高的高度 + if (m_imgMap[key]->GetTexture()->getFrameSize().height > m_BasicTileHeight) + m_BasicTileHeight = m_imgMap[key]->GetTexture()->getFrameSize().height; + DrawInfo info; - info.rect.x = Index * m_imgMap[key]->getSize().width + m_imgMap[key]->getPos().x; - info.rect.y = m_imgMap[key]->getPos().y; - info.rect.w = m_imgMap[key]->getSize().width; - info.rect.h = m_imgMap[key]->getSize().height; - info.texture = key; - // 添加到绘制信息列表 + info.sprite_ = key; + info.pos = {Index * 224, ImgOffset}; m_drawQueue.push_back(info); } @@ -98,6 +76,9 @@ void Tile::InitExTile(std::string Path, int Index) TileInfo data; InitInfo(Path, data); // 读取信息 + float ImgOffset = std::get(data["pos"]); + if (ImgOffset > m_tileY) + m_tileY = ImgOffset; std::string path = std::get(data["path"]); int index = std::get(data["idx"]); // 如果是个空地板 给他默认赋值 @@ -109,28 +90,23 @@ void Tile::InitExTile(std::string Path, int Index) // 判断是否存在标识符对应的纹理,如果不存在则创建 if (m_imgMap.find(key) == m_imgMap.end()) { - RefPtr tex = new Texture(); - tex->Init(path, index); + RefPtr tex = new Sprite(path, index); m_imgMap[key] = tex; } - // 构造绘制信息 + // 如果进了这个函数默认有1格 + if (m_ExTileHeight == 0) + m_ExTileHeight = 120; + // Ex地板高度120一格 + if (((Index / m_Tile_X_Count) * 120) > m_ExTileHeight) + m_ExTileHeight = ((Index / m_Tile_X_Count) * 120); + DrawInfo info; - info.rect.x = Index * m_imgMap[key]->getSize().width + m_imgMap[key]->getPos().x; - info.rect.y = m_imgMap[key]->getPos().y + 560 + ((Index / m_tileX) * 120); - info.rect.w = m_imgMap[key]->getSize().width; - info.rect.h = m_imgMap[key]->getSize().height; - info.texture = key; - // 添加到绘制信息列表 + info.sprite_ = key; + info.pos = {Index * 224, m_tileY + m_BasicTileHeight + ((Index / m_Tile_X_Count) * 120)}; m_drawQueue.push_back(info); } -// void Tile::SetPos(Vec2 pos) -// { -// // pos.y += std::get(m_data["pos"]); -// // Sprite::SetPos(pos); -// } - void Tile::InitInfo(std::string Path, TileInfo &m_data) { ScriptData Data = AssetManager::GetInstance().GetScriptInfo(Path); @@ -151,46 +127,13 @@ void Tile::InitInfo(std::string Path, TileInfo &m_data) } } -void Tile::PreRender() +void Tile::DrawTile() { - m_rect.x = GetWorldPos().x; - m_rect.y = GetWorldPos().y; - - //计算摄像机位置 知道自己需要渲染多大的区域 - // m_srcRect = {0, 0, m_parentMap->_MapLength, m_parentMap->_MapHeight}; - // Vec2 Pos = Global_Game::GetInstance().GetCamera() -> _currentPosition; - // m_srcRect = {Pos.x < 1067 ? 0 : Pos.x - 1067, Pos.y, 1067, 600}; -} - -void Tile::Render() -{ - RenderManager *renderer = Game::GetInstance().GetRenderer(); - SDL_FPoint AnchorPos = {0, 0}; - if (!m_isBuild) + BeginDraw(); + for (auto &info : m_drawQueue) { - // 保存原始的正交矩阵 设置纹理对应的正交矩阵 和 视口 - auto oldOm = Game::GetInstance().GetRenderer()->GetOrthoMatrix(); - Game::GetInstance().GetRenderer()->SetOrthoMatrix(glm::ortho(0.0f, (float)m_texture->getSize().width, (float)m_texture->getSize().height, 0.0f, -1.0f, 1.0f)); - glViewport(0, 0, m_texture->getSize().width, m_texture->getSize().height); - - glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); // 绑定FBO - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - for (auto &info : m_drawQueue) - { - renderer->DrawTexture(m_imgMap[info.texture], nullptr, &info.rect, 0.f, &AnchorPos, SDL_FLIP_NONE); - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑FBO - - // 还原原始的正交矩阵 和 视口 - Game::GetInstance().GetRenderer()->SetOrthoMatrix(oldOm); - glViewport(0, 0, Game::GetInstance().Screen_W, Game::GetInstance().Screen_H); - m_isBuild = true; + m_imgMap[info.sprite_]->SetPosition(info.pos); + DrawActor(m_imgMap[info.sprite_]); } - - renderer->SetCurrentShaderProgram("flip_y"); - - renderer->DrawTexture(m_texture, nullptr, &m_rect, 0.f, &AnchorPos, SDL_FLIP_NONE); - - renderer->SetCurrentShaderProgram("normal"); + EndDraw(); } diff --git a/source_game/Actor/Map/Tile.h b/source_game/Actor/Map/Tile.h index b2a1343..a45decd 100644 --- a/source_game/Actor/Map/Tile.h +++ b/source_game/Actor/Map/Tile.h @@ -1,9 +1,12 @@ #pragma once #include "Asset/AssetManager.h" #include "EngineFrame/Component/Canvas.h" +#include "EngineFrame/Component/Sprite.h" class GameMap; -class Tile : public Actor +class Tile : public Canvas { + +public: struct ImgKey { std::string img; @@ -20,46 +23,40 @@ class Tile : public Actor }; struct DrawInfo { - ImgKey texture; - SDL_FRect rect = {0, 0, 0, 0}; + ImgKey sprite_; + glm::vec2 pos; }; -public: - using TileInfoBody = std::variant; + using TileInfoBody = std::variant; using TileInfo = std::unordered_map; private: - /**纹理 */ - RefPtr m_texture = nullptr; - /**FBO */ - GLuint m_fbo = 0; - /**地图父对象 */ - GameMap* m_parentMap; - - /**绘制纹理集 */ - std::map> m_imgMap; + /**父对象地图 */ + GameMap *m_parentMap; + /**绘制精灵集 */ + std::map> m_imgMap; /**绘制队列 */ std::vector m_drawQueue; - /**渲染大小 */ - SDL_Rect m_srcRect; - SDL_FRect m_rect; - - /**大纹理构造完成Flag */ - bool m_isBuild = false; + /**基础地板宽度 */ + float m_BasicTileWidth = 0; + /**基础地板高度 */ + float m_BasicTileHeight = 0; + /**Ex地板高度 */ + float m_ExTileHeight = 0; - /**Tile横轴个数 */ - int m_tileX = 0; + /**横向地板数量 */ + int m_Tile_X_Count = 0; + /**纵向最大偏移 */ + float m_tileY = 0; public: - Tile(GameMap *parentMap, std::vector normal,std::vector ex); + Tile(GameMap *parentMap, std::vector normal, std::vector ex); ~Tile(); - void InitTile(std::string Path,int Index); + void InitTile(std::string Path, int Index); void InitExTile(std::string Path, int Index); void InitInfo(std::string Path, TileInfo &m_data); - void PreRender() override; - void Render() override; - // void SetPos(Vec2 pos) override; + void DrawTile(); }; diff --git a/source_game/Actor/Object/ActiveObject.cpp b/source_game/Actor/Object/ActiveObject.cpp index 26cfdd1..862cacc 100644 --- a/source_game/Actor/Object/ActiveObject.cpp +++ b/source_game/Actor/Object/ActiveObject.cpp @@ -19,7 +19,7 @@ VecSpeed3 ActiveObject::GetSpeed() return this->Speed; } -void ActiveObject::Update(float deltaTime) +void ActiveObject::OnUpdate(float deltaTime) { VecFPos3 MovePos; @@ -51,6 +51,4 @@ void ActiveObject::Update(float deltaTime) } MoveBy(MovePos); - - BaseObject::Update(deltaTime); } diff --git a/source_game/Actor/Object/ActiveObject.h b/source_game/Actor/Object/ActiveObject.h index b0022ca..472bbc2 100644 --- a/source_game/Actor/Object/ActiveObject.h +++ b/source_game/Actor/Object/ActiveObject.h @@ -11,13 +11,12 @@ public: int _zRemainderMove = 0; public: - void - SetPosition(VecFPos3 pos) override; + void SetPosition(VecFPos3 pos) override; void SetYpos(int y) override; void SetSpeed(VecSpeed3 speed); VecSpeed3 GetSpeed(); public: - void Update(float deltaTime) override; + void OnUpdate(float deltaTime) override; }; diff --git a/source_game/Actor/Object/BaseObject.cpp b/source_game/Actor/Object/BaseObject.cpp index 8b0d0d5..e32d8af 100644 --- a/source_game/Actor/Object/BaseObject.cpp +++ b/source_game/Actor/Object/BaseObject.cpp @@ -3,7 +3,7 @@ BaseObject::BaseObject() { - Init(); // 调用了RenderBase的Init函数 对象才会被执行回调 + // 对象要使用中心锚点 SetAnchor({0.5f, 0.5f}); } @@ -11,21 +11,16 @@ BaseObject::~BaseObject() { } -void BaseObject::Update(float deltaTime) -{ - Actor::Update(deltaTime); -} - void BaseObject::SetPosition(VecFPos3 pos) { if(pos == this->Position) return; if (pos.y != this->Position.y) { - SetRenderZOrder(pos.y); // 设置渲染顺序 + SetZOrder(pos.y); // 设置渲染顺序 } this->Position = pos; - SetPos(Vec2{this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition(this->Position.x, this->Position.y - this->Position.z); } VecFPos3 BaseObject::GetPosition() @@ -38,7 +33,7 @@ void BaseObject::SetXpos(int x) if (x == this->Position.x) return; this->Position.x = x; - SetPos({this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition(this->Position.x, this->Position.y - this->Position.z); } void BaseObject::SetYpos(int y) @@ -47,10 +42,10 @@ void BaseObject::SetYpos(int y) return; if (y != this->Position.y) { - SetRenderZOrder(y); // 设置渲染顺序 + SetZOrder(y); // 设置渲染顺序 } this->Position.y = y; - SetPos({this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition(this->Position.x, this->Position.y - this->Position.z); } void BaseObject::SetZpos(int z) @@ -58,7 +53,7 @@ void BaseObject::SetZpos(int z) if (z == this->Position.z) return; this->Position.z = z; - SetPos({this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition(this->Position.x, this->Position.y - this->Position.z); } int BaseObject::GetXpos() @@ -84,11 +79,11 @@ void BaseObject::MoveBy(VecFPos3 pos) return; if (RealPos.y != this->Position.y) { - SetRenderZOrder(RealPos.y); // 设置渲染顺序 + SetZOrder(RealPos.y); // 设置渲染顺序 } if (RealPos != this->Position){ this->Position = RealPos; - SetPos({this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition({this->Position.x, this->Position.y - this->Position.z}); } } @@ -100,28 +95,28 @@ void BaseObject::MoveBy(int x, int y, int z) return; if (RealPos.y != this->Position.y) { - SetRenderZOrder(RealPos.y); // 设置渲染顺序 + SetZOrder(RealPos.y); // 设置渲染顺序 } if (RealPos != this->Position) { this->Position = RealPos; - SetPos({this->Position.x, this->Position.y - this->Position.z}); + Actor::SetPosition({this->Position.x, this->Position.y - this->Position.z}); } } void BaseObject::SetDirection(int dir) { this->Direction = dir; - Vec2 sc = GetScale(); + glm::vec2 sc = GetScale(); // 朝右 if (dir == 0) { - SetScale(Vec2({SDL_fabsf(sc.x), sc.y})); + SetScale(SDL_fabsf(sc.x), sc.y); } // 朝左 else if (dir == 1) { - SetScale(Vec2({-SDL_fabsf(sc.x), sc.y})); + SetScale(-SDL_fabsf(sc.x), sc.y); } } diff --git a/source_game/Actor/Object/BaseObject.h b/source_game/Actor/Object/BaseObject.h index 8bd0cb0..c7e7cb2 100644 --- a/source_game/Actor/Object/BaseObject.h +++ b/source_game/Actor/Object/BaseObject.h @@ -16,8 +16,6 @@ public: BaseObject(/* args */); ~BaseObject(); - void Update(float deltaTime) override; - // 数据储存器 ObjectVars _ObjectVars; diff --git a/source_game/Actor/Object/CharacterObject.cpp b/source_game/Actor/Object/CharacterObject.cpp index 54384f6..50594de 100644 --- a/source_game/Actor/Object/CharacterObject.cpp +++ b/source_game/Actor/Object/CharacterObject.cpp @@ -1,5 +1,7 @@ #include "CharacterObject.h" #include "Asset/Squirrel/SquirrelManager.h" +#include "Actor/Map/GameMap.h" +#include "Actor/Map/GameWorld.h" CharacterObject::~CharacterObject() { @@ -12,9 +14,6 @@ void CharacterObject::Construction(int job) // 创建装备管理器 _EquipmentManager = new Chr_Equipment(); _EquipmentManager->Init(this); - // // 创建阴影对象 - // _Shadow = new Chr_Shadow(); - // _Shadow->Init(this); // 创建动画管理器(一定要先创建装备管理器再创建动画管理器 因为需要读取身上的装备) _AnimationManager = new Chr_Animation(); _AnimationManager->Init(this); @@ -51,19 +50,35 @@ void CharacterObject::ControllerMsg(CONTROLLER_MSG_TYPE msgType, void *msgData) } } -void CharacterObject::Update(float deltaTime) +void CharacterObject::SetPosition(VecFPos3 pos) { - ActiveObject::Update(deltaTime); -} - -void CharacterObject::SetPos(Vec2 pos) -{ - BaseObject::SetPos(pos); - if(_Shadow)_Shadow->SetPos(this->GetPos()); + BaseObject::SetPosition(pos); } void CharacterObject::SetDirection(int dir) { BaseObject::SetDirection(dir); - if(_Shadow)_Shadow->SetDirection(this->GetDirection()); +} + +void CharacterObject::OnUpdate(float deltaTime) +{ + ActiveObject::OnUpdate(deltaTime); + + // 判断是否要进行区域移动 + if (!IsTeleportArea) + { + GameMap::MapMoveArea Info = this->_AffMap->CheckIsItMoveArea(this->GetPosition()); + if (Info.town != -2 && Info.area != -2) + { + IsTeleportArea = true; + // 调用世界类移动自己 + GameWorld::GetWorld()->MoveCharacter(this, Info.town, Info.area); + } + } + else + { + GameMap::MapMoveArea Info = this->_AffMap->CheckIsItMoveArea(this->GetPosition()); + if (Info.town == -2 && Info.area == -2) + IsTeleportArea = false; + } } diff --git a/source_game/Actor/Object/CharacterObject.h b/source_game/Actor/Object/CharacterObject.h index e1f818c..e79b969 100644 --- a/source_game/Actor/Object/CharacterObject.h +++ b/source_game/Actor/Object/CharacterObject.h @@ -1,10 +1,10 @@ #pragma once +#include "EngineFrame/Base/Actor.h" #include "Actor/Object/ActiveObject.h" #include "Asset/Character/Chr_Animation.h" #include "Asset/Character/Chr_Equipment.h" #include "Asset/Character/Chr_Controller.h" #include "Asset/Character/Chr_StateMachine.h" -#include "Asset/Character/Chr_Shadow.h" #include "Global/Global_Enum.h" class CharacterObject : public ActiveObject { @@ -18,12 +18,12 @@ public: // 角色状态机 RefPtr _StateMachine = nullptr; - // 角色阴影对象 - RefPtr _Shadow = nullptr; // 职业 int Job = 0; // 转职职业 如果是-1则没有转职 int GrowType = -1; + /** 传送区域Flag */ + bool IsTeleportArea = false; public: ~CharacterObject(); @@ -39,7 +39,8 @@ public: // 控制器信息 void ControllerMsg(CONTROLLER_MSG_TYPE msgType, void* msgData); - void Update(float deltaTime) override; - void SetPos(Vec2 pos) override; + void SetPosition(VecFPos3 pos) override; void SetDirection(int dir) override; + + void OnUpdate(float deltaTime) override; }; diff --git a/source_game/Asset/Character/Chr_Animation.cpp b/source_game/Asset/Character/Chr_Animation.cpp index e53855c..2c4261f 100644 --- a/source_game/Asset/Character/Chr_Animation.cpp +++ b/source_game/Asset/Character/Chr_Animation.cpp @@ -22,21 +22,20 @@ void Chr_Animation::CreateSkinmationBySlot(std::string actionName, std::string s if (slotName == "skin_avatar") { // 如果穿戴了皮肤时装 读取variation - RefPtr Equip = ((CharacterObject *)m_Parent)->_EquipmentManager->GetEquip(slotName); + RefPtr Equip = ((CharacterObject *)GetParent())->_EquipmentManager->GetEquip(slotName); if (Equip) { Animation::ReplaceData Data(0, 0); + for (auto &Variation : Equip->JobAni[this->chr_parent->Job]) { Data.Param1 = Variation.ImgFormat[0]; Data.Param2 = Variation.ImgFormat[1]; - // 构造好Ani以后 统一设置为不可见 然后放入ActionAnis表 + // 构造Ani RefPtr Ani = new Animation(path, FormatImgPath, Data); - Ani->SetVisible(false); - - Ani->SetRenderZOrder(Variation.Layer); - this->AddChild(Ani); - ActionAnis[actionName].push_back(Ani); + Ani->SetZOrder(Variation.Layer); + // 将构造好的Ani 添加进AniMap + ActionAnis[actionName]->AddAnimation(Ani); // 构造一下阴影对象 //TODO // RefPtr ShadowAni = new Animation(path, FormatImgPath, Data); @@ -49,10 +48,9 @@ void Chr_Animation::CreateSkinmationBySlot(std::string actionName, std::string s } else { - RefPtr Equip = ((CharacterObject *)m_Parent)->_EquipmentManager->GetEquip(slotName); + RefPtr Equip = ((CharacterObject *)GetParent())->_EquipmentManager->GetEquip(slotName); if (Equip) { - // SDL_Log("CreateSkinmationBySlot %s", slotName.c_str()); Animation::ReplaceData Data(0, 0); for (auto &Variation : Equip->JobAni[this->chr_parent->Job]) { @@ -64,12 +62,11 @@ void Chr_Animation::CreateSkinmationBySlot(std::string actionName, std::string s // 组装动画路径 std::string AniPath = EquipPath + Variation.AnimationGroup + path.substr(path.find_last_of("/"), path.length()); - // 构造好Ani以后 统一设置为不可见 然后放入ActionAnis表 + // 构造Ani RefPtr Ani = new Animation(AniPath, FormatImgPath, Data); - Ani->SetVisible(false); - Ani->SetRenderZOrder(Variation.Layer); - this->AddChild(Ani); - ActionAnis[actionName].push_back(Ani); + Ani->SetZOrder(Variation.Layer); + // 将构造好的Ani 添加进AniMap + ActionAnis[actionName]->AddAnimation(Ani); } } } @@ -77,24 +74,30 @@ void Chr_Animation::CreateSkinmationBySlot(std::string actionName, std::string s void Chr_Animation::Init(CharacterObject *parent) { - // 调用RenderBase类的初始化 才能挂上标签 - RenderBase::Init(); parent->AddChild(this); chr_parent = parent; GlobalCharacterScript::CharacterConfig Config = Global_Game::GetInstance().CharacterConfigs[parent->Job]; - + // 遍历所有动作Ani路径 for (const auto &pair : Config.animationPath) { std::string ActionName = pair.first; std::string Path = std::string("character/" + Config.jobTag + "/" + Tool_toLowerCase(pair.second)); + //以动作为Key构造AniMap + ActionAnis[ActionName] = new AnimationMap(); + ActionAnis[ActionName]->SetVisible(false); + // 遍历所有时装部位构造Ani for (auto &AvatarPartPair : AvatarPart) { std::string AvatarPartName = AvatarPartPair.first; CreateSkinmationBySlot(ActionName, AvatarPartName, Path); } + + // 将所有时装部位的Ani添加到动作AniMap以后完成构造 + ActionAnis[ActionName]->CompleteConstruction(); + this->AddChild(ActionAnis[ActionName]); } // 设置初始动作 SetAction("rest"); @@ -103,18 +106,9 @@ void Chr_Animation::Init(CharacterObject *parent) void Chr_Animation::SetAction(std::string actionName) { // 先将原动作的Ani设置为不可见 - for (auto Ani : ActionAnis[CurrentActionTag]) - { - Ani->Reset(); - Ani->SetVisible(false); - } + ActionAnis[CurrentActionTag]->Reset(); + ActionAnis[CurrentActionTag]->SetVisible(false); // 再将新动作的Ani设置为可见 - for (auto Ani : ActionAnis[actionName]) - { - Ani->SetVisible(true); - } + ActionAnis[actionName]->SetVisible(true); CurrentActionTag = actionName; - - // 设置阴影的动作 - if(chr_parent->_Shadow)chr_parent->_Shadow->SetAction(actionName); } diff --git a/source_game/Asset/Character/Chr_Animation.h b/source_game/Asset/Character/Chr_Animation.h index 5fc170d..0e00cee 100644 --- a/source_game/Asset/Character/Chr_Animation.h +++ b/source_game/Asset/Character/Chr_Animation.h @@ -1,13 +1,14 @@ #pragma once -#include "EngineFrame/Component/Animation.h" +#include "EngineFrame/Base/Actor.h" +#include "EngineFrame/Component/AnimationMap.h" #include #include #include class CharacterObject; -class Chr_Animation : public RenderBase +class Chr_Animation : public Actor { // 动作动画集合 - using ActionAniList = std::map>>; + using ActionAniList = std::map>; public: inline static const std::unordered_map diff --git a/source_game/Asset/Character/Chr_Controller.cpp b/source_game/Asset/Character/Chr_Controller.cpp index 7286366..c11bcc1 100644 --- a/source_game/Asset/Character/Chr_Controller.cpp +++ b/source_game/Asset/Character/Chr_Controller.cpp @@ -28,7 +28,6 @@ float Chr_Controller::ConvertAxisValue(Sint16 rawValue) void Chr_Controller::Init(CharacterObject *pCharacter) { - addTag(Tag::HANDEL_EVENT); m_pCharacter = pCharacter; } diff --git a/source_game/Asset/Character/Chr_Controller.h b/source_game/Asset/Character/Chr_Controller.h index 2542798..95fab82 100644 --- a/source_game/Asset/Character/Chr_Controller.h +++ b/source_game/Asset/Character/Chr_Controller.h @@ -1,8 +1,8 @@ #pragma once -#include "EngineFrame/Base/BaseNode.h" +#include "EngineFrame/Base/Node.h" #include "Tool/Common.h" class CharacterObject; -class Chr_Controller : public BaseNode +class Chr_Controller : public Node { private: CharacterObject *m_pCharacter = nullptr; diff --git a/source_game/Asset/Character/Chr_Shadow.cpp b/source_game/Asset/Character/Chr_Shadow.cpp deleted file mode 100644 index d6cfd05..0000000 --- a/source_game/Asset/Character/Chr_Shadow.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "Chr_Shadow.h" -#include "Global/Global_Game.h" -#include "Actor/Object/CharacterObject.h" -#include "Asset/Character/Chr_Equipment.h" - -void Chr_Shadow::Init(CharacterObject *parent) -{ - // 调用RenderBase类的初始化 才能挂上标签 - RenderBase::Init(); - chr_parent = parent; - SetRenderZOrder(1000000); - SetPos(parent->GetPos()); -} - -void Chr_Shadow::SetAction(std::string actionName) -{ - // 先将原动作的Ani设置为不可见 - for (auto Ani : ActionAnis[CurrentActionTag]) - { - Ani->Reset(); - Ani->SetVisible(false); - } - // 再将新动作的Ani设置为可见 - for (auto Ani : ActionAnis[actionName]) - { - Ani->SetVisible(true); - } - CurrentActionTag = actionName; -} - -void Chr_Shadow::SetDirection(int dir) -{ - Vec2 sc = GetScale(); - // 朝右 - if (dir == 0) - { - SetScale(Vec2({SDL_fabsf(sc.x), sc.y})); - } - // 朝左 - else if (dir == 1) - { - SetScale(Vec2({-SDL_fabsf(sc.x), sc.y})); - } -} diff --git a/source_game/Asset/Character/Chr_Shadow.h b/source_game/Asset/Character/Chr_Shadow.h deleted file mode 100644 index 65fcfba..0000000 --- a/source_game/Asset/Character/Chr_Shadow.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "EngineFrame/Component/Animation.h" -#include -#include -#include -class CharacterObject; -class Chr_Shadow : public RenderBase -{ - // 动作动画集合 - using ActionAniList = std::map>>; - -private: - /* data */ -public: - // 父对象 - CharacterObject *chr_parent; - // 时装部位对应的动作动画集合 - ActionAniList ActionAnis; - // 当前动作Tag - std::string CurrentActionTag = "waiting"; - - // 初始化时 - void Init(CharacterObject *parent); - // 设置动作 - void SetAction(std::string actionName); - - // 设置方向 - void SetDirection(int dir); -}; diff --git a/source_game/Asset/Character/Chr_StateMachine.cpp b/source_game/Asset/Character/Chr_StateMachine.cpp index 625bfdb..ae98bd4 100644 --- a/source_game/Asset/Character/Chr_StateMachine.cpp +++ b/source_game/Asset/Character/Chr_StateMachine.cpp @@ -5,7 +5,6 @@ void Chr_StateMachine::Init(CharacterObject *parent) { chr_Parent = parent; - addTag(Tag::UPDATE); parent->AddChild(this); } void Chr_StateMachine::ChangeState(int state) diff --git a/source_game/Asset/Character/Chr_StateMachine.h b/source_game/Asset/Character/Chr_StateMachine.h index b6ae9d4..59f87b2 100644 --- a/source_game/Asset/Character/Chr_StateMachine.h +++ b/source_game/Asset/Character/Chr_StateMachine.h @@ -1,8 +1,8 @@ #pragma once #include "Global/Global_Enum.h" -#include "EngineFrame/Base/Actor.h" +#include "EngineFrame/Base/Node.h" class CharacterObject; -class Chr_StateMachine : public Actor +class Chr_StateMachine : public Node { private: // 父对象 diff --git a/source_game/Asset/Monster/Mon_Animation.cpp b/source_game/Asset/Monster/Mon_Animation.cpp index 83af2d8..4234268 100644 --- a/source_game/Asset/Monster/Mon_Animation.cpp +++ b/source_game/Asset/Monster/Mon_Animation.cpp @@ -4,7 +4,6 @@ void Mon_Animation::Init(MonsterObject *parent) { // 调用RenderBase类的初始化 才能挂上标签 - RenderBase::Init(); parent->AddChild(this); mon_parent = parent; diff --git a/source_game/Asset/Monster/Mon_Animation.h b/source_game/Asset/Monster/Mon_Animation.h index 3d1760a..aaf5ad7 100644 --- a/source_game/Asset/Monster/Mon_Animation.h +++ b/source_game/Asset/Monster/Mon_Animation.h @@ -4,7 +4,7 @@ #include #include class MonsterObject; -class Mon_Animation : public RenderBase +class Mon_Animation : public Actor { // 动作动画集合 using ActionAniList = std::map>; diff --git a/source_game/Asset/Squirrel/Sqr_UI.hpp b/source_game/Asset/Squirrel/Sqr_UI.hpp index 2355423..9c4d72a 100644 --- a/source_game/Asset/Squirrel/Sqr_UI.hpp +++ b/source_game/Asset/Squirrel/Sqr_UI.hpp @@ -78,7 +78,7 @@ static SQInteger SQR_GetZOrder(HSQUIRRELVM v) SQUserPointer A_obj; sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - sq_pushinteger(v, Aobj->GetRenderZOrder()); + sq_pushinteger(v, Aobj->GetZOrder()); return 1; } @@ -89,7 +89,7 @@ static SQInteger SQR_SetZOrder(HSQUIRRELVM v) SQInteger Value; sq_getinteger(v, 3, &Value); Actor *Aobj = (Actor *)A_obj; - Aobj->SetRenderZOrder(Value); + Aobj->SetZOrder(Value); return 0; } @@ -98,7 +98,7 @@ static SQInteger SQR_GetPos(HSQUIRRELVM v) SQUserPointer A_obj; sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - Vec2 Pos = Aobj->GetPos(); + glm::vec2 Pos = Aobj->GetPosition(); sq_newtable(v); sq_pushstring(v, _SC("x"), -1); @@ -117,7 +117,7 @@ static SQInteger SQR_SetPos(HSQUIRRELVM v) if (sq_gettop(v) == 3) { - Vec2 Pos; + glm::vec2 Pos; sq_pushnull(v); // null iterator while (SQ_SUCCEEDED(sq_next(v, 3))) { @@ -139,7 +139,7 @@ static SQInteger SQR_SetPos(HSQUIRRELVM v) sq_pop(v, 1); Actor *Aobj = (Actor *)A_obj; - Aobj->SetPos(Pos); + Aobj->SetPosition(Pos); } else if (sq_gettop(v) == 4) { @@ -147,7 +147,7 @@ static SQInteger SQR_SetPos(HSQUIRRELVM v) sq_getfloat(v, 3, &X); sq_getfloat(v, 4, &Y); Actor *Aobj = (Actor *)A_obj; - Aobj->SetPos(Vec2(X, Y)); + Aobj->SetPosition(glm::vec2(X, Y)); } return 0; } @@ -157,7 +157,7 @@ static SQInteger SQR_GetWorldPos(HSQUIRRELVM v) SQUserPointer A_obj; sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - Vec2 Pos = Aobj->GetWorldPos(); + glm::vec2 Pos = Aobj->ConvertToWorld(glm::vec2(0, 0)); sq_newtable(v); sq_pushstring(v, _SC("x"), -1); @@ -175,7 +175,7 @@ static SQInteger SQR_GetAlpha(HSQUIRRELVM v) sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - sq_pushfloat(v, Aobj->GetAlpha()); + sq_pushfloat(v, Aobj->GetOpacity()); return 1; } static SQInteger SQR_SetAlpha(HSQUIRRELVM v) @@ -185,7 +185,7 @@ static SQInteger SQR_SetAlpha(HSQUIRRELVM v) SQFloat Value; sq_getfloat(v, 3, &Value); Actor *Aobj = (Actor *)A_obj; - Aobj->SetAlpha(Value); + Aobj->SetOpacity(Value); return 0; } @@ -194,7 +194,7 @@ static SQInteger SQR_GetScale(HSQUIRRELVM v) SQUserPointer A_obj; sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - Vec2 Pos = Aobj->GetScale(); + glm::vec2 Pos = Aobj->GetScale(); sq_newtable(v); sq_pushstring(v, _SC("x"), -1); sq_pushfloat(v, Pos.x); @@ -212,7 +212,7 @@ static SQInteger SQR_SetScale(HSQUIRRELVM v) if (sq_gettop(v) == 3) { - Vec2 Pos; + glm::vec2 Pos; sq_pushnull(v); // null iterator while (SQ_SUCCEEDED(sq_next(v, 3))) { @@ -242,7 +242,7 @@ static SQInteger SQR_SetScale(HSQUIRRELVM v) sq_getfloat(v, 3, &X); sq_getfloat(v, 4, &Y); Actor *Aobj = (Actor *)A_obj; - Aobj->SetScale(Vec2(X, Y)); + Aobj->SetScale(X, Y); } return 0; } @@ -342,7 +342,7 @@ static SQInteger SQR_GetVisible(HSQUIRRELVM v) SQUserPointer A_obj; sq_getuserpointer(v, 2, &A_obj); Actor *Aobj = (Actor *)A_obj; - sq_pushbool(v, Aobj->GetVisible()); + sq_pushbool(v, Aobj->IsVisible()); return 1; } @@ -391,7 +391,7 @@ static SQInteger SQR_Canvas_DrawImg(HSQUIRRELVM v) sq_getinteger(v, 4, &Idx); sq_GetVec2(v, 5, &Pos); Canvas *Aobj = (Canvas *)A_obj; - Aobj->DrawImg(ImgPath, Idx, Pos); + // Aobj->DrawImg(ImgPath, Idx, Pos); return 0; } @@ -406,7 +406,7 @@ static SQInteger SQR_Canvas_DrawImgRect(HSQUIRRELVM v) sq_getinteger(v, 4, &Idx); sq_GetFRect(v, 5, &Rect); Canvas *Aobj = (Canvas *)A_obj; - Aobj->DrawImg(ImgPath, Idx, Rect); + // Aobj->DrawImg(ImgPath, Idx, Rect); return 0; } diff --git a/source_game/Global/Global_Game.cpp b/source_game/Global/Global_Game.cpp index d42190a..11dd6e3 100644 --- a/source_game/Global/Global_Game.cpp +++ b/source_game/Global/Global_Game.cpp @@ -72,9 +72,12 @@ void Global_Game::InitGame() EquipmentPathMap = GlobalEquipmentScript::InitEquipmentLst(); // 读取怪物配置文件 MonsterPathMap = GlobalMonsterScript::InitMonsterLst(); + // 读取城镇配置文件 + TownPathMap = GlobalTownScript::InitTownLst(); // 初始化松鼠脚本管理器 - SquirrelManager::GetInstance().Init(); + SquirrelManager::GetInstance() + .Init(); // 读取存档 SavaManager::GetInstance().Init(); @@ -105,6 +108,30 @@ GlobalMonsterScript::MonsterConfig Global_Game::GetMonsterInfo(int id) return MonsterConfig; } +const std::map &Global_Game::GetTownMap() const +{ + return TownPathMap; +} + +GlobalTownScript::TownConfig *Global_Game::GetTownInfo(int id) +{ + if (TownInfoMap.count(id)) + return TownInfoMap[id]; + if (!TownPathMap.count(id)) + { + SDL_LogError(0, "城镇ID不存在"); + return nullptr; + } + std::string path = TownPathMap[id]; + GlobalTownScript::TownConfig *TownConfig = GlobalTownScript::GetTownConfig(path); + if (TownConfig) + { + TownInfoMap[id] = TownConfig; + return TownConfig; + } + return nullptr; +} + RefPtr Global_Game::GetCamera() { return _GameCamera; diff --git a/source_game/Global/Global_Game.h b/source_game/Global/Global_Game.h index d219ebc..02148e5 100644 --- a/source_game/Global/Global_Game.h +++ b/source_game/Global/Global_Game.h @@ -3,6 +3,7 @@ #include "Global/Script/CharacterConfig.h" #include "Global/Script/EquipmentConfig.h" #include "Global/Script/MonsterConfig.h" +#include "Global/Script/TownConfig.h" #include "Global/Save/SavaManager.h" #include "Global/GameCamera.h" class Global_Game @@ -43,6 +44,12 @@ public: std::map MonsterInfoMap; // 信息 GlobalMonsterScript::MonsterConfig GetMonsterInfo(int id); // 获取怪物信息 + // 城镇相关 + std::map TownPathMap; // 路径 + std::map TownInfoMap; // 信息 + const std::map& GetTownMap() const; // 获取城镇路径 + GlobalTownScript::TownConfig *GetTownInfo(int id); // 获取城镇信息 + // 游戏摄像机 RefPtr _GameCamera = nullptr; diff --git a/source_game/Global/Script/TownConfig.cpp b/source_game/Global/Script/TownConfig.cpp new file mode 100644 index 0000000..c9e6314 --- /dev/null +++ b/source_game/Global/Script/TownConfig.cpp @@ -0,0 +1,58 @@ +#include "TownConfig.h" +#include "Asset/AssetManager.h" +#include "Tool/Tool_String.h" +namespace GlobalTownScript +{ + std::map InitTownLst() + { + std::map TownList; + ScriptData Data = AssetManager::GetInstance().GetScriptInfo("town/town.lst"); + while (!Data.IsEnd()) + { + std::string Index = Data.Get(); + std::string MonsterPath = std::string("town/") + Tool_toLowerCase(Data.Get()); + TownList[std::stoi(Index)] = MonsterPath; + } + return TownList; + } + + TownConfig *GetTownConfig(std::string TownPath) + { + TownConfig *Config = new TownConfig(); + ScriptData Data = AssetManager::GetInstance().GetScriptInfo(TownPath); + while (!Data.IsEnd()) + { + std::string Segment = Data.Get(); + + if (Segment == "[entering title]") + { + Config->Entering_Title = "sprite/" + Tool_toLowerCase(Data.Get()); + } + else if (Segment == "[cutscene image]") + { + Config->Entering_Cutscene = "sprite/" + Tool_toLowerCase(Data.Get()); + } + else if (Segment == "[area]") + { + int Index = std::stoi(Data.Get()); + std::string MapPath = "map/" + Tool_toLowerCase(Data.Get()); + std::string TypeBuffer = Data.Get(); + std::string Type = TypeBuffer.substr(1, TypeBuffer.size() - 2); + int GenerateXPos = -1; + int GenerateYPos = -1; + + if (Type == "gate") + { + GenerateXPos = std::stoi(Data.Get()); + GenerateYPos = std::stoi(Data.Get()); + } + Config->AreaLst[Index] = AreaConfig{MapPath, Type, GenerateXPos, GenerateYPos}; + } + else if (Segment == "[name]") + { + Config->TownName = Data.Get(); + } + } + return Config; + } +} \ No newline at end of file diff --git a/source_game/Global/Script/TownConfig.h b/source_game/Global/Script/TownConfig.h new file mode 100644 index 0000000..a2ac344 --- /dev/null +++ b/source_game/Global/Script/TownConfig.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +namespace GlobalTownScript +{ + struct AreaConfig + { + /**地图路径 */ + std::string MapPath; + /**地图类型 */ + std::string MapType; + /**生成坐标X */ + int GenerateXPos = 0; + /**生成坐标Y */ + int GenerateYPos = 0; + }; + + /**城镇配置 */ + struct TownConfig + { + /**城镇名称 */ + std::string TownName; + /**进入城镇时的标题Img */ + std::string Entering_Title; + /**进入城镇时的过场Img */ + std::string Entering_Cutscene; + /**需要的任务进入条件 */ + int NeedQuestID = -1; + /**需要的等级进入条件 */ + int NeedLevel = -1; + /**区域 */ + std::map AreaLst; + }; + + // 读取城镇列表 + std::map InitTownLst(); + + // 读取城镇配置 + TownConfig *GetTownConfig(std::string TownPath); +} diff --git a/source_game/Scene/Scene_Loading_UI.cpp b/source_game/Scene/Scene_Loading_UI.cpp index d8cc7d2..8a30589 100644 --- a/source_game/Scene/Scene_Loading_UI.cpp +++ b/source_game/Scene/Scene_Loading_UI.cpp @@ -1,6 +1,6 @@ #include "Scene/Scene_Loading_UI.h" #include "Scene/Scene_MainUi.h" -#include "Scene/Scene_Test.h" +#include "Actor/Map/GameWorld.h" #include "Scene_Loading_UI.h" #include "EngineFrame/Component/Sprite.h" #include "EngineFrame/Component/Text.h" @@ -15,6 +15,10 @@ void Scene_Loading_UI::Enter() { Mix_PlayMusic(music, 1); } + else + { + printf("Mix_LoadMUS: %s\n", Mix_GetError()); + } RefPtr actor = new Actor; AddChild(actor); @@ -22,19 +26,19 @@ void Scene_Loading_UI::Enter() RefPtr BackGroundSp = new Sprite("ImagePacks2/Loading1.png"); actor->AddChild(BackGroundSp); RefPtr BackGround2Sp = new Sprite("ImagePacks2/Loading0.png"); - BackGround2Sp->SetPos(Vec2{0, 686}); + BackGround2Sp->SetPosition(0, 686); actor->AddChild(BackGround2Sp); RefPtr LoadCircleSp = new Sprite("ImagePacks2/Loading2.png"); LoadCircleSp->SetName("LoadCircle"); - LoadCircleSp->SetPos(Vec2{1280 - 60, 686 - 60}); + LoadCircleSp->SetPosition(1280 - 60, 686 - 60); LoadCircleSp->SetBlendMode(LINEARDODGE); - LoadCircleSp->SetAnchor(Vec2{0.5f, 0.5f}); + LoadCircleSp->SetAnchor(0.5f, 0.5f); actor->AddChild(LoadCircleSp); actor->SetCallbackOnUpdate("Rotate", [LoadCircleSp](float deltaTime) mutable - { + { float angle = LoadCircleSp->GetRotation(); - LoadCircleSp->SetRotation(angle + 180.0f * deltaTime); }); + LoadCircleSp->SetRotation(angle - 180.0f * deltaTime); }); } void Scene_Loading_UI::Update(float deltaTime) @@ -43,7 +47,7 @@ void Scene_Loading_UI::Update(float deltaTime) if (Global_Game::GetInstance().InitFlag) { // 设定游戏层场景 - RefPtr scene = new Scene_Test; + RefPtr scene = new GameWorld; Game::GetInstance().ChangeScene(scene); // 设定UI层场景 RefPtr sceneUI = new Scene_MainUi; diff --git a/source_game/Scene/Scene_Test.cpp b/source_game/Scene/Scene_Test.cpp deleted file mode 100644 index 7ced874..0000000 --- a/source_game/Scene/Scene_Test.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include "Scene_Test.h" -#include -#include "EngineCore/Game.h" -#include "EngineFrame/Component/AnimationManager.h" -#include "EngineFrame/Component/Text.h" -#include "EngineFrame/Component/Canvas.h" -#include "Global/Global_Game.h" -Scene_Test::Scene_Test() -{ -} - -Scene_Test::~Scene_Test() -{ - SDL_Log("Scene_Test::我的ID是%d --- 我被释放了!", this->MyId); -} - -void Scene_Test::Enter() -{ - map = new GameMap; - map->LoadMap("map/cataclysm/town/elvengard/new_elvengard.map"); - map->Enter(this); - AddChild(map); - map->SetCallbackOnUpdate("csas", [this](float dt) - { - Vec2 pos = map->GetPos(); - pos.x -= 10 * dt; - map->SetPos(pos); }); - - // RefPtr obj = new CharacterObject(); - // obj->SetPosition({1000, 300, 0}); - // obj->Construction(0); - // map->AddObject(obj); - - // RefPtr monster = new MonsterObject(); - // monster->SetPosition({1200, 301, 0}); - // monster->Construction(1); - // monster->SetDirection(1); - // map->AddObject(monster); - - return; - - // // SDL_Log("Scene_Test::进入测试场景!"); - RefPtr actor = new Actor; - AddChild(actor); - - // RefPtr sprite = new Sprite("ImagePacks2/test_white_background.png"); - - RefPtr chr = new Sprite("sprite/interface2/hud/hud.img", 0); - chr->SetPos(Vec2{200, 100}); - // chr->SetAlpha(0.5); - // chr->SetShadow(); - // actor->AddComponent(chr); - - RefPtr canvas = new Canvas(VecSize{250, 720}); - actor->AddChild(canvas); - canvas->DrawImg("sprite/interface2/hud/hud.img", 0, Vec2{0, 0}); - - // canvas->AddChild(chr); - - // RefPtr Am = new AnimationManager; - // RefPtr ani4 = new Animation("map/cataclysm/town/hendonmyre/animation/object/gateall_02.ani"); - // Am->AddAnimation(ani4); - - // // Am->SetPos(Vec2{200, 100}); - // actor->AddChild(Am); - - // // sprite2->UnsetClipRect(); - - // RefPtr actor2 = new Actor; - // actor->AddChild(actor2); - - // RefPtr ani4 = new Animation("map/cataclysm/town/hendonmyre/animation/object/gateall_02.ani"); - // // ani4->SetScale(Vec2{-1.0, 1.0}); - // actor->AddChild(ani4); - - // RefPtr ani3 = new Animation("map/cataclysm/town/elvengard/animation/obj/serialight01_right.ani"); - // actor->AddChild(ani3); - - // RefPtr ani4 = new Animation("map/cataclysm/town/hendonmyre/animation/object/gateall_02.ani"); - // ani4->SetPos(VecPos{300, 500}); - // actor->AddChild(ani4); - - // for (size_t i = 0; i < 400; i++) - // { - // RefPtr actor = new Actor; - // AddChild(actor); - - // // RefPtr ani3 = new Animation("common/commoneffect/animation/priestslowheal1.ani"); - // // actor->AddComponent(ani3); - // // ani3->SetRenderZOrder(1000); - - // // RefPtr ani = new Animation("common/anton/main.ani"); - // // actor->AddComponent(ani); - // // ani->SetRenderZOrder(500); - - // RefPtr sprite = new Sprite("sprite/item/avatar/swordman/0sm_acap.img", 0); - // actor->AddComponent(sprite); - // // RefPtr ani2 = new Animation("common/anton/face/0/0.ani"); - // // actor->AddComponent(ani2); - // // ani2->SetRenderZOrder(1000); - // } -} - -void Scene_Test::HandleEvents(SDL_Event *e) -{ - Scene::HandleEvents(e); -} - -void Scene_Test::Update(float deltaTime) -{ - Scene::Update(deltaTime); -} - -void Scene_Test::Render() -{ - Scene::Render(); -} - -void Scene_Test::Exit() -{ - SDL_Log("Scene_Test::退出测试场景!当前引用计数%d", this->GetRefCount()); -} diff --git a/source_game/Scene/Scene_Test.h b/source_game/Scene/Scene_Test.h deleted file mode 100644 index f007493..0000000 --- a/source_game/Scene/Scene_Test.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "EngineFrame/Scene/Scene.h" -#include "Asset/AssetManager.h" -#include "EngineFrame/Component/Animation.h" -#include "Actor/Map/GameMap.h" -#include "Actor/Object/CharacterObject.h" -#include "Actor/Object/MonsterObject.h" - -class Scene_Test : public Scene -{ -private: - GameMap *map = nullptr; - -public: - Scene_Test(/* args */); - ~Scene_Test(); - -public: - void Enter() override; - void HandleEvents(SDL_Event *e) override; - void Update(float deltaTime) override; - void Render() override; - void Exit() override; - -public: - int MyId = 0; - -}; diff --git a/source_game/main.cpp b/source_game/main.cpp index 26ab642..5b07cff 100644 --- a/source_game/main.cpp +++ b/source_game/main.cpp @@ -16,6 +16,7 @@ #endif #include "Asset/Asset_ImagePack.h" +#include "Asset/Asset_SoundPack.h" #include "Asset/Asset_Script.h" #include "squirrel/SquirrelEx.h" #include "Tool/Logger.h" @@ -56,7 +57,7 @@ int main(int argc, char *argv[]) socketInitializeDefault(); nxlinkStdio(); // 启用nxlink调试输出 curl_global_init(CURL_GLOBAL_DEFAULT); // 初始化libcurl - chdir("/switch/Lenheart/"); + chdir("/switch/DNF/"); #else AllocConsole(); SetConsoleTitleA("Yosin & Engine"); @@ -65,8 +66,12 @@ int main(int argc, char *argv[]) // 初始化Image资源系统 Asset_ImagePack::GetInstance(); + // 初始化音频资源系统 + Asset_SoundPack::GetInstance(); + // 初始化脚本资源系统 Asset_Script::GetInstance(); + // 初始化线程池 ThreadPool::GetInstance(); // 初始化日志系统