feat(资源管理): 添加NPK文件格式支持及精灵创建功能

实现NPK文件格式的解析和缓存管理,支持从NPK文件中加载精灵图像
添加Sprite::createFromNpk方法用于从NPK创建精灵
修改Texture和Sprite相关方法以支持const数据
添加zlib依赖用于NPK文件解压
优化Asset::listFilesWithExtension的扩展名匹配逻辑
This commit is contained in:
2026-03-20 04:21:42 +08:00
parent 29dec1f64b
commit 18111dae6b
11 changed files with 646 additions and 55 deletions

View File

@@ -15,7 +15,8 @@ public:
virtual ~Sprite();
static Ptr<Sprite> createFromFile(const std::string& path);
static Ptr<Sprite> createFromMemory(uint8* data, int width, int height, int channels);
static Ptr<Sprite> createFromMemory(const uint8* data, int width, int height, int channels);
static Ptr<Sprite> createFromNpk(const std::string& imgPath, size_t frameIndex = 0);
void Render() override;

View File

@@ -11,7 +11,7 @@ namespace frostbite2D {
class Texture : public RefObject {
public:
static Ptr<Texture> loadFromFile(const std::string& path);
static Ptr<Texture> createFromMemory(uint8* data, int width, int height, int channels);
static Ptr<Texture> createFromMemory(const uint8* data, int width, int height, int channels);
static Ptr<Texture> createEmpty(int width, int height);
~Texture();

View File

@@ -0,0 +1,102 @@
#pragma once
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/resource/binary_reader.h>
#include <frostbite2D/resource/asset.h>
#include <optional>
#include <string>
#include <map>
#include <vector>
#include <list>
#include <memory>
#include <zlib.h>
namespace frostbite2D {
struct ImageFrame {
int32 type = 0;
int32 compressionType = 0;
int32 width = 0;
int32 height = 0;
int32 xPos = 0;
int32 yPos = 0;
int32 frameXPos = 0;
int32 frameYPos = 0;
uint32 offset = 0;
int32 size = 0;
std::vector<uint8> data;
};
struct ImgRef {
std::string path;
std::string npkFile;
size_t frameCount = 0;
uint32 offset = 0;
uint32 size = 0;
bool loaded = false;
};
struct CachedImageData {
std::vector<ImageFrame> frames;
uint64 lastUseTime = 0;
size_t memoryUsage = 0;
};
class NpkArchive {
public:
static NpkArchive& get();
NpkArchive(const NpkArchive&) = delete;
NpkArchive& operator=(const NpkArchive&) = delete;
NpkArchive(NpkArchive&&) = delete;
NpkArchive& operator=(NpkArchive&&) = delete;
void setImagePackDirectory(const std::string& dir);
const std::string& getImagePackDirectory() const;
void init();
void close();
bool isOpen() const;
bool hasImg(const std::string& path) const;
std::optional<ImgRef> getImg(const std::string& path);
std::vector<std::string> listImgs() const;
std::optional<ImageFrame> getImageFrame(const ImgRef& img, size_t index);
size_t getFrameCount(const ImgRef& img) const;
void setCacheSize(size_t maxBytes);
void clearCache();
size_t getCacheUsage() const;
void setDefaultImg(const std::string& imgPath, size_t frameIndex = 0);
const std::string& getDefaultImgPath() const;
size_t getDefaultImgFrame() const;
private:
NpkArchive() = default;
~NpkArchive() = default;
std::string normalizePath(const std::string& path) const;
void scanNpkFiles();
bool parseNpkFile(const std::string& npkPath);
bool loadImgData(ImgRef& img);
void parseColor(const uint8* tab, int type, uint8* saveByte, int offset);
void evictCacheIfNeeded(size_t requiredSize);
void updateCacheUsage(const std::string& imgPath);
std::string readNpkInfoString(BinaryReader& reader);
static const uint8 NPK_KEY[256];
std::string imagePackDirectory_ = "ImagePacks2";
bool initialized_ = false;
std::map<std::string, ImgRef> imgIndex_;
std::map<std::string, CachedImageData> imageCache_;
std::list<std::string> lruList_;
size_t maxCacheSize_ = 512 * 1024 * 1024;
size_t currentCacheSize_ = 0;
std::string defaultImgPath_;
size_t defaultImgFrame_ = 0;
};
}

View File

@@ -1,6 +1,7 @@
#include <frostbite2D/2d/sprite.h>
#include <frostbite2D/core/application.h>
#include <frostbite2D/graphics/renderer.h>
#include <frostbite2D/resource/npk_archive.h>
#include <SDL2/SDL.h>
#include <algorithm>
@@ -30,7 +31,7 @@ Ptr<Sprite> Sprite::createFromFile(const std::string &path) {
return sprite;
}
Ptr<Sprite> Sprite::createFromMemory(uint8* data, int width, int height, int channels) {
Ptr<Sprite> Sprite::createFromMemory(const uint8* data, int width, int height, int channels) {
Ptr<Texture> texture = Texture::createFromMemory(data, width, height, channels);
if (!texture) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -176,5 +177,68 @@ Quad Sprite::createQuad() const {
return quad;
}
Ptr<Sprite> Sprite::createFromNpk(const std::string& imgPath, size_t frameIndex) {
NpkArchive& npk = NpkArchive::get();
auto imgOpt = npk.getImg(imgPath);
if (imgOpt) {
auto frameOpt = npk.getImageFrame(*imgOpt, frameIndex);
if (frameOpt && !frameOpt->data.empty()) {
const ImageFrame& frame = *frameOpt;
std::vector<uint8> convertedData(frame.data.size());
for (size_t i = 0; i < frame.data.size(); i += 4) {
uint8 b = frame.data[i + 0];
uint8 g = frame.data[i + 1];
uint8 r = frame.data[i + 2];
uint8 a = frame.data[i + 3];
convertedData[i + 0] = r;
convertedData[i + 1] = g;
convertedData[i + 2] = b;
convertedData[i + 3] = a;
}
return createFromMemory(
convertedData.data(),
frame.width,
frame.height,
4
);
}
}
const std::string& defaultPath = npk.getDefaultImgPath();
if (!defaultPath.empty()) {
auto defaultImgOpt = npk.getImg(defaultPath);
if (defaultImgOpt) {
auto defaultFrameOpt = npk.getImageFrame(*defaultImgOpt, npk.getDefaultImgFrame());
if (defaultFrameOpt && !defaultFrameOpt->data.empty()) {
const ImageFrame& frame = *defaultFrameOpt;
std::vector<uint8> convertedData(frame.data.size());
for (size_t i = 0; i < frame.data.size(); i += 4) {
uint8 b = frame.data[i + 0];
uint8 g = frame.data[i + 1];
uint8 r = frame.data[i + 2];
uint8 a = frame.data[i + 3];
convertedData[i + 0] = r;
convertedData[i + 1] = g;
convertedData[i + 2] = b;
convertedData[i + 3] = a;
}
return createFromMemory(
convertedData.data(),
frame.width,
frame.height,
4
);
}
}
}
return nullptr;
}
}

View File

@@ -88,7 +88,7 @@ Ptr<Texture> Texture::loadFromFile(const std::string& path) {
return texture;
}
Ptr<Texture> Texture::createFromMemory(uint8* data, int width, int height, int channels) {
Ptr<Texture> Texture::createFromMemory(const uint8* data, int width, int height, int channels) {
if (!data) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from memory: null data");
return nullptr;

View File

@@ -2,6 +2,8 @@
#include <fstream>
#include <sstream>
#include <system_error>
#include <algorithm>
#include <cctype>
namespace frostbite2D {
@@ -268,10 +270,13 @@ std::vector<std::string> Asset::listAll(const std::string &directoryPath,
std::vector<std::string>
Asset::listFilesWithExtension(const std::string &directoryPath,
const std::string &extension, bool recursive) {
const std::string &extension, bool recursive) {
std::vector<std::string> files;
fs::path fullPath = toPath(resolveFullPath(directoryPath));
fs::path ext = toPath(extension);
std::string extLower = extension;
std::transform(extLower.begin(), extLower.end(), extLower.begin(),
[](unsigned char c) { return std::tolower(c); });
std::error_code ec;
if (!fs::exists(fullPath, ec) || !fs::is_directory(fullPath, ec)) {
@@ -279,15 +284,22 @@ Asset::listFilesWithExtension(const std::string &directoryPath,
}
try {
auto checkExtension = [&](const fs::path& filePath) {
std::string fileExt = fromPath(filePath.extension());
std::transform(fileExt.begin(), fileExt.end(), fileExt.begin(),
[](unsigned char c) { return std::tolower(c); });
return fileExt == extLower;
};
if (recursive) {
for (const auto &entry : fs::recursive_directory_iterator(fullPath, ec)) {
if (entry.is_regular_file() && entry.path().extension() == ext) {
if (entry.is_regular_file() && checkExtension(entry.path())) {
files.push_back(fromPath(entry.path()));
}
}
} else {
for (const auto &entry : fs::directory_iterator(fullPath, ec)) {
if (entry.is_regular_file() && entry.path().extension() == ext) {
if (entry.is_regular_file() && checkExtension(entry.path())) {
files.push_back(fromPath(entry.path()));
}
}

View File

@@ -0,0 +1,394 @@
#include <frostbite2D/resource/npk_archive.h>
#include <algorithm>
#include <cctype>
#include <SDL.h>
namespace frostbite2D {
const uint8 NpkArchive::NPK_KEY[256] = {
112, 117, 99, 104, 105, 107, 111, 110, 64, 110, 101, 111, 112, 108, 101, 32,
100, 117, 110, 103, 101, 111, 110, 32, 97, 110, 100, 32, 102, 105, 103, 104,
116, 101, 114, 32, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78,
70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70,
68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68,
78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78,
70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70,
68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68,
78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78,
70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70,
68, 78, 70, 68, 78, 70, 68, 78, 70, 68, 78, 70, 0
};
NpkArchive& NpkArchive::get() {
static NpkArchive instance;
return instance;
}
void NpkArchive::setImagePackDirectory(const std::string& dir) {
imagePackDirectory_ = dir;
}
const std::string& NpkArchive::getImagePackDirectory() const {
return imagePackDirectory_;
}
void NpkArchive::init() {
if (initialized_) {
close();
}
scanNpkFiles();
initialized_ = true;
}
void NpkArchive::close() {
imgIndex_.clear();
imageCache_.clear();
lruList_.clear();
currentCacheSize_ = 0;
initialized_ = false;
}
bool NpkArchive::isOpen() const {
return initialized_;
}
std::string NpkArchive::normalizePath(const std::string& path) const {
std::string result = path;
std::transform(result.begin(), result.end(), result.begin(),
[](unsigned char c) { return std::tolower(c); });
return result;
}
void NpkArchive::scanNpkFiles() {
Asset& asset = Asset::get();
std::string npkDir = asset.resolvePath(imagePackDirectory_);
if (!asset.isDirectory(npkDir)) {
SDL_Log("NPK directory not found: %s", npkDir.c_str());
return;
}
std::vector<std::string> files = asset.listFilesWithExtension(npkDir, ".npk");
SDL_Log("Scanning %d NPK files...", static_cast<int>(files.size()));
for (const auto &file : files) {
parseNpkFile(file);
}
}
bool NpkArchive::parseNpkFile(const std::string& npkPath) {
Asset& asset = Asset::get();
BinaryReader reader(npkPath);
if (!reader.isOpen()) {
SDL_Log("Failed to open NPK file: %s", npkPath.c_str());
return false;
}
std::string npkFileName = asset.getFileName(npkPath);
std::string header = reader.readNullTerminatedString();
if (header.find("NeoplePack_Bill") == std::string::npos) {
return false;
}
int32 imageCount = reader.readInt32();
for (int32 i = 0; i < imageCount; ++i) {
int32 offset = reader.readInt32();
int32 length = reader.readInt32();
std::string imgPath = readNpkInfoString(reader);
ImgRef img;
img.path = normalizePath(imgPath);
img.npkFile = npkFileName;
img.offset = static_cast<uint32>(offset);
img.size = static_cast<uint32>(length);
img.loaded = false;
imgIndex_[img.path] = img;
}
return true;
}
bool NpkArchive::hasImg(const std::string& path) const {
return imgIndex_.find(normalizePath(path)) != imgIndex_.end();
}
std::optional<ImgRef> NpkArchive::getImg(const std::string& path) {
auto it = imgIndex_.find(normalizePath(path));
if (it == imgIndex_.end()) {
return std::nullopt;
}
return it->second;
}
std::vector<std::string> NpkArchive::listImgs() const {
std::vector<std::string> result;
result.reserve(imgIndex_.size());
for (const auto& pair : imgIndex_) {
result.push_back(pair.first);
}
return result;
}
std::optional<ImageFrame> NpkArchive::getImageFrame(const ImgRef& img, size_t index) {
std::string imgPath = normalizePath(img.path);
auto cacheIt = imageCache_.find(imgPath);
if (cacheIt == imageCache_.end()) {
auto it = imgIndex_.find(imgPath);
if (it == imgIndex_.end()) {
return std::nullopt;
}
if (!loadImgData(it->second)) {
return std::nullopt;
}
cacheIt = imageCache_.find(imgPath);
if (cacheIt == imageCache_.end()) {
return std::nullopt;
}
}
updateCacheUsage(imgPath);
if (index >= cacheIt->second.frames.size()) {
return std::nullopt;
}
return cacheIt->second.frames[index];
}
size_t NpkArchive::getFrameCount(const ImgRef& img) const {
std::string imgPath = normalizePath(img.path);
auto it = imgIndex_.find(imgPath);
if (it == imgIndex_.end()) {
return 0;
}
return it->second.frameCount;
}
bool NpkArchive::loadImgData(ImgRef& img) {
Asset& asset = Asset::get();
std::string npkPath = asset.combinePath(imagePackDirectory_, img.npkFile);
npkPath = asset.resolvePath(npkPath);
BinaryReader reader(npkPath);
if (!reader.isOpen()) {
SDL_Log("Failed to open NPK for IMG: %s", npkPath.c_str());
return false;
}
reader.seek(img.offset);
std::string flag = reader.readNullTerminatedString();
if (flag.find("Neople Img File") == std::string::npos) {
return false;
}
int64 tableLength = reader.readInt64();
reader.readInt32();
int32 indexCount = reader.readInt32();
img.frameCount = static_cast<size_t>(indexCount);
CachedImageData cachedData;
cachedData.frames.resize(indexCount);
size_t dataStartPos = img.offset + static_cast<uint32>(tableLength) + 32;
for (int32 i = 0; i < indexCount; ++i) {
ImageFrame& frame = cachedData.frames[i];
frame.type = reader.readInt32();
if (frame.type == 17) {
int32 refIndex = reader.readInt32();
frame.compressionType = refIndex;
frame.width = cachedData.frames[refIndex].width;
frame.height = cachedData.frames[refIndex].height;
frame.xPos = cachedData.frames[refIndex].xPos;
frame.yPos = cachedData.frames[refIndex].yPos;
frame.frameXPos = cachedData.frames[refIndex].frameXPos;
frame.frameYPos = cachedData.frames[refIndex].frameYPos;
frame.offset = cachedData.frames[refIndex].offset;
frame.size = cachedData.frames[refIndex].size;
continue;
}
frame.compressionType = reader.readInt32();
frame.width = reader.readInt32();
frame.height = reader.readInt32();
int32 size = reader.readInt32();
frame.xPos = reader.readInt32();
frame.yPos = reader.readInt32();
frame.frameXPos = reader.readInt32();
frame.frameYPos = reader.readInt32();
frame.size = size;
if (i == 0) {
frame.offset = static_cast<uint32>(dataStartPos);
} else {
frame.offset = cachedData.frames[i - 1].offset + cachedData.frames[i - 1].size;
}
}
for (int32 i = 0; i < indexCount; ++i) {
ImageFrame& frame = cachedData.frames[i];
if (frame.type == 17) {
int32 refIndex = frame.compressionType;
frame.data = cachedData.frames[refIndex].data;
continue;
}
reader.seek(frame.offset);
std::vector<uint8> compressedData = reader.readBytes(static_cast<size_t>(frame.size));
int32 deSize = frame.width * frame.height * 4;
std::vector<uint8> decompressedData(deSize);
unsigned long realSize = static_cast<unsigned long>(deSize);
int uncompressResult = uncompress(
decompressedData.data(),
&realSize,
compressedData.data(),
static_cast<unsigned long>(frame.size)
);
if (uncompressResult != Z_OK) {
SDL_Log("Failed to uncompress image data: %d", uncompressResult);
continue;
}
if (frame.type != 16) {
int32 pngByteSize = deSize * 2;
frame.data.resize(pngByteSize);
for (int32 e = 0; e < pngByteSize; e += 4) {
uint8 needData[2] = {0, 0};
if ((e / 4) * 2 + 1 < static_cast<int32>(decompressedData.size())) {
needData[0] = decompressedData[(e / 4) * 2];
needData[1] = decompressedData[(e / 4) * 2 + 1];
}
parseColor(needData, frame.type, frame.data.data(), e);
}
} else {
frame.data = std::move(decompressedData);
}
cachedData.memoryUsage += frame.data.size();
}
imageCache_[img.path] = std::move(cachedData);
currentCacheSize_ += imageCache_[img.path].memoryUsage;
lruList_.push_front(img.path);
img.loaded = true;
imgIndex_[img.path] = img;
return true;
}
void NpkArchive::parseColor(const uint8* tab, int type, uint8* saveByte, int offset) {
uint8 a = 0, r = 0, g = 0, b = 0;
switch (type) {
case 0x0e:
a = static_cast<uint8>(tab[1] >> 7);
r = static_cast<uint8>((tab[1] >> 2) & 0x1f);
g = static_cast<uint8>((tab[0] >> 5) | ((tab[1] & 3) << 3));
b = static_cast<uint8>(tab[0] & 0x1f);
a = static_cast<uint8>(a * 0xff);
r = static_cast<uint8>((r << 3) | (r >> 2));
g = static_cast<uint8>((g << 3) | (g >> 2));
b = static_cast<uint8>((b << 3) | (b >> 2));
break;
case 0x0f:
a = static_cast<uint8>(tab[1] & 0xf0);
r = static_cast<uint8>((tab[1] & 0xf) << 4);
g = static_cast<uint8>(tab[0] & 0xf0);
b = static_cast<uint8>((tab[0] & 0xf) << 4);
break;
}
saveByte[offset + 0] = b;
saveByte[offset + 1] = g;
saveByte[offset + 2] = r;
saveByte[offset + 3] = a;
}
void NpkArchive::setCacheSize(size_t maxBytes) {
maxCacheSize_ = maxBytes;
evictCacheIfNeeded(0);
}
void NpkArchive::clearCache() {
imageCache_.clear();
lruList_.clear();
currentCacheSize_ = 0;
}
size_t NpkArchive::getCacheUsage() const {
return currentCacheSize_;
}
void NpkArchive::evictCacheIfNeeded(size_t requiredSize) {
while (currentCacheSize_ + requiredSize > maxCacheSize_ && !lruList_.empty()) {
std::string oldest = lruList_.back();
lruList_.pop_back();
auto it = imageCache_.find(oldest);
if (it != imageCache_.end()) {
currentCacheSize_ -= it->second.memoryUsage;
imageCache_.erase(it);
}
}
}
void NpkArchive::updateCacheUsage(const std::string& imgPath) {
auto it = std::find(lruList_.begin(), lruList_.end(), imgPath);
if (it != lruList_.end()) {
lruList_.erase(it);
}
lruList_.push_front(imgPath);
auto cacheIt = imageCache_.find(imgPath);
if (cacheIt != imageCache_.end()) {
cacheIt->second.lastUseTime = SDL_GetTicks();
}
}
std::string NpkArchive::readNpkInfoString(BinaryReader& reader) {
if (reader.eof()) {
return "";
}
std::vector<uint8> encrypted = reader.readBytes(256);
if (encrypted.size() < 256) {
return "";
}
std::vector<char> decrypted(256);
for (int i = 0; i < 256; ++i) {
decrypted[i] = static_cast<char>(encrypted[i] ^ NPK_KEY[i]);
}
return std::string(decrypted.data());
}
void NpkArchive::setDefaultImg(const std::string& imgPath, size_t frameIndex) {
defaultImgPath_ = normalizePath(imgPath);
defaultImgFrame_ = frameIndex;
}
const std::string& NpkArchive::getDefaultImgPath() const {
return defaultImgPath_;
}
size_t NpkArchive::getDefaultImgFrame() const {
return defaultImgFrame_;
}
}

Binary file not shown.

View File

@@ -14,8 +14,9 @@
#include <frostbite2D/resource/script_parser.h>
#include <frostbite2D/audio/audio_system.h>
#include <frostbite2D/audio/sound.h>
#include <frostbite2D/audio/music.h>
#include <frostbite2D/audio/sound.h>
#include <frostbite2D/resource/npk_archive.h>
using namespace frostbite2D;
@@ -42,59 +43,59 @@ int main(int argc, char **argv) {
// 尝试加载精灵
auto sprite = Sprite::createFromFile("assets/player.png");
if (sprite) {
sprite->SetPosition(100, 100);
sprite->SetPosition(0, 0);
menuScene->AddActor(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();
if (archive.open("assets/Script.pvf")) {
archive.init();
// 文件内容读取
if (auto rawData = archive.getFileRawData("region/balmayer_north.rgn")) {
ScriptParser parser(*rawData, "script/example.bin");
// // 方式1迭代解析
// while (!parser.isEnd()) {
// if (auto value = parser.next()) {
// switch (value->type) {
// case ScriptValueType::Integer:
// SDL_Log("Integer: %d", value->intValue);
// break;
// case ScriptValueType::Float:
// SDL_Log("Float: %f", value->floatValue);
// break;
// case ScriptValueType::String:
// SDL_Log("String: %s", value->stringValue.c_str());
// break;
// default:
// break;
// }
// }
// }
// auto &archive = PvfArchive::get();
// if (archive.open("assets/Script.pvf")) {
// archive.init();
// // 文件内容读取
// if (auto rawData = archive.getFileRawData("region/balmayer_north.rgn")) {
// ScriptParser parser(*rawData, "script/example.bin");
// // // 方式1迭代解析
// // while (!parser.isEnd()) {
// // if (auto value = parser.next()) {
// // switch (value->type) {
// // case ScriptValueType::Integer:
// // SDL_Log("Integer: %d", value->intValue);
// // break;
// // case ScriptValueType::Float:
// // SDL_Log("Float: %f", value->floatValue);
// // break;
// // case ScriptValueType::String:
// // SDL_Log("String: %s", value->stringValue.c_str());
// // break;
// // default:
// // break;
// // }
// // }
// // }
// // 方式2批量解析
// parser.reset();
auto allValues = parser.parseAll();
for (const auto &value : allValues) {
// 处理 value
switch (value.type) {
case ScriptValueType::Integer:
SDL_Log("Integer: %d", value.intValue);
break;
case ScriptValueType::Float:
SDL_Log("Float: %f", value.floatValue);
break;
case ScriptValueType::String:
SDL_Log("String: %s", value.stringValue.c_str());
break;
default:
break;
}
}
}
}
// // // 方式2批量解析
// // parser.reset();
// auto allValues = parser.parseAll();
// for (const auto &value : allValues) {
// // 处理 value
// switch (value.type) {
// case ScriptValueType::Integer:
// SDL_Log("Integer: %d", value.intValue);
// break;
// case ScriptValueType::Float:
// SDL_Log("Float: %f", value.floatValue);
// break;
// case ScriptValueType::String:
// SDL_Log("String: %s", value.stringValue.c_str());
// break;
// default:
// break;
// }
// }
// }
// }
AudioSystem::get().init();
AudioSystem::get().setMasterVolume(1.0f);
@@ -108,6 +109,19 @@ 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(0, 0);
menuScene->AddActor(sprite1);
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create sprite from NPK!");
}
app.run();
app.shutdown();

View File

@@ -3,6 +3,7 @@ add_requires("libsdl2", {configs = {shared = true,wayland = true}})
add_requires("libsdl2_image")
add_requires("libsdl2_mixer")
add_requires("glm")
add_requires("zlib")
target("Frostbite2D")
set_kind("binary")
@@ -17,6 +18,7 @@ target("Frostbite2D")
add_packages("libsdl2_image")
add_packages("libsdl2_mixer")
add_packages("glm")
add_packages("zlib")
-- 复制着色器文件到输出目录
after_build(function (target)

View File

@@ -5,6 +5,7 @@ add_requires("libsdl2", {configs = {shared = true}})
add_requires("libsdl2_image", {configs = {shared = true}})
add_requires("libsdl2_mixer", {configs = {shared = true}})
add_requires("glm")
add_requires("zlib")
target("Frostbite2D")
set_kind("binary")
@@ -19,6 +20,7 @@ target("Frostbite2D")
add_packages("libsdl2_image")
add_packages("libsdl2_mixer")
add_packages("glm")
add_packages("zlib")
-- 复制 assets 目录到输出目录
after_build(function (target)