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

Author Topic: Novice Problem Using Views  (Read 2612 times)

0 Members and 1 Guest are viewing this topic.

uncreativename

  • Newbie
  • *
  • Posts: 4
    • View Profile
Novice Problem Using Views
« on: October 06, 2016, 04:23:52 am »
edited the first post

Hi, I'm honestly not sure what I'm doing wrong here, I think I might be fundamentally misunderstanding how views work.

I'm using SFML 2.31 for Visual Studio 2015 on Windows 10.  Using DLLs.

I'm not completely sure how to make the code more minimal since I'm not sure where the problem might be.  Sorry.  I'm kind of new to this.  I did try to comment it nicely, but if you have any suggestions as to how I can better help you help me, that would be fine.

The goal of the program is to have part of a an std::vector of sprites that represent the level, made up of the same tile, appear in the window, and when the user presses a wasd key, it should scroll in the appropriate direction and stop at the edges.

The texture/all the sprites are 50px x 50px btw.

It displays nicely at the start, but then when I push any of the wasd keys it freaks out and crashes.

#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>

struct Var {
        int w; //window width, view width
        int h; //window height, view height
        int s; //tile size
        int half; //half the size of the world
        int size; //size of the world
        bool foundStart; //for checking what to display in view
};

struct GameSprite {
        sf::Sprite sprite;
        int rx; //x value of the right side of the sprite
        int by; //y value of the bottom side of the sprite
};

struct ViewData {
        float leftX, rightX, topY, bottomY, center;
        ViewData(Var &var) {
                center = var.half;
                leftX = center - (var.w / 2);
                rightX = center + (var.w / 2);
                topY = center - (var.h / 2);
                bottomY = center + (var.h / 2);
        }
};

char input(sf::RenderWindow &window) {
        char inputChar = '\0';
        sf::Event event;
        while (window.pollEvent(event)) {
                if (event.type == sf::Event::Closed) {
                        window.close();
                }
                if (event.type == sf::Event::KeyPressed) {
                        if (event.key.code == sf::Keyboard::W) {
                                inputChar = 'w';
                        }
                        else if (event.key.code == sf::Keyboard::A) {
                                inputChar = 'a';
                        }
                        else if (event.key.code == sf::Keyboard::S) {
                                inputChar = 's';
                        }
                        else if (event.key.code == sf::Keyboard::D) {
                                inputChar = 'd';
                        }
                }
        }
        return inputChar;
}

void collision(ViewData &viewData, Var &var, sf::View &view) {
        if (viewData.leftX < 0) {
                view.move(0 - viewData.leftX, 0);
                viewData.leftX = 0;
                viewData.rightX = var.w;
        }
        else if (viewData.rightX > var.size) {
                view.move(var.size - viewData.rightX, 0);
                viewData.rightX = var.size;
                viewData.leftX = var.size - var.w;
        }
        else if (viewData.topY < 0) {
                view.move(0, 0 - viewData.topY);
                viewData.topY = 0;
                viewData.bottomY = var.h;
        }
        else if (viewData.bottomY > var.size) {
                view.move(0, var.size - viewData.bottomY);
                viewData.bottomY = var.size;
                viewData.topY = var.size - var.h;
        }
}

bool findStart(GameSprite &sprite, ViewData &viewData) {
        if (sprite.rx >= viewData.leftX && sprite.sprite.getPosition().x < viewData.rightX) {
                if (sprite.by >= viewData.topY && sprite.sprite.getPosition().y < viewData.bottomY) {
                        return true;
                }
        }
        return false;
}

int main() {
        Var var = { 800, 600, 50, 2500, 5000, false };
        sf::View view(sf::Vector2f(var.half, var.half), sf::Vector2f(var.w, var.h));
        sf::RenderWindow window(sf::VideoMode(var.w, var.h), "test");
        window.setView(view);
        ViewData viewData(var);
        sf::Texture texture;
        texture.loadFromFile("background.png");
        char in = '\0';
        int arrayStart[2];
        int arrayEnd[2];
        int x = 0;
        int y = 0;
        std::vector<std::vector<GameSprite> > sprite;
        sprite.resize(100);
        for (int a = 0; a < 100; ++a) {
                sprite[a].resize(100);
        }
        for (int a = 0; a < 100; ++a) {
                for (int b = 0; b < 100; ++b) {
                        sprite[a][b].sprite.setTexture(texture);
                        sprite[a][b].sprite.setPosition(x, y);
                        sprite[a][b].rx = x + var.s;
                        sprite[a][b].by = y + var.s;
                        if (!var.foundStart) {
                                if (findStart(sprite[a][b], viewData)) { //checks to see where to start drawing
                                        arrayStart[0] = a;
                                        arrayStart[1] = b;
                                        arrayEnd[0] = arrayStart[0] + (var.w / var.s) + 1;
                                        arrayEnd[1] = arrayStart[1] + (var.h / var.s) + 1;
                                        var.foundStart = true;
                                }
                        }
                        y += var.s;
                }
                y = 0;
                x += var.s;
        }
        while (window.isOpen()) {
                var.foundStart = false;
                in = input(window);
                if (in != '\0') {
                        switch (in) {
                        case 'w':
                                view.move(0, -20);
                                viewData.topY -= 20;
                                viewData.bottomY -= 20;
                                break;
                        case 'a':
                                view.move(-20, 0);
                                viewData.leftX -= 20;
                                viewData.rightX -= 20;
                                break;
                        case 's':
                                view.move(0, 20);
                                viewData.topY += 20;
                                viewData.bottomY += 20;
                                break;
                        case 'd':
                                view.move(20, 0);
                                viewData.rightX += 20;
                                viewData.leftX += 20;
                                break;
                        }
                        in = '\0';
                        collision(viewData, var, view); //checks to see if the view is at the edge of the world, moves it back
                        for (int a = 0; !var.foundStart; ++a) {
                                for (int b = 0; !var.foundStart; ++b) {
                                        if (findStart(sprite[a][b], viewData)) { //checks if this is a valid location to start drawing to the view
                                                arrayStart[0] = a;
                                                arrayStart[1] = b;
                                                arrayEnd[0] = arrayStart[0] + (var.w / var.s) + 1;
                                                arrayEnd[1] = arrayStart[1] + (var.h / var.s) + 1;
                                                var.foundStart = true;
                                        }
                                }
                        }
                }
                window.setView(view);
                window.clear();
                for (int a = arrayStart[0]; a < arrayEnd[0]; ++a) {
                        for (int b = arrayStart[1]; b < arrayEnd[1]; ++b) {
                                window.draw(sprite[a][b].sprite);
                        }
                }
                window.display();
        }
        return 0;
}
« Last Edit: October 07, 2016, 03:27:42 am by uncreativename »

DarkRoku12

  • Full Member
  • ***
  • Posts: 203
  • Lua coder.
    • View Profile
    • Email
Re: Novice Problem Using Views
« Reply #1 on: October 06, 2016, 05:04:48 am »
i would try to help you but your code is too long and an lazy tonight  :P ::).

Sorry but i have to say it: >:(
 /*rant coming*/


Why most of the "help" requested by users involve <threads> in this forum?

Yes we like speed, we need speed but: Threads ARE NOT speed, threads COULD and only COULD bring speed is are used in the correct way.

The correct way is:
You have the need: So you need more speed because your actual implementation will be slow/impossible without a boost.
You have the knowledge so: You have tested and understood the code that will use threads and know how to use threads.

I don't really know what is the damn obsession with threads  :-\

Let me list some problems:

-Globals: A lot of pointless globals. Why are bad?

sf::RenderWindow window;
bool game = true; //this goes to false only if the window is closed, and ends the program
bool gotChar = false; //if the user inputs a wasd key
char inputChar; //which key was pressed
sf::Sprite sprite[100][100]; //the game world.  each sprite is 50 x 50
float viewX, viewY; //the top left corners of the view, in terms of the game world
bool readyToDraw = true;

 

A lot of data races!!! Read what is data race
Synchronization:
Atomic in C++
while (game) {  //  input thread
game = false;  // Input thread
while (game) { // display thread
while (game) { // main thread

//main
     gotChar = false;
     window.setView(view);
      readyToDraw = true;
//display
        window.clear();
        while (readyToDraw) {
//input
while (window.pollEvent(event)) {
gotChar = true;
readyToDraw = false;
 

Bad optimization:
On C/C++ for loops conditions are executed every time, insted of once as happens in several others programming languages.

        while (readyToDraw)
         {
            x = viewX / 50; //THIS
            y = viewX / 50; // THIS
            ay = y;
            for (x; x < ((viewX + view.getSize().x) / 50) /*THIS*/; ++x) {
                for (y; y < ((viewY + view.getSize().y) / 50)/*THIS*/; ++y) {
                    window.draw(sprite[x][y]);
                }
                y = ay; //resets y to what it was before the loop
            }
        }

//(viewX + view.getSize().x)  //This line is executed as many times as the loop repeats.
 

THIS can be moved out to from the inner loop, naive example:

/* Defined in some part
struct viewData
{
  float viewX , viewY ;
  float halfX , halfY ;
}
*/

       /* Having any pointer or reference of a viewData named myViewData
           and when needed recalculate its members.
      */
 
       
        while (readyToDraw)
         {
            x = myViewData.halfX ;
            y = myViewData.halfY ;
            ay = y;
            const sf::Vector2f &size = view.getSize() ;
            float condA = ( myViewData.viewX + size.x ) / 50 ;
            float condB = ( myViewData.viewY + size.y ) / 50 ;
            for (x; x < condA ; ++x)
           {
                for (y; y < condB ; ++y)
                {
                    window.draw(sprite[x][y]);
                }
                y = ay; //resets y to what it was before the loop
            }
        }
 

Another optimization problem:
A lot of 'if' when a 'switch-case' is possible and when non of the if statements are in a 'probability order'
Switch-case. It's both more readable and more optimizable.
Wikipedia talks more about it.
if (inputChar == 'w') {
view.move(0, 20);
viewY += 20;
}
/* Using else if on the code below will be more optimized that an if-if-if...if
    I don't think an 'a' can be magically turned on a 's' or 'd' .. etc in the same loop
*/

if (inputChar == 'a') {
view.move(-20, 0);
viewX -= 20;
}
if (inputChar == 's') {
view.move(0, -20);
viewY -= 20;
}
if (inputChar == 'd') {
view.move(20, 0);
viewX += 20;
}


if (event.type == sf::Event::KeyPressed) {
if (event.key.code == sf::Keyboard::W) {
    inputChar = 'w';
    gotChar = true;
    readyToDraw = false;
}
if (event.key.code == sf::Keyboard::A) {
    inputChar = 'a';
    gotChar = true;
    readyToDraw = false;
}
if (event.key.code == sf::Keyboard::S) {
    inputChar = 's';
    gotChar = true;
    readyToDraw = false;
}
if (event.key.code == sf::Keyboard::D) {
    inputChar = 'd';
    gotChar = true;
    readyToDraw = false;
}
 


And probably an SFML team member or Hapax
(click to show/hide)
will recommend you a good C++ book.
« Last Edit: October 06, 2016, 06:02:53 am by DarkRoku »
I would like a spanish/latin community...
Problems building for Android? Look here

uncreativename

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Novice Problem Using Views
« Reply #2 on: October 06, 2016, 12:24:03 pm »
Thanks for the suggestions.  I'll take them into account and post an updated version of my code tonight :)

DarkRoku12

  • Full Member
  • ***
  • Posts: 203
  • Lua coder.
    • View Profile
    • Email
Re: Novice Problem Using Views
« Reply #3 on: October 06, 2016, 03:06:03 pm »
Please , try what you want to do without using threads.
If better to use an std:: vector or std::array if the size if fixed.
So you can have  the .size() method to get the real size of the container.

Use to struct/class to group variables that have co-relation.
On C++ you can have the advantage of inheritance, abstraction (dont abuse using this feature), polymorphism, and encapsulation.

I would like a spanish/latin community...
Problems building for Android? Look here

uncreativename

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Novice Problem Using Views
« Reply #4 on: October 07, 2016, 02:23:50 am »
Updated my code in the first post

DarkRoku12

  • Full Member
  • ***
  • Posts: 203
  • Lua coder.
    • View Profile
    • Email
Re: Novice Problem Using Views
« Reply #5 on: October 07, 2016, 08:35:06 am »
I made a code that could help you in what you want.

"start_mini.png" is a texture, i will attach it.

For the vertex array i use sf::Triangles for render, see abc.png to have a better idea.

The images of the bug are packed in a zip file.




#include <vector>
#include <iostream>
#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>

#define SHOW_BOUNDS

using namespace std ;
struct Bounds // Or sf::FloatRect.
{
   float x , y , w , h ;
   Bounds( float a , float b , float c , float d ) : x(a) , y(b) , w(c) , h(d) {}
   Bounds() : x(0) , y(0) , w(0) , h(0) {}
};

struct Dimensions
{
   int rows , columns ;
   Dimensions( int r , int c ) : rows(r) , columns( c ) {}
};

struct Tiles : public sf::Drawable , public sf::Transformable
{
    sf::VertexArray sprites ;
        sf::View view ;
        sf::Texture *texture ;
        sf::Clock clock ;
        Bounds bounds ;
        Dimensions dims ;

        Tiles( Dimensions _dims , Bounds _bounds ) : sprites( sf::Triangles ) , bounds( _bounds ) ,  dims( _dims )
        {
           sprites.resize( dims.rows * dims.columns * 6 ) ;
           // A rectangle is formed by 2 triangles. triangle = 3 sides. 2 triangles = 6.
           calcTexture() ;      
        }

        void calcTransform( sf::Transformable trans = sf::Transformable() )
        {
            float x = bounds.x     ; float y = bounds.y ;
                float width = bounds.w ; float height = bounds.h ;

                sf::Vector2f middle( bounds.w * 0.5f , bounds.h * 0.5f ) ;

                int r = 0 ; int c = 0 ;
                for( size_t i = 0 ; i < sprites.getVertexCount() ; i += 6 )
                {  
                     if( r >= dims.rows ){ r = 0 ; ++c ; }
                         
                     trans.setPosition( r * bounds.w , c * bounds.h ) ;

                         const sf::Transform &tr = trans.getTransform() ;

                     int count = i ;
                         // First triangle.
                     sf::Vertex &A = sprites[count++] ; // i + 0
                         sf::Vertex &B = sprites[count++] ; // i + 1
                     sf::Vertex &C = sprites[count++] ; // i + 2
                         // Second triangle.
                         sf::Vertex &D = sprites[count++] ; // i + 3
                     sf::Vertex &E = sprites[count++] ; // i + 4
                         sf::Vertex &F = sprites[count] ;   // i + 5
                                                 
                         A.position = tr.transformPoint( sf::Vector2f( x , y ) ) ;
                         B.position = tr.transformPoint( sf::Vector2f( x , y + height  ) ) ;
                         C.position = tr.transformPoint( sf::Vector2f( x + width , y + height )  ) ;

                         D.position = A.position ;
                         E.position = tr.transformPoint( sf::Vector2f(  x + width , y ) ) ;
                         F.position = C.position ;
                         ++r ;
                }
        }

        void calcTexture()
        {
            float x = bounds.x     ; float y = bounds.y ;
                float width = bounds.w ; float height = bounds.h ;
               
                for( size_t i = 0 ; i < sprites.getVertexCount() ; i += 6 )
                {    
                     int count = i ;
                         // First triangle.
                     sf::Vertex &A = sprites[count++] ; // i + 0
                         sf::Vertex &B = sprites[count++] ; // i + 1
                     sf::Vertex &C = sprites[count++] ; // i + 2
                         // Second triangle.
                         sf::Vertex &D = sprites[count++] ; // i + 3
                     sf::Vertex &E = sprites[count++] ; // i + 4
                         sf::Vertex &F = sprites[count] ;   // i + 5
                                                 
                     A.texCoords = sf::Vector2f( x , y ) ;
                         B.texCoords = sf::Vector2f( x , y + height ) ;
                         C.texCoords = sf::Vector2f( x + width , y + height ) ;

                         D.texCoords = A.texCoords ;
                         E.texCoords = sf::Vector2f( x + width , y ) ;
                         F.texCoords = C.texCoords ;
                         
                }

        }

        void setAtMiddle()
        {
          sf::FloatRect b = sprites.getBounds() ;
          setOrigin( ( b.width - b.left ) * 0.5f , ( b.height - b.top ) * 0.5f ) ;
        }

        void process( Bounds limits , float pixelsPerSeconds )
        {  
            const float &pps = pixelsPerSeconds ;
                const float &delta = clock.restart().asSeconds() ;
           
                sf::Vector2f walk( 0 , 0 ) ;

                if( sf::Keyboard::isKeyPressed( sf::Keyboard::Left ) ) // Could be A.
                {
                   walk.x = -pps * delta ;
                }
                else if( sf::Keyboard::isKeyPressed( sf::Keyboard::Right ) ) // Could be D.
                {
                   walk.x = pps * delta ;
                }

                if( sf::Keyboard::isKeyPressed( sf::Keyboard::Up ) ) // Could be W.
                {
                   walk.y = -pps * delta ;
                }
                else if( sf::Keyboard::isKeyPressed( sf::Keyboard::Down ) ) // Could be S.
                {
                   walk.y = pps * delta ;
                }

                const sf::Vector2f &origin = getOrigin() ;
                const sf::Vector2f pos = getPosition() + walk ;

                sf::Vector2f offset( 0 , 0 ) ;

                if( pos.x - origin.x <= limits.x )
                {
                    offset.x = limits.x - ( pos.x - origin.x ) ;
                }
            else if( pos.x + origin.x >= limits.w )
                {
                        offset.x = limits.w - ( pos.x + origin.x ) ;
                }

                if( pos.y - origin.y <= limits.y )
                {
                    offset.y = limits.y - ( pos.y - origin.y ) ;
                }
            else if( pos.y + origin.y >= limits.h )
            {
                    offset.y = limits.h - ( pos.y + origin.y ) ;
                }

                move( walk + offset ) ;
               
        }

    virtual void draw( sf::RenderTarget &target , sf::RenderStates states ) const
    {
            const sf::View &oldView = target.getView() ; // Get the old view.
       
                target.setView( view ) ;
       
                states.texture = texture ;
                states.transform = getTransform() ;
                target.draw( sprites , states ) ;
               
                #if defined( SHOW_BOUNDS )
                        auto b = sprites.getBounds() ;
                        sf::RectangleShape r( { b.width , b.height } ) ;
                        r.setFillColor( sf::Color( 255 , 0 , 0 , 180 ) ) ;
                        r.setPosition( getPosition() ) ;
                        r.setOrigin( getOrigin() ) ;
                        target.draw( r ) ;
                #endif

                target.setView( oldView ) ; // Re-apply the old view
                // Useful in case you need to retrieve the old view. (Totally optional).
    }

};

int main()
{
    sf::RenderWindow window( sf::VideoMode(800, 600) , "SFML window" ) ;
        window.setFramerateLimit( 60 ) ;

        sf::Texture texture ; texture.loadFromFile( "star_mini.png" ) ;

        const sf::Vector2f &texSize = static_cast< sf::Vector2f >( texture.getSize() ) ;

        Tiles myTiles( { 10 , 10 } , { 0 , 0 , texSize.x , texSize.y } ) ;
              myTiles.view = sf::View( { 400 , 300 } , { 800 , 600 } ) ;
                  myTiles.texture = &texture ; // Remember to keep the texture Alive!
                  myTiles.calcTransform() ;
                  myTiles.setAtMiddle() ;
                  myTiles.setPosition( { 400 , 300 } ) ;
   
    while ( window.isOpen() )
    {
        // Process events
        sf::Event event ;
        while ( window.pollEvent( event ) )
        {
            // Close window : exit
            if (event.type == sf::Event::Closed)
                window.close() ;
        }

                myTiles.process( Bounds( 0 , 0 , 800 , 600 ) , 100 ) ;

        window.clear() ;
        window.draw( myTiles ) ;
        window.display() ;
    }
    return EXIT_SUCCESS ;
}

 
« Last Edit: October 15, 2016, 01:01:31 am by DarkRoku »
I would like a spanish/latin community...
Problems building for Android? Look here

uncreativename

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Novice Problem Using Views
« Reply #6 on: October 07, 2016, 11:29:04 am »
Thanks so much for all the help.  I'll test it when I get home this evening :]