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

[SFML 2.X] Cienie w grze 2D RPG

Ostatnio zmodyfikowano 2024-11-21 00:22
Autor Wiadomość
tBane
Temat założony przez niniejszego użytkownika
[SFML 2.X] Cienie w grze 2D RPG
» 2024-11-05 18:40:29
Witam. Mam problem. Nie wiem jak obliczyć pozycję cienia. Próbowałem na wiele sposobów i nic mi nie wychodzi. Może ktoś ma jakiś pomysł ?

C/C++
virtual void draw() {
   
   
if( isAlive ) {
       
       
sf::Sprite shadow;
       
shadow = sprite;
       
shadow.setColor( sf::Color( 0, 0, 0, 128 ) );
       
shadow.setOrigin( currentTexture->cx, currentTexture->cy );
       
shadow.setScale( 1.0f, 1.5f );
       
sf::Vector2f p;
       
p.x = position.x;
       
p.y = position.y -( float( currentTexture->texture->getSize().y ) - currentTexture->cy ) * shadow.getScale().y / 2.0f;
       
shadow.setPosition( p );
       
       
window->draw( shadow );
       
       
window->draw( sprite );
       
window->draw( lifeBarBackground );
       
window->draw( lifeBar );
       
       
Unit::draw();
   
}
}
P-181816
tBane
Temat założony przez niniejszego użytkownika
» 2024-11-05 18:52:30
Eh zagalopowałem się i za dużo kombinowałem a tu wystarczyło odjąć 10 od pozycji xD

C/C++
virtual void draw() {
   
   
if( isAlive ) {
       
       
sf::Sprite shadow;
       
shadow = sprite;
       
shadow.setColor( sf::Color( 0, 0, 0, 128 ) );
       
shadow.setOrigin( currentTexture->cx, currentTexture->cy );
       
shadow.setScale( 1.0f, 1.5f );
       
sf::Vector2f p;
       
p.x = position.x;
       
p.y = position.y - 10; // serio ? xD
       
shadow.setPosition( p );
       
       
window->draw( shadow );
       
       
window->draw( sprite );
       
window->draw( lifeBarBackground );
       
window->draw( lifeBar );
       
       
Unit::draw();
   
}
}
P-181817
tBane
Temat założony przez niniejszego użytkownika
» 2024-11-05 19:01:48
Teraz zauważyłem swój błąd logiczny. Cienie mogą być rzucane także na inne obiekty. I teraz zastanawiam się jak to zaimplementować. Jak na razie mam takie coś ale wiem, że to nie spełnia swojej roli, gdyż cienie jako Sprajty mogą na siebie nachodzić. Mam wstępny pomysł, żeby użyć dwóch tekstur jedna do renderowania gry, a druga do renderowania cieni i na końcu tę z cieniem dodawać do tej pierwszej. Ale czy to dobry pomysł ?

P-181818
DejaVu
» 2024-11-05 20:41:16
GPT 4o:
Masz rację – właściwe podejście polega na **najpierw renderowaniu wszystkich cieni**, a dopiero potem renderowaniu obiektów. Dzięki temu unikniesz sytuacji, w której cień jednego obiektu zasłania inny obiekt, co może wyglądać nieestetycznie. Poniżej wyjaśnię szczegółowo, dlaczego i jak to zrobić w praktyce.

### Poprawne podejście: Cienie najpierw, potem obiekty

1. **Koncepcja cienia „pod obiektem”**: W tradycyjnej perspektywie 2D cień powinien znajdować się **pod wszystkimi obiektami**. Dzięki temu cienie będą widoczne na powierzchni "podłogi" lub tła, ale nie będą zasłaniać obiektów, które są "przed" nimi na osi \( Y \).
   
2. **Unikanie zasłaniania**: Jeśli najpierw narysujesz wszystkie cienie, a potem same obiekty, unikniesz efektu, w którym cień jednego obiektu zasłania inny obiekt znajdujący się z tyłu.

### Przykład implementacji w SFML – krok po kroku

W poprzedniej wersji kodu dokonamy drobnej modyfikacji, aby rozdzielić rysowanie cieni i obiektów. Poniżej znajduje się przykład z poprawionym renderowaniem:

#### 1. Rozszerzenie klasy `GameObject`

Dodamy dwie oddzielne metody do rysowania: jedną do rysowania cienia, a drugą do rysowania obiektu.

C/C++
#include <SFML/Graphics.hpp>

class GameObject {
public:
   
sf::Sprite sprite;
   
sf::Sprite shadow;
   
   
GameObject( const sf::Texture & texture ) {
       
sprite.setTexture( texture );
       
shadow.setTexture( texture );
       
shadow.setColor( sf::Color( 0, 0, 0, 128 ) ); // Półprzezroczysty cień
   
}
   
   
void setPosition( float x, float y ) {
       
sprite.setPosition( x, y );
       
shadow.setPosition( x + 10, y + 10 ); // Przesunięcie cienia
   
}
   
   
// Metoda do rysowania tylko cienia
   
void drawShadow( sf::RenderWindow & window ) {
       
window.draw( shadow );
   
}
   
   
// Metoda do rysowania samego obiektu
   
void drawObject( sf::RenderWindow & window ) {
       
window.draw( sprite );
   
}
}
;

#### 2. Funkcja renderująca: Rysowanie najpierw cieni, potem obiektów

W funkcji renderującej najpierw rysujemy cienie wszystkich obiektów, a następnie rysujemy same obiekty w odpowiedniej kolejności.

C/C++
#include <vector>
#include <algorithm>

bool compareYPosition( const GameObject & a, const GameObject & b ) {
   
return a.sprite.getPosition().y < b.sprite.getPosition().y;
}

void renderScene( sf::RenderWindow & window, std::vector < GameObject > & objects ) {
   
// Sortowanie obiektów według osi Y, aby zachować odpowiednią kolejność
   
std::sort( objects.begin(), objects.end(), compareYPosition );
   
   
// Najpierw rysujemy wszystkie cienie
   
for( auto & obj: objects ) {
       
obj.drawShadow( window );
   
}
   
   
// Potem rysujemy wszystkie obiekty
   
for( auto & obj: objects ) {
       
obj.drawObject( window );
   
}
}

#### 3. Główna pętla gry

Kod głównej pętli gry pozostaje bez zmian. Rysowanie obiektów odbywa się przez wywołanie `renderScene` w odpowiedniej kolejności.

C/C++
int main() {
   
sf::RenderWindow window( sf::VideoMode( 800, 600 ), "2D Shadows Example" );
   
   
sf::Texture texture;
   
texture.loadFromFile( "path/to/texture.png" );
   
   
std::vector < GameObject > objects;
   
for( int i = 0; i < 50; ++i ) {
       
GameObject obj( texture );
       
obj.setPosition( rand() % 800, rand() % 600 );
       
objects.push_back( obj );
   
}
   
   
while( window.isOpen() ) {
       
sf::Event event;
       
while( window.pollEvent( event ) ) {
           
if( event.type == sf::Event::Closed )
               
 window.close();
           
       
}
       
       
window.clear( sf::Color::White );
       
       
// Renderowanie sceny
       
renderScene( window, objects );
       
       
window.display();
   
}
   
   
return 0;
}

### Dlaczego to działa

1. **Separacja renderowania cieni i obiektów**: Dzięki rozdzieleniu rysowania cieni i obiektów mamy pewność, że każdy cień jest „pod” obiektem, a nie go zasłania.
2. **Kolejność osi Y**: Sortowanie po osi \( Y \) gwarantuje, że obiekty bliżej dołu ekranu (czyli „bliżej” kamery) będą narysowane później, co daje efekt zasłaniania dla obiektów znajdujących się wyżej na osi \( Y \).

### Efekt końcowy

Taki sposób renderowania sprawia, że obiekty zasłaniają się nawzajem tylko na podstawie swojej pozycji, a cienie wyglądają naturalnie, tworząc iluzję głębi bez skomplikowanego kodu czy problemów z zasłanianiem.
P-181819
tBane
Temat założony przez niniejszego użytkownika
» 2024-11-05 21:19:39
Ok. Mam jeszcze jedno pytanie. A mianowicie znikają mi cienie w dolnej części ekranu. Z kodem jest wszystko ok raczej. Myśle, że to wina SFML. Gdy główny sprite jest niewidoczny wtedy i sprajt cienia zanika.
Tu mam jeszcze link do git'a jak kod będzie niewystarczający .. https://github.com/tBane1995/test


C/C++
#ifndef NatureObjects_hpp
#define NatureObjects_hpp

class Nature
    : public GameObject
{
public:
   
Texture * texture;
   
sf::Sprite sprite;
   
sf::Sprite shadow;
   
   
Nature( string name, float width, float length, float height )
        :
GameObject( name, 0, 0, width, length, height, true, false )
   
{
       
type = GameObjectType::Nature;
       
this->texture = getTexture( name );
       
sprite = sf::Sprite();
       
sprite.setTexture( * texture->texture );
       
sprite.setOrigin( texture->cx, texture->cy );
       
sprite.setPosition( position );
       
       
shadow = sf::Sprite();
       
shadow.setTexture( * texture->texture );
       
shadow.setColor( sf::Color( 0, 0, 0, 48 ) );
       
shadow.setOrigin( texture->cx, texture->cy );
       
sf::Vector2f scale;
       
scale.x = 1.0f + 0.15f * float( texture->texture->getSize().x ) / 256.0f;
       
scale.y = 1.0f + 0.30f * float( texture->texture->getSize().y ) / 256.0f;
       
shadow.setScale( scale );
       
shadow.setPosition( position );
       
   
}
   
   
Nature( GameObject * object, float x, float y )
        :
GameObject( object, x, y )
   
{
       
type = GameObjectType::Nature;
       
       
this->texture = getTexture( name );
       
       
sprite = sf::Sprite();
       
sprite.setTexture( * texture->texture );
       
sprite.setOrigin( texture->cx, texture->cy );
       
sprite.setPosition( position );
       
       
shadow = sf::Sprite();
       
shadow.setTexture( * texture->texture );
       
shadow.setColor( sf::Color( 0, 0, 0, 48 ) );
       
shadow.setOrigin( texture->cx, texture->cy );
       
sf::Vector2f scale;
       
scale.x = 1.0f + 0.15f * float( texture->texture->getSize().x ) / 256.0f;
       
scale.y = 1.0f + 0.30f * float( texture->texture->getSize().y ) / 256.0f;
       
shadow.setScale( scale );
       
shadow.setPosition( position );
   
}
   
   
virtual ~Nature() {
       
    }
   
   
virtual void setPosition( sf::Vector2f position ) {
       
this->position = position;
       
sprite.setPosition( position );
       
shadow.setPosition( position );
   
}
   
   
virtual void update( float dt ) {
       
    }
   
   
virtual void draw() {
       
       
if( mouseIsHover )
           
 GameObject::draw();
       
       
window->draw( shadow );
       
window->draw( sprite );
       
       
   
}
}
;

std::vector < Nature * > natures;

#endif
P-181820
DejaVu
» 2024-11-05 21:42:59
Zapewne masz warunek ograniczający, aby nie renderować obiektów 'poza' sceną, co jest dobrą optymalizacją. Jeżeli renderowanie 'cieni' jest powiązane z renderowaniem 'obiektów' to musisz zadbać o to, aby renderować obiekty 'poza sceną', których 'cień' wejdzie na scenę.
P-181821
tBane
Temat założony przez niniejszego użytkownika
» 2024-11-05 22:30:15
No właśnie mój warunek ograniczający jest z przedziału [-2*screenWidth, -2*screenHeight] [2*screenWidth, 2*screenHeight]. Więc on na pewno nie wpływa na ten render.
P-181822
tBane
Temat założony przez niniejszego użytkownika
» 2024-11-05 22:38:46
Dobra. Miałeś rację :-)
Znalazłem fragment kodu odpowiedzialny za to renderowanie.

Dzięki za pomoc! :-)
P-181823
« 1 » 2
  Strona 1 z 2 Następna strona