Files
SunnyLand/src/engine/scene/level_loader.cpp
T
2025-06-16 14:44:47 +08:00

119 lines
4.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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