feat(角色): 添加角色阴影渲染功能
新增 CharacterShadowActor 类用于处理角色阴影的渲染 在 CharacterObject 中实现阴影的同步和渲染逻辑 移除 GameDebugActor 中不再使用的合成纹理预览代码 添加 EnsureCompositeTextureReady 方法确保纹理准备就绪
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<float>(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<CharacterShadowActor>();
|
||||
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;
|
||||
|
||||
61
Game/src/character/CharacterShadowActor.cpp
Normal file
61
Game/src/character/CharacterShadowActor.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "character/CharacterShadowActor.h"
|
||||
#include <algorithm>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user