完成关卡加载器-图片层
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#include "parallax_component.h"
|
||||
#include "transform_component.h"
|
||||
#include "../render/renderer.h"
|
||||
#include "../render/camera.h"
|
||||
#include "../render/sprite.h"
|
||||
#include "../object/game_object.h"
|
||||
#include "../core/context.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace engine::component {
|
||||
|
||||
ParallaxComponent::ParallaxComponent(const std::string& texture_id, const glm::vec2& scroll_factor, const glm::bvec2& repeat)
|
||||
: sprite_(engine::render::Sprite(texture_id)), // 视差背景默认为整张图片
|
||||
scroll_factor_(scroll_factor),
|
||||
repeat_(repeat)
|
||||
{
|
||||
spdlog::trace("ParallaxComponent 初始化完成,纹理 ID: {}", texture_id);
|
||||
}
|
||||
|
||||
void ParallaxComponent::init() {
|
||||
if (!owner_) {
|
||||
spdlog::error("ParallaxComponent 初始化时,GameObject 为空。");
|
||||
return;
|
||||
}
|
||||
transform_ = owner_->getComponent<TransformComponent>();
|
||||
if (!transform_) {
|
||||
spdlog::error("ParallaxComponent 初始化时,GameObject 上没有找到 TransformComponent 组件。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ParallaxComponent::render(engine::core::Context& context) {
|
||||
if (is_hidden_ || !transform_) {
|
||||
return;
|
||||
}
|
||||
// 直接调用视差滚动绘制函数
|
||||
context.getRenderer().drawParallax(context.getCamera(), sprite_, transform_->getPosition(), scroll_factor_, repeat_, transform_->getScale());
|
||||
}
|
||||
|
||||
} // namespace engine::component
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "component.h"
|
||||
#include "../render/sprite.h"
|
||||
#include <string>
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace engine::component {
|
||||
class TransformComponent;
|
||||
|
||||
/**
|
||||
* @brief 在背景中渲染可滚动纹理的组件,以创建视差效果。
|
||||
*
|
||||
* 该组件根据相机的位置和滚动因子来移动纹理。
|
||||
*/
|
||||
class ParallaxComponent final : public Component {
|
||||
friend class engine::object::GameObject;
|
||||
private:
|
||||
TransformComponent* transform_ = nullptr; ///< @brief 缓存变换组件
|
||||
|
||||
engine::render::Sprite sprite_; ///< @brief 精灵对象
|
||||
glm::vec2 scroll_factor_; ///< @brief 滚动速度因子 (0=静止, 1=随相机移动, <1=比相机慢)
|
||||
glm::bvec2 repeat_; ///< @brief 是否沿着X和Y轴周期性重复
|
||||
bool is_hidden_ = false; ///< @brief 是否隐藏(不渲染)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param texture_id 背景纹理的资源 ID。
|
||||
* @param scroll_factor 控制背景相对于相机移动速度的因子。
|
||||
* (0, 0) 表示完全静止。
|
||||
* (1, 1) 表示与相机完全同步移动。
|
||||
* (0.5, 0.5) 表示以相机一半的速度移动。
|
||||
*/
|
||||
ParallaxComponent(const std::string& texture_id, const glm::vec2& scroll_factor, const glm::bvec2& repeat);
|
||||
|
||||
// --- 设置器 ---
|
||||
void setSprite(const engine::render::Sprite& sprite) { sprite_ = sprite; } ///< @brief 设置精灵对象
|
||||
void setScrollFactor(const glm::vec2& factor) { scroll_factor_ = factor; } ///< @brief 设置滚动速度因子
|
||||
void setRepeat(const glm::bvec2& repeat) { repeat_ = repeat; } ///< @brief 设置是否重复
|
||||
void setHidden(bool hidden) { is_hidden_ = hidden; } ///< @brief 设置是否隐藏(不渲染)
|
||||
|
||||
// --- 获取器 ---
|
||||
const engine::render::Sprite& getSprite() const { return sprite_; } ///< @brief 获取精灵对象
|
||||
const glm::vec2& getScrollFactor() const { return scroll_factor_; } ///< @brief 获取滚动速度因子
|
||||
const glm::bvec2& getRepeat() const { return repeat_; } ///< @brief 获取是否重复
|
||||
bool isHidden() const { return is_hidden_; } ///< @brief 获取是否隐藏(不渲染)
|
||||
|
||||
protected:
|
||||
// 核心循环函数覆盖
|
||||
void update(float, engine::core::Context&) override {} // 必须实现纯虚函数,留空
|
||||
void init() override;
|
||||
void render(engine::core::Context& context) override;
|
||||
};
|
||||
|
||||
} // namespace engine::component
|
||||
@@ -0,0 +1,119 @@
|
||||
#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
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
namespace engine::scene {
|
||||
class Scene;
|
||||
|
||||
class LevelLoader final {
|
||||
std::string map_path_; ///< @brief 地图路径(拼接路径时需要)
|
||||
public:
|
||||
LevelLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief 加载关卡数据到指定的 Scene 对象中。
|
||||
* @param map_path Tiled JSON 地图文件的路径。
|
||||
* @param scene 要加载数据的目标 Scene 对象。
|
||||
* @return bool 是否加载成功。
|
||||
*/
|
||||
bool loadLevel(const std::string& map_path, Scene& scene);
|
||||
|
||||
private:
|
||||
void loadImageLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载图片图层
|
||||
void loadTileLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载瓦片图层
|
||||
void loadObjectLayer(const nlohmann::json& layer_json, Scene& scene); ///< @brief 加载对象图层
|
||||
|
||||
/**
|
||||
* @brief 解析图片路径,合并地图路径和相对路径。例如:
|
||||
* 1. 地图路径:"assets/maps/level1.tmj"
|
||||
* 2. 相对路径:"../textures/Layers/back.png"
|
||||
* 3. 最终路径:"assets/textures/Layers/back.png"
|
||||
* @param image_path (图片)相对路径
|
||||
* @return std::string 解析后的完整路径。
|
||||
*/
|
||||
std::string resolvePath(std::string image_path);
|
||||
};
|
||||
|
||||
} // namespace engine::scene
|
||||
@@ -3,6 +3,9 @@
|
||||
#include "../../engine/object/game_object.h"
|
||||
#include "../../engine/component/transform_component.h"
|
||||
#include "../../engine/component/sprite_component.h"
|
||||
#include "../../engine/scene/level_loader.h"
|
||||
#include "../../engine/input/input_manager.h"
|
||||
#include "../../engine/render/camera.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <SDL3/SDL_rect.h>
|
||||
|
||||
@@ -15,6 +18,10 @@ GameScene::GameScene(std::string name, engine::core::Context& context, engine::s
|
||||
}
|
||||
|
||||
void GameScene::init() {
|
||||
// 加载关卡(level_loader通常加载完成后即可销毁,因此不存为成员变量)
|
||||
engine::scene::LevelLoader level_loader;
|
||||
level_loader.loadLevel("assets/maps/level1.tmj", *this);
|
||||
|
||||
// 创建 test_object
|
||||
createTestObject();
|
||||
|
||||
@@ -32,13 +39,14 @@ void GameScene::render() {
|
||||
|
||||
void GameScene::handleInput() {
|
||||
Scene::handleInput();
|
||||
testCamera();
|
||||
}
|
||||
|
||||
void GameScene::clean() {
|
||||
Scene::clean();
|
||||
}
|
||||
|
||||
// --- 私有方法 ---
|
||||
// --- 测试方法 ---
|
||||
|
||||
void GameScene::createTestObject() {
|
||||
spdlog::trace("在 GameScene 中创建 test_object...");
|
||||
@@ -53,4 +61,13 @@ void GameScene::createTestObject() {
|
||||
spdlog::trace("test_object 创建并添加到 GameScene 中。");
|
||||
}
|
||||
|
||||
void GameScene::testCamera() {
|
||||
auto& camera = context_.getCamera();
|
||||
auto& input_manager = context_.getInputManager();
|
||||
if (input_manager.isActionDown("move_up")) camera.move(glm::vec2(0, -1));
|
||||
if (input_manager.isActionDown("move_down")) camera.move(glm::vec2(0, 1));
|
||||
if (input_manager.isActionDown("move_left")) camera.move(glm::vec2(-1, 0));
|
||||
if (input_manager.isActionDown("move_right")) camera.move(glm::vec2(1, 0));
|
||||
}
|
||||
|
||||
} // namespace game::scene
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
private:
|
||||
// 测试函数
|
||||
void createTestObject();
|
||||
void testCamera();
|
||||
};
|
||||
|
||||
} // namespace game::scene
|
||||
Reference in New Issue
Block a user