feat(npc): 添加NPC交互高亮效果及复合纹理支持

实现NPC交互时的绿色高亮效果,通过InteractionHighlightSprite类实现
重构NpcAnimation支持复合纹理渲染,优化高亮效果的性能
添加ShaderManager获取所有加载Shader的方法,优化渲染器uniform更新逻辑
This commit is contained in:
2026-04-07 23:17:34 +08:00
parent caad22cca7
commit 5af657c5c9
11 changed files with 418 additions and 56 deletions

View File

@@ -42,10 +42,13 @@ bool NpcObject::Construction(int npcId) {
worldPosition_ = CharacterWorldPosition();
config_.reset();
animation_ = nullptr;
interactionHighlight_ = nullptr;
nameOutlineLabels_.fill(nullptr);
nameLabel_ = nullptr;
interactable_ = true;
interacting_ = false;
interactionHighlighted_ = false;
syncedHighlightCompositeVersion_ = 0;
auto config = npc::loadNpcConfig(npcId);
if (!config) {
@@ -68,11 +71,13 @@ bool NpcObject::Construction(int npcId) {
config_ = *config;
npcId_ = npcId;
animation_ = animation;
EnsureInteractionHighlight();
AddChild(animation_);
EnsureNameLabel();
SetWorldPosition({});
SetDirection(1);
SetInteractionHighlighted(false);
RefreshNameLabel();
SDL_Log("NpcObject: npc %d construction complete", npcId_);
return true;
@@ -84,6 +89,7 @@ void NpcObject::SetDirection(int direction) {
animation_->SetDirection(direction_);
}
RefreshNameLabel();
SyncInteractionHighlight();
}
void NpcObject::SetNpcPosition(const Vec2& pos) {
@@ -108,6 +114,19 @@ void NpcObject::SetInteractable(bool interactable) {
}
}
void NpcObject::SetInteractionHighlighted(bool highlighted) {
if (interactionHighlighted_ == highlighted) {
SyncInteractionHighlight();
return;
}
interactionHighlighted_ = highlighted;
if (!interactionHighlighted_) {
syncedHighlightCompositeVersion_ = 0;
}
SyncInteractionHighlight();
}
void NpcObject::BeginInteract() {
if (CanInteract()) {
interacting_ = true;
@@ -144,6 +163,19 @@ bool NpcObject::IsAnimationFinished() const {
void NpcObject::Update(float deltaTime) {
Actor::Update(deltaTime);
SyncInteractionHighlight();
}
void NpcObject::EnsureInteractionHighlight() {
if (interactionHighlight_) {
return;
}
interactionHighlight_ = MakePtr<InteractionHighlightSprite>();
interactionHighlight_->SetZOrder(-1);
interactionHighlight_->SetPosition(0.0f, 0.0f);
interactionHighlight_->SetVisible(false);
AddChild(interactionHighlight_);
}
void NpcObject::EnsureNameLabel() {
@@ -206,6 +238,38 @@ void NpcObject::RefreshNameLabel() {
}
}
void NpcObject::SyncInteractionHighlight() {
if (!interactionHighlight_) {
return;
}
if (!interactionHighlighted_ || !animation_ ||
!animation_->EnsureCompositeTextureReady() ||
!animation_->HasCompositeTexture()) {
interactionHighlight_->SetVisible(false);
return;
}
uint64 compositeVersion = animation_->GetCompositeVersion();
if (syncedHighlightCompositeVersion_ != compositeVersion) {
Ptr<Texture> compositeTexture = animation_->GetCompositeTexture();
Vec2 compositeSize = animation_->GetCompositeTextureSize();
if (!compositeTexture || compositeSize.x <= 0.0f || compositeSize.y <= 0.0f) {
interactionHighlight_->SetVisible(false);
return;
}
interactionHighlight_->SetTexture(compositeTexture);
interactionHighlight_->SetSize(compositeSize);
interactionHighlight_->SetGroundAnchorInTexture(
animation_->GetCompositeGroundAnchorInTexture());
interactionHighlight_->SetPosition(0.0f, 0.0f);
syncedHighlightCompositeVersion_ = compositeVersion;
}
interactionHighlight_->SetVisible(true);
}
void NpcObject::SyncActorPositionFromWorld() {
SetPosition(worldPosition_.ToScreenPosition());
SetZOrder(worldPosition_.y);