The development has reached this point, leaving out some details:
The next question is whether or not an entity should know its own coordinates in the grid. The problem is that it involves duplicate data. The (x,y) coordinates stored in the entity object, and the actual position of the object in the grid, need to be kept consistent with each other. It is easy to imagine a lot of code like this:
grid.move(this, Right); x++;
The first line tells the grid to move the entity to a new cell, and the second updates the entity's coordinates to match. The danger of inconsistency is very real, and increases enormously as the number of code fragments like the one above increases. Any problems can be very tough to debug.
One approach is for an entity not to know its own coordinates. Only the grid knows, and can therefore carefully keep them consistent. Several variations on this theme are possible, but two problems inevitably arise. The first is that the resulting system can't be described as simple or natural. The second is that, although most things that an entity wants to do can be done with directions, e.g. "move right" or "find the entity to the right" and so on, occasionally actual coordinates come into play, e.g a long-distance teleport, or a monster which chases the player.
Entities have already been split across two classes, Cell
as a
generic base class and Entity
for custom details. So a good
approach is for a Cell
to know here it is, but not an
Entity
. The Cell
class might look like this:
class Cell { private Grid<Cell> grid; private int x, y; int x() { return x; } int y() { return y; } ... }
An object of the Cell
class knows its own coordinates, but the
class consists of a relatively small amount of stable code, so that consistency
can be kept under control. The coordinates are private, and so the
Entity
class only has read access to them. That encapsulates the
coordinate consistency problem inside the Cell
class.
The Cell
class shown above has a weakness. The grid field has
class Grid<Cell>
. So, when an entity finds a neighbouring
entity in the grid, it only knows that the neighbour has type Cell
,
not Entity
(unless it cheats by using casting, for example). While
this isn't a disaster, it is worth making the framework class Cell
a bit more sophisticated, to ease the implementation of game-specific custom
classes, like this:
public class Cell<E extends Cell<E>> private Grid<E> grid; private int x, y; int x() { return x; } int y() { return y; } ... }
The Cell
class is given a parameter E
, which will
later be filled in by the Entity
class. Then entities will be able
to access other entities using variables of class Entity
. The
E
parameter (which will end up referring to the Entity
class) must be a class which extends Cell
. In fact, it must extend
Cell<E>
, which leads to the strangely cyclic but correct class
declaration class Cell<E extends Cell<E>>
.
Implementing the Cell
class is a bit tricky. For example,
suppose there is a method to move this entity to the coordinates of another
entity:
void move(E to) { ... x = to.x; y = to.y; }
This fails because to.x
and to.y
are not
accessible. The to
variable has class E
whereas the
x
and y
fields are private to the Cell
class. However, any object of class E
automatically has class
Cell<E>
because of the class declaration, so the problem can be
fixed without casts or compiler warnings:
void move(E e) { ... Cell<E> c = e; x = c.x; y = c.y; }
There is another problem. Suppose there is a method:
void wake() { grid.wake(x, y, this); }
This fails because the variable this
has type
Cell<E>
instead of type E
. All objects of type
Cell<E>
are actually going to be of type E
, but the
compiler doesn't know that. A cast (E)this
is needed, but it
causes a warning. However, a single tiny method can be defined with the warning
suppressed:
@SuppressWarnings("unchecked") private E thisE() { return (E)this; }