feat(角色): 添加角色阴影渲染功能

新增 CharacterShadowActor 类用于处理角色阴影的渲染
在 CharacterObject 中实现阴影的同步和渲染逻辑
移除 GameDebugActor 中不再使用的合成纹理预览代码
添加 EnsureCompositeTextureReady 方法确保纹理准备就绪
This commit is contained in:
2026-04-07 07:08:53 +08:00
parent 808431f92c
commit e570fec599
12 changed files with 259 additions and 218 deletions

View File

@@ -4,10 +4,7 @@
#include "ui/NineSliceActor.h"
#include <SDL2/SDL.h>
#include <algorithm>
#include <cmath>
#include <frostbite2D/2d/sprite.h>
#include <frostbite2D/2d/text_sprite.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/scene/scene.h>
#include <limits>
@@ -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> 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<Sprite>();
compositePreview_->SetName("debugCompositePreview");
compositePreview_->SetZOrder(1);
AddChild(compositePreview_);
compositeOriginMarker_ = MakePtr<DebugCrosshairActor>();
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<Texture> 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<unsigned long long>(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