#include #include #include #include #include #include #include #include "FrameBuffer.h" #include "SDLDisplay.h" #include "SpriteAssetLoader.h" #include "SpriteRasterizer.h" #include "Image.h" #include "Sprite.h" #include "SpriteAnimator.h" #include "AnimationSystem.h" static std::string FindAssetPath(const std::string& fileName) { const char* roots[] = { "game/assets/sprites/", "../game/assets/sprites/", "../../game/assets/sprites/", "../../../game/assets/sprites/" }; for (size_t i = 0; i < sizeof(roots) / sizeof(roots[0]); ++i) { const std::string path = std::string(roots[i]) + fileName; std::ifstream file(path.c_str(), std::ios::binary); if (file.good()) { return path; } } return std::string("game/assets/sprites/") + fileName; } static bool LoadSpriteImage(const std::string& fileName, RenderData::Image& image) { const std::string path = FindAssetPath(fileName); if (!Asset::SpriteAssetLoader::Load(path, image)) { std::cerr << "Load sprite failed: " << path << std::endl; return false; } return true; } static RenderData::Image ResizeImageNearest(const RenderData::Image& source, int32_t width, int32_t height) { if (!source.is_valid() || width <= 0 || height <= 0) { return RenderData::Image(); } RenderData::Image result(width, height); for (int32_t y = 0; y < height; ++y) { const int32_t sourceY = y * source.get_height() / height; for (int32_t x = 0; x < width; ++x) { const int32_t sourceX = x * source.get_width() / width; result.set_pixel(x, y, source.get_pixel_fast(sourceX, sourceY)); } } return result; } static RenderData::Image ResizeImageToFit(const RenderData::Image& source, int32_t maxWidth, int32_t maxHeight) { if (!source.is_valid() || maxWidth <= 0 || maxHeight <= 0) { return RenderData::Image(); } const float scaleX = static_cast(maxWidth) / source.get_width(); const float scaleY = static_cast(maxHeight) / source.get_height(); const float scale = std::min(scaleX, scaleY); const int32_t width = std::max(1, static_cast(source.get_width() * scale)); const int32_t height = std::max(1, static_cast(source.get_height() * scale)); return ResizeImageNearest(source, width, height); } static bool LoadFrames( const std::vector& fileNames, std::vector& images, std::vector& frames, int32_t maxFrameWidth, int32_t maxFrameHeight) { images.clear(); frames.clear(); images.reserve(fileNames.size()); for (size_t i = 0; i < fileNames.size(); ++i) { RenderData::Image image; if (!LoadSpriteImage(fileNames[i], image)) { return false; } images.push_back(ResizeImageToFit(image, maxFrameWidth, maxFrameHeight)); } frames.reserve(images.size()); for (size_t i = 0; i < images.size(); ++i) { frames.push_back(RenderData::Sprite(&images[i])); } return !frames.empty(); } int main(int argc, char* argv[]) { (void)argc; (void)argv; const int32_t width = 800; const int32_t height = 480; Platform::SDLDisplay display; if (!display.init(width, height)) { return -1; } RenderData::Image originalBackground; if (!LoadSpriteImage("background.sprite", originalBackground)) { display.shutdown(); return -1; } RenderData::Image background = ResizeImageNearest(originalBackground, width, height); std::vector frameFiles; frameFiles.push_back("Tom-stand.sprite"); frameFiles.push_back("Tom-listhen.sprite"); frameFiles.push_back("Tom-openmouse1.sprite"); frameFiles.push_back("Tom-openmouse2.sprite"); frameFiles.push_back("Tom-say1.sprite"); frameFiles.push_back("Tom-say2.sprite"); frameFiles.push_back("Tom-say3.sprite"); frameFiles.push_back("Tom-say4.sprite"); std::vector frameImages; std::vector frames; if (!LoadFrames(frameFiles, frameImages, frames, width * 7 / 10, height * 9 / 10)) { display.shutdown(); return -1; } Core::FrameBuffer frameBuffer(width, height); Rasterizer::SpriteRasterizer spriteRasterizer(&frameBuffer); Game::SpriteAnimator tomAnimator(frames, 0.12f, true); Game::AnimationSystem animationSystem; const RenderData::Sprite* firstFrame = tomAnimator.get_current_sprite(); const int32_t tomX = firstFrame ? (width - firstFrame->width) / 2 : 0; const int32_t tomY = firstFrame ? height - firstFrame->height - 20 : 0; animationSystem.add(&tomAnimator, tomX, tomY); std::cout << "Sprite animation test" << std::endl; std::cout << "Space: play/pause, R: reset, Esc: quit" << std::endl; std::cout << "Background: " << originalBackground.get_width() << "x" << originalBackground.get_height() << " -> " << background.get_width() << "x" << background.get_height() << std::endl; std::cout << "Tom frame 0: " << frameImages[0].get_width() << "x" << frameImages[0].get_height() << std::endl; bool shouldQuit = false; uint32_t lastTime = display.get_time_ms(); while (!shouldQuit) { SDL_Event event; while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { shouldQuit = true; } else if (event.type == SDL_KEYDOWN) { if (event.key.repeat != 0) { continue; } if (event.key.keysym.sym == SDLK_ESCAPE) { shouldQuit = true; } else if (event.key.keysym.sym == SDLK_SPACE) { if (tomAnimator.is_playing()) { tomAnimator.stop(); } else { tomAnimator.play(); } } else if (event.key.keysym.sym == SDLK_r) { tomAnimator.reset(); tomAnimator.play(); } } } const uint32_t currentTime = display.get_time_ms(); const float deltaTime = static_cast(currentTime - lastTime) * 0.001f; lastTime = currentTime; animationSystem.update(deltaTime); frameBuffer.clear(RenderData::Color(18, 18, 24, 255)); spriteRasterizer.DrawImage(background, 0, 0); animationSystem.draw(spriteRasterizer); display.present(&frameBuffer); SDL_Delay(16); } display.shutdown(); return 0; }