Domanda

I have been trying to implement 2D tile based water into my game. I started out making the tiles appear on screen etc. I have a drawing function that draws each tile type. The problem I'm having is that when I call this function the Tiles that are water don't change position. Which makes me believe that this code isn't functioning properly. This code is called on every loop. This should update the masses of all the water tiles. For some reason nothing is happening. The water is staying in its original positions. My tiles are in a vector of the tile class is just

Tiles()
{
    TileProp // the type of tile (GROUND,AIR,WATER)
    Mass
    NewMass
} 

void App::SimulateCompression()
{
float Flow = 0;
float remainingmass = 0;

int ID = 0;

//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
    for(int Y = 0; Y < MAP_HEIGHT; Y++)
    {
        //Skip inert ground blocks
        if(TileList[ID].TileProp == TILE_GROUND) continue;

        //Custom push-only flow
        Flow = 0;
        remainingmass = TileList[ID].Mass;
        if(remainingmass <= 0) continue;

        //The block below this one
        if(TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].TileProp != TILE_GROUND)
        {
            Flow = GetStableWaterState(remainingmass + TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].Mass /*mass[x][y-1]*/) - TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].Mass;
            if(Flow > MinFlow){Flow *= 0.5; /*leads to smoother flow*/}
            int tempA = Min(MaxSpeed, remainingmass);
            if(Flow > tempA){Flow = tempA;}
            if(Flow < 0){Flow = 0;}

            TileList[ID].NewMass -= Flow;
            TileList[Rect2Lin(TILE_SIZE,X,(Y-1))].NewMass += Flow;
            remainingmass -= Flow;
        }

        if(remainingmass <= 0) continue;

        //Left
        if(TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].TileProp != TILE_GROUND)
        {
            //Equalize the amount of water in this block and it's neighbour
            Flow = (TileList[ID].Mass - TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].Mass)/4;
            if(Flow > MinFlow){Flow *= 0.5;}
            if(Flow > remainingmass){Flow = remainingmass;}
            if(Flow < 0){Flow = 0;}

            TileList[ID].NewMass -= Flow;
            TileList[Rect2Lin(TILE_SIZE,(X - 1),Y)].NewMass += Flow;
            remainingmass -= Flow;
        }

        if(remainingmass <= 0) continue;

        //Right
        if(TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].TileProp != TILE_GROUND)
        {
            //Equalize the amount of water in this block and it's neighbour
            Flow = (TileList[ID].Mass - TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].Mass)/4;
            if(Flow > MinFlow){Flow *= 0.5;}

            if(Flow > remainingmass){Flow = remainingmass;}
            if(Flow < 0){Flow = 0;}

            TileList[ID].NewMass -= Flow;
            TileList[Rect2Lin(TILE_SIZE,(X + 1),Y)].NewMass += Flow;
            remainingmass -= Flow;
        }

        if(remainingmass <= 0) continue;

        //Up. Only compressed water flows upwards
        if(TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].TileProp != TILE_GROUND)
        {
            Flow = remainingmass - GetStableWaterState(remainingmass + TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].Mass);
            if (Flow > MinFlow){Flow *= 0.5;}

            int tempB = Min(MaxSpeed, remainingmass);
            if(Flow > tempB){Flow = tempB;}
            if(Flow < 0){Flow = 0;}

            TileList[ID].NewMass -= Flow;
            TileList[Rect2Lin(TILE_SIZE,X,(Y + 1))].NewMass += Flow;
            remainingmass -= Flow;
        }
        ID++;
    }
}

ID = 0;
//Copy the new mass values 
for (int X = 0; X < MAP_WIDTH; X++)
{
    for (int Y = 0; Y < MAP_HEIGHT; Y++)
    {
        TileList[ID].Mass = TileList[ID].NewMass;
        ID++;
    }
}

ID = 0;
for(int X = 0; X < MAP_WIDTH; X++)
{
    for(int Y = 0; Y < MAP_HEIGHT; Y++)
    {
        //Skip ground blocks
        if(TileList[ID].TileProp == TILE_GROUND) continue;

        //Flag/unflag water blocks
        if(TileList[ID].Mass > MinMass)
        {
            TileList[ID].TileProp = TILE_WATER;
        }else
        {
            TileList[ID].TileProp = TILE_AIR;
        }
        ID++;
    }
}

//Remove any water that has left the map
for(int X = 0; X < MAP_WIDTH; X++)
{
    TileList[X].Mass = 0;
    TileList[Rect2Lin(TILE_SIZE,X,MAP_HEIGHT - 1)].Mass = 0;

}

for(int Y = 0; Y < MAP_HEIGHT; Y++)
{
    TileList[Rect2Lin(TILE_SIZE,0,Y)].Mass = 0;
    TileList[Rect2Lin(TILE_SIZE,(MAP_WIDTH - 1),Y)].Mass = 0;
}

}
È stato utile?

Soluzione

Ok, so ID is going to 0 because after ID hits 34 It just leaves the two nested for loops... Why would it do that?

//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
    for(int Y = 0; Y < MAP_HEIGHT; Y++)
    {
        //Skip inert ground blocks
        if(TileList[ID].TileProp == TILE_GROUND) continue;

        ...

        ID++;
    }
}

TileList[34] is probably a ground tile. At which point you hit that first if over and over (since you never get to the ID++ at the very end of the loop) until you have exhausted the for-loops.

Try this:

//Calculate and apply flow for each block
for (int X = 0; X < MAP_WIDTH; X++)
{
    for(int Y = 0; Y < MAP_HEIGHT; Y++)
    {
        int ID = Rect2Lin(TILE_SIZE,X,Y));

        //Skip inert ground blocks
        if(TileList[ID].TileProp == TILE_GROUND) continue;

        ...
    }
}

EDIT:

Ok, this works on my system:

#include <GL/glut.h>
#include <vector>

using namespace std;

// simple Eigen::Matrix work-alike
template< typename T >
class Matrix
{
public:
    Matrix( const size_t rows, const size_t cols ) 
        : mStride( cols )
        , mHeight( rows )
        , mStorage( rows * cols ) 
    {}

    T& operator()( const size_t row, const size_t col )
    {
        return mStorage[ row * mStride + col ];
    }

    const T& operator()( const size_t row, const size_t col ) const 
    {
        return mStorage[ row * mStride + col ];
    }

    size_t rows() const { return mHeight; }
    size_t cols() const { return mStride; }

private:
    vector< T > mStorage;
    size_t mStride;
    size_t mHeight;
};

struct Cell
{
    enum Type{ AIR, GROUND, WATER };

    Cell() 
        : mType( AIR )
        , mMass( 0 )
        , mNewMass( 0 )
    {}

    Type mType;
    float mMass;
    float mNewMass;
};

const float MaxMass = 1.0f; 
const float MinMass = 0.0001f; 
const float MaxCompress = 0.02f; 
const float MaxSpeed = 1.0f;
const float MinFlow = 0.01f;

//Take an amount of water and calculate how it should be split among two
//vertically adjacent cells. Returns the amount of water that should be in 
//the bottom cell. 
float get_stable_state_b( float total_mass )
{
    if ( total_mass <= 1 )
    {
        return 1;
    } 
    else if ( total_mass < 2*MaxMass + MaxCompress )
    {
        return (MaxMass*MaxMass + total_mass*MaxCompress)/(MaxMass + MaxCompress);
    } 
    else
    {
        return (total_mass + MaxCompress)/2;
    }
}

template< typename T >
T constrain( const T& val, const T& minVal, const T& maxVal )
{
    return max( minVal, min( val, maxVal ) );
}

typedef Matrix< Cell > State;
void stepState( State& cur )
{
    for( size_t y = 1; y < cur.rows()-1; ++y )
    {
        for( size_t x = 1; x < cur.cols()-1; ++x )
        {
            Cell& center = cur( y, x );

            // Skip inert ground blocks
            if( center.mType == Cell::GROUND )
                continue;

            // Custom push-only flow
            float Flow = 0;
            float remaining_mass = center.mMass;
            if( remaining_mass <= 0 )
                continue;

            // The block below this one
            Cell& below = cur( y-1, x );
            if( below.mType != Cell::GROUND )
            {
                Flow = get_stable_state_b( remaining_mass + below.mMass ) - below.mMass;
                if( Flow > MinFlow )
                {
                    //leads to smoother flow
                    Flow *= 0.5;
                }
                Flow = constrain( Flow, 0.0f, min(MaxSpeed, remaining_mass) );

                center.mNewMass -= Flow;
                below.mNewMass += Flow;
                remaining_mass -= Flow;
            }

            if ( remaining_mass <= 0 ) 
                continue;

            // Left
            Cell& left = cur( y, x-1 );
            if ( left.mType != Cell::GROUND )
            {
                // Equalize the amount of water in this block and it's neighbour
                Flow = ( center.mMass - left.mMass ) / 4;
                if ( Flow > MinFlow )
                {
                    Flow *= 0.5;
                }
                Flow = constrain(Flow, 0.0f, remaining_mass);
                center.mNewMass -= Flow;
                left.mNewMass += Flow;
                remaining_mass -= Flow;
            }

            if ( remaining_mass <= 0 ) 
                continue;

            // Right
            Cell& right = cur( y, x+1 );
            if ( right.mType != Cell::GROUND )
            {
                // Equalize the amount of water in this block and it's neighbour
                Flow = ( center.mMass - right.mMass ) / 4;
                if ( Flow > MinFlow )
                {
                    Flow *= 0.5;
                }
                Flow = constrain(Flow, 0.0f, remaining_mass);
                center.mNewMass -= Flow;
                right.mNewMass += Flow;
                remaining_mass -= Flow;
            }

            if ( remaining_mass <= 0 ) 
                continue;

            // The block above this one
            Cell& above = cur( y+1, x );
            if( above.mType != Cell::GROUND )
            {
                Flow = remaining_mass - get_stable_state_b( remaining_mass + above.mMass );
                if( Flow > MinFlow )
                {
                    //leads to smoother flow
                    Flow *= 0.5;
                }
                Flow = constrain( Flow, 0.0f, min(MaxSpeed, remaining_mass) );

                center.mNewMass -= Flow;
                above.mNewMass += Flow;
                remaining_mass -= Flow;
            }
        }
    }

    for( size_t y = 0; y < cur.rows(); ++y )
    {
        for( size_t x = 0; x < cur.cols(); ++x )
        {
            cur( y, x ).mMass = cur( y, x ).mNewMass;
        }
    }

    for( size_t y = 0; y < cur.rows(); ++y )
    {
        for( size_t x = 0; x < cur.cols(); ++x )
        {
            Cell& center = cur( y, x );
            if( center.mType == Cell::GROUND ) 
            {
                center.mMass = center.mNewMass = 0.0f;
                continue;
            }
            if( center.mMass > MinMass )
            {
                center.mType = Cell::WATER;
            }
            else
            {
                center.mType = Cell::AIR;
                center.mMass = 0.0f;
            }
        }
    }

    // Remove any water that has left the map
    for( size_t x = 0; x < cur.cols(); ++x )
    {
        cur( 0, x ).mMass = 0;
        cur( cur.rows()-1, x ).mMass = 0;
    }
    for( size_t y = 0; y < cur.rows(); ++y )
    {
        cur( y, 0 ).mMass = 0;
        cur( y, cur.cols()-1 ).mMass = 0;
    }
}

void showState( const State& state )
{
    glPolygonMode( GL_FRONT, GL_LINE );
    glBegin( GL_QUADS );
    glColor3ub( 0, 0, 0 );
    for( size_t y = 0; y < state.rows(); ++y )
    {
        for( size_t x = 0; x < state.cols(); ++x )
        {
            glVertex2f( x+0, y+0 );
            glVertex2f( x+1, y+0 );
            glVertex2f( x+1, y+1 );
            glVertex2f( x+0, y+1 );
        }
    }
    glEnd();

    glPolygonMode( GL_FRONT, GL_FILL );
    glBegin( GL_QUADS );
    for( size_t y = 0; y < state.rows(); ++y )
    {
        for( size_t x = 0; x < state.cols(); ++x )
        {
            if( state( y, x ).mType == Cell::AIR )
                continue;

            float height = 1.0f;
            if( state( y, x ).mType == Cell::GROUND )
            {
                glColor3ub( 152, 118, 84 );
            }
            else
            {
                glColor3ub( 0, 135, 189 );
                height = min( 1.0f, state( y, x ).mMass );
            }

            glVertex2f( x+0, y );
            glVertex2f( x+1, y );
            glVertex2f( x+1, y + height );
            glVertex2f( x+0, y + height );
        }
    }
    glEnd();
}

State state( 20, 20 );
void mouse( int button, int button_state, int x, int y )
{
    float pctX = (float)x / glutGet( GLUT_WINDOW_WIDTH );
    float pctY = 1.0f - ( (float)y / glutGet( GLUT_WINDOW_HEIGHT ) );
    size_t cellX = pctX * state.cols();
    size_t cellY = pctY * state.rows();
    Cell& cur = state( cellY, cellX );

    if( button_state == GLUT_UP )
        return;

    if( button == GLUT_LEFT_BUTTON )
    {
        cur.mType = ( cur.mType == Cell::GROUND ? Cell::AIR : Cell::GROUND );
        cur.mMass = cur.mNewMass = 0.0f;
    }

    if( button == GLUT_RIGHT_BUTTON )
    {
        cur.mType = Cell::WATER;
        cur.mMass = cur.mNewMass = 1.0f;
    }
}


void display()
{
    static bool firstTime = true;
    if( firstTime )
    {
        firstTime = false;
        for( size_t y = 0; y < state.rows(); ++y )
        {
            for( size_t x = 0; x < state.cols(); ++x )
            {
                state( y, x ).mType = (Cell::Type)( rand() % 3 );
                state( y, x ).mMass = 1.0f;
                state( y, x ).mNewMass = 1.0f;
            }
        }  
    }

    glClearColor( 1, 1, 1, 1 );
    glClear( GL_COLOR_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    glOrtho( 0, state.cols(), 0, state.rows(), -1, 1);

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

    stepState( state );
    showState( state );

    glutSwapBuffers();
}

void timer(int extra)
{
    glutPostRedisplay();
    glutTimerFunc(16, timer, 0);
}

int main( int argc, char **argv )
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 640, 480 );
    glutCreateWindow( "Cells" );
    glutDisplayFunc( display );
    glutMouseFunc( mouse );
    glutTimerFunc(0, timer, 0);
    glutMainLoop();
    return 0;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top