简体   繁体   English

落沙模拟中的水

[英]Water in a falling sand simulation

奇怪的水物理演示

I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner.我目前正在 C++ 和 SDL2 中开发一个非常简单的“落沙”模拟游戏,并且在让水以更真实的方式流动时遇到问题。 I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free).我基本上有一个单元格网格,我从下到上、从左到右进行迭代,如果我找到一个水单元格,我只需在下面、从下到左、从下到右、从左到右检查空单元格并且它移动到它找到的第一个(如果两个对角单元格或两个水平单元格都是空闲的,它会随机选择)。 I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.然后我将它移入的单元格标记为已处理,以便不再检查该循环的 rest。

My problem is a sort of 'left-bias' in how the particles move;我的问题是粒子如何移动的一种“左偏”; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way.如果我在屏障上方生成一个正方形的水细胞,一旦粒子开始到达屏障,它们基本上都会向左移动而不会移动,而右侧的细胞将以正确的方式向下流动。 So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left.所以不是形成一个漂亮的三角形,均匀地流向两边,整个形状只会向左移动。 This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it.每当我从左到右迭代时,这种效果就会逆转,所以我知道这与此有关,但到目前为止,我一直在努力修复它。 I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing.我最初认为这是我如何将单元格标记为已处理的问题,但在数小时的测试中我没有发现该系统存在明显的错误。 Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing?有没有人在开发这样的模拟时遇到过类似的挑战,或者知道我错过了什么? Any help would be very much appreciated.任何帮助将不胜感激。

EDIT: Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that.编辑:好的,所以我取得了一些进展,但是我遇到了另一个似乎与迭代无关的错误,因为现在我保存了旧单元格的副本并从中读取以决定更新,然后更新原始单元格并显示它。 This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally.这已经使沙子工作得更好,但是水平检查自由细胞的水现在在水平移动时“消失”了。 I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.我整个早上都在测试它,但还没有找到解决方案,我认为这可能与我复制 arrays 的方式有关,但据我所知,它似乎有效。

New snippets:新片段:

Simulation.cpp模拟.cpp

void Simulation::update()
{
    copyStates(m_cells, m_oldCells); // so now oldcells is the last new state

    for(int y = m_height - 1; y>= 0; y--)
    for(int x = 0; x < m_width; x++)
        {
            Cell* c = getOldCell(x, y); // check in the old state for possible updates
            switch(c->m_type)
            {
                case EMPTY:
                    break;
                case SAND:
                    if(c->m_visited == false) update_sand(x, y);
                    break;
                case WATER:
                    if(c->m_visited == false) update_water(x, y);
                    break;
                default:
                    break;
            }
        }
}

void Simulation::update_water(int x, int y)
{
    bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
    bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
    bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
    bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
    bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;

    // choose random dir if both are possible
    if(d_left && d_right)
    {
        int r = rand() % 2;
        if(r) d_right = false;
        else d_left = false;
    }

    if(left && right)
    {
        int r = rand() % 2;
        if(r) right = false;
        else left = false;
    }
    
    if(down)
    {
        getCell(x, y+1)->m_type = WATER; // we now update the new state
        getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
    } else if(d_left)
    {
        getCell(x-1, y+1)->m_type = WATER;
        getOldCell(x-1, y+1)->m_visited = true;
    } else if(d_right)
    {
        getCell(x+1, y+1)->m_type = WATER;
        getOldCell(x+1, y+1)->m_visited = true;
    } else if(left)
    {
        getCell(x-1, y)->m_type = WATER;
        getOldCell(x-1, y)->m_visited = true;
    } else if(right)
    {
        getCell(x+1, y)->m_type = WATER;
        getOldCell(x+1, y)->m_visited = true;
    }
    
    if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
    {
        getCell(x, y)->m_type = EMPTY;
    }
}

void Simulation::copyStates(Cell* from, Cell* to)
{
    for(int x = 0; x < m_width; x++)
    for(int y = 0; y < m_height; y++)
    {
        to[x + y * m_width].m_type = from[x + y * m_width].m_type;
        to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
    }
}

Main.cpp主文件

sim.update();

Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
    sim.getCell(x, y)->m_visited = false;
    if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
    if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
    if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}


screen.render();

I've attached a gif showing the problem, hopefully this might help make it a little clearer.我附上了一个显示问题的 gif,希望这可能有助于使它更清楚一点。 You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)你可以看到沙子被正常放置,然后是水和它在放置后形成的奇怪图案(注意它在生成时是如何向左移动的,不像沙子)

You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.您还必须将目标位置标记为已访问,以阻止多个单元格移动到同一位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM