Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Automatyczne wyznaczanie klatek z Sprite Sheetu

Ostatnio zmodyfikowano 2025-10-28 17:58
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
» 2025-10-26 21:46:09
Już mam ręczne ustawianie rozmiaru i obliczanie animacji/klatek. Teraz chcę zrobić automat który niekoniecznie musi zawsze dobrze obliczać szerokośćKlatki/wysokośćKlatki/liczbęAnimacji/liczbęKlatek ale zazwyczaj żeby działał dla poprawnie narysowanych spritesheetów

// edit
mam pomysł na wyznaczanie nr animacji i nr klatki
animacjaID = rectPosY / maxFrameHeight;
klatkaID = rectPosX / maxFrameWidth;

Co o tym myślisz pekfos ?
P-183277
pekfos
» 2025-10-26 22:16:50
Twoja koncepcja nie rozwiązuje problemu jak tą animację wyświetlić w grze. Masz animację z klatkami 32x32 dla jednego aktora, z klatkami 68x68 dla drugiego, itp. Jak te animacje wyświetlisz? Musisz ręcznie podać przesunięcie dla tych animacji, zależnie od tego co w nich jest. Nie wiedziałem co masz, więc opisałem rozwiązanie które rozwiązuje wszystko. Jak masz edytor do animacji, to nie chcesz mieć jeszcze kroku ręcznego dopasowywania przez wpisanie stałych do kodu, prawda? W animowanych modelach 3d, wszystko jest względem punktu 0,0,0, bo nie ma problemu tam że model idzie w ujemne współrzędne. Tu są tylko dodatnie współrzędne, więc musisz mieć offset. Z takiego edytora powinien wychodzić plik graficzny i plik z opisem animacji. W ogólnym przypadku, nie ma żadnej reguły jak klatki są zorganizowane w pliku graficznym.
P-183278
tBane
Temat założony przez niniejszego użytkownika
» 2025-10-26 22:30:32
Eh. Nic nie rozumiem. Jutro spróbuję napisać kod moją metodą i wrzucę na forum. Ok?
P-183279
skovv
» 2025-10-27 00:08:39
Najlepiej bez zabawy ręcznie wpisać ale jak już musisz to:
- ustal kolor tła jakie ma animacja(jakiś kolor albo przezroczyste)
- sprawdzaj cały obrazek w poziomych liniach od góry do momentu aż znajdziesz inne Pixel. Jeśli znajdziesz to bool coswykryto = true, int start = aktualney
- dalej sprawdzasz te poziome linie w za każdym razem z y += 1 do momentu aż znajdziesz linie gdzie cała linia ma kolor tła, wtedy int przewidywanawysokosc=y
- teraz lecisz piony od x=0 na wysokościach od 0 do przewidywanawysokosc. Jak coś znajdzie to coswykryto = true
- lecisz dalej aż znów cała linia będzie pusta i masz int przewidywanaszerokosc=x

Da Ci to (najpewniej) klatkę, mozesz teraz napisać prowizoryczny system liczenia klatek(najlepiej w prawo):

Bool git = false
Int maybex = przewidywanaszerokosc
While(!git){
If(przewidywanaszerokosc podzielona przez maybex daje wynik z resztą) maybex += 1;
Else {
Bool byłobrazek = false;
Ilośćklatek = przewidywanaszerokosc / maybex;
For(i = 0; i<ilość klatek; i++){
Tu sprawdzaj czy w pionowej lini na pozycji x = i*maybex - czy było na tej linii coś poza tłem. Jeśli tak to byłobrazek= true;
}
If byłobrazek to maybex += 1
Else git = true
}
Jak wyjdzie ci te maybex to sprawdzaj czy pasuje taki wymiar dla klatkay. Myślę że cos w tym stylu

Sorry ale pisze z telefonu, musiałem pilnie wyjechać na 2 dni i bez pc :D
P-183280
tBane
Temat założony przez niniejszego użytkownika
» 2025-10-27 13:59:27
Ok. Mam taki kod. Co o nim sądzicie?

No i dla pierwszego sprajta działa. Wypisuje 4 klatki.


Dla drugiego sprajta generuje 12 klatek (4x troll + 4x outline czarny + 4x ogień). Przydałoby się jakoś scalić te recty. Chyba nawet mam pomysł jak to zrobić. Po prostu przeiteruję wszystkie recty i zrobię sf::IntRect contains


Dla trzeciego sprajta ładuje 55 klatek. Nie wiem skąd te dodatkowe ale pewnie to pojedyncze piksele. Warto by wyznaczyć tylko te największe klatki. Jako, że w moim programie najmniejsza dopuszczalna klatka ma 16x16 to odrzucę te mniejsze.



Kod:
C/C++
#include <iostream>
#include <string>
#include <functional>
#define NOMINMAX
#include <windows.h>
#include <fcntl.h>
#include <io.h>

#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"

std::vector < sf::IntRect > framesRects;

void loadFrame( sf::Image & image, int x, int y, sf::IntRect & frameRect, sf::Color backgroundColor ) {
   
   
if( x < 0 || y < 0 || x >= image.getSize().x || y >= image.getSize().y )
       
 return;
   
   
if( image.getPixel( sf::Vector2u( x, y ) ) != backgroundColor ) {
       
       
if( x < frameRect.position.x )
           
 frameRect.position.x = x;
       
       
if( y < frameRect.position.y )
           
 frameRect.position.y = y;
       
       
if( x >= frameRect.position.x + frameRect.size.x )
           
 frameRect.size.x = x - frameRect.position.x + 1;
       
       
if( y >= frameRect.position.y + frameRect.size.y )
           
 frameRect.size.y = y - frameRect.position.y + 1;
       
       
image.setPixel( sf::Vector2u( x, y ), backgroundColor );
       
       
loadFrame( image, x + 1, y, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y, frameRect, backgroundColor );
       
loadFrame( image, x, y + 1, frameRect, backgroundColor );
       
loadFrame( image, x, y - 1, frameRect, backgroundColor );
       
loadFrame( image, x + 1, y + 1, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y - 1, frameRect, backgroundColor );
       
loadFrame( image, x + 1, y - 1, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y + 1, frameRect, backgroundColor );
       
       
   
}
   
}

void loadAnimations( sf::Image image ) {
   
   
sf::Color backgroundColor = image.getPixel( sf::Vector2u( 0, 0 ) );
   
   
for( int y = 0; y < image.getSize().y; y++ ) {
       
for( int x = 0; x < image.getSize().x; x++ ) {
           
if( image.getPixel( sf::Vector2u( x, y ) ) != backgroundColor ) {
               
sf::IntRect frameRect;
               
frameRect.position.x = x;
               
frameRect.position.y = y;
               
frameRect.size.x = 1;
               
frameRect.size.y = 1;
               
framesRects.push_back( frameRect );
               
loadFrame( image, x, y, frameRect, backgroundColor );
           
}
        }
    }
}

int main() {
   
_setmode( _fileno( stdout ), _O_U16TEXT ); // wide char UTF-16 output
   
sf::RenderWindow * window = new sf::RenderWindow( sf::VideoMode( { 800, 600 } ), "Anim Loader", sf::Style::Titlebar | sf::Style::Resize | sf::Style::Close );
   
window->setFramerateLimit( 60 );
   
   
   
// FPS counter
   
sf::Clock fpsClock;
   
float fps = 0.0f;
   
int frameCount = 0;
   
   
sf::Image spriteSheet;
   
spriteSheet.loadFromFile( "spr.png" );
   
loadAnimations( spriteSheet );
   
   
std::wcout << "Loaded " << framesRects.size() << " frames.\n";
   
   
while( window->isOpen() ) {
       
       
// calculate the FPS
       
frameCount++;
       
       
if( fpsClock.getElapsedTime().asSeconds() >= 1.0f ) {
           
fps = frameCount / fpsClock.restart().asSeconds();
           
frameCount = 0;
           
window->setTitle( L"Anim Paint - FPS: " + std::to_wstring( int( fps ) ) );
       
}
       
       
// handle events
       
while( const std::optional event = window->pollEvent() ) {
           
           
if( event->is < sf::Event::Closed >() )
               
 window->close();
           
       
}
       
       
// update
       
        // render
       
window->clear( sf::Color( 56, 56, 56 ) );
       
       
window->display();
       
       
//cursor->draw();
       
   
}
   
   
}
P-183281
skovv
» 2025-10-27 14:08:43
Czyli ogółem działa? Czyta 55 klatek bo 9x6. Możesz sprawdzac po obliczeniu klatki x y czy któres klatki nie są puste.
P-183282
tBane
Temat założony przez niniejszego użytkownika
» 2025-10-27 14:11:23
no właśnie sprawdzam...

if( image.getPixel( sf::Vector2u( x, y ) ) != backgroundColor )


i usuwam cały kształt ...
Więc nie wiem skąd te dodatkowe klatki, może jakieś piksele tam są.

Tak usunąłem w dwóch miejscach piksele w spritesheet'cie i teraz pokazuje 53 klatki, wiec gdzieś jakieś piksele jeszcze są. Ale chyba wszystko dobrze działa.
P-183283
tBane
Temat założony przez niniejszego użytkownika
» 2025-10-27 14:39:47
Dodałem scalanie klatek i usuwanie tych z rozmiarem poniżej 16x16. Teraz poprawnie ładuje 50 klatek :-)

C/C++
#include <iostream>
#include <string>
#include <functional>
#define NOMINMAX
#include <windows.h>
#include <fcntl.h>
#include <io.h>

#include "SFML/Window.hpp"
#include "SFML/Graphics.hpp"



void loadFrame( sf::Image & image, int x, int y, std::vector < sf::IntRect > & framesRectsList, sf::IntRect & frameRect, sf::Color backgroundColor ) {
   
   
if( x < 0 || y < 0 || x >= image.getSize().x || y >= image.getSize().y )
       
 return;
   
   
if( image.getPixel( sf::Vector2u( x, y ) ) != backgroundColor ) {
       
       
if( x < frameRect.position.x )
           
 frameRect.position.x = x;
       
       
if( y < frameRect.position.y )
           
 frameRect.position.y = y;
       
       
if( x >= frameRect.position.x + frameRect.size.x )
           
 frameRect.size.x = x - frameRect.position.x + 1;
       
       
if( y >= frameRect.position.y + frameRect.size.y )
           
 frameRect.size.y = y - frameRect.position.y + 1;
       
       
image.setPixel( sf::Vector2u( x, y ), backgroundColor );
       
       
loadFrame( image, x + 1, y, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x, y + 1, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x, y - 1, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x + 1, y + 1, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y - 1, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x + 1, y - 1, framesRectsList, frameRect, backgroundColor );
       
loadFrame( image, x - 1, y + 1, framesRectsList, frameRect, backgroundColor );
       
       
   
}
   
}

void mergeFrames( std::vector < sf::IntRect > & framesRectsList ) {
   
   
int i = 0;
   
while( i < framesRectsList.size() ) {
       
       
for( int j = 0; j < framesRectsList.size(); j++ ) {
           
if( i == j )
               
 continue;
           
           
sf::IntRect & rectA = framesRectsList[ i ];
           
sf::IntRect & rectB = framesRectsList[ j ];
           
if( rectA.findIntersection( rectB ) ) {
               
// merge rectB into rectA
               
int left = std::min( rectA.position.x, rectB.position.x );
               
int top = std::min( rectA.position.y, rectB.position.y );
               
int right = std::max( rectA.position.x + rectA.size.x, rectB.position.x + rectB.size.x );
               
int bottom = std::max( rectA.position.y + rectA.size.y, rectB.position.y + rectB.size.y );
               
rectA.position.x = left;
               
rectA.position.y = top;
               
rectA.size.x = right - left + 1;
               
rectA.size.y = bottom - top + 1;
               
// remove rectB
               
framesRectsList.erase( framesRectsList.begin() + j );
               
if( j < i )
                   
 i--;
               
               
j--;
           
}
        }
       
       
i += 1;
       
   
}
   
}

void deleteSmallFrames( std::vector < sf::IntRect > & framesRectsList ) {
   
   
for( int i = 0; i < framesRectsList.size(); i++ ) {
       
sf::IntRect & rect = framesRectsList[ i ];
       
if( rect.size.x < 16 || rect.size.y < 16 ) {
           
framesRectsList.erase( framesRectsList.begin() + i );
           
i--;
       
}
    }
}

void loadAnimations( sf::Image image, std::vector < sf::IntRect > & framesRectsList ) {
   
   
sf::Color backgroundColor = image.getPixel( sf::Vector2u( 0, 0 ) );
   
   
for( int y = 0; y < image.getSize().y; y++ ) {
       
for( int x = 0; x < image.getSize().x; x++ ) {
           
if( image.getPixel( sf::Vector2u( x, y ) ) != backgroundColor ) {
               
sf::IntRect frameRect;
               
frameRect.position.x = x;
               
frameRect.position.y = y;
               
frameRect.size.x = 1;
               
frameRect.size.y = 1;
               
loadFrame( image, x, y, framesRectsList, frameRect, backgroundColor );
               
framesRectsList.push_back( frameRect );
           
}
        }
    }
   
   
mergeFrames( framesRectsList );
   
deleteSmallFrames( framesRectsList );
   
}

int main() {
   
_setmode( _fileno( stdout ), _O_U16TEXT ); // wide char UTF-16 output
   
sf::RenderWindow * window = new sf::RenderWindow( sf::VideoMode( { 800, 600 } ), "Anim Loader", sf::Style::Titlebar | sf::Style::Resize | sf::Style::Close );
   
window->setFramerateLimit( 60 );
   
   
   
// FPS counter
   
sf::Clock fpsClock;
   
float fps = 0.0f;
   
int frameCount = 0;
   
   
sf::Image spriteSheet;
   
spriteSheet.loadFromFile( "sprite sheet — kopia.png" );
   
std::vector < sf::IntRect > framesRectsList;
   
loadAnimations( spriteSheet, framesRectsList );
   
   
std::wcout << "Loaded " << framesRectsList.size() << " frames.\n";
   
   
while( window->isOpen() ) {
       
       
// calculate the FPS
       
frameCount++;
       
       
if( fpsClock.getElapsedTime().asSeconds() >= 1.0f ) {
           
fps = frameCount / fpsClock.restart().asSeconds();
           
frameCount = 0;
           
window->setTitle( L"Anim Paint - FPS: " + std::to_wstring( int( fps ) ) );
       
}
       
       
// handle events
       
while( const std::optional event = window->pollEvent() ) {
           
           
if( event->is < sf::Event::Closed >() )
               
 window->close();
           
       
}
       
       
// update
       
        // render
       
window->clear( sf::Color( 56, 56, 56 ) );
       
       
window->display();
       
   
}
   
   
}
P-183284
1 « 2 » 3 4
Poprzednia strona Strona 2 z 4 Następna strona