First off, to define what we mean by camera. My camera in this project is just a position in 2D space, a width and a height. It does not take care of rendering itself, and is independent of screen size.
The camera is a delicate element, there are a lot of ways to implement it and a lot of variation in those ways. In this particular architecture is complicated because once we set up a camera, which by definition is a node, every node in the selected subtree needs to deal with it, and we want to avoid cluttering the draw function with a camera parameter that might even not be needed.
Methods I discarded
Using a singleton, easy to access from any sub node, very little security on who controls it, the game can only have one “view”, and it doesn’t fit in the spirit of the node based system. An easy patch.
Passing the camera by parameter on the draw, but this forces all draw routines to have a parameter they might not need (see GUI), creating an overloaded Draw is dangerous, as you let the user too easily forget to use the one with the camera. Another problem is even if you do it like this, you must position the camera somewhere, and it doesn’t make much sense to do so with a node, so we would have to add a special element to one of our nodes, which is the whole thing I was trying to avoid by using generic nodes.
Setting the camera as a static member of a main class, like the scene, and let anyone access it like that. That’s a more “local” version than the singleton, but it suffers from most of the same problems and a few more, like the variable can be accessed with no Scene created (BAD), and we mark just ONE type of node as capable of using the camera, thus limiting the use of the camera element.
The solution I found best is to derive the camera like just another node, that can be added anywhere. The node it is added to effectively takes on the camera’s position (by setting the position of the parent node to it’s negated position in every update of the camera), and that node and all the subtree it spans are displaced as required by just the normal draw mechanism, which includes adding the parent’s (now camera’s) absolute position to our relative position.
The other detail is we need a proper camera that is capable of culling. For this we implement a cull method in node, which does accept a Camera object, but is only used by the camera behind scenes, and only by those “affected” by a camera, thus removing the problem mentioned with overriding the Draw method. Every node can override this cull method to suit it’s needs, and the camera calls it every update, keeping only a list of elements that are inside the camera as the ones ready to draw.
That’s basically it.