feat(动画系统): 实现动画系统基础功能

添加动画组件及相关数据结构,支持从PVF加载动画资源
实现动画播放、帧控制、插值逻辑等功能
更新主程序以测试动画系统
This commit is contained in:
2026-03-28 04:29:11 +08:00
parent 08570f3aab
commit b5e5fe5e63
7 changed files with 888 additions and 27 deletions

View File

@@ -0,0 +1,81 @@
#pragma once
#include <frostbite2D/2d/actor.h>
#include <frostbite2D/animation/animation_data.h>
#include <functional>
#include <string>
#include <vector>
#include <unordered_map>
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<std::string(std::string, ReplaceData)> 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<Sprite> currentFrame_ = nullptr;
float nextFrameDelay_ = 9999999.0f;
bool isLooping_ = true;
std::function<void(int)> changeFrameCallback_;
std::function<void()> endCallback_;
std::vector<animation::AniFrame> frames_;
std::vector<Ptr<Sprite>> spriteFrames_;
std::unordered_map<std::string, animation::AniFlag> animationFlag_;
std::string type_ = "normal";
std::string aniPath_;
std::function<std::string(std::string, ReplaceData)> additionalOptions_;
ReplaceData additionalOptionsData_;
Vec2 maxSize_ = Vec2::Zero();
std::vector<animation::AniFrame> interpolationData_;
};
} // namespace frostbite2D

View File

@@ -0,0 +1,102 @@
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <variant>
#include <vector>
#include <string>
#include <unordered_map>
#include <optional>
namespace frostbite2D {
class BinaryReader;
namespace animation {
// ---------------------------------------------------------------------------
// 类型定义
// ---------------------------------------------------------------------------
using AniFlag = std::variant<
int,
float,
Vec2,
std::string,
std::vector<int>,
std::vector<float>>;
// ---------------------------------------------------------------------------
// 数据结构
// ---------------------------------------------------------------------------
struct AniFrame {
std::string imgPath; // 图片路径
int imgIndex = 0; // 图片索引
Vec2 imgPos; // 图片位置
std::vector<std::vector<int>> attackBox; // 攻击框 [x, y, w, h, type, param]
std::vector<std::vector<int>> damageBox; // 受击框 [x, y, w, h, type, param]
std::unordered_map<std::string, AniFlag> flag; // 帧特效数据
int delay = 0; // 延迟(毫秒)
};
struct AniInfo {
std::vector<std::string> imgList; // 图片列表
std::vector<AniFrame> frames; // 帧列表
std::unordered_map<std::string, AniFlag> flag; // 动画特效数据
};
struct AlsAniInfo {
std::string path; // 路径
std::vector<int> layer; // 图层 [zOrder, subLayer]
};
struct AlsInfo {
std::unordered_map<std::string, AlsAniInfo> 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<AniInfo> 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<AlsInfo> parseAlsFromPvf(const std::string& path);
} // namespace animation
} // namespace frostbite2D

View File

@@ -0,0 +1,292 @@
#include <frostbite2D/animation/animation.h>
#include <frostbite2D/2d/sprite.h>
#include <frostbite2D/resource/npk_archive.h>
#include <frostbite2D/resource/pvf_archive.h>
#include <frostbite2D/types/type_math.h>
#include <SDL2/SDL.h>
#include <algorithm>
using namespace frostbite2D::animation;
namespace frostbite2D {
Animation::Animation() {
}
Animation::Animation(const std::string& aniPath) {
Init(aniPath);
}
Animation::Animation(const std::string& aniPath,
std::function<std::string(std::string, ReplaceData)> 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<Sprite> 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<Sprite>();
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<int>(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<Animation>(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<float>(frameInfo.delay);
auto& flagBuf = frameInfo.flag;
if (flagBuf.count("SET_FLAG")) {
if (changeFrameCallback_) {
auto flagValue = std::get<int>(flagBuf.at("SET_FLAG"));
changeFrameCallback_(flagValue);
}
}
if (flagBuf.count("PLAY_SOUND")) {
}
if (flagBuf.count("IMAGE_RATE")) {
auto rate = std::get<Vec2>(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<float>(flagBuf.at("IMAGE_ROTATE"));
currentFrame_->SetRotation(rotation);
}
if (flagBuf.count("INTERPOLATION")) {
if (interpolationData_.empty()) {
interpolationData_.push_back(frames_[currentFrameIndex_]);
if (currentFrameIndex_ + 1 < static_cast<int>(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<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.at("RGBA"));
}
if (newData.flag.count("RGBA")) {
newRgbaData = std::get<std::vector<int>>(newData.flag.at("RGBA"));
}
std::vector<int> rgbaData = {
static_cast<int>(oldRgbaData[0] + (newRgbaData[0] - oldRgbaData[0]) * interRate),
static_cast<int>(oldRgbaData[1] + (newRgbaData[1] - oldRgbaData[1]) * interRate),
static_cast<int>(oldRgbaData[2] + (newRgbaData[2] - oldRgbaData[2]) * interRate),
static_cast<int>(oldRgbaData[3] + (newRgbaData[3] - oldRgbaData[3]) * interRate)};
currentFrame_->SetColor(
static_cast<float>(rgbaData[0]) / 255.0f,
static_cast<float>(rgbaData[1]) / 255.0f,
static_cast<float>(rgbaData[2]) / 255.0f,
static_cast<float>(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<Vec2>(oldData.flag.at("IMAGE_RATE"));
}
if (newData.flag.count("IMAGE_RATE")) {
newRateData = std::get<Vec2>(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<float>(oldData.flag.at("IMAGE_ROTATE"));
}
if (newData.flag.count("IMAGE_ROTATE")) {
newAngleData = std::get<float>(newData.flag.at("IMAGE_ROTATE"));
}
float angleData = math::lerp(oldAngleData, newAngleData, interRate);
currentFrame_->SetRotation(angleData);
}
}
Vec2 Animation::GetMaxSize() const {
return maxSize_;
}
} // namespace frostbite2D

View File

@@ -0,0 +1,381 @@
#include <frostbite2D/animation/animation_data.h>
#include <frostbite2D/resource/binary_reader.h>
#include <frostbite2D/resource/pvf_archive.h>
#include <algorithm>
#include <cctype>
#include <sstream>
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<char>(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<int> 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<float>(reader.readInt32());
frame.imgPos.y = static_cast<float>(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<int> rgba = {
static_cast<int>(read256(reader)),
static_cast<int>(read256(reader)),
static_cast<int>(read256(reader)),
static_cast<int>(read256(reader))};
frame.flag.emplace(key, rgba);
break;
}
case 11: {
uint16 effectType = reader.readUInt16();
key = "GRAPHIC_EFFECT_" + getAniEffectType(effectType);
std::vector<float> effect;
switch (effectType) {
case 5:
effect.push_back(static_cast<float>(read256(reader)));
effect.push_back(static_cast<float>(read256(reader)));
effect.push_back(static_cast<float>(read256(reader)));
break;
case 6:
effect.push_back(static_cast<float>(read256(reader)));
effect.push_back(static_cast<float>(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<int> 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<AniInfo> 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<AlsInfo> 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

Binary file not shown.

View File

@@ -24,6 +24,8 @@
#include <frostbite2D/resource/audio_database.h> #include <frostbite2D/resource/audio_database.h>
#include <frostbite2D/resource/sound_pack_archive.h> #include <frostbite2D/resource/sound_pack_archive.h>
#include <frostbite2D/animation/animation.h>
using namespace frostbite2D; using namespace frostbite2D;
int main(int argc, char **argv) { int main(int argc, char **argv) {
@@ -43,9 +45,30 @@ int main(int argc, char **argv) {
// SDL_Log("Starting main loop..."); // 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<Scene>(); auto menuScene = MakePtr<Scene>();
SceneManager::get().PushScene(menuScene); SceneManager::get().PushScene(menuScene);
auto ani = MakePtr<Animation>(
"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<Actor>(); auto TestActor = MakePtr<Actor>();
menuScene->AddChild(TestActor); menuScene->AddChild(TestActor);
@@ -85,20 +108,14 @@ int main(int argc, char **argv) {
return true; return true;
}); });
// // 尝试加载精灵 auto sprite1 = Sprite::createFromNpk("sprite/newtitle/nangua.img", 0);
// auto sprite = Sprite::createFromFile("assets/player.png"); if (sprite1) {
// if (sprite) { sprite1->SetPosition(220, 10);
// sprite->SetPosition(320, 300); // sprite1->SetScale(2.0f);
// sprite->SetOpacity(0.8f); menuScene->AddChild(sprite1);
// // sprite->SetAnchor(Vec2(0.5f, 0.5f)); } else {
// // sprite->SetRotation(30.f); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from NPK!");
// 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 &archive = PvfArchive::get(); // // auto &archive = PvfArchive::get();
// // if (archive.open("assets/Script.pvf")) { // // if (archive.open("assets/Script.pvf")) {
@@ -159,19 +176,7 @@ int main(int argc, char **argv) {
// SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load background music!"); // 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(); // SoundPackArchive &archive = SoundPackArchive::get();
// archive.setSoundPackDirectory("assets/SoundPacks"); // archive.setSoundPackDirectory("assets/SoundPacks");