diff --git a/Frostbite2D/include/frostbite2D/animation/animation.h b/Frostbite2D/include/frostbite2D/animation/animation.h new file mode 100644 index 0000000..003c512 --- /dev/null +++ b/Frostbite2D/include/frostbite2D/animation/animation.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace frostbite2D { + +class Sprite; + +class Animation : public Actor { +public: + struct ReplaceData { + int param1 = 0; + int param2 = 0; + ReplaceData() = default; + ReplaceData(int p1, int p2) : param1(p1), param2(p2) {} + }; + +public: + Animation(); + explicit Animation(const std::string& aniPath); + Animation(const std::string& aniPath, + std::function additionalOptions, + ReplaceData data); + ~Animation(); + + void Init(const std::string& aniPath); + + void Update(float deltaTime) override; + void OnAdded(Actor* parent); + void SetVisible(bool visible); + +public: + void FlushFrame(int index); + void Reset(); + animation::AniFrame GetCurrentFrameInfo(); + void SetFrameIndex(int index); + void InterpolationLogic(); + + Vec2 GetMaxSize() const; + + bool IsUsable() const { return usable_; } + void SetUsable(bool usable) { usable_ = usable; } + + int GetCurrentFrameIndex() const { return currentFrameIndex_; } + int GetTotalFrameCount() const { return totalFrameCount_; } + +public: + bool usable_ = true; + int currentFrameIndex_ = 0; + int totalFrameCount_ = 0; + float currentFrameTime_ = 0.0f; + Ptr currentFrame_ = nullptr; + float nextFrameDelay_ = 9999999.0f; + + bool isLooping_ = true; + + std::function changeFrameCallback_; + std::function endCallback_; + + std::vector frames_; + std::vector> spriteFrames_; + + std::unordered_map animationFlag_; + + std::string type_ = "normal"; + std::string aniPath_; + + std::function additionalOptions_; + ReplaceData additionalOptionsData_; + + Vec2 maxSize_ = Vec2::Zero(); + + std::vector interpolationData_; +}; + +} // namespace frostbite2D diff --git a/Frostbite2D/include/frostbite2D/animation/animation_data.h b/Frostbite2D/include/frostbite2D/animation/animation_data.h new file mode 100644 index 0000000..617a3ac --- /dev/null +++ b/Frostbite2D/include/frostbite2D/animation/animation_data.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace frostbite2D { + +class BinaryReader; + +namespace animation { + +// --------------------------------------------------------------------------- +// 类型定义 +// --------------------------------------------------------------------------- + +using AniFlag = std::variant< + int, + float, + Vec2, + std::string, + std::vector, + std::vector>; + +// --------------------------------------------------------------------------- +// 数据结构 +// --------------------------------------------------------------------------- + +struct AniFrame { + std::string imgPath; // 图片路径 + int imgIndex = 0; // 图片索引 + Vec2 imgPos; // 图片位置 + std::vector> attackBox; // 攻击框 [x, y, w, h, type, param] + std::vector> damageBox; // 受击框 [x, y, w, h, type, param] + std::unordered_map flag; // 帧特效数据 + int delay = 0; // 延迟(毫秒) +}; + +struct AniInfo { + std::vector imgList; // 图片列表 + std::vector frames; // 帧列表 + std::unordered_map flag; // 动画特效数据 +}; + +struct AlsAniInfo { + std::string path; // 路径 + std::vector layer; // 图层 [zOrder, subLayer] +}; + +struct AlsInfo { + std::unordered_map aniList; // ALS 动画列表 +}; + +// --------------------------------------------------------------------------- +// 工具函数 +// --------------------------------------------------------------------------- + +std::string getAniFlag(int type); +std::string getAniEffectType(int type); +std::string getAniFlipType(int type); +std::string getAniDamageType(int type); + +// --------------------------------------------------------------------------- +// 解析函数 +// --------------------------------------------------------------------------- + +/** + * @brief 解析 .ani 二进制数据 + * @param data 二进制数据 + * @param size 数据大小 + * @return 解析后的动画信息 + */ +AniInfo parseAniInfo(const char* data, size_t size); + +/** + * @brief 从 PVF 路径解析 .ani 文件 + * @param path PVF 中的文件路径(如 "character/player/attack.ani") + * @return 解析后的动画信息,解析失败返回 std::nullopt + */ +std::optional parseAniFromPvf(const std::string& path); + +/** + * @brief 解析 .als 文本数据 + * @param data 文本数据 + * @return 解析后的 ALS 信息 + */ +AlsInfo parseAlsInfo(const std::string& data); + +/** + * @brief 从 PVF 路径解析 .als 文件 + * @param path PVF 中的文件路径 + * @return 解析后的 ALS 信息,解析失败返回 std::nullopt + */ +std::optional parseAlsFromPvf(const std::string& path); + +} // namespace animation + +} // namespace frostbite2D diff --git a/Frostbite2D/src/frostbite2D/animation/animation.cpp b/Frostbite2D/src/frostbite2D/animation/animation.cpp new file mode 100644 index 0000000..0b6bb1e --- /dev/null +++ b/Frostbite2D/src/frostbite2D/animation/animation.cpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace frostbite2D::animation; + +namespace frostbite2D { + +Animation::Animation() { +} + +Animation::Animation(const std::string& aniPath) { + Init(aniPath); +} + +Animation::Animation(const std::string& aniPath, + std::function additionalOptions, + ReplaceData data) + : additionalOptions_(additionalOptions) + , additionalOptionsData_(data) { + Init(aniPath); +} + +Animation::~Animation() { +} + +void Animation::Init(const std::string& aniPath) { + auto info = animation::parseAniFromPvf(aniPath); + if (!info) { + SDL_Log("Failed to load animation: %s", aniPath.c_str()); + usable_ = false; + return; + } + + aniPath_ = aniPath; + animationFlag_ = info->flag; + frames_ = std::move(info->frames); + + if (animationFlag_.count("LOOP")) { + isLooping_ = true; + } else { + isLooping_ = false; + } + + for (size_t i = 0; i < frames_.size(); i++) { + AniFrame& frameObj = frames_[i]; + Ptr spriteObj = nullptr; + + if (!frameObj.imgPath.empty()) { + if (additionalOptions_) { + frameObj.imgPath = additionalOptions_(frameObj.imgPath, additionalOptionsData_); + } + + auto sprite = Sprite::createFromNpk(frameObj.imgPath, frameObj.imgIndex); + if (sprite) { + sprite->SetPosition(frameObj.imgPos); + sprite->SetVisible(false); + spriteObj = sprite; + } + } + + if (!spriteObj) { + spriteObj = MakePtr(); + spriteObj->SetVisible(false); + } + + spriteFrames_.push_back(spriteObj); + + auto spriteSize = spriteObj->GetSize(); + if (maxSize_.x < spriteSize.x) maxSize_.x = spriteSize.x; + if (maxSize_.y < spriteSize.y) maxSize_.y = spriteSize.y; + + AddChild(spriteObj); + } + + if (currentFrameTime_ == 0.0f && !spriteFrames_.empty()) { + SetSize(spriteFrames_[0]->GetSize()); + } + + totalFrameCount_ = static_cast(frames_.size()); + + auto alsPath = aniPath + ".als"; + if (PvfArchive::get().hasFile(alsPath)) { + auto alsInfo = animation::parseAlsFromPvf(alsPath); + if (alsInfo && !alsInfo->aniList.empty()) { + size_t lastSlash = aniPath.find_last_of('/'); + std::string dir = (lastSlash != std::string::npos) ? aniPath.substr(0, lastSlash + 1) : ""; + + for (auto& ani : alsInfo->aniList) { + auto subAni = MakePtr(dir + ani.second.path); + if (ani.second.layer.size() >= 2) { + subAni->SetZOrder(ani.second.layer[1]); + } + AddChild(subAni); + } + } + } + + FlushFrame(0); +} + +void Animation::Update(float deltaTime) { + if (usable_ && IsVisible()) { + float dtMs = deltaTime * 1000.0f; + currentFrameTime_ += dtMs; + + while (currentFrameTime_ >= nextFrameDelay_) { + currentFrameTime_ -= nextFrameDelay_; + + if (currentFrameIndex_ < (totalFrameCount_ - 1)) { + FlushFrame(currentFrameIndex_ + 1); + } else { + if (isLooping_) { + FlushFrame(0); + } else { + usable_ = false; + if (endCallback_) { + endCallback_(); + } + break; + } + } + } + } else { + for (auto& sp : spriteFrames_) { + sp->SetVisible(false); + } + } +} + +void Animation::OnAdded(Actor* parent) { + FlushFrame(0); +} + +void Animation::SetVisible(bool visible) { + if (visible) { + if (currentFrame_) { + currentFrame_->SetVisible(true); + } + } + Actor::SetVisible(visible); +} + +void Animation::FlushFrame(int index) { + if (currentFrame_) { + currentFrame_->SetVisible(false); + } + + currentFrameIndex_ = index; + currentFrame_ = spriteFrames_[currentFrameIndex_]; + currentFrame_->SetVisible(true); + + AniFrame& frameInfo = frames_[currentFrameIndex_]; + nextFrameDelay_ = static_cast(frameInfo.delay); + + auto& flagBuf = frameInfo.flag; + + if (flagBuf.count("SET_FLAG")) { + if (changeFrameCallback_) { + auto flagValue = std::get(flagBuf.at("SET_FLAG")); + changeFrameCallback_(flagValue); + } + } + + if (flagBuf.count("PLAY_SOUND")) { + } + + if (flagBuf.count("IMAGE_RATE")) { + auto rate = std::get(flagBuf.at("IMAGE_RATE")); + currentFrame_->SetScale(rate); + currentFrame_->SetPosition(frameInfo.imgPos.x * rate.x, frameInfo.imgPos.y * rate.y); + } + + if (flagBuf.count("GRAPHIC_EFFECT_LINEARDODGE")) { + currentFrame_->SetBlendMode(BlendMode::Additive); + } + + if (flagBuf.count("IMAGE_ROTATE")) { + auto rotation = std::get(flagBuf.at("IMAGE_ROTATE")); + currentFrame_->SetRotation(rotation); + } + + if (flagBuf.count("INTERPOLATION")) { + if (interpolationData_.empty()) { + interpolationData_.push_back(frames_[currentFrameIndex_]); + if (currentFrameIndex_ + 1 < static_cast(frames_.size())) { + interpolationData_.push_back(frames_[currentFrameIndex_ + 1]); + } + } + } else { + if (!interpolationData_.empty()) { + interpolationData_.clear(); + } + } + + SetSize(currentFrame_->GetSize()); +} + +void Animation::Reset() { + usable_ = true; + currentFrameTime_ = 0.0f; + FlushFrame(0); +} + +AniFrame Animation::GetCurrentFrameInfo() { + return frames_[currentFrameIndex_]; +} + +void Animation::SetFrameIndex(int index) { + FlushFrame(index); + currentFrameTime_ = 0.0f; +} + +void Animation::InterpolationLogic() { + if (interpolationData_.empty()) { + return; + } + + float interRate = math::lerp(0.0f, 1.0f, currentFrameTime_ / nextFrameDelay_); + + AniFrame& oldData = interpolationData_[0]; + AniFrame& newData = interpolationData_[1]; + + { + std::vector oldRgbaData = {255, 255, 255, 250}; + std::vector newRgbaData = {255, 255, 255, 250}; + + if (oldData.flag.count("RGBA")) { + oldRgbaData = std::get>(oldData.flag.at("RGBA")); + } + if (newData.flag.count("RGBA")) { + newRgbaData = std::get>(newData.flag.at("RGBA")); + } + + std::vector rgbaData = { + static_cast(oldRgbaData[0] + (newRgbaData[0] - oldRgbaData[0]) * interRate), + static_cast(oldRgbaData[1] + (newRgbaData[1] - oldRgbaData[1]) * interRate), + static_cast(oldRgbaData[2] + (newRgbaData[2] - oldRgbaData[2]) * interRate), + static_cast(oldRgbaData[3] + (newRgbaData[3] - oldRgbaData[3]) * interRate)}; + + currentFrame_->SetColor( + static_cast(rgbaData[0]) / 255.0f, + static_cast(rgbaData[1]) / 255.0f, + static_cast(rgbaData[2]) / 255.0f, + static_cast(rgbaData[3]) / 255.0f); + } + + { + Vec2 posData = Vec2::lerp(oldData.imgPos, newData.imgPos, interRate); + currentFrame_->SetPosition(posData); + } + + { + Vec2 oldRateData = Vec2::One(); + Vec2 newRateData = Vec2::One(); + + if (oldData.flag.count("IMAGE_RATE")) { + oldRateData = std::get(oldData.flag.at("IMAGE_RATE")); + } + if (newData.flag.count("IMAGE_RATE")) { + newRateData = std::get(newData.flag.at("IMAGE_RATE")); + } + + Vec2 rateData = Vec2::lerp(oldRateData, newRateData, interRate); + currentFrame_->SetScale(rateData); + } + + { + float oldAngleData = 0.0f; + float newAngleData = 0.0f; + + if (oldData.flag.count("IMAGE_ROTATE")) { + oldAngleData = std::get(oldData.flag.at("IMAGE_ROTATE")); + } + if (newData.flag.count("IMAGE_ROTATE")) { + newAngleData = std::get(newData.flag.at("IMAGE_ROTATE")); + } + + float angleData = math::lerp(oldAngleData, newAngleData, interRate); + currentFrame_->SetRotation(angleData); + } +} + +Vec2 Animation::GetMaxSize() const { + return maxSize_; +} + +} // namespace frostbite2D diff --git a/Frostbite2D/src/frostbite2D/animation/animation_data.cpp b/Frostbite2D/src/frostbite2D/animation/animation_data.cpp new file mode 100644 index 0000000..c8b15a3 --- /dev/null +++ b/Frostbite2D/src/frostbite2D/animation/animation_data.cpp @@ -0,0 +1,381 @@ +#include +#include +#include +#include +#include +#include + +namespace frostbite2D { + +namespace animation { + +// --------------------------------------------------------------------------- +// 工具函数实现 +// --------------------------------------------------------------------------- + +static std::string toLowerCase(const std::string& str) { + std::string result = str; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return result; +} + +std::string getAniFlag(int type) { + switch (type) { + case 0: return {"LOOP"}; + case 1: return {"SHADOW"}; + case 3: return {"COORD"}; + case 7: return {"IMAGE_RATE"}; + case 8: return {"IMAGE_ROTATE"}; + case 9: return {"RGBA"}; + case 10: return {"INTERPOLATION"}; + case 11: return {"GRAPHIC_EFFECT"}; + case 12: return {"DELAY"}; + case 13: return {"DAMAGE_TYPE"}; + case 14: return {"DAMAGE_BOX"}; + case 15: return {"ATTACK_BOX"}; + case 16: return {"PLAY_SOUND"}; + case 17: return {"PRELOAD"}; + case 18: return {"SPECTRUM"}; + case 23: return {"SET_FLAG"}; + case 24: return {"FLIP_TYPE"}; + case 25: return {"LOOP_START"}; + case 26: return {"LOOP_END"}; + case 27: return {"CLIP"}; + case 28: return {"OPERATION"}; + default: return {}; + } +} + +std::string getAniEffectType(int type) { + switch (type) { + case 0: return {"NONE"}; + case 1: return {"DODGE"}; + case 2: return {"LINEARDODGE"}; + case 3: return {"DARK"}; + case 4: return {"XOR"}; + case 5: return {"MONOCHROME"}; + case 6: return {"SPACEDISTORT"}; + default: return {}; + } +} + +std::string getAniFlipType(int type) { + switch (type) { + case 1: return {"HORIZON"}; + case 2: return {"VERTICAL"}; + case 3: return {"ALL"}; + default: return {}; + } +} + +std::string getAniDamageType(int type) { + switch (type) { + case 0: return {"NORMAL"}; + case 1: return {"SUPERARMOR"}; + case 2: return {"UNBREAKABLE"}; + default: return {}; + } +} + +// --------------------------------------------------------------------------- +// 辅助函数 +// --------------------------------------------------------------------------- + +static uint8 read256(BinaryReader& reader) { + uint8 value = reader.readUInt8(); + return value == 255 ? 256 : value; +} + +static uint16 readUShort(BinaryReader& reader) { + return reader.readUInt16(); +} + +static int16 readShort(BinaryReader& reader) { + return reader.readInt16(); +} + +// --------------------------------------------------------------------------- +// 解析函数实现 +// --------------------------------------------------------------------------- + +AniInfo parseAniInfo(const char* data, size_t size) { + AniInfo info; + BinaryReader reader(data, size); + + if (!reader.isOpen() || reader.size() < 4) { + return info; + } + + uint16 frameMax = reader.readUInt16(); + uint16 imgCount = reader.readUInt16(); + + for (uint16 i = 0; i < imgCount; i++) { + int buf = reader.readInt32(); + std::string imgPath = "sprite/" + reader.readString(buf); + info.imgList.push_back(toLowerCase(imgPath)); + } + + uint16 aniHeaderItemCount = reader.readUInt16(); + for (uint16 i = 0; i < aniHeaderItemCount; i++) { + uint16 type = reader.readUInt16(); + switch (type) { + case 0: + case 1: { + std::string key = getAniFlag(type); + int value = reader.readUInt8(); + if (!key.empty()) { + info.flag.emplace(key, value); + } + break; + } + case 3: + case 28: { + std::string key = getAniFlag(type); + int value = reader.readUInt16(); + if (!key.empty()) { + info.flag.emplace(key, value); + } + break; + } + case 18: { + reader.readUInt8(); + reader.readInt32(); + reader.readInt32(); + reader.readInt32(); + read256(reader); + read256(reader); + read256(reader); + read256(reader); + reader.readUInt16(); + break; + } + default: + break; + } + } + + for (uint16 i = 0; i < frameMax; i++) { + AniFrame frame; + + uint16 boxItemCount = reader.readUInt16(); + for (uint16 j = 0; j < boxItemCount; j++) { + uint16 boxType = reader.readUInt16(); + std::vector boxData; + for (int k = 0; k < 6; k++) { + boxData.push_back(reader.readInt32()); + } + if (boxType == 15) { + frame.attackBox.push_back(boxData); + } else { + frame.damageBox.push_back(boxData); + } + } + + int16 indexBuf = reader.readInt16(); + if (indexBuf != -1) { + frame.imgPath = info.imgList[indexBuf]; + frame.imgIndex = reader.readUInt16(); + } else { + frame.imgPath = ""; + frame.imgIndex = 0; + } + + frame.imgPos.x = static_cast(reader.readInt32()); + frame.imgPos.y = static_cast(reader.readInt32()); + + uint16 imgFlagCount = reader.readUInt16(); + for (uint16 j = 0; j < imgFlagCount; j++) { + uint16 imgFlagType = reader.readUInt16(); + std::string key; + int value; + + switch (imgFlagType) { + case 0: + case 1: + case 10: { + key = getAniFlag(imgFlagType); + value = reader.readUInt8(); + if (!key.empty()) { + frame.flag.emplace(key, value); + } + break; + } + case 3: { + key = "COORD"; + value = reader.readUInt16(); + frame.flag.emplace(key, value); + break; + } + case 17: { + key = "PRELOAD"; + value = 1; + frame.flag.emplace(key, value); + break; + } + case 7: { + key = "IMAGE_RATE"; + Vec2 rate{reader.readFloat(), reader.readFloat()}; + frame.flag.emplace(key, rate); + break; + } + case 8: { + key = "IMAGE_ROTATE"; + float rateBuffer = reader.readFloat(); + frame.flag.emplace(key, rateBuffer); + break; + } + case 9: { + key = "RGBA"; + std::vector rgba = { + static_cast(read256(reader)), + static_cast(read256(reader)), + static_cast(read256(reader)), + static_cast(read256(reader))}; + frame.flag.emplace(key, rgba); + break; + } + case 11: { + uint16 effectType = reader.readUInt16(); + key = "GRAPHIC_EFFECT_" + getAniEffectType(effectType); + std::vector effect; + switch (effectType) { + case 5: + effect.push_back(static_cast(read256(reader))); + effect.push_back(static_cast(read256(reader))); + effect.push_back(static_cast(read256(reader))); + break; + case 6: + effect.push_back(static_cast(read256(reader))); + effect.push_back(static_cast(read256(reader))); + break; + } + frame.flag.emplace(key, effect); + break; + } + case 12: { + value = reader.readInt32(); + frame.delay = value; + break; + } + case 13: { + key = "DAMAGE_TYPE"; + std::string dtype = getAniDamageType(reader.readUInt16()); + frame.flag.emplace(key, dtype); + break; + } + case 16: { + int soundSize = reader.readInt32(); + key = "PLAY_SOUND"; + std::string sound = reader.readString(soundSize); + frame.flag.emplace(key, sound); + break; + } + case 23: { + key = "SET_FLAG"; + value = reader.readInt32(); + frame.flag.emplace(key, value); + break; + } + case 24: { + key = "FLIP_TYPE"; + std::string ftValue = getAniFlipType(reader.readUInt16()); + frame.flag.emplace(key, ftValue); + break; + } + case 25: { + key = "LOOP_START"; + frame.flag.emplace(key, 1); + break; + } + case 26: { + key = "LOOP_END"; + value = reader.readInt32(); + frame.flag.emplace(key, value); + break; + } + case 27: { + key = "CLIP"; + std::vector clipArr = { + reader.readInt16(), + reader.readInt16(), + reader.readInt16(), + reader.readInt16()}; + frame.flag.emplace(key, clipArr); + break; + } + default: + break; + } + } + + info.frames.push_back(frame); + } + + return info; +} + +std::optional parseAniFromPvf(const std::string& path) { + auto& pvf = PvfArchive::get(); + if (!pvf.isOpen()) { + return std::nullopt; + } + + auto rawData = pvf.getFileRawData(path); + if (!rawData) { + return std::nullopt; + } + + return parseAniInfo(rawData->data.get(), rawData->size); +} + +AlsInfo parseAlsInfo(const std::string& data) { + AlsInfo info; + + std::istringstream stream(data); + std::string line; + + while (std::getline(stream, line)) { + if (line.empty()) continue; + + std::istringstream lineStream(line); + std::string segment; + lineStream >> segment; + + if (segment == "[use animation]") { + std::string aniPath, aniKey; + lineStream >> aniPath >> aniKey; + info.aniList[aniKey].path = toLowerCase(aniPath); + } else if (segment == "[none effect add]") { + int layer1, layer2; + std::string aniKey; + lineStream >> layer1 >> layer2 >> aniKey; + info.aniList[aniKey].layer = {layer1, layer2}; + } else if (segment == "[add]") { + int layer1, layer2; + std::string aniKey; + lineStream >> layer1 >> layer2 >> aniKey; + info.aniList[aniKey].layer = {layer1, layer2}; + } + } + + return info; +} + +std::optional parseAlsFromPvf(const std::string& path) { + auto& pvf = PvfArchive::get(); + if (!pvf.isOpen()) { + return std::nullopt; + } + + auto content = pvf.getFileContent(path); + if (!content) { + return std::nullopt; + } + + return parseAlsInfo(*content); +} + +} // namespace animation + +} // namespace frostbite2D diff --git a/Game/assets/ImagePacks2/!HUD_CW.NPK b/Game/assets/ImagePacks2/!HUD_CW.NPK index b93b362..4fc7a81 100644 Binary files a/Game/assets/ImagePacks2/!HUD_CW.NPK and b/Game/assets/ImagePacks2/!HUD_CW.NPK differ diff --git a/Game/assets/ImagePacks2/sprite_monster_goblin_event.NPK b/Game/assets/ImagePacks2/sprite_monster_goblin_event.NPK new file mode 100644 index 0000000..549e59b Binary files /dev/null and b/Game/assets/ImagePacks2/sprite_monster_goblin_event.NPK differ diff --git a/Game/src/main.cpp b/Game/src/main.cpp index 6fde6f9..613db75 100644 --- a/Game/src/main.cpp +++ b/Game/src/main.cpp @@ -24,6 +24,8 @@ #include #include +#include + using namespace frostbite2D; int main(int argc, char **argv) { @@ -43,9 +45,30 @@ int main(int argc, char **argv) { // SDL_Log("Starting main loop..."); + auto &pvf = PvfArchive::get(); + if (pvf.open("assets/Script.pvf")) { + pvf.init(); + SDL_Log("PVF initialized successfully"); + } + + NpkArchive &npk = NpkArchive::get(); + npk.setImagePackDirectory("assets/ImagePacks2"); + npk.setDefaultImg("sprite/interface/base.img", 0); + npk.init(); + auto menuScene = MakePtr(); SceneManager::get().PushScene(menuScene); + auto ani = MakePtr( + "monster/event/bluemarble/goblin/animation_goblin2/move.ani"); + + if (ani) { + SDL_Log("Animation created successfully"); + ani->SetAnchor(Vec2(0.5f, 0.5f)); + ani->SetPosition(640, 360); + menuScene->AddChild(ani); + } + auto TestActor = MakePtr(); menuScene->AddChild(TestActor); @@ -85,21 +108,15 @@ int main(int argc, char **argv) { return true; }); - // // 尝试加载精灵 - // auto sprite = Sprite::createFromFile("assets/player.png"); - // if (sprite) { - // sprite->SetPosition(320, 300); - // sprite->SetOpacity(0.8f); - // // sprite->SetAnchor(Vec2(0.5f, 0.5f)); - // // sprite->SetRotation(30.f); - // sprite->SetZOrder(2000); - // // sprite->SetScale(Vec2(-1.0f, 1.0f)); - // menuScene->AddChild(sprite); - // SDL_Log("Sprite created and added to scene"); - // } else { - // SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from file!"); - // } - + auto sprite1 = Sprite::createFromNpk("sprite/newtitle/nangua.img", 0); + if (sprite1) { + sprite1->SetPosition(220, 10); + // sprite1->SetScale(2.0f); + menuScene->AddChild(sprite1); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from NPK!"); + } + // // auto &archive = PvfArchive::get(); // // if (archive.open("assets/Script.pvf")) { // // archive.init(); @@ -159,19 +176,7 @@ int main(int argc, char **argv) { // SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load background music!"); // } - // NpkArchive &npk = NpkArchive::get(); - // npk.setImagePackDirectory("assets/ImagePacks2"); - // npk.setDefaultImg("sprite/interface/base.img", 0); - // npk.init(); - // auto sprite1 = Sprite::createFromNpk("sprite/newtitle/nangua.img", 0); - // if (sprite1) { - // sprite1->SetPosition(220, 10); - // // sprite1->SetScale(2.0f); - // sprite->AddChild(sprite1); - // } else { - // SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from NPK!"); - // } // SoundPackArchive &archive = SoundPackArchive::get(); // archive.setSoundPackDirectory("assets/SoundPacks");