From e570fec5999c031c9ee68dfc0b3283ac47a72c41 Mon Sep 17 00:00:00 2001 From: Lenheart <947330670@qq.com> Date: Tue, 7 Apr 2026 07:08:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=A7=92=E8=89=B2):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E9=98=B4=E5=BD=B1=E6=B8=B2=E6=9F=93=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 CharacterShadowActor 类用于处理角色阴影的渲染 在 CharacterObject 中实现阴影的同步和渲染逻辑 移除 GameDebugActor 中不再使用的合成纹理预览代码 添加 EnsureCompositeTextureReady 方法确保纹理准备就绪 --- Frostbite2D/include/frostbite2D/2d/actor.h | 5 + Frostbite2D/src/frostbite2D/2d/actor.cpp | 11 + Game/include/character/CharacterAnimation.h | 1 + Game/include/character/CharacterObject.h | 13 +- Game/include/character/CharacterShadowActor.h | 32 +++ Game/include/common/debug/GameDebugActor.h | 13 -- Game/include/map/GameMap.h | 3 + Game/src/character/CharacterAnimation.cpp | 5 + Game/src/character/CharacterObject.cpp | 84 +++++++ Game/src/character/CharacterShadowActor.cpp | 61 ++++++ Game/src/common/debug/GameDebugActor.cpp | 205 +----------------- Game/src/map/GameMap.cpp | 44 +++- 12 files changed, 259 insertions(+), 218 deletions(-) create mode 100644 Game/include/character/CharacterShadowActor.h create mode 100644 Game/src/character/CharacterShadowActor.cpp diff --git a/Frostbite2D/include/frostbite2D/2d/actor.h b/Frostbite2D/include/frostbite2D/2d/actor.h index eae689e..84a5125 100644 --- a/Frostbite2D/include/frostbite2D/2d/actor.h +++ b/Frostbite2D/include/frostbite2D/2d/actor.h @@ -66,6 +66,10 @@ public: void SetScale(const Vec2& scale); void SetScale(float scale); + const Vec2& GetSkew() const { return skew_; } + void SetSkew(const Vec2& skew); + void SetSkew(float skewX, float skewY); + const Transform2D& GetLocalTransform() const; const Transform2D& GetWorldTransform() const; @@ -176,6 +180,7 @@ private: Vec2 position_; float rotation_; Vec2 scale_ = Vec2(1.0f, 1.0f); + Vec2 skew_; Vec2 size_; Vec2 anchor_; bool visible_; diff --git a/Frostbite2D/src/frostbite2D/2d/actor.cpp b/Frostbite2D/src/frostbite2D/2d/actor.cpp index 56fabb7..9ade100 100644 --- a/Frostbite2D/src/frostbite2D/2d/actor.cpp +++ b/Frostbite2D/src/frostbite2D/2d/actor.cpp @@ -58,6 +58,16 @@ void Actor::SetScale(float scale) { markTransformDirty(); } +void Actor::SetSkew(const Vec2& skew) { + skew_ = skew; + markTransformDirty(); +} + +void Actor::SetSkew(float skewX, float skewY) { + skew_ = Vec2(skewX, skewY); + markTransformDirty(); +} + void Actor::SetSize(const Vec2& size) { size_ = size; markTransformDirty(); @@ -123,6 +133,7 @@ void Actor::updateLocalTransform() const { Vec2 anchorOffset = Vec2(anchor_.x * size_.x, anchor_.y * size_.y); localTransform_ = Transform2D::translation(position_.x, position_.y) * Transform2D::rotation(rotation_) * + Transform2D::skewing(skew_.x, skew_.y) * Transform2D::scaling(scale_.x, scale_.y) * Transform2D::translation(-anchorOffset.x, -anchorOffset.y); } diff --git a/Game/include/character/CharacterAnimation.h b/Game/include/character/CharacterAnimation.h index 0611f6c..6a74ac4 100644 --- a/Game/include/character/CharacterAnimation.h +++ b/Game/include/character/CharacterAnimation.h @@ -51,6 +51,7 @@ public: animation::AniFrame GetCurrentFrameInfo() const; void SetActionFrameFlagCallback(ActionFrameFlagCallback callback); void SetActionEndCallback(ActionEndCallback callback); + bool EnsureCompositeTextureReady(); bool HasCompositeTexture() const; Ptr GetCompositeTexture() const; Vec2 GetCompositeTextureSize() const; diff --git a/Game/include/character/CharacterObject.h b/Game/include/character/CharacterObject.h index bd80e93..ff8f1a0 100644 --- a/Game/include/character/CharacterObject.h +++ b/Game/include/character/CharacterObject.h @@ -5,6 +5,7 @@ #include "character/CharacterDataLoader.h" #include "character/CharacterEquipmentManager.h" #include "character/CharacterInputRouter.h" +#include "character/CharacterShadowActor.h" #include "character/CharacterStateMachine.h" #include #include @@ -31,7 +32,7 @@ class GameMap; class CharacterObject : public Actor { public: CharacterObject() = default; - ~CharacterObject() override = default; + ~CharacterObject() override; /// @brief 二段初始化角色。 /// @param jobId 职业 id,用来加载职业配置、动作定义和动画资源。 @@ -77,7 +78,10 @@ public: /// /// 顺序固定为:命令缓冲推进 -> 采样实时输入 -> 生成意图 -> 状态机更新 /// -> motor 推进 -> 投影到 Actor 坐标。 + void Update(float deltaTime) override; void OnUpdate(float deltaTime) override; + void OnAdded(Actor* parent) override; + void PrepareRenderFrame(); /// @brief 统一输入入口。 /// @@ -182,6 +186,9 @@ private: void ApplyMapMovementConstraints(const CharacterWorldPosition& previousPosition); void QueueMapTransitionIfNeeded(); void SyncActorPositionFromWorld(); + void SyncShadowAttachment(); + void SyncShadowPresentation(); + void DetachShadowActor(); bool SetActionStrict(const std::string& actionName, const char* phase, const std::string& requestedActionId); @@ -238,6 +245,10 @@ private: /// 真正负责播放角色分层动画的 Actor 子节点。 RefPtr animationManager_ = nullptr; + /// ???? normal ??????????? + RefPtr shadowActor_ = nullptr; + GameMap* shadowAttachedMap_ = nullptr; + /// 缓存上一帧 deltaTime,方便状态和脚本系统查询。 float lastDeltaTime_ = 0.0f; diff --git a/Game/include/character/CharacterShadowActor.h b/Game/include/character/CharacterShadowActor.h new file mode 100644 index 0000000..a57a52b --- /dev/null +++ b/Game/include/character/CharacterShadowActor.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace frostbite2D { + +struct CharacterShadowFrameSnapshot { + bool visible = false; + Ptr texture = nullptr; + Vec2 textureSize = Vec2::Zero(); + Vec2 groundAnchorInTexture = Vec2::Zero(); + Vec2 groundScreenPosition = Vec2::Zero(); + int zOrder = 0; + uint64 textureVersion = 0; +}; + +class CharacterShadowActor : public Sprite { +public: + CharacterShadowActor(); + + void ApplyFrameSnapshot(const CharacterShadowFrameSnapshot& snapshot); + +private: + void ApplyTextureState(const CharacterShadowFrameSnapshot& snapshot); + + Ptr appliedTexture_ = nullptr; + Vec2 appliedTextureSize_ = Vec2::Zero(); + Vec2 appliedGroundAnchorInTexture_ = Vec2::Zero(); + uint64 appliedTextureVersion_ = 0; +}; + +} // namespace frostbite2D diff --git a/Game/include/common/debug/GameDebugActor.h b/Game/include/common/debug/GameDebugActor.h index 1fae139..96ca9b4 100644 --- a/Game/include/common/debug/GameDebugActor.h +++ b/Game/include/common/debug/GameDebugActor.h @@ -1,14 +1,11 @@ #pragma once #include -#include - namespace frostbite2D { class CharacterObject; class GameMap; class NineSliceActor; -class Sprite; class TextSprite; /** @@ -36,10 +33,6 @@ private: void initOverlay(); void updateOverlay(); void updateMapDebugHighlight(); - void updateCompositePreview(); - Vec2 computeScreenMatchedPreviewSize(const Vec2& textureSize) const; - void updateCompositePreviewMarker(const Vec2& textureSize, const Vec2& previewSize, - const Vec2& originInTexture); void setOverlayVisible(bool visible); GameDebugActor(); @@ -50,12 +43,6 @@ private: RefPtr background_; RefPtr coordText_; RefPtr actionText_; - RefPtr compositeText_; - RefPtr compositePreview_; - RefPtr compositeOriginMarker_; - CharacterObject* previewCharacter_ = nullptr; - uint64 previewCompositeVersion_ = 0; - bool previewTextureAvailable_ = false; }; } // namespace frostbite2D diff --git a/Game/include/map/GameMap.h b/Game/include/map/GameMap.h index bfe414d..f92becd 100644 --- a/Game/include/map/GameMap.h +++ b/Game/include/map/GameMap.h @@ -30,9 +30,11 @@ public: /// 地图进入场景后的初始化入口,目前主要负责音乐播放。 void Enter(); void Update(float deltaTime) override; + void Render() override; /// 将运行时对象挂到 normal 层,并按 y 值设置基础排序。 void AddObject(RefPtr object); + void AddObjectToLayer(const std::string& layerName, RefPtr object); /// 返回地图推荐的默认相机关注点。 Vec2 GetDefaultCameraFocus() const; @@ -75,6 +77,7 @@ private: void InitMoveArea(); /// 将各层转换到屏幕空间;远景层在这里做视差滚动。 void updateLayerPositions(const Vec2& cameraFocus); + void PrepareRuntimeObjectsForRender(); /// 原始地图配置,作为运行时装配地图内容的输入。 game::MapConfig mapConfig_; diff --git a/Game/src/character/CharacterAnimation.cpp b/Game/src/character/CharacterAnimation.cpp index 74b7f9b..a5d2f8f 100644 --- a/Game/src/character/CharacterAnimation.cpp +++ b/Game/src/character/CharacterAnimation.cpp @@ -441,6 +441,11 @@ void CharacterAnimation::SetActionEndCallback(ActionEndCallback callback) { RefreshRuntimeCallbacks(); } +bool CharacterAnimation::EnsureCompositeTextureReady() { + RefreshCompositeTextureIfNeeded(); + return HasCompositeTexture(); +} + bool CharacterAnimation::HasCompositeTexture() const { return compositeCanvas_ && compositeCanvas_->IsCanvasReady() && GetCurrentCompositeFrameInfo().valid; diff --git a/Game/src/character/CharacterObject.cpp b/Game/src/character/CharacterObject.cpp index 5ed1f7b..344778c 100644 --- a/Game/src/character/CharacterObject.cpp +++ b/Game/src/character/CharacterObject.cpp @@ -1,4 +1,5 @@ #include "character/CharacterObject.h" +#include "character/CharacterShadowActor.h" #include "common/math/GameMath.h" #include "map/GameMap.h" #include "world/GameWorld.h" @@ -43,14 +44,24 @@ Vec3 ToWorldVector(const CharacterWorldPosition& position) { static_cast(position.z)); } +constexpr int kShadowNormalLayerZBias = 1000000; + } // namespace +CharacterObject::~CharacterObject() { + DetachShadowActor(); + shadowActor_ = nullptr; +} + bool CharacterObject::Construction(int jobId) { ScopedStartupTrace startupTrace("CharacterObject::Construction"); // Reset all runtime state before rebuilding the character from config. EnableEventReceive(); + DetachShadowActor(); RemoveAllChildren(); animationManager_ = nullptr; + shadowActor_ = nullptr; + shadowAttachedMap_ = nullptr; config_.reset(); currentAction_.clear(); actionLibrary_ = CharacterActionLibrary(); @@ -102,6 +113,9 @@ bool CharacterObject::Construction(int jobId) { } AddChild(animationManager_); + shadowActor_ = MakePtr(); + shadowAttachedMap_ = nullptr; + { ScopedStartupTrace stageTrace("CharacterObject initial action setup"); if (!RequireAction("idle", "CharacterObject::Construction")) { @@ -172,6 +186,57 @@ void CharacterObject::SyncActorPositionFromWorld() { SetZOrder(worldPosition.y); } +void CharacterObject::DetachShadowActor() { + if (shadowActor_ && shadowActor_->GetParent()) { + shadowActor_->GetParent()->RemoveChild(shadowActor_); + } + shadowAttachedMap_ = nullptr; +} + +void CharacterObject::SyncShadowAttachment() { + if (!shadowActor_) { + return; + } + + GameMap* map = FindOwningMap(); + if (!map) { + DetachShadowActor(); + return; + } + + if (shadowAttachedMap_ == map && shadowActor_->GetParent()) { + return; + } + + if (shadowActor_->GetParent()) { + shadowActor_->GetParent()->RemoveChild(shadowActor_); + } + + map->AddObjectToLayer("normal", shadowActor_); + shadowAttachedMap_ = map; +} + +void CharacterObject::SyncShadowPresentation() { + if (!shadowActor_) { + return; + } + + CharacterShadowFrameSnapshot snapshot; + snapshot.groundScreenPosition = motor_.position.ToGroundPosition(); + snapshot.zOrder = motor_.position.y - kShadowNormalLayerZBias; + + + if (HasCompositeTexture()) { + snapshot.visible = true; + snapshot.texture = GetCompositeTexture(); + snapshot.textureSize = GetCompositeTextureSize(); + snapshot.groundAnchorInTexture = GetCompositeGroundAnchorInTexture(); + snapshot.textureVersion = GetCompositeTextureVersion(); + } + + shadowActor_->ApplyFrameSnapshot(snapshot); +} + GameMap* CharacterObject::FindOwningMap() const { Actor* node = GetParent(); while (node) { @@ -317,6 +382,19 @@ void CharacterObject::ApplyHit(const HitContext& hit) { stateMachine_.ForceHurt(*this, hurtAction); } +void CharacterObject::Update(float deltaTime) { + Actor::Update(deltaTime); + SyncShadowAttachment(); + SyncShadowPresentation(); +} + +void CharacterObject::PrepareRenderFrame() { + if (animationManager_) { + animationManager_->EnsureCompositeTextureReady(); + } + SyncShadowPresentation(); +} + void CharacterObject::OnUpdate(float deltaTime) { // Fixed update order keeps input, state transitions, and motion deterministic. lastDeltaTime_ = deltaTime; @@ -332,6 +410,12 @@ void CharacterObject::OnUpdate(float deltaTime) { SetFacing(motor_.facing); } +void CharacterObject::OnAdded(Actor* parent) { + (void)parent; + SyncShadowAttachment(); + SyncShadowPresentation(); +} + bool CharacterObject::OnEvent(const Event& event) { if (!IsEventReceiveEnabled() || !inputEnabled_) { return false; diff --git a/Game/src/character/CharacterShadowActor.cpp b/Game/src/character/CharacterShadowActor.cpp new file mode 100644 index 0000000..0cf1b29 --- /dev/null +++ b/Game/src/character/CharacterShadowActor.cpp @@ -0,0 +1,61 @@ +#include "character/CharacterShadowActor.h" +#include + +namespace frostbite2D { +namespace { + +constexpr Color kShadowColor(0.0f, 0.0f, 0.0f, 0.42f); +const Vec2 kShadowScale(0.9f, 0.30f); +constexpr float kShadowSkewX = 50.0f; + +} // namespace + +CharacterShadowActor::CharacterShadowActor() { + SetName("characterShadow"); + SetColor(kShadowColor); + SetScale(kShadowScale); + // Anchor stays on the ground point while the upper silhouette falls left. + SetSkew(kShadowSkewX, 0.0f); + SetBlendMode(BlendMode::Normal); + SetVisible(false); +} + +void CharacterShadowActor::ApplyFrameSnapshot( + const CharacterShadowFrameSnapshot& snapshot) { + bool hasTexture = snapshot.visible && snapshot.texture && snapshot.textureSize.x > 0.0f && + snapshot.textureSize.y > 0.0f; + if (!hasTexture) { + SetVisible(false); + return; + } + + ApplyTextureState(snapshot); + SetPosition(snapshot.groundScreenPosition); + SetZOrder(snapshot.zOrder); + SetVisible(true); +} + +void CharacterShadowActor::ApplyTextureState( + const CharacterShadowFrameSnapshot& snapshot) { + if (appliedTexture_ == snapshot.texture && + appliedTextureSize_ == snapshot.textureSize && + appliedGroundAnchorInTexture_ == snapshot.groundAnchorInTexture && + appliedTextureVersion_ == snapshot.textureVersion) { + return; + } + + SetTexture(snapshot.texture); + SetSize(snapshot.textureSize); + + float safeWidth = std::max(snapshot.textureSize.x, 1.0f); + float safeHeight = std::max(snapshot.textureSize.y, 1.0f); + SetAnchor(std::clamp(snapshot.groundAnchorInTexture.x / safeWidth, 0.0f, 1.0f), + std::clamp(snapshot.groundAnchorInTexture.y / safeHeight, 0.0f, 1.0f)); + + appliedTexture_ = snapshot.texture; + appliedTextureSize_ = snapshot.textureSize; + appliedGroundAnchorInTexture_ = snapshot.groundAnchorInTexture; + appliedTextureVersion_ = snapshot.textureVersion; +} + +} // namespace frostbite2D diff --git a/Game/src/common/debug/GameDebugActor.cpp b/Game/src/common/debug/GameDebugActor.cpp index b710254..6ab6c93 100644 --- a/Game/src/common/debug/GameDebugActor.cpp +++ b/Game/src/common/debug/GameDebugActor.cpp @@ -4,10 +4,7 @@ #include "ui/NineSliceActor.h" #include #include -#include -#include #include -#include #include #include @@ -20,37 +17,8 @@ constexpr float kDebugHudMarginY = 12.0f; constexpr float kDebugHudPaddingX = 8.0f; constexpr float kDebugHudPaddingY = 6.0f; constexpr float kDebugHudLineGap = 4.0f; -constexpr float kDebugHudPreviewGap = 8.0f; -constexpr float kDebugHudOriginMarkerHalfExtent = 3.0f; constexpr char kDebugHudPopupImg[] = "sprite/interface/newstyle/windows/popup/popup.img"; -class DebugCrosshairActor : public Actor { -public: - void Render() override { - if (!IsVisible()) { - return; - } - - Renderer& renderer = Renderer::get(); - Vec2 center = GetWorldTransform().transformPoint(Vec2::Zero()); - Color color = Colors::Yellow; - color.a *= GetWorldOpacity(); - if (color.a <= 0.0f) { - return; - } - - float lineLength = kDebugHudOriginMarkerHalfExtent * 2.0f + 1.0f; - renderer.drawQuad( - Rect(center.x - kDebugHudOriginMarkerHalfExtent, center.y, lineLength, - 1.0f), - color); - renderer.drawQuad( - Rect(center.x, center.y - kDebugHudOriginMarkerHalfExtent, 1.0f, - lineLength), - color); - } -}; - void ConfigureTextLine(RefPtr textSprite, const char* name, int zOrder) { if (!textSprite) { @@ -117,11 +85,6 @@ void GameDebugActor::SetDebugMap(GameMap* map) { } void GameDebugActor::SetTrackedCharacter(CharacterObject* character) { - if (trackedCharacter_ != character) { - previewCharacter_ = nullptr; - previewCompositeVersion_ = 0; - previewTextureAvailable_ = false; - } trackedCharacter_ = character; updateOverlay(); } @@ -132,16 +95,6 @@ void GameDebugActor::ClearDebugContext() { } debugMap_ = nullptr; trackedCharacter_ = nullptr; - previewCharacter_ = nullptr; - previewCompositeVersion_ = 0; - previewTextureAvailable_ = false; - if (compositePreview_) { - compositePreview_->SetTexture(nullptr); - compositePreview_->SetSize(0.0f, 0.0f); - } - if (compositeOriginMarker_) { - compositeOriginMarker_->SetVisible(false); - } setOverlayVisible(false); } @@ -151,7 +104,7 @@ void GameDebugActor::OnUpdate(float deltaTime) { } void GameDebugActor::initOverlay() { - if (background_ || coordText_ || actionText_ || compositeText_ || compositePreview_) { + if (background_ || coordText_ || actionText_) { return; } @@ -173,42 +126,17 @@ void GameDebugActor::initOverlay() { ConfigureTextLine(actionText_, "debugActionText", 1); AddChild(actionText_); - compositeText_ = TextSprite::create(); - ConfigureTextLine(compositeText_, "debugCompositeText", 1); - AddChild(compositeText_); - - compositePreview_ = MakePtr(); - compositePreview_->SetName("debugCompositePreview"); - compositePreview_->SetZOrder(1); - AddChild(compositePreview_); - - compositeOriginMarker_ = MakePtr(); - compositeOriginMarker_->SetName("debugCompositeOriginMarker"); - compositeOriginMarker_->SetZOrder(2); - compositeOriginMarker_->SetVisible(false); - AddChild(compositeOriginMarker_); - setOverlayVisible(false); } void GameDebugActor::updateOverlay() { - if (!coordText_ || !actionText_ || !compositeText_ || !compositePreview_) { + if (!coordText_ || !actionText_) { setOverlayVisible(false); return; } updateMapDebugHighlight(); if (!trackedCharacter_) { - if (compositePreview_) { - compositePreview_->SetTexture(nullptr); - compositePreview_->SetSize(0.0f, 0.0f); - } - if (compositeOriginMarker_) { - compositeOriginMarker_->SetVisible(false); - } - previewCharacter_ = nullptr; - previewCompositeVersion_ = 0; - previewTextureAvailable_ = false; setOverlayVisible(false); return; } @@ -233,28 +161,14 @@ void GameDebugActor::updateOverlay() { frameCount); actionText_->SetText(actionTextBuffer); - updateCompositePreview(); - Vec2 coordTextSize = coordText_->GetTextSize(); Vec2 actionTextSize = actionText_->GetTextSize(); - Vec2 compositeTextSize = compositeText_->GetTextSize(); - Vec2 previewSize = compositePreview_->IsVisible() ? compositePreview_->GetSize() - : Vec2::Zero(); - - float textBlockWidth = std::max(coordTextSize.x, - std::max(actionTextSize.x, compositeTextSize.x)); - float panelWidth = std::max(textBlockWidth, previewSize.x) + kDebugHudPaddingX * 2.0f; + float panelWidth = std::max(coordTextSize.x, actionTextSize.x) + kDebugHudPaddingX * 2.0f; float contentHeight = coordTextSize.y; if (actionText_->IsVisible()) { contentHeight += kDebugHudLineGap + actionTextSize.y; } - if (compositeText_->IsVisible()) { - contentHeight += kDebugHudLineGap + compositeTextSize.y; - } - if (compositePreview_->IsVisible()) { - contentHeight += kDebugHudPreviewGap + previewSize.y; - } float panelHeight = contentHeight + kDebugHudPaddingY * 2.0f; Vec2 panelSize(panelWidth, panelHeight); @@ -275,20 +189,6 @@ void GameDebugActor::updateOverlay() { currentY += actionTextSize.y; } - if (compositeText_->IsVisible()) { - currentY += kDebugHudLineGap; - compositeText_->SetPosition(kDebugHudPaddingX, currentY); - currentY += compositeTextSize.y; - } - - if (compositePreview_->IsVisible()) { - currentY += kDebugHudPreviewGap; - compositePreview_->SetPosition(kDebugHudPaddingX, currentY); - updateCompositePreviewMarker(trackedCharacter_->GetCompositeTextureSize(), - compositePreview_->GetSize(), - trackedCharacter_->GetCompositeOriginInTexture()); - } - setOverlayVisible(true); } @@ -310,94 +210,6 @@ void GameDebugActor::updateMapDebugHighlight() { debugMap_->SetDebugHighlightedMoveAreaIndex(moveAreaIndex); } -void GameDebugActor::updateCompositePreview() { - if (!trackedCharacter_ || !compositePreview_ || !compositeText_) { - return; - } - - Ptr compositeTexture = trackedCharacter_->GetCompositeTexture(); - bool textureAvailable = - trackedCharacter_->HasCompositeTexture() && compositeTexture != nullptr; - uint64 compositeVersion = - textureAvailable ? trackedCharacter_->GetCompositeTextureVersion() : 0; - bool needsRefresh = previewCharacter_ != trackedCharacter_ || - previewCompositeVersion_ != compositeVersion || - previewTextureAvailable_ != textureAvailable; - - if (needsRefresh) { - previewCharacter_ = trackedCharacter_; - previewCompositeVersion_ = compositeVersion; - previewTextureAvailable_ = textureAvailable; - - if (textureAvailable) { - compositePreview_->SetTexture(compositeTexture); - } else { - compositePreview_->SetTexture(nullptr); - compositePreview_->SetSize(0.0f, 0.0f); - compositePreview_->SetVisible(false); - if (compositeOriginMarker_) { - compositeOriginMarker_->SetVisible(false); - } - } - } - - if (!textureAvailable) { - compositeText_->SetText("Composite: unavailable"); - compositeText_->SetVisible(true); - return; - } - - Vec2 textureSize = trackedCharacter_->GetCompositeTextureSize(); - Vec2 previewSize = computeScreenMatchedPreviewSize(textureSize); - Vec2 origin = trackedCharacter_->GetCompositeOriginInTexture(); - Vec2 groundAnchor = trackedCharacter_->GetCompositeGroundAnchorInTexture(); - compositePreview_->SetSize(previewSize); - compositePreview_->SetVisible(true); - updateCompositePreviewMarker(textureSize, previewSize, origin); - - char compositeTextBuffer[256]; - SDL_snprintf( - compositeTextBuffer, sizeof(compositeTextBuffer), - "Composite: tex %.0fx%.0f preview %.0fx%.0f origin(%.0f, %.0f) ground(%.0f, %.0f) ver %llu", - textureSize.x, textureSize.y, previewSize.x, previewSize.y, origin.x, - origin.y, groundAnchor.x, groundAnchor.y, - static_cast(trackedCharacter_->GetCompositeTextureVersion())); - compositeText_->SetText(compositeTextBuffer); - compositeText_->SetVisible(true); -} - -Vec2 GameDebugActor::computeScreenMatchedPreviewSize(const Vec2& textureSize) const { - if (!trackedCharacter_ || textureSize.x <= 0.0f || textureSize.y <= 0.0f) { - return Vec2::Zero(); - } - - Renderer& renderer = Renderer::get(); - Camera* worldCamera = renderer.getCamera(); - float zoom = worldCamera ? worldCamera->getZoom() : 1.0f; - Vec2 worldScale = math::extractScale(trackedCharacter_->GetWorldTransform().matrix); - return Vec2(std::max(textureSize.x * std::abs(worldScale.x) * zoom, 1.0f), - std::max(textureSize.y * std::abs(worldScale.y) * zoom, 1.0f)); -} - -void GameDebugActor::updateCompositePreviewMarker(const Vec2& textureSize, - const Vec2& previewSize, - const Vec2& originInTexture) { - if (!compositeOriginMarker_ || !compositePreview_ || textureSize.x <= 0.0f || - textureSize.y <= 0.0f || previewSize.x <= 0.0f || previewSize.y <= 0.0f) { - if (compositeOriginMarker_) { - compositeOriginMarker_->SetVisible(false); - } - return; - } - - float scaleX = previewSize.x / textureSize.x; - float scaleY = previewSize.y / textureSize.y; - Vec2 previewTopLeft = compositePreview_->GetPosition(); - compositeOriginMarker_->SetPosition(previewTopLeft.x + originInTexture.x * scaleX, - previewTopLeft.y + originInTexture.y * scaleY); - compositeOriginMarker_->SetVisible(compositePreview_->IsVisible()); -} - void GameDebugActor::setOverlayVisible(bool visible) { if (background_) { background_->SetVisible(visible); @@ -408,17 +220,6 @@ void GameDebugActor::setOverlayVisible(bool visible) { if (actionText_) { actionText_->SetVisible(visible); } - if (compositeText_) { - compositeText_->SetVisible(visible); - } - if (compositePreview_) { - compositePreview_->SetVisible(visible && previewTextureAvailable_); - } - if (compositeOriginMarker_) { - compositeOriginMarker_->SetVisible(visible && previewTextureAvailable_ && - compositePreview_ && - compositePreview_->IsVisible()); - } } } // namespace frostbite2D diff --git a/Game/src/map/GameMap.cpp b/Game/src/map/GameMap.cpp index 4d06623..311ab6f 100644 --- a/Game/src/map/GameMap.cpp +++ b/Game/src/map/GameMap.cpp @@ -1,5 +1,6 @@ #include "audio/MapAudioController.h" #include "common/math/GameMath.h" +#include "character/CharacterObject.h" #include "map/GameMap.h" #include #include @@ -157,6 +158,29 @@ void GameMap::Enter() { void GameMap::Update(float deltaTime) { Actor::Update(deltaTime); } +void GameMap::Render() { + PrepareRuntimeObjectsForRender(); + Actor::Render(); +} + +void GameMap::PrepareRuntimeObjectsForRender() { + auto layerIt = layerMap_.find("normal"); + if (layerIt == layerMap_.end() || !layerIt->second) { + return; + } + + for (auto it = layerIt->second->GetChildren().begin(); + it != layerIt->second->GetChildren().end(); ++it) { + if (!*it) { + continue; + } + + if (auto* character = dynamic_cast((*it).Get())) { + character->PrepareRenderFrame(); + } + } +} + void GameMap::InitTile() { if (mapConfig_.tilePaths.empty() && mapConfig_.extendedTilePaths.empty()) { return; @@ -403,12 +427,28 @@ void GameMap::updateLayerPositions(const Vec2 &cameraFocus) { } void GameMap::AddObject(RefPtr object) { + AddObjectToLayer("normal", object); +} + +void GameMap::AddObjectToLayer(const std::string& layerName, RefPtr object) { if (!object) { return; } - // Keep dynamic objects on the normal layer and sort them by y. + + auto layerIt = layerMap_.find(layerName); + if (layerIt == layerMap_.end() || !layerIt->second) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "GameMap: layer %s missing, fallback to normal", + layerName.c_str()); + layerIt = layerMap_.find("normal"); + if (layerIt == layerMap_.end() || !layerIt->second) { + return; + } + } + + // Keep runtime objects sorted by their ground y inside the target layer. object->SetZOrder(static_cast(object->GetPosition().y)); - layerMap_["normal"]->AddObject(object); + layerIt->second->AddObject(object); } Vec3 GameMap::CheckIsItMovable(const Vec3& curPos, const Vec3& posOffset) const {