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

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - Prometheus

Pages: [1]
1
Graphics / Re: Views, Viewports, and Endless Parallax Backgrounds
« on: May 09, 2014, 05:16:57 pm »
Thanks for the quick responses, guys.

I went with that sort of implementation a while ago fallahn, but it's no longer feasible with how everything has evolved. So, I took to a different method of rendering/moving things (and this one is much better overall). Thanks for your feedback, though!

Ixrec, you're right. I was originally trying to cull all non-visible objects on the fly with a quadtree implementation (and got understandably poor performance), but for some reason I disregarded the option of preallocating the background objects into a series of gridded containers. It's a simple solution that eluded me likely due to some goofy and deprecated self-imposed restrictions. Whoops.

I'll refactor my code and get back to you with the results.

2
Graphics / Views, Viewports, and Endless Parallax Backgrounds
« on: May 09, 2014, 07:05:27 am »
Hey, guys.

I'm working on a 2D space-sim game. There are a bunch of background elements: stars made of vertices and allocated in a VertexArray, large images for nebulas, and so on.

I'm looking for a way to get the background to loop around on itself -- in other words, once you hit the edge of the game world, instead of flying off into uncharted territory, you continue along the other side none the wiser.

I thought of a few ways of implementing this, the seeming simplest of which incorporated sf::Views and viewports. Each parallax layer would have its own view so objects could be moved at varying speeds, and when the player approached an edge, the active view's viewport would shrink and a new view's viewport from the opposite side of the map would grow to fill in the blanks.

I had more or less finished this sort of implementation before I realized that I would have to draw all of my background elements multiple times (once for each view). I can't think of any way to limit the background objects to those within a given view, since there are just so many vertices in my VertexArrays: the game slowed to a crawl when I last tried.

I can post what I've got, but first I figured I would ask about my methodology before cluttering this post with code. Is what I'm trying to do feasible (and then not entirely illogical), or is there some better way that I haven't come up with yet?

Thanks for reading through my post. Let me know what you think!

3
Perhaps bumping this thread is a bit premature, but I am still looking for feedback from the SFML community. (The thread was about to fall off the first page.)

Thanks.

4
But since you use a single VertexArray and use a Translate to move the Stars, I'm not really sure if you would gain any advantage in using a View.

Thanks for the comment. I was thinking that views may be a convenient method of wrapping the background/world around itself, but that's really just an addendum to the main topic at hand.

I'm looking forward to hearing from more of the community.

5
Hey, guys.

I've been working on a side project of mine for a while now, and I've really taken to SFML -- it's lightweight and convenient to use. The project is, at its most fundamental level, a simple 2D top-down game based in space. It's fairly standard fare: the player controls a ship and flies around completing objectives.

I've got a few questions on methodology, however, that I would appreciate feedback on from the SFML community.

  • Object Rendering
    There's a bit of history to this, so bear with me. The game's background consists of several objects: stars, nebulas, and the like -- all procedurally generated. Objects are assigned parallax depths and are moved correspondingly slowly compared to objects nearer the top layer. Each area of the game is of fixed size and loops around itself.

    The stars were the first background element that I implemented, and so they have gone through several iterations:

      Version History:
      • Version 1:
        This was nothing more than a proof-of-concept naive implementation. Stars were objects that contained a position (sf::Vector2f) and its own sf::Circleshape. The star vector was looped through twice: once for moving each star, and again for drawing. It worked, but its performance was understandably horrid.
      • Version 1.5:
        This version removed all the sf::Circleshape copies floating around. There were only 20-something star variations, so I constructed that many const sf::Circleshape objects and stored pointers to them in the stars. No change to performance, but memory usage was significantly improved.
      • Version 2:
        This version included an implementation of a quadtree. The star vector was looped through once in its entirety to check whether they were on screen and, if so, added to the quadtree. Once done, all stars in the quadtree were individually drawn. This version saw performance gains over the naive approach.
      • Version 3 (current):
        This version scrapped the sf::Circleshape/quadtree implementation (and, indeed, the whole star object) in favor of an sf::VertexArray implementation. Every "star" contains between 5 and 11 points, depending on its size. Instead of looping through an array of objects to determine which are on screen and drawing only those, all the stars are drawn regardless of position. This implementation is faster than previous versions on some machines, and is about equal on others.

      I am looking for suggestions regarding two key points in Version 3:
      • While the rendering of these star objects is faster in the latest iteration due to the GPU-friendly sf::VertexArrays, I am finding it difficult to efficiently implement the background looping. Previously, I would move each star as necessary, check whether their new position exists outside the fixed game world (and loop them around if so), then add them to the quadtree for eventual rendering. Now, however, there are significantly more vertices than there were sf::Circleshapes, so it is unreasonable to loop through each to check their position (and since stars come in different sizes, I cannot conveniently use pointer arithmetic to check the center vertices).
      • Similarly, it doesn't appear feasible to perform bounds-checking against the screen for all of the vertices (if I were to implement another quadtree, for instance).

      Do you guys have any ideas as to how I may either further optimize the current iteration or efficiently reimplement the background looping/wrapping? Code at bottom.
  • More Rendering
    At one point very early on, I rendered all of my stars to an sf::RenderTexture. I have since switched to rendering to an sf::RenderWindow instead, but I have noticed one distinct visual difference: with the sf::RenderTexture, stars looked consistent and maintained their forms, whereas with the sf::RenderWindow their internal geometry appears to reconfigure every cycle they are moved. That is, their constituent elements appear to jump around, as opposed to moving synchronously with one another.

    This reorganization is very noticeable with larger star sizes (radius 2.f to 2.5f), and manifests as flickering for smaller star sizes. This occurs for both the sf::Circleshape and sf::VertexArray implementations, and appears to be unaffected by the number of constituent points for a given star. Do you guys have any ideas as to why this is occurring, as well as suggestions for avoiding this behavior?

Code (suggestions and criticism welcome):

How stars are created:

void Starfield::CreateStar(const StarParameters& star_parameters) {
    ColorName color = FilterColor(star_parameters.color_value);
   
    unsigned int num_points = 0;
    sf::Vector2f center_position(star_parameters.position);
    center_position *= kLevelFactors[level_]; // parallax
    center_position += screen_size_ / 2.f;
   
    // determine the number of points by star size
    if (star_size_ == kSmall)
        num_points = 6U;
    else if (star_size_ == kMedium)
        num_points = 8U;
    else
        num_points = 11U;
   
    // center point for circle (combintion of triangles)
    sf::Vertex center(center_position, kStarColors[color]);
    // points along circumference
    sf::VertexArray outliers(sf::Points, num_points - 1);
   
    // figure out where the points lie along the circumference
    for (int index = 0; index < num_points - 1; ++index) {
        float rotation = to_Radians(to_SFMLDegrees((index + 1) * 360.f / (num_points - 1)));
        sf::Vector2f position(center_position.x + star_parameters.radius * cos(rotation),
                              center_position.y + star_parameters.radius * sin(rotation));
        outliers[index] = sf::Vertex(position, kStarColors[color]);
    }
   
    // append vertices in groups of three (triangles)
    for (int index = 0; index < num_points - 1; ++index) {
        stars_.append(center);
        stars_.append(outliers[index]);
        if (index == num_points - 2)
            stars_.append(outliers[1]);
        else
            stars_.append(outliers[index + 1]);
    }
   
    return;
}

How stars were moved prior to Version 3:

void Starfield::MoveStars(const sf::Vector2f& screen_offset) {
    quadtree_->Clear();
    // parallax
    sf::Vector2f scaled_offset = screen_offset * kLevelFactors[level_];

    // world bounds (really should use an sf::FloatRect here)
    float left_bound = half_screen_size_.x - half_field_size_;
    float right_bound = half_screen_size_.x + half_field_size_;
    float top_bound = half_screen_size_.y - half_field_size_;
    float bottom_bound = half_screen_size_.y + half_field_size_;
   
    for (Star* star : stars_) {
        // move star
        star->MoveStar(scaled_offset);
       
        sf::Vector2f loop_offset(0.f, 0.f);
       
        // check if star left world bounds
        if (star->position().x < left_bound)
            loop_offset.x += field_size_;
        else if (star->position().x > right_bound)
            loop_offset.x -= field_size_;
           
        if (star->position().y > bottom_bound)
            loop_offset.y -= field_size_;
        else if (star->position().y < top_bound)
            loop_offset.y += field_size_;
       
        // loop the star if needed
        if (loop_offset.x || loop_offset.y)
            star->MoveStar(loop_offset);

        // if the star is on screen, insert it in the quadtree for later rendering
        // (screen bounds check performed by Insert function)
        quadtree_->Insert(star);
    }
   
    return;
}

How stars are moved and drawn now:

void Starfield::MoveStars(const sf::Vector2f& screen_offset) {
    // no screen or world bounds check
    offset_.translate(screen_offset * kLevelFactors[level_]);

    return;
}

void Starfield::draw(sf::RenderTarget& target, sf::RenderStates states) const {
    states.transform = offset_;
    target.draw(stars_, states);
   
    return;
}



A final addendum: I'm aware of the existence of sf::View, and that it can be used for both scrolling the game world as well as looping the background. I'm not quite sure how it works -- I hadn't really considered it until just recently -- but I suppose I'm not opposed to its use. I'll give it a shot if there's general consensus that it would be a valuable addition. That said, I'm still looking for ideas on how to better utilize sf::VertexArrays and the like.

Thanks for taking the time to read through glance at my perhaps excessively long post.

Pages: [1]
anything