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

Author Topic: Inherit from sf::Shape (sf::CircleShape)  (Read 7746 times)

0 Members and 1 Guest are viewing this topic.

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Inherit from sf::Shape (sf::CircleShape)
« on: February 04, 2020, 02:20:04 am »
I am making an asteroids-like game, with the spaceship as a basic triangle shape, and the asteroids (obviously) are circular. I have already made the spaceship class by inheriting from sf::Sprite, but I want to inherit from sf::CircleShape for the asteroids so that I can use triangle-circle collision. I could also have a circle shape as a member of the class, but then I would have to move the circle shape every time I moved the asteroid, and I think this would be simpler. Here is my code so far:

Asteroid.h:
#include <SFML\Graphics.hpp>

class Asteroid : public sf::CircleShape {
  public:
    Asteroid(int radius);
  private:
    sf::Texture _texture;
};
 

Asteroid.cpp:
#include "Asteroid.h"

Asteroid::Asteroid(int radius) : sf::CircleShape(radius) {
    _texture.loadFromFile("images\\asteroid.png");
    setTexture(&_texture);
    setTextureRect(sf::IntRect(0, 0, 20, 20));
    setOrigin(getRadius() / 2, getRadius() / 2);
}

 

main.cpp:
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Spaceship.h"
#include "Asteroid.h"

using namespace std;

const int WIDTH = 720;
const int HEIGHT = 680;

sf::RenderWindow window;

int main() {

    window.create(sf::VideoMode(WIDTH, HEIGHT, 32), "Asteroids!", sf::Style::Titlebar | sf::Style::Close);

    Spaceship spaceship;
    Asteroid asteroid(10);

    sf::Event event;
    while (window.isOpen()) {
        while (window.pollEvent(event)) {
            switch (event.type) {

            case sf::Event::Closed: {
                window.close();
                break;
            }

            default:
                break;
            }
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                spaceship.speedY = -5;
            else
                spaceship.speedY = 5;
        else
            spaceship.speedY = 0;

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
                spaceship.speedX = -5;
            else
                spaceship.speedX = 5;
        else
            spaceship.speedX = 0;

        spaceship.move(spaceship.speedX, spaceship.speedY);

        window.clear();
        window.draw(asteroid);
        window.draw(spaceship);

        window.display();

        sf::sleep(sf::milliseconds(10));
    }

    return 0;
}
 

The Asteroid header and implementation files are quite simple, as I ran into the problem fairly early on. I can't get the Asteroid to draw on the screen. "window.draw(asteroid);" gives no errors, but the asteroid image does not display on the screen.

Any help would be appreciated.
As soon as you expect a program to be perfect, it stops being good enough.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #1 on: February 04, 2020, 08:59:17 am »
You aren't checking the return value from loadFromFile, so it might just fail to load your image.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #2 on: February 04, 2020, 06:30:16 pm »
Thank you for your reply.

I have changed my code:

#include "Asteroid.h"
#include <iostream>

Asteroid::Asteroid(int radius) : sf::CircleShape(radius) {
    if ((_texture.loadFromFile("images\\asteroid.png"))) std::cout << "loaded image successfully" << std::endl;
    setTexture(&_texture);
    setTextureRect(sf::IntRect(0, 0, 20, 20));
    setOrigin(getRadius() / 2, getRadius() / 2);
}
 

output:
loaded image successfully

So this is not the problem. What else am I missing?

Thanks again.
As soon as you expect a program to be perfect, it stops being good enough.

Mortal

  • Sr. Member
  • ****
  • Posts: 284
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #3 on: February 05, 2020, 05:28:57 am »
your code looks fine, there is no problem with it, but, making the asteroid's radius 10 pixels is quite small. give it higher value like 200 or 300 pixels, it's okay, also set the asteroid's position at center of screen for debugging, to make sure,the asteroid is visible and it's lay within viewport of window. also, make sure of the texture rect is mapped to a valid spot in that texture, means it does't picked transparent or color similar to window's background color or perhaps, the texture itself is corrupted or fully transparent. if that the case then change it with a valid texture or better, use "sf::Image" to create an image with opaque or solid color like "sf::Color::Red". and feed it to the texture update method "sf::Texture::update()". check the official documentation for texture update method.
« Last Edit: February 05, 2020, 06:37:14 am by Mortal »

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #4 on: February 06, 2020, 06:56:20 pm »
Thank you for your reply.

Here is my changed code:

main.cpp:
#include <SFML/Graphics.hpp>
#include <iostream>
#include "Spaceship.h"
#include "Asteroid.h"

using namespace std;

const int WIDTH = 720;
const int HEIGHT = 680;

sf::RenderWindow window;

int main() {

    window.create(sf::VideoMode(WIDTH, HEIGHT, 32), "Asteroids!", sf::Style::Titlebar | sf::Style::Close);

    Spaceship spaceship;
    Asteroid asteroid(200);

    sf::Event event;
    while (window.isOpen()) {
        while (window.pollEvent(event)) {
            switch (event.type) {

            case sf::Event::Closed: {
                window.close();
                break;
            }

            default:
                break;
            }
        }

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up) || sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                spaceship.speedY = -5;
            else
                spaceship.speedY = 5;
        else
            spaceship.speedY = 0;

        if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
                spaceship.speedX = -5;
            else
                spaceship.speedX = 5;
        else
            spaceship.speedX = 0;

        spaceship.move(spaceship.speedX, spaceship.speedY);

        window.clear();
        window.draw(asteroid);
        window.draw(spaceship);

        window.display();

        sf::sleep(sf::milliseconds(10));
    }

    return 0;
}
 

Asteroid.cpp:
#include "Asteroid.h"
#include <iostream>

Asteroid::Asteroid(int radius) : sf::CircleShape(radius) {
    // setFillColor(sf::Color::White);
    if ((_texture.loadFromFile("C:\\Users\\Tad\\OneDrive\\Documents\\C++\\SFML_Asteroids\\images\\asteroid.png"))) std::cout << "loaded image successfully" << std::endl;
    setTexture(&_texture);
    setTextureRect(sf::IntRect(0, 0, getRadius() * 2, getRadius() * 2));
    setOrigin(getRadius() / 2, getRadius() / 2);
    setPosition(720 / 2, 680 / 2);
}
 

I found out one of the problems was that the image I was using for the asteroid was not the same across as it was high. So I made my own asteroid image. Now the asteroid is displaying, but part of the top left side is cut off. I will attach a screenshot.

Thanks again for any help.
As soon as you expect a program to be perfect, it stops being good enough.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #5 on: February 06, 2020, 09:31:30 pm »
But why are you using a CircleShape rather than a simple Sprite?
Laurent Gomila - SFML developer

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #6 on: February 06, 2020, 10:39:10 pm »
Because the asteroid is a circle, I thought it would be easier to implement circle-based collisions if I used the circleshape, which has methods like getRaidus(). And I would still like to know what's causing the weird rendering.
As soon as you expect a program to be perfect, it stops being good enough.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #7 on: February 07, 2020, 09:42:07 am »
First, what you draw and what you use to compute collisions don't have to be the same thing; and it's usually not. Because you want detailed stuff on screen, and simpler geometry to speed up computations of collisions.

The circle is the simplest shape in collision detections: it's defined by a center point and a radius. And it has no specific direction, everything is the same around its 360 degrees. However it's more complex in rendering, because your GPU works with triangles; as you can see in your last capture, SFML can only draw a rough approximation of the circle (notice the segmented shape), you would have to subdivide it much more (ie. create more triangles) to see a smooth circle. The sprite, on the other hand, is very simple as it's only made of two triangles -- in your case the circular shape is simply obtained with transparency on the texture itself.

So, store the center and radius of your shape for collision detection, and use a sprite to draw it. And avoid public inheritance: your asteroid is not a circle shape, it just uses one to draw itself on screen.

To answer your question, your last capture shows a mismatch between the texture and the circle shape: the former is smaller than the latter and for some reason it's not stretched to cover the whole circle. That's most likely because you're incorrectly calling setTextureRect -- you shouldn't.
Laurent Gomila - SFML developer

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #8 on: February 07, 2020, 04:30:03 pm »
Thank you. I will use a sprite instead.

And avoid public inheritance: your asteroid is not a circle shape, it just uses one to draw itself on screen.

So are you saying I sould always avoid public inheritance in SFML? Or would it be ok to inherit from a sprite?
As soon as you expect a program to be perfect, it stops being good enough.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #9 on: February 08, 2020, 10:35:39 am »
It's almost always a bad idea to inherit drawable entities, unless you really want to extend them. Because you don't want your Asteroid class to expose low-level functions such as setTexture etc., these details are configured internally by your class and should not be accessible outside.
Laurent Gomila - SFML developer

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #10 on: February 11, 2020, 03:03:48 am »
Thank you for the information.

That's most likely because you're incorrectly calling setTextureRect -- you shouldn't.

How else am I supposed to set the rect for a textured sf::Shape? I understand that I shouldn't use one in this case, and I won't, but I may need to in the future.

It's almost always a bad idea to inherit drawable entities, unless you really want to extend them. Because you don't want your Asteroid class to expose low-level functions such as setTexture etc., these details are configured internally by your class and should not be accessible outside.

I think I may do it anyway, if only just this once. I am doing this project purely as a way to learn C++ and SFML better - kind of my own learning by doing project. I will try to avoid public inheritance with SFML in the future.
As soon as you expect a program to be perfect, it stops being good enough.

G.

  • Hero Member
  • *****
  • Posts: 1593
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #11 on: February 11, 2020, 06:33:36 am »
As he said, your texture rect is probably bigger than your texture.
If you want to apply a 300 * 300 px texture to a 400 * 400 px circle or sprite you want a textureRect of 300 * 300, right?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #12 on: February 11, 2020, 07:54:24 am »
The texture rect is the area of the texture that you want to show. If it's the full image, then use the texture size -- or do nothing, as it's the default behavior ;)

Note that it can also be bigger than the texture, if you want a repeating effect.
Laurent Gomila - SFML developer

Composer3

  • Newbie
  • *
  • Posts: 20
    • View Profile
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #13 on: February 11, 2020, 11:36:44 pm »
Ok, so setTextureRect is unnecessary? This would have saved a lot of headache for me if this had been mentioned in the docs.
As soon as you expect a program to be perfect, it stops being good enough.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Inherit from sf::Shape (sf::CircleShape)
« Reply #14 on: February 12, 2020, 08:01:29 am »
I think the API doc is clear about what this function does, and about the default behavior in case you don't call it. And of course you don't have to call it if you don't need to, like with any other function -- it's usually the other way round: if a function call is mandatory, it is clearly mentioned and highlighted in the documentation ;)
« Last Edit: February 12, 2020, 08:03:08 am by Laurent »
Laurent Gomila - SFML developer