views:

113

answers:

3

I'm working on a multiplayer flash game. The server informs each client what other players are near the player. To do this the server has to check which clients are near each other continuously. The following is what I am using at this moment, as a temporary solution:

private function checkVisibilities()
{
    foreach ($this->socketClients as $socketClient1)
    { //loop every socket client
        if (($socketClient1->loggedIn()) && ($socketClient1->inWorld()))
        { //if this client is logged in and in the world
            foreach ($this->socketClients as $cid2 => $socketClient2)
            { //loop every client for this client to see if they are near
                if ($socketClient1 != $socketClient2)
                { //if it is not the same client
                    if (($socketClient2->loggedIn()) && ($socketClient2->inWorld())
                    { //if this client is also logged in and also in the world
                        if ((abs($socketClient1->getCharX() - $socketClient2->getCharX()) + abs($socketClient1->getCharY() - $socketClient2->getCharY())) < Settings::$visibilities_range)
                        { //the clients are near each other
                            if (!$socketClient1->isVisible($cid2))
       { //not yet visible -> add
                                 $socketClient1->addVisible($cid2);
                            }
                        }
                        else
                        { //the clients are not near each other
                            if ($socketClient1->isVisible($cid2))
                            { //still visible -> remove
                                $socketClient1->removeVisible($cid2);
                            }
                        }
                    }
                    else
                    { //the client is not logged in
                        if ($socketClient1->isVisible($cid2))
                        { //still visible -> remove
                            $socketClient1->removeVisible($cid2);
                        }
                    }    
               }
         }
     }
}

It works fine. However, so far I've only been playing with 2 players at a time. This function is looping every client for every client. So, with 100 players that would be 100 * 100 = 10.000 loops every time the function is run. This doesn't seem the best or most efficient way to do it.

Now I wonder what you folks think about my current setup and if you have any suggestions on a better way of handling these visibilities.

Update: I forgot to mention that the world is infinite. It is actually "the universe". There are no maps. Also, it is a two dimensional (2D) game.

Thanks in advance.

+3  A: 

The most straightforward solution is to partition the world into a uniform grid, like so:

_|____|____|____|_
 |    |    |    |
_|____|____|____|_
 |    |    |    |
_|____|____|____|_
 |    |    |    |
_|____|____|____|_
 |    |    |    |

Then insert your objects into any grid tile that they intersect:

_|____|____|____|_
 | @  |    |    |
_|____|____|____|_
 |    |d d |    |
_|____|____|____|_
 |    | d  |  d |
_|____|____|____|_
 |    |    |    |

Now to do a query for nearby objects, you only need to look at nearby cells. For example, to see who within one tile from the player (@), you only need to check in 9 tiles, not the whole map:

/|////|////|____|_
/|/@//|////|    |
/|////|////|____|_
/|////|d/d/|    |
/|////|////|____|_
 |    | d  |  d |
_|____|____|____|_
 |    |    |    |

Depending on your world, however, this technique can be quite wasteful: there could be a lot of empty cells. If this becomes a problem, you may want to implement a more complex spatial index.

ianh
I forgot to add: the world in my game is infinite. There are no maps.
Tom
Does that influence your answer?
Tom
That will probably only influence his answer if by 'nearby' you mean the entire universe, instead of a fixed space around position X. I think this answer is still valid.
Cloud
So you would have to dynamically create new columns depending on the used size of the universe? What if player A is at 50x20 while player B is at 123400x523452 and so is player C? The column width would have to be constantly adjusted, so that you would have to move the players from column to column all the time? Or am I seeing this wrong?
Tom
If the definition of being near another player is dynamic then the cells will need to be sized accordingly. I would make the size of the cell half the distance you have defined as near. If that distance is changing all the time then this may not be the method for you. Every time you need to resize the cells you will have to reassign all the objects to the cells they're in. Which could be just as or even more expensive.
resolveaswontfix
The distance for someone else to be near is not dynamic. The size of the universe is however.
Tom
You could maintain a collection of grids, one for each area containing objects which can interact with one another. In the infinite case, however, you're probably better off with a quadtree.You should be careful, however, that your number representation can really handle an infinite world. I remember reading about some game (Dungeon Siege, maybe?) where, during development, they were having problems with precision. As the player went further and further from the origin, weird things started to happen, due to space getting more and more quantized.
ianh
Alright. So I assume in any case my simple current method does not suffice (even when improved as Kylotan suggested). I think I will create a self-balancing quad tree. As the universe expands (this could happen a few times a day maximum, the world is dynamic) the quad tree would rebuild itself.
Tom
+2  A: 

Try using a quad tree to represent the players' locations.
The wiki article for this is here.
What it does is keeping the objects you give it in space (users) in a tree which partitions the space (plane) as much as needed. As for the infinity problem - nothing in programming is really infinite, so define a border which cannot be passed by the users (go even for a very large number for a coordinate, something that will take a user 100 years or so to get to).

Asaf
+5  A: 

The first thing I would say is that your code looks inside-out. Why do you have a high level game logic function that has to do the grunt-work of checking which clients are logged in and in the world? All that networking stuff should be removed from the game logic so that it's done on a higher level and the in-game logic only has to handle the players who are currently playing and in the world. This leaves you with a simple question: are these 2 players near enough to each other? A simple distance check suffices here, as you already have.

The next thing is to reduce the amount of looping you do. Distance is generally a commutative property so you don't need to check the distance between A and B as well as between B and A. To do this, whereas your first loop goes through all the clients, the second loop only needs to iterate over all the clients that come after the first one. This halves the number of iterations you need to do.

You also don't have to to do this continuously, as you state. You just have to do it often enough to ensure that the game runs smoothly. If movement speed is not all that high then you might only have to do this every few seconds for it to be good enough.

If this is still not good enough for you then some sort of spatial hashing system as described by ianh is a good way of reducing the number of queries you do. A grid is easiest but some sort of tree structure (ideally self-balancing) is another option.

Kylotan