Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: sf::setTextureRect Not Working As Expected?  (Read 1868 times)

0 Members and 1 Guest are viewing this topic.

1.Y.6

  • Newbie
  • *
  • Posts: 1
    • View Profile
sf::setTextureRect Not Working As Expected?
« on: September 29, 2022, 10:18:27 am »
This is a problem that I've been troubleshooting for a full week & it's making me lose my mind. And the problem? Let me explain:

Whenever I set my sprite's texture rect, it just gives me a white square instead of the texture file cropped. And if you're asking if my file exists & if I set the correct directory for it, I'm 100% sure it is as it loads whenever I remove the "sf::setTextureRect" code. I've read others' problems here in the SFML Forums as well as in the subreddit and applied to my code, but none of them worked.

Here are the code files:

game.h (where I load everything):

#pragma once

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <cmath>

#include "player.hpp"

class Game {
  private:
    // Window Specifications
    sf::RenderWindow* window;
    sf::VideoMode videoMode;

    // Window Event Handler
    sf::Event event;

    // The Player Itself
    Player* player;

    // Private Functions
    void initWindow();
    void initPlayer();

  public:
    // Constructor & Destructor
    Game();
    virtual ~Game();

    // Accessor
    const bool isRunning() const;
   
    // Game Functions
    void pollEvents();
    void update();
    void render();
};

game.cpp (where I handle everything):

#include "game.hpp"

//////////////////////////////// GAME WINDOW SPECIFICATIONS ////////////////////////////////
// Private Game Window Function
void Game::initWindow() {
  this->videoMode.width = 800;
  this->videoMode.height = 600;

  this->window = new sf::RenderWindow(this->videoMode, "SFML Game Sample", sf::Style::Titlebar | sf::Style::Close);
  this->window->setFramerateLimit(120);
  this->window->setKeyRepeatEnabled(false);
  this->window->setVerticalSyncEnabled(false);
}
////////////////////////////////////////////////////////////////////////////////////////////

void Game::initPlayer() {
  this->player = new Player();
}

// Game Constructor
Game::Game() {
  this->initWindow();
  this->initPlayer();
}

// Game Destructor
Game::~Game() {
  delete this->window;
  delete this->player;
}

// Game Accessor
const bool Game::isRunning() const {
  return this->window->isOpen();
}

//////////////////// While-Loop Event to Keep the Game Window Running ////////////////////
void Game::pollEvents() {
  while (this->window->pollEvent(this->event)) {
    switch (this->event.type) {
      case sf::Event::Closed:
        this->window->close();
        break;
      case sf::Event::KeyPressed:
        if (this->event.key.code == sf::Keyboard::Escape)
          this->window->close();
        break;
    }
  }
}
//////////////////////////////////////////////////////////////////////////////////////////

// Game Functions
void Game::update() {
  // Call The Poll Event to Update Game
  this->pollEvents();
}

void Game::render() {
  // Clears Each Frame
  this->window->clear(sf::Color(125, 125, 125, 255));

  // Render Player
  this->player->render(*this->window);

  // Displays Frame in Game Window
  this->window->display();
}

player.h (of course the player itself):

#pragma once

#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <iostream>
#include <cmath>

class Player {
  private:
    friend int main();

    // Private Player Variables
    sf::Sprite sprite;
    sf::Texture texture;
    sf::IntRect currentFrame;

    // Private Player Functions
    void initTexture();
    void initSprite();

  public:
    // Constructor & Destructor
    Player();
    virtual ~Player();

    // Player Functions
    void update();
    void render(sf::RenderTarget & rt);
};

player.cpp:

#include "player.hpp"

void Player::initTexture() {
  // Load The Texture File
  if(!this->sprite.setTexture(this->texture)) {
    this->texture.loadFromFile("Textures/spring.png");
  }
}

void Player::initSprite() {
  // Set a Texture to The Sprite
  std::cout << "ERROR: Could Not Load Texture File.\n";

  // Crops The Spritesheet The Way I Want it
  this->currentFrame = sf::IntRect(2, 2, 120, 120); // THIS IS THE
  this->sprite.setTextureRect(this->currentFrame);  // PROBLEM !!!

  // Set Sprite Position
  this->sprite.setOrigin(sf::Vector2f(sprite.getLocalBounds().width, sprite.getLocalBounds().height) / 2.f);
  this->sprite.setPosition(300.f, 300.f);

  // Resize The Sprite
  this->sprite.setScale(sf::Vector2f(1.5f, 1.5f));
}

// Constructor
Player::Player() {
    this->initTexture();
    this->initSprite();
}

// Destructor
Player::~Player() {
 
}

void Player::update() {
  // still empty for now.
}

void Player::render(sf::RenderTarget & rt) {
  rt.draw(this->sprite);
}

Although it WORKS whenever I put sf::IntRect inside this->sprite.loadTextureFile() in player.cpp, but that's not what I want because my goal here is to make a spritesheet animation..
 
I need an answer asap, as well as an explanation so I can troubleshoot all of this by myself in the future. Again, this has already wasted me a full week & I'm getting frustrated the more time flies >:(.
« Last Edit: October 02, 2022, 03:43:01 am by 1.Y.6 »

fallahn

  • Hero Member
  • *****
  • Posts: 507
  • Buns.
    • View Profile
    • Trederia
Re: sf::setTextureRect Not Working As Expected?
« Reply #1 on: September 29, 2022, 12:07:23 pm »
The white square problem is usually caused by some unintentional copy of the texture or accidental memory mismanagement. The usual paradigm in SFML is to keep textures external from the drawable classes which use them, so that copying the drawable won't make a copy of the texture. Often some sort of resource management is used. SFML also provides an interface for creating custom drawables such as a Player class in a consistent way by inheriting sf::Transformable and sf::Drawable, which would look something like this:

class Player final : public sf::Transformable, public sf::Drawable
{
public:
    void setTexture(const sf::Texture& texture)
    {
        m_sprite.setTexture(texture);
    }

    void setTextureRect(sf::IntRect rect)
    {
        m_sprite.setTextureRect(rect);
    }

private:
    sf::Sprite m_sprite;
    void draw(sf::RenderTarget& target, sf::RenderStates states) const override
    {
        states.transform *= getTransform();
        target.draw(m_sprite, states);
    }
};
 

How this works is explained in the documentation. Note that the class does not have a texture member. Rather, you would use it like this:

int main()
{
    sf::Texture playerTexture;
    playerTexture.loadFromFile("test.png");

    Player player;
    player.setTexture(playerTexture);
    player.setTextureRect({ 2,2,100,100 });

    sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
    sf::Clock frameClock;

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
        }

        float elapsed = frameClock.restart().asSeconds();
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
        {
            player.move(-20.f * elapsed, 0.f);
        }
        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
        {
            player.move(20.f * elapsed, 0.f);
        }


        window.clear(sf::Color::Black);
        window.draw(player);
        window.display();
    }

    return 0;
}
 

With the texture in its own scope (either as in the example or stored in a resource manager) copying drawable classes like Player can be done safely without accidentally copying any textures they use. There is also no need to use new/delete in this example, everything is fine on the stack. And even if there were a reason to use the heap smart pointers would be the way to go.

kojack

  • Sr. Member
  • ****
  • Posts: 343
  • C++/C# game dev teacher.
    • View Profile
Re: sf::setTextureRect Not Working As Expected?
« Reply #2 on: September 29, 2022, 01:02:24 pm »
One little thing, setTexture doesn't return anything, you can't use it to test if the texture is valid. The check (and error message) should be on the loadFromFile line instead.
(Although if the texture is valid, that won't cause the problem you asked about, its just something I noticed)