LPC Frames Analyzer is a tool built for 2D game developers who want to speed up their workflow when working with spritesheets generated using the LPC Character Generator. It focuses on one thing: making extraction quick and clean.
The LPC Generator is an amazing tool - especially, if you don't have time or skills to create character art from scratch. It makes getting started incredibly easy.
But once i began using it in real project, i ran into a frustrating issue:
Some animations don't fit perfectly into the standard 64x64 frame size, which causes visible shifting when played in-game.
The following gifs illustrate the problem:
| Default (visible jittering) | Analyzed (steady in place) |
|
|
As you can see - after the image is analyzed - the animation stays in place.
LPC Frames Analyzer automatically detects frames (both those that fit into 64x64 standard grid, as well as oversized ones) and applies positional offsets to ensure that every animation stays visually stable.
Instead of manually adjusting each frame, the tool analyzes the entire spritesheet and corrects aligment issues in a consistent, and predictable way.
This allows you to:
- eliminate animation jitter
- keep characters visually anchored
- save hours of manual tweaking.
⚡ Hybrid Frame Detection - The tool combines fixed-grid detection with an intelligent pixel scanner. It's smart enough to handle standard 64x64 cycles, as well as oversized 'special' frames like spear thrusts or sword swinging in one go.
📐 Pixel Perfect Alignment - The tool doesn't just cut images; it understands them. It automatically detects offsets of each frame that keeps your character anchored, even when the sprite size changes.
🛠️ Granular Control - While the automation is powerful, you are always in charge. You can manually override offsets, adjust 'starting points' or change scanner precision to handle your spritesheets.
🎬 Instant Visual Feedback - Features built-in animation player. You can view your processed clips at full speed or frame-by-frame to catch any alignment issues before you even touch a single line of code.
🚀 Batch Processing - Select an entire directory of spritesheets and analyze them all at once. The tool will queue them up and process everything in one go.
💾 Flexible Export Options - Save results in universal JSON format for maximum compatability.
This project was built using the following technologies and libraries:
- C++ 20: core language and standard library.
- SFML: window management, handling graphics.
- ImGui-SFML: integration of Dear ImGui for SFML to provide Graphical User Interface.
- nlohmann/json: JSON Parsing & exporting.
- GoogleTest: unit testing.
- CMake: build system to configure and link the project and its dependencies.
The project uses CMake for simple, cross-platform building so the project can be initialized with minimal setup. You must not worry about downloading manually any packages, as if you follow this steps, everything will be fetched by git & cmake automatically.
- Open command terminal (cmd)
- Navigate to directory where you want to clone the project (cd C:/Projects)
- Clone the repository (git clone --recurse-submodules https://github.com/matheoheo/LPCFramesAnalyzer.git)
- Enter the project folder (cd LPCFramesAnalyzer)
- Create build directory and navigate there (mkdir build && cd build)
- Configure the project (cmake ..)
- Build the project (cmake --build .) | Results in debug build | For release build, please use: (cmake --build . --config Release)
To start the application from command terminal, you must build target: cmake --build . --target LPCAnalyzer. Then you can navigate to Debug folder and run the app cd Debug && LPCAnalyzer.exe.
You can also build additional targets such as examples and tests.
cmake --build . --target LPCJsonExample
cmake --build . --target LPCMattExample
cmake --build . --target LPCTests
After building, the executables will by located in build directory (Debug/ or Release/ folders, depending on your configuration)
If you don't want to build the application, you can just download the released version from this link
I believe, that using this application is straightforward. This following section will explain it to you, so you can start quickly.
-
The Spritesheet
First thing you need to have is a generated spritesheet using this website. (I have also included basic generated spritesheets in the 'assets' directory if you just wanna see how this tool works)
-
The application
When you open the application, you will be greeted in the Menu section - this one is very minimalistic. All you have to do is just click Start Analyzer.
-
Importing
First step, in our process is to select spritesheets that we want to analyze. For that, you just need to type a path to directory with your files and select them from list.
-
Calibration
In this step, you can calibrate settings which impact the algorithm.
Here are explainations for each option:a) Analysis Mode
- Classic Grid: The algorithm will only process frames that are size of 64x64 - those, that fit in the standard grid. It will stop, when the grid turns into 'oversized' frames.
- Dynamic Scanner: The Spritesheet Processor will only scan for frames that are different than standard ones (like sword slash, or other).
- Full Processor: Hybrid approach. It will scan the whole image with both of those ways. Recommended one.
b) Scanner Precision - those are important only for dynamic scanning.
- Horizontal Search Limit: This is a distance between frames in a single row (in X axis).
- Vertical Jump Limit: This is a distance between rows of frames (in Y Axis).
| Horizontal Limit | Vertical Limit |
|
|
In most cases, the default values of 180 are good enough.
c) Starting Point - this settings are the essential part of algorithm. The detection of dynamic position is very important, because it determines where the Classic Grid ends and Dynamic Scanner starts.
This illustration shows where is this point.
The area inside rectangle are all valid points for this position. Algorithm will detect it for you, but if detection point is not how it is supposed to be, then user can also specify it themselves.You can always preview the spritesheet while calibrating to determine the best settings for you specific spritesheet.
-
Output and Exporting
In the final stage, you can preview all selected spritesheets and their animations.
Use the Preview section to play animations and verify they remain stable.
If something doesn't look right, the Frame Inspector allows you to manually adjust position, size and offsets for individual frames.
You can also tweak animation speed using Frame Time, or disable offset to compare the before/after result.
Once your animations are done, and look correct you can export the results in two different formats depending on your needs.
This is default format used by LPC Frames Analyzer. It's structure, and parsing is handled by my seperate project, which you can explore here: Marser
The JSON export is added for maximum compatibility.
It does not depend on any specific parser - you can read and process it using standard JSON libraries in your project.
Both formats are equally good and present the same data.
Once the data is exported, integrating it into your project is straightforward.
You can parse JSON file using any standard library. For this example, i have used nlohmann/json:
#include <sfml/Graphics.hpp>
#include <nlohmann/json.hpp>
#include <fstream>
#include <iostream>
struct FrameRect
{
sf::IntRect rect;
sf::Vector2i offset;
};
struct Animation
{
std::string name;
std::string direction;
std::vector<FrameRect> frames;
};
//This function shows, how You can implement a functionality to load result animations from json file.
std::vector<Animation> jsonParseFile(const std::filesystem::path& path)
{
std::ifstream file(path);
if (!file)
{
std::cout << "Failed to open .json file\n";
return {};
}
nlohmann::json j;
file >> j;
std::vector<Animation> result;
for (const auto& anim : j["animations"])
{
auto& thisAnim = result.emplace_back();
thisAnim.name = anim["name"];
thisAnim.direction = anim["dir"];
for (const auto& framesList : anim["frames"])
{
const auto& fRect = framesList["rect"];
const auto& fOffset = framesList["offset"];
sf::IntRect rect(
{ fRect["x"].get<int>(), fRect["y"].get<int>() },
{ fRect["w"].get<int>(), fRect["h"].get<int>() });
sf::Vector2i offset(fOffset["x"], fOffset["y"]);
thisAnim.frames.push_back({ .rect = rect, .offset = offset });
}
}
return result;
}The .matt format is handled by a dedicated parser. Firstly, you need to integrate Marser to your project.
The format, in itself is very similar to the json one. Following code illustrates how to parse .matt result.
#include <sfml/Graphics.hpp>
#include <matt/parser/Parser.h>
struct FrameRect
{
sf::IntRect rect;
sf::Vector2i offset;
};
struct Animation
{
std::string name;
std::string direction;
std::vector<FrameRect> frames;
};
//This function shows, how You can implement a functionality to load result animations from matt file.
std::vector<Animation> mattParseFile(const std::filesystem::path& path)
{
std::vector<Animation> result;
matt::parser::Value root = matt::parser::Parser::parseFile(path);
for (auto& anim : root["animations"].asList())
{
auto& thisAnim = anim.asMap();
auto& newAnim = result.emplace_back();
newAnim.direction = thisAnim["dir"].asString();
newAnim.name = thisAnim["name"].asString();
auto& framesList = thisAnim["frames"].asList();
for (auto& frame : framesList)
{
auto& val = frame.asMap();
sf::IntRect rect(
sf::Vector2i(val["rect"]["x"].asInt(), val["rect"]["y"].asInt()),
sf::Vector2i(val["rect"]["w"].asInt(), val["rect"]["h"].asInt()));
sf::Vector2i offset(val["offset"]["x"].asInt(), val["offset"]["y"].asInt());
newAnim.frames.push_back({ .rect = rect, .offset = offset });
}
}
return result;
}While LPC Frames Analyzer is designed to handle most LPC spritesheets reliably, there are some known limitations.
Sometimes the offset calculation might be mismatched with expected values and cause shifting mid-animation anyway like in the following example:
| Analyzed | Offset |
|
|
As you can see, even after processing the Spritesheet, some of the frames are misaligned (gif on the left). The offset is wrong which causes shifting.
It occurs when a character holds a weapon (like sword or staff) that extend below the character's feet during an animation frame. In this case, the algorithm might mistakenly treat the weapon position as anchor point, which leads to wrong offset.
The good news is, that you can easily fix it by playing with offset values and finding the correct one.
| Fixed | Proper offset |
|
|
The LPC Frame Analyzer is tested only on spritesheets created by this generator. Using any other generators that are not identical to this one's format will most probably cause failures.
The credits for usage of the spritesheet that was used as an example in Tutorial section can be found here
Credits for the spritesheet used in Limitations is available here
The project is licensed under MIT License - full text available in LICENSE
Smart Animation Naming: Automatically assign default names to animations based on their position in LPC Sheet (e.g., "Walk", "Slash"), so you do not have to rename them manually.
Better Directional Sorting: Improved logic for grouping animations by direction.
CLI Support: I'm planning to add a Command Line Interface version to automate processing of hundreds of spritesheets at once using scripts.













