119 lines
4.5 KiB
C++
119 lines
4.5 KiB
C++
#include "level_loader.h"
|
||||
|
|
#include "../component/parallax_component.h"
|
|||
|
|
#include "../component/transform_component.h"
|
|||
|
|
#include "../object/game_object.h"
|
|||
|
|
#include "../scene/scene.h"
|
|||
|
|
#include "../core/context.h"
|
|||
|
|
#include "../render/sprite.h"
|
|||
|
|
#include <nlohmann/json.hpp>
|
|||
|
|
#include <fstream>
|
|||
|
|
#include <spdlog/spdlog.h>
|
|||
|
|
#include <glm/vec2.hpp>
|
|||
|
|
#include <filesystem>
|
|||
|
|
|
|||
|
|
namespace engine::scene {
|
|||
|
|
|
|||
|
|
bool LevelLoader::loadLevel(const std::string& level_path, Scene& scene) {
|
|||
|
|
map_path_ = level_path;
|
|||
|
|
// 1. 加载 JSON 文件
|
|||
|
|
std::ifstream file(level_path);
|
|||
|
|
if (!file.is_open()) {
|
|||
|
|
spdlog::error("无法打开关卡文件: {}", level_path);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 解析 JSON 数据
|
|||
|
|
nlohmann::json json_data;
|
|||
|
|
try {
|
|||
|
|
file >> json_data;
|
|||
|
|
} catch (const nlohmann::json::parse_error& e) {
|
|||
|
|
spdlog::error("解析 JSON 数据失败: {}", e.what());
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 加载图层数据
|
|||
|
|
if (!json_data.contains("layers") || !json_data["layers"].is_array()) { // 地图文件中必须有 layers 数组
|
|||
|
|
spdlog::error("地图文件 '{}' 中缺少或无效的 'layers' 数组。", level_path);
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
for (const auto& layer_json : json_data["layers"]) {
|
|||
|
|
// 获取各图层对象中的类型(type)字段
|
|||
|
|
std::string layer_type = layer_json.value("type", "none");
|
|||
|
|
if (!layer_json.value("visible", true)) {
|
|||
|
|
spdlog::info("图层 '{}' 不可见,跳过加载。", layer_json.value("name", "Unnamed"));
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据图层类型决定加载方法
|
|||
|
|
if (layer_type == "imagelayer") {
|
|||
|
|
loadImageLayer(layer_json, scene);
|
|||
|
|
} else if (layer_type == "tilelayer") {
|
|||
|
|
loadTileLayer(layer_json, scene);
|
|||
|
|
} else if (layer_type == "objectgroup") {
|
|||
|
|
loadObjectLayer(layer_json, scene);
|
|||
|
|
} else {
|
|||
|
|
spdlog::warn("不支持的图层类型: {}", layer_type);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
spdlog::info("关卡加载完成: {}", level_path);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LevelLoader::loadImageLayer(const nlohmann::json& layer_json, Scene& scene) {
|
|||
|
|
// 获取纹理相对路径 (会自动处理'\/'符号)
|
|||
|
|
const std::string& image_path = layer_json.value("image", "");
|
|||
|
|
if (image_path.empty()) {
|
|||
|
|
spdlog::error("图层 '{}' 缺少 'image' 属性。", layer_json.value("name", "Unnamed"));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
auto texture_id = resolvePath(image_path);
|
|||
|
|
|
|||
|
|
// 获取图层偏移量(json中没有则代表未设置,给默认值即可)
|
|||
|
|
const glm::vec2 offset = glm::vec2(layer_json.value("offsetx", 0.0f), layer_json.value("offsety", 0.0f));
|
|||
|
|
|
|||
|
|
// 获取视差因子及重复标志
|
|||
|
|
const glm::vec2 scroll_factor = glm::vec2(layer_json.value("parallaxx", 1.0f), layer_json.value("parallaxy", 1.0f));
|
|||
|
|
const glm::bvec2 repeat = glm::bvec2(layer_json.value("repeatx", false), layer_json.value("repeaty", false));
|
|||
|
|
|
|||
|
|
// 获取图层名称
|
|||
|
|
const std::string& layer_name = layer_json.value("name", "Unnamed");
|
|||
|
|
|
|||
|
|
/* 可用类似方法获取其它各种属性,这里我们暂时用不上 */
|
|||
|
|
|
|||
|
|
// 创建游戏对象
|
|||
|
|
auto game_object = std::make_unique<engine::object::GameObject>(layer_name);
|
|||
|
|
// 依次添加Transform,Parallax组件
|
|||
|
|
game_object->addComponent<engine::component::TransformComponent>(offset);
|
|||
|
|
game_object->addComponent<engine::component::ParallaxComponent>(texture_id, scroll_factor, repeat);
|
|||
|
|
// 添加到场景中
|
|||
|
|
scene.addGameObject(std::move(game_object));
|
|||
|
|
spdlog::info("加载图层: '{}' 完成", layer_name);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LevelLoader::loadTileLayer(const nlohmann::json&, Scene&)
|
|||
|
|
{
|
|||
|
|
// TODO
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void LevelLoader::loadObjectLayer(const nlohmann::json&, Scene&)
|
|||
|
|
{
|
|||
|
|
// TODO
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
std::string LevelLoader::resolvePath(std::string image_path)
|
|||
|
|
{
|
|||
|
|
try {
|
|||
|
|
// 获取地图文件的父目录(相对于可执行文件) “assets/maps/level1.tmj” -> “assets/maps”
|
|||
|
|
auto map_dir = std::filesystem::path(map_path_).parent_path();
|
|||
|
|
// 合并路径(相对于可执行文件)并返回。 /* std::filesystem::canonical:解析路径中的当前目录(.)和上级目录(..)导航符,
|
|||
|
|
/* 得到一个干净的路径 */
|
|||
|
|
auto final_path = std::filesystem::canonical(map_dir / image_path);
|
|||
|
|
return final_path.string();
|
|||
|
|
} catch (const std::exception& e) {
|
|||
|
|
spdlog::error("解析路径失败: {}", e.what());
|
|||
|
|
return image_path;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} // namespace engine::scene
|