This commit is contained in:
2025-10-06 04:18:49 +08:00
commit df2cacdb92
2784 changed files with 1280840 additions and 0 deletions

View File

@@ -0,0 +1,309 @@
#include "Animation.h"
#include "Asset/AssetManager.h"
#include "Asset/Asset_Script.h"
#include "Tool/Math.h"
#include "EngineFrame/Actor/Actor.h"
Animation::Animation()
{
}
Animation::Animation(std::string AniPath)
{
Init(AniPath);
}
Animation::Animation(std::string AniPath, std::function<std::string(std::string, Animation::ReplaceData)> AdditionalOptions, Animation::ReplaceData data)
{
this->AdditionalOptions = AdditionalOptions;
this->AdditionalOptionsData = data;
Init(AniPath);
}
Animation::~Animation()
{
}
void Animation::Init(std::string AniPath)
{
Actor::Init();
AniScriptParser::AniInfo Info = AssetManager::GetInstance().StructAniInfo(AniPath);
this->AniPath = AniPath;
this->AnimationFlag = Info.Flag;
this->FrameArr = Info.Frame;
for (size_t i = 0; i < this->FrameArr.size(); i++)
{
AniScriptParser::AniFrame FrameObj = this->FrameArr[i];
RefPtr<Sprite> SpriteObj = nullptr;
if (!FrameObj.Img_Path.empty())
{
if (AdditionalOptions)
{
FrameObj.Img_Path = AdditionalOptions(FrameObj.Img_Path, this->AdditionalOptionsData);
}
SpriteObj = new Sprite(FrameObj.Img_Path, FrameObj.Img_Index);
SpriteObj->SetAnchor(VecFPos(0.5f, 0.5f));
SpriteObj->SetPos(VecFPos(FrameObj.Img_Pos.x, FrameObj.Img_Pos.y));
SpriteObj->SetVisible(false);
}
else
{
SpriteObj = new Sprite();
SpriteObj->SetVisible(false);
SDL_Log("Animation::Init() SpriteObj is nullptr");
}
SpriteArr.push_back(SpriteObj);
AddChild(SpriteObj);
}
// 初始化完毕 如果是第一次初始化 而非重新构造 设置大小为第0帧大小否则天空 地板等依靠大小的初始化会有问题
if (CurrentIndexT == 0)
SetSize(SpriteArr[0]->GetSize());
// 记录总帧数
TotalFrameIndex = FrameArr.size();
// TODO 染色
// 判断是否有Als
if (Asset_Script::GetInstance().GetFileInfo(AniPath + ".als"))
{
AniScriptParser::AlsInfo Info = AssetManager::GetInstance().StructAlsInfo(AniPath + ".als");
if (Info.AniList.size() > 0)
{
std::string Dir = AniPath.substr(0, AniPath.find_last_of("/") + 1);
for (auto &Ani : Info.AniList)
{
RefPtr<Animation> AlsAniObj = new Animation(Dir + Ani.second.path);
AlsAniObj->SetRenderZOrder(Ani.second.layer[1]);
AddChild(AlsAniObj);
}
}
}
FlushFrame(0);
}
void Animation::HandleEvents(SDL_Event *e)
{
}
void Animation::Update(float deltaTime)
{
// 可用性检查
if (IsUsability && Visible)
{
float dt_ms = deltaTime * 1000.0f;
// 累加当前帧时间
CurrentIndexT += dt_ms;
// 插值模式判断
InterpolationLogic();
// 循环处理:只要当前时间超过帧延迟,就切换帧(支持一次跳过多帧)
while (CurrentIndexT >= NextFrameDelay)
{
CurrentIndexT -= NextFrameDelay;
// 如果当前帧小于总帧数就切换
if (CurrentFrameIndex < (TotalFrameIndex - 1))
{
FlushFrame(CurrentFrameIndex + 1);
}
// 说明播放完毕了
else
{
// 如果有循环
if (AnimationFlag.count("LOOP"))
{
FlushFrame(0);
}
// 没有循环触发状态机回调
else
{
// 将不再可用
IsUsability = false;
if (EndCallback)
EndCallback();
break;
}
}
}
Actor::Update(deltaTime);
}
}
void Animation::Render()
{
if (!Visible)
return;
Actor::Render();
}
void Animation::OnAdded(BaseNode *node)
{
Actor::OnAdded(node);
FlushFrame(0);
}
void Animation::Clear()
{
}
void Animation::FlushFrame(int Index)
{
// 关闭旧帧显示
if (CurrentFrame)
CurrentFrame->SetVisible(false);
// 同步当前帧编号
CurrentFrameIndex = Index;
// 当前帧更换为本帧
CurrentFrame = SpriteArr[CurrentFrameIndex];
// 开启新帧显示
CurrentFrame->SetVisible(true);
// 如果是整体染色 则直接使用染色帧
// if (DyeAllFlag) CurrentFrame = DyeFrameList[CurrentFrameIndex];
AniScriptParser::AniFrame FrameInfo = FrameArr[CurrentFrameIndex];
// 设置下帧延迟
NextFrameDelay = FrameInfo.Delay;
std::unordered_map<std::string, AniScriptParser::AniFlag> FlagBuf = FrameInfo.Flag;
// 关键帧
if (FlagBuf.count("SET_FLAG"))
{
if (ChangeFrameCallback)
ChangeFrameCallback(std::get<int>(FlagBuf["SET_FLAG"]));
}
// 播放音效
if (FlagBuf.count("PLAY_SOUND"))
{
// TODO 还没有做音效的播放
}
// 缩放
if (FlagBuf.count("IMAGE_RATE"))
{
VecFPos Rate = std::get<VecFPos>(FlagBuf["IMAGE_RATE"]);
CurrentFrame->SetAnchor(VecFPos{0.5f, 0.5f});
CurrentFrame->SetScale(VecFPos{Rate.x, Rate.y});
}
// 线性减淡
if (FlagBuf.count("GRAPHIC_EFFECT_LINEARDODGE"))
{
CurrentFrame->SetBlendMode(SDL_BLENDMODE_ADD);
}
// 旋转
if (FlagBuf.count("IMAGE_ROTATE"))
{
CurrentFrame->SetAnchor(VecFPos{0.5f, 0.5f});
CurrentFrame->SetRotation(std::get<float>(FlagBuf["IMAGE_ROTATE"]));
}
// 染色
// if (!DyeAllFlag)
// 插值模式
if (FlagBuf.count("INTERPOLATION"))
{
// 初始化插值数据
if (InterpolationFlag.size() == 0)
{
// 旧插值数据
InterpolationFlag.push_back(FrameArr[CurrentFrameIndex]);
// 新插值数据
InterpolationFlag.push_back(FrameArr[CurrentFrameIndex + 1]);
}
}
else
{
if (InterpolationFlag.size() > 0)
{
InterpolationFlag.clear();
}
}
// 如果有描边
if (IsOutline)
{
}
// Ani的大小同步为精灵帧对象的大小
SetSize(CurrentFrame->GetSize());
// 裁切 //TODO
}
void Animation::Reset()
{
IsUsability = true;
CurrentIndexT = 0;
FlushFrame(0);
}
AniScriptParser::AniFrame Animation::GetCurrentFrameInfo()
{
return FrameArr[CurrentFrameIndex];
}
void Animation::SetFrameIndex(int Index)
{
FlushFrame(Index);
CurrentIndexT = 0;
}
void Animation::InterpolationLogic()
{
if (InterpolationFlag.size() == 0)
return;
// 插值倍率
float InterRate = Math::getUniformVelocity(0, 100, CurrentIndexT, NextFrameDelay) / 100.0f;
AniScriptParser::AniFrame OldData = InterpolationFlag[0];
AniScriptParser::AniFrame NewData = InterpolationFlag[1];
// RGBA插值
{
std::vector<int> OldRgbaData = {255, 255, 255, 250};
std::vector<int> NewRgbaData = {255, 255, 255, 250};
if (OldData.Flag.count("RGBA"))
OldRgbaData = std::get<std::vector<int>>(OldData.Flag["RGBA"]);
if (NewData.Flag.count("RGBA"))
NewRgbaData = std::get<std::vector<int>>(NewData.Flag["RGBA"]);
std::vector<int> RgbaData = {
(int)(OldRgbaData[0] + (NewRgbaData[0] - OldRgbaData[0]) * InterRate),
(int)(OldRgbaData[1] + (NewRgbaData[1] - OldRgbaData[1]) * InterRate),
(int)(OldRgbaData[2] + (NewRgbaData[2] - OldRgbaData[2]) * InterRate),
(int)(OldRgbaData[3] + (NewRgbaData[3] - OldRgbaData[3]) * InterRate)};
// TODO 染色 和 透明度还没弄
}
// 坐标
{
VecFPos PosData = {
(OldData.Img_Pos.x + (NewData.Img_Pos.x - OldData.Img_Pos.x) * InterRate),
(OldData.Img_Pos.y + (NewData.Img_Pos.y - OldData.Img_Pos.y) * InterRate)};
CurrentFrame->SetPos(PosData);
}
// 缩放
{
VecFPos OldRateData = {1.0f, 1.0f};
VecFPos NewRateData = {1.0f, 1.0f};
if (OldData.Flag.count("IMAGE_RATE"))
{
OldRateData = std::get<VecFPos>(OldData.Flag["IMAGE_RATE"]);
}
if (NewData.Flag.count("IMAGE_RATE"))
{
NewRateData = std::get<VecFPos>(NewData.Flag["IMAGE_RATE"]);
}
VecFPos RateData = {
OldRateData.x + (NewRateData.x - OldRateData.x) * InterRate,
OldRateData.y + (NewRateData.y - OldRateData.y) * InterRate};
CurrentFrame->SetAnchor(VecFPos{0.5f, 0.5f});
CurrentFrame->SetScale(RateData);
}
// 旋转
{
float OldAngleData = 0.0;
float NewAngleData = 0.0;
if (OldData.Flag.count("IMAGE_ROTATE"))
{
OldAngleData = std::get<float>(OldData.Flag["IMAGE_ROTATE"]);
}
if (NewData.Flag.count("IMAGE_ROTATE"))
{
NewAngleData = std::get<float>(NewData.Flag["IMAGE_ROTATE"]);
}
CurrentFrame->SetRotation(OldAngleData + (NewAngleData - OldAngleData) * InterRate);
}
}

View File

@@ -0,0 +1,95 @@
#pragma once
#include "EngineFrame/Actor/Actor.h"
#include "EngineFrame/Component/Sprite.h"
#include "Asset/AnimationStruct.h"
#include <functional>
#include <tuple>
#include <utility>
class Animation : public Actor
{
public:
struct ReplaceData
{
int Param1;
int Param2;
ReplaceData() : Param1(0), Param2(0) {}
ReplaceData(int _Param1, int _Param2) : Param1(_Param1), Param2(_Param2) {}
};
public:
Animation(/* args */);
Animation(std::string AniPath);
Animation(std::string AniPath, std::function<std::string(std::string, Animation::ReplaceData)> AdditionalOptions, Animation::ReplaceData);
~Animation();
void Init(std::string AniPath);
void HandleEvents(SDL_Event *e) override;
void Update(float deltaTime) override;
void Render() override;
void OnAdded(BaseNode *node) override;
void Clear() override;
public:
void FlushFrame(int Index);
void Reset();
AniScriptParser::AniFrame GetCurrentFrameInfo();
void SetFrameIndex(int Index);
void InterpolationLogic();
// TODO SetOutline
// TODO SetDye
// TODO SetCrop
public:
// Ani是否可用
bool IsUsability = true;
// 当前帧数
int CurrentFrameIndex = 0;
// 总帧数
int TotalFrameIndex = 0;
// 当前帧时间
float CurrentIndexT = 0;
// 当前帧
RefPtr<Sprite> CurrentFrame = nullptr;
// 下帧延迟
float NextFrameDelay = 9999999;
// 染色Flag
bool DyeingFlag = false;
// 插值模式
std::vector<AniScriptParser::AniFrame> InterpolationFlag;
// 关键帧回调
std::function<void(int)> ChangeFrameCallback;
// 结束回调
std::function<void()> EndCallback;
// Ani的标签
std::unordered_map<std::string, AniScriptParser::AniFlag> AnimationFlag;
// 帧对象数组
std::vector<AniScriptParser::AniFrame> FrameArr;
// 图片精灵帧对象
std::vector<RefPtr<Sprite>> SpriteArr;
// Ani类型
std::string Type = "normal";
// Ani路径
std::string AniPath;
// 是否描边
bool IsOutline = false;
// // 描边颜色
// OutlineColor = null;
// // 描边对象List
// OutlineList = null;
// // 当前描边对象
// CurrentOutline = null;
// // 染色颜色
// DyeColor = null;
// // 染色帧List
// DyeFrameList = null;
// // 整体染色
// DyeAllFlag = false;
// // 裁切数据
// CropRect = null;
// 附加选项
std::function<std::string(std::string, Animation::ReplaceData)> AdditionalOptions;
Animation::ReplaceData AdditionalOptionsData;
};

View File

@@ -0,0 +1,7 @@
#include "Component.h"
#include "EngineFrame/Actor/Actor.h"
void Component::Init()
{
addTag(Tag::COMPONENT);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "EngineFrame/Base/BaseNode.h"
#include <SDL.h>
class Actor;
class Component : public BaseNode
{
public:
void Init() override;
};

View File

@@ -0,0 +1,86 @@
#include "RenderBase.h"
#include "EngineFrame/Actor/Actor.h"
RenderBase::RenderBase()
{
}
RenderBase::~RenderBase()
{
}
void RenderBase::CalcRenderInfo()
{
this->CalcRenderInfoFlag = true;
}
void RenderBase::Init()
{
Component::Init();
// 标记该组件需要渲染和更新
addTag(Tag::RENDER);
addTag(Tag::UPDATE);
addTag(Tag::TRANSFORM);
// 计算渲染信息
CalcRenderInfo();
}
void RenderBase::Update(float deltaTime)
{
if (!Visible)
return;
Component::Update(deltaTime);
}
void RenderBase::Render()
{
if (!Visible)
return;
Component::Render();
}
void RenderBase::SetIterationPos(VecFPos pos)
{
Component::SetIterationPos(pos);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetIterationScale(VecFPos scale)
{
Component::SetIterationScale(scale);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetIterationRotation(float angle)
{
if (!Visible)
return;
Component::SetIterationRotation(angle);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetPos(VecFPos pos)
{
Component::SetPos(pos);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetScale(VecFPos scale)
{
Component::SetScale(scale);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetRotation(float angle)
{
Component::SetRotation(angle);
CalcRenderInfo(); // 更新渲染信息
}
void RenderBase::SetAnchor(VecFPos anchor)
{
Component::SetAnchor(anchor);
CalcRenderInfo(); // 更新渲染信息
}

View File

@@ -0,0 +1,50 @@
#pragma once
#include "EngineFrame/Component/Component.h"
#include "Tool/TransformT.h"
class RenderBase : public Component
{
public:
struct RenderGuidanceInfo
{
SDL_Rect rect;
float rotation;
SDL_RendererFlip flip = SDL_FLIP_NONE;
VecPos AnchorPos;
bool Visible = true;
bool IsInScreen;
};
public:
RenderBase(/* args */);
~RenderBase();
public:
void Init() override;
void Update(float deltaTime) override;
void Render() override;
public:
public:
// 设置迭代的坐标
void SetIterationPos(VecFPos pos) override;
// 设置迭代的缩放
void SetIterationScale(VecFPos scale) override;
// 设置迭代的旋转角度
void SetIterationRotation(float angle) override;
// 设置坐标
void SetPos(VecFPos pos) override;
// 设置缩放
void SetScale(VecFPos scale) override;
// 设置旋转角度
void SetRotation(float angle) override;
// 设置中心点
void SetAnchor(VecFPos anchor) override;
// 计算渲染信息
void CalcRenderInfo() override;
};

View File

@@ -0,0 +1,159 @@
#include "Sprite.h"
#include "EngineCore/Game.h"
#include "Text.h"
Sprite::Sprite()
{
}
Sprite::Sprite(std::string imgPath, int Index)
{
this->imgPath = imgPath;
this->Index = Index;
m_texture = new Texture(imgPath, Index);
Init();
CalcRenderInfoLogic(); // 第一次计算
}
Sprite::Sprite(std::string PngPath)
{
m_texture = new Texture(PngPath);
Init();
CalcRenderInfoLogic(); // 第一次计算
}
Sprite::~Sprite()
{
}
RefPtr<Texture> Sprite::GetTexture()
{
return m_texture;
}
void Sprite::SetIterationPos(VecFPos pos)
{
RenderBase::SetIterationPos(pos);
CalcRenderInfo();
}
void Sprite::HandleEvents(SDL_Event *e)
{
}
void Sprite::Update(float deltaTime)
{
if (CalcRenderInfoFlag && Visible)
CalcRenderInfoLogic();
}
void Sprite::CalcRenderInfoLogic()
{
// 计算缩放因子和翻转状态
float scaleX = transformIter.scale.x * transform.scale.x;
float scaleY = transformIter.scale.y * transform.scale.y;
// X轴和Y轴上是否翻转的标志
bool flipX = scaleX < 0;
bool flipY = scaleY < 0;
_RenderGuidanceInfo.flip = SDL_FLIP_NONE;
// 更新翻转状态
if (flipX)
_RenderGuidanceInfo.flip = static_cast<SDL_RendererFlip>(SDL_FLIP_HORIZONTAL | _RenderGuidanceInfo.flip);
if (flipY)
_RenderGuidanceInfo.flip = static_cast<SDL_RendererFlip>(SDL_FLIP_VERTICAL | _RenderGuidanceInfo.flip);
// 纹理数据里带的偏移数据 有这个偏移才能保证动画播放时视觉中心点在一个点上
int texturePosX = flipX ? -(m_texture->TextureSize.width + m_texture->TexturePos.x) + SDL_abs(transform.position.x * 2) : m_texture->TexturePos.x;
int texturePosY = flipY ? -(m_texture->TextureSize.height + m_texture->TexturePos.y) + SDL_abs(transform.position.y * 2) : m_texture->TexturePos.y;
// 先计算Img坐标与精灵坐标合成后的真实坐标
int RealPosX = transform.position.x + texturePosX;
int RealPosY = transform.position.y + texturePosY;
// 计算在世界中的位置
int baseX = transformIter.position.x + RealPosX;
int baseY = transformIter.position.y + RealPosY;
// 获取当前帧的原始尺寸
int frameWidth = m_texture->TextureSize.width;
int frameHeight = m_texture->TextureSize.height;
// 原始锚点偏移(基于帧尺寸)
int origAnchorOffsetX = static_cast<int>(frameWidth * Anchor.x);
int origAnchorOffsetY = static_cast<int>(frameHeight * Anchor.y);
// 缩放的绝对值
float absScaleX = SDL_abs(scaleX);
float absScaleY = SDL_abs(scaleY);
// 缩放后的尺寸
int scaledWidth = static_cast<int>(frameWidth * absScaleX);
int scaledHeight = static_cast<int>(frameHeight * absScaleY);
// 缩放后的锚点偏移
int scaledAnchorOffsetX = static_cast<int>(origAnchorOffsetX * absScaleX);
int scaledAnchorOffsetY = static_cast<int>(origAnchorOffsetY * absScaleY);
// 计算缩放后的锚点偏移与原锚点偏移的差值
int scaleOffsetX = scaledAnchorOffsetX - origAnchorOffsetX;
int scaleOffsetY = scaledAnchorOffsetY - origAnchorOffsetY;
// 最终位置计算:世界位置 + 缩放锚点差值 - 缩放后的锚点偏移 + 纹理数据里带的偏移数据 (计算出绘制点的左上角)
int Xpos = baseX + scaleOffsetX;
int Ypos = baseY + scaleOffsetY;
// 更新渲染信息
_RenderGuidanceInfo.rect = {Xpos, Ypos, scaledWidth, scaledHeight};
_RenderGuidanceInfo.AnchorPos = {scaledAnchorOffsetX, scaledAnchorOffsetY};
// 屏幕内检测
int screenWidth = Game::GetInstance().Screen_W;
int screenHeight = Game::GetInstance().Screen_H;
bool isInScreen = (Xpos + scaledWidth >= 0 && Xpos <= screenWidth && Ypos + scaledHeight >= 0 && Ypos <= screenHeight);
_RenderGuidanceInfo.IsInScreen = isInScreen;
this->Size = {scaledWidth, scaledHeight};
_RenderGuidanceInfo.rotation = transformIter.rotation + transform.rotation;
CalcRenderInfoFlag = false;
}
void Sprite::Render()
{
if (!Visible)
return;
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
if (!m_texture)
return;
if (_RenderGuidanceInfo.IsInScreen && _RenderGuidanceInfo.Visible)
{
if (_RenderGuidanceInfo.rotation != 0.f || _RenderGuidanceInfo.flip != SDL_FLIP_NONE)
{
SDL_Point AnchorPos = _RenderGuidanceInfo.AnchorPos;
SDL_RenderCopyEx(renderer, m_texture->GetTexture(), NULL, &_RenderGuidanceInfo.rect, _RenderGuidanceInfo.rotation, &AnchorPos, _RenderGuidanceInfo.flip);
}
else
{
SDL_RenderCopy(renderer, m_texture->GetTexture(), NULL, &_RenderGuidanceInfo.rect);
// // 设置绘制颜色
// SDL_SetRenderDrawColor(renderer, 255, 0, 0, 128);
// // 绘制填充矩形
// SDL_RenderFillRect(renderer, &_RenderGuidanceInfo.rect);
}
Game::GetInstance().RenderCount++;
}
}
void Sprite::Clear()
{
}
void Sprite::SetBlendMode(SDL_BlendMode blendMode)
{
if (GetBlendMode() != blendMode)
m_texture->SetBlendMode(blendMode);
}
SDL_BlendMode Sprite::GetBlendMode()
{
return m_texture->GetBlendMode();
}

View File

@@ -0,0 +1,77 @@
#pragma once
#include <string>
#include "Asset/Asset_ImagePack.h"
#include "EngineFrame/Component/RenderBase.h"
#include "EngineFrame/Render/Texture.h"
class Game;
/**
* @brief Sprite类继承自Component类用于表示游戏中的精灵组件
*/
class Sprite : public RenderBase
{
protected:
/* data */
RefPtr<Texture> m_texture = nullptr;
public:
/**
* @brief Sprite类的默认构造函数
*/
Sprite(/* args */);
/**
* @brief Sprite类的带参数构造函数
* @param imgPath 纹理图片路径
* @param Index 索引值
*/
Sprite(std::string imgPath, int Index);
/**
* @brief Sprite类的带参数构造函数
* @param imgPath 纹理图片文件路径
*/
Sprite(std::string PngPath);
/**
* @brief Sprite类的析构函数
*/
~Sprite();
/**
* @brief 处理事件
* @param e SDL事件指针
*/
void HandleEvents(SDL_Event *e) override;
/**
* @brief 更新组件状态
* @param deltaTime 时间增量
*/
void Update(float deltaTime) override;
/**
* @brief 渲染组件
* @param deltaTime 时间增量
*/
void Render() override;
/**
* @brief 清理组件资源
*/
void Clear() override;
/**
* @brief 获取纹理
* @return SDL_Texture* 纹理指针
*/
RefPtr<Texture> GetTexture();
void SetIterationPos(VecFPos pos) override;
public:
RenderGuidanceInfo _RenderGuidanceInfo;
std::string imgPath;
int Index;
public:
// 计算渲染信息
void CalcRenderInfoLogic();
// 设置混合模式
void SetBlendMode(SDL_BlendMode blendMode);
// 获取混合模式
SDL_BlendMode GetBlendMode();
};

View File

@@ -0,0 +1,207 @@
#include "EngineFrame/Component/Text.h"
#include "Text.h"
#include "EngineCore/Game.h"
Text::Text()
{
}
Text::Text(std::string Str, TTF_Font *font, SDL_Color color)
{
m_text = Str;
m_font = font;
m_text_color = color;
Init(Str, font, color);
}
Text::Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize)
{
m_text = Str;
m_font = font;
m_text_color = textColor;
m_stroke_color = strokeColor;
m_stroke_size = strokeSize;
Init(Str, font, textColor, strokeColor, strokeSize);
}
Text::~Text()
{
}
void Text::Init(std::string Str, TTF_Font *font, SDL_Color color)
{
// 标记该组件需要渲染和更新
addTag(Tag::RENDER);
addTag(Tag::UPDATE);
// TTF_SetFontOutline(font, 1);
// 先渲染为表面
SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), color);
if (!textSurface)
{
SDL_LogError(0, "文字渲染为表面失败TTF_Error:%s", TTF_GetError());
}
// 再将表面转换为纹理
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
m_texture = SDL_CreateTextureFromSurface(renderer, textSurface);
if (!m_texture)
{
SDL_LogError(0, "表面转换为纹理失败SDL_Error:%s", SDL_GetError());
}
// 设置纹理过滤模式为最近邻,避免缩放模糊
SDL_SetTextureScaleMode(m_texture, SDL_ScaleModeNearest);
Size.x = textSurface->w;
Size.y = textSurface->h;
TextureSize.x = textSurface->w;
TextureSize.y = textSurface->h;
// 释放表面
SDL_FreeSurface(textSurface);
}
void Text::Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize)
{
// 先保存原始字体的轮廓设置
int originalOutline = TTF_GetFontOutline(font);
// 设置字体轮廓大小(描边宽度)
TTF_SetFontOutline(font, strokeSize);
// 渲染描边(使用描边颜色)
SDL_Surface *strokeSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), strokeColor);
if (!strokeSurface)
{
SDL_LogError(0, "描边渲染为表面失败TTF_Error:%s", TTF_GetError());
TTF_SetFontOutline(font, originalOutline); // 恢复原始设置
return;
}
// 恢复字体轮廓设置,用于渲染文字本身
TTF_SetFontOutline(font, 0);
// 渲染文字本身(使用文字颜色)
SDL_Surface *textSurface = TTF_RenderUTF8_Blended(font, Str.c_str(), textColor);
if (!textSurface)
{
SDL_LogError(0, "文字渲染为表面失败TTF_Error:%s", TTF_GetError());
SDL_FreeSurface(strokeSurface);
TTF_SetFontOutline(font, originalOutline); // 恢复原始设置
return;
}
// 创建一个合并描边和文字的表面
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
// 计算最终纹理大小(描边会增加额外尺寸)
int finalWidth = strokeSurface->w;
int finalHeight = strokeSurface->h;
// 创建一个临时表面用于合并描边和文字
SDL_Surface *finalSurface = SDL_CreateRGBSurfaceWithFormat(
0, finalWidth, finalHeight, 32, SDL_PIXELFORMAT_RGBA32);
if (!finalSurface)
{
SDL_LogError(0, "创建最终表面失败SDL_Error:%s", SDL_GetError());
SDL_FreeSurface(textSurface);
SDL_FreeSurface(strokeSurface);
TTF_SetFontOutline(font, originalOutline);
return;
}
// 将描边绘制到最终表面
SDL_Rect strokeRect = {0, 0, strokeSurface->w, strokeSurface->h};
SDL_BlitSurface(strokeSurface, nullptr, finalSurface, &strokeRect);
// 计算文字在描边中间的位置
SDL_Rect textRect = {
strokeSize, // X偏移描边宽度
strokeSize, // Y偏移描边宽度
textSurface->w,
textSurface->h};
SDL_BlitSurface(textSurface, nullptr, finalSurface, &textRect);
// 将合并后的表面转换为纹理
m_texture = SDL_CreateTextureFromSurface(renderer, finalSurface);
if (!m_texture)
{
SDL_LogError(0, "表面转换为纹理失败SDL_Error:%s", SDL_GetError());
}
// 设置尺寸信息
Size.x = finalSurface->w;
Size.y = finalSurface->h;
TextureSize.x = finalSurface->w;
TextureSize.y = finalSurface->h;
// 释放所有临时表面
SDL_FreeSurface(textSurface);
SDL_FreeSurface(strokeSurface);
SDL_FreeSurface(finalSurface);
// 恢复字体原始轮廓设置
TTF_SetFontOutline(font, originalOutline);
}
void Text::HandleEvents(SDL_Event *e)
{
}
void Text::Update(float deltaTime)
{
}
void Text::Render()
{
SDL_Renderer *renderer = Game::GetInstance().GetRenderer();
if (!renderer || !m_texture)
return;
SDL_Rect dstrect = {Pos.x, Pos.y, Size.x, Size.y};
SDL_RenderCopy(renderer, m_texture, NULL, &dstrect);
}
void Text::Clear()
{
}
void Text::SetPos(SDL_Point pos)
{
Pos = pos;
}
SDL_Point Text::GetPos()
{
return Pos;
}
void Text::SetText(std::string Str)
{
if (Str == m_text)
return;
if (!m_font)
{
SDL_LogError(0, "SetText失败字体指针为空");
return;
}
// 如果有原纹理先删除原纹理
if (m_texture)
{
SDL_DestroyTexture(m_texture);
m_texture = nullptr; // 置空指针
}
m_text = Str;
// 根据是否有描边选择对应的Init方法
if (m_stroke_size > 0)
{
Init(Str, m_font, m_text_color, m_stroke_color, m_stroke_size);
}
else
{
Init(Str, m_font, m_text_color);
}
}
std::string Text::GetText()
{
return m_text;
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include <string>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include "EngineFrame/Component/Component.h"
class Game;
class Text : public Component
{
private:
/* data */
SDL_Texture *m_texture = nullptr;
public:
Text(/* args */);
Text(std::string Str, TTF_Font *font, SDL_Color color);
Text(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize);
~Text();
// 显式引入基类的Init方法避免隐藏
using Component::Init;
void Init(std::string Str, TTF_Font *font, SDL_Color color);
void Init(std::string Str, TTF_Font *font, SDL_Color textColor, SDL_Color strokeColor, int strokeSize);
void HandleEvents(SDL_Event *e) override;
void Update(float deltaTime) override;
void Render() override;
void Clear() override;
SDL_Texture *GetTexture();
public:
// 组件标签
Tag m_tag = Tag::RENDER | Tag::UPDATE; // 标记该组件需要渲染和更新
std::string m_text;
TTF_Font *m_font;
SDL_Color m_text_color;
SDL_Color m_stroke_color;
int m_stroke_size = 0;
SDL_Point Pos = {0, 0}; // 位置坐标
SDL_Point TextureSize = {0, 0}; // 纹理大小
SDL_Point Size = {0, 0}; // 大小
SDL_Point Anchor = {0, 0}; // 中心点
float Angle = 0.0f; // 旋转角度
SDL_RendererFlip flip = SDL_FLIP_NONE; // 翻转
public:
// 设置坐标
void SetPos(SDL_Point pos);
// 设置文本
void SetText(std::string Str);
// 获取坐标
SDL_Point GetPos();
// 获取文本
std::string GetText();
};