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

Author Topic: [Solved] Simultaneous Drawing/Rendering  (Read 4249 times)

0 Members and 1 Guest are viewing this topic.

juiceeYay

  • Newbie
  • *
  • Posts: 16
    • View Profile
[Solved] Simultaneous Drawing/Rendering
« on: January 02, 2015, 01:18:29 am »
I am attempting to implement the following: a variable (large) number of stars are going to be drawn, which is easy enough, already implemented that. Each star though will have a variable number (< 10) of planets orbiting them, with each planet being constantly in motion regardless of its visibility, but will only be drawn if they are clicked on. What I am concerned with is the actual orbital motion behavior, the code below I am using has two problems:

First is that it is not orbiting at the speed I want it to (want one orbit to take 10 seconds as I said in the comments in my code below) and second is that running the third block of code causes the 'test' planet to complete one orbit, disappear, then the 'other' planet completes one orbit, disappears, and starts again from the beginning. I obviously want them to be visible at the same time, not one and then the other.

class Star : public sf::Sprite
{
private:
    void Init(int xCoord,int yCoord)
    {
        setPosition(xCoord,yCoord);
    }
public:
    int xPos;
    int yPos;
   
    Star(int inX, int inY) : xPos(inX), yPos(inY)
    {
        Init(xPos,yPos);
    }
}

class Celestial : public sf::Sprite
{
public:
    void someFun(int radi, Star &parent,sf::RenderWindow &in)
    {
        double twoPi = 2*Pi;
        double orbSpeed = (twoPi*radi) / 10; //divide by '10' because want 10 sec orbit
        double s = orbSpeed*0.1; //multiply by '0.1' because want it to move every 0.1 seconds
        double increment = s / radi;
        double theta = 0.00;
       
        while(theta < twoPi)
        {
            in.clear();
            theta += increment;
           
            if(theta == twoPi)
                theta = 0;
           
            double paramX = radi*cos(theta) + parent.xPos;
            double paramY = radi*sin(theta) + parent.yPos;
           
            setPosition(paramX,paramY);
           
            in.draw(*this);
            in.display();
        }
    }
}

test.someFun(100,sol,window);
other.someFun(140,sol,window);
« Last Edit: January 04, 2015, 07:36:39 pm by juiceeYay »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Simultaneous Drawing/Rendering
« Reply #1 on: January 02, 2015, 01:25:37 am »
The big problem here is that you've basically put your game loop inside the Celestial class, so when you call someFun suddenly your program runs nothing but the loop inside someFun until that object finishes an orbit.  Then the same thing happens next time you call it.  This is why the other object isn't getting drawn.

There should only be one game loop, and it should be in main() (or in a place like Engine::run() which main() calls exactly once).


Regarding the timing problem, the big issue is that you aren't passing in a deltaTime value from the main game loop.  There's no way to get smooth, consistent and frame-independent movement without that.  I also can't make a lot of sense out of your existing code (eg, why would the radius of the orbit be used to calculate the number of radians it should move each frame?) so I'm guessing it doesn't quite do what you think it does.  I believe a correct and readable Celestial class would look more like this:
class Celestial : public sf::Sprite {
private:
    int m_radi;
    Star m_parent;
    double m_secondsPerOrbit;
    double m_orbitTheta;
public:
    Celestial(int radi, Star parent, int m_secondsPerOrbit) :
    m_radi(radi), m_parent(parent), m_secondsPerOrbit(secondsPerOrbit), m_orbitTheta(0)
    {}

    void draw(sf::RenderTarget& renderTarget, sf::Time deltaTime)
    {
        m_orbitTheta += deltaTime.asSeconds() / m_secondsPerOrbit;
           
        setPosition(static_cast<int>(radi*cos(m_orbitTheta) + m_parent.xPos),
                    static_cast<int>(radi*sin(m_orbitTheta) + m_parent.yPos));
           
        renderTarget.draw(*this);
        renderTarget.draw(m_parent); // I probably shouldn't be drawing my own parent...
    }
}
« Last Edit: January 02, 2015, 01:41:52 am by Ixrec »

juiceeYay

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Simultaneous Drawing/Rendering
« Reply #2 on: January 02, 2015, 01:43:57 am »
Forgot to say the last block of code is inside a game loop, specifically 'while (window.isOpen())'.

That's what I figured though, but I can't think of how to get the sprite to move in orbit without calling draws in 'someFun', since not having it in there never lets the window to display since it will always be in the while loop of 'someFun'.

Also, sorry, few small edits, didn't mean to have a draw call for the parent in 'someFun'.
« Last Edit: January 02, 2015, 01:48:00 am by juiceeYay »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Simultaneous Drawing/Rendering
« Reply #3 on: January 02, 2015, 01:47:52 am »
You are supposed to call draw() inside of your Celestial's someFun()/draw() function, that's entirely correct.  What you aren't supposed to have in there is a while() loop that keeps track of time and calls clear/draw/display many times before finally existing (ie, a game loop).

I can't tell if you're confused about this, but display() is only supposed to be called once at the end of the main game loop after everybody has been drawn, not once after every draw() call.

juiceeYay

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Simultaneous Drawing/Rendering
« Reply #4 on: January 02, 2015, 02:33:44 am »
Shouldn't I call clear inside 'someFun' as well? The display idea makes sense, I am still unclear though how your code will work. What would the loop that calls your 'draw' look like inside the game loop?

Also since 'theta' would keep incrementing, wouldn't this eventually cause the game to crash since 'theta' would keep increasing towards infinity?

What is the point of needing a Time object if we're only using it as a float value? Can't I just use '0.01' instead of deltaTime? Or have deltaTime be of float type?
« Last Edit: January 02, 2015, 02:36:08 am by juiceeYay »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Simultaneous Drawing/Rendering
« Reply #5 on: January 02, 2015, 02:40:05 am »
Quote
Shouldn't I call clear inside 'someFun' as well? The display idea makes sense,
It's exactly the same for clear and display.  You call them exactly once per frame in the main game loop, and each entity only worries about how to draw itself.

Quote
Also since 'theta' would keep incrementing, wouldn't this eventually cause the game to crash since 'theta' would keep increasing towards infinity?
I don't see why big numbers would make things crash (the sin() and cos() functions can handle larger angles than that), but you're right, I should I have put a modulo 2pi in there.

Edit: Come to think of it, sin() and cos() will return the same results whether we make the variable wrap around or not, so technically it doesn't matter.

Quote
I am still unclear though how your code will work. What would the loop that calls your 'draw' look like inside the game loop?

int main() {
    sf::RenderWindow window(...);

    Star sol(...);
    Celestial test(100,sol,10);
    Celestial other(140,sol,10);

    sf::Clock clock;
    while(window.isOpen()) {
        // process events

        sf::Time deltaTime = clock.restart();

        window.clear();
        test.draw(window, deltaTime);
        other.draw(window, deltaTime);
        window.display();
    }
}

The main loop of an SFML program is always supposed to look something like this.  You should see very similar loops in the official tutorials (like this one: http://www.sfml-dev.org/tutorials/2.2/graphics-draw.php#the-drawing-window).
« Last Edit: January 02, 2015, 02:51:13 am by Ixrec »

juiceeYay

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Simultaneous Drawing/Rendering
« Reply #6 on: January 03, 2015, 01:05:59 am »
Thank you for clarifying the proper usage of clear and display as well as clarifying the game loop, helped to produce the result I was looking for.

Going back to the large theta, yes, sine and cosine will of course return the same results, but I'm concerned that after a while 'theta' might eventually reach the maximum possible value for an integer (2147483647 isn't it?) or a double since it will infinitely increase and thus cause an error.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Simultaneous Drawing/Rendering
« Reply #7 on: January 03, 2015, 01:48:28 am »
I guess that technically is undefined behavior.  Since the % operator is integer-only I was worried doing modulo on a floating point might be non-trivial, but it looks like there is a standard function for floating-point modulo so I definitely agree with using that to keep it safely between 0 and 2pi.

Glad to hear the rest of it is working now. =)

juiceeYay

  • Newbie
  • *
  • Posts: 16
    • View Profile
Re: Simultaneous Drawing/Rendering
« Reply #8 on: January 04, 2015, 03:06:38 am »
Ah I see, thank you again then for pointing that out, didn't think about float modulo.

I do have another question, although it is quite a bit unrelated to what we've discussed in this topic. Don't know if I should make another topic, private message you (if you don't mind), or just to carry on here.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Simultaneous Drawing/Rendering
« Reply #9 on: January 04, 2015, 03:35:28 am »
If it's unrelated best to put it somewhere else.  PM if you think 1-on-1 would be more constructive, thread if you think multiple pairs of eyeballs would help.