-
Notifications
You must be signed in to change notification settings - Fork 19
Scene
The xy::Scene
object is used to maintain a proper node graph of entities, as well as rendering all entities with drawable components. Once an entity is created it needs to be added to a scene so that it can be updated, interact with other entities in the scene and be drawn if necessary. Scene
objects take ownership of any entities added to them, and return a pointer to the newly added entity. Management of returned pointers should be handled carefully, because they may become invalid when an entity is destroyed. All entities emit an event on destruction via the message bus which can be handled to identify and nullify any references as necessary. (See messages and entities). Scenes have seven layers to which an entity can be added, defining in which order drawable entities are rendered. Layer names are found in the enum Scene::Layer
:
enum Layer
{
BackRear = 0,
BackMiddle,
BackFront,
FrontRear,
FrontMiddle,
FrontFront,
UI,
Count
};
It should be noted that Scene::Layer::Count
is not a valid layer, and is used internally by xygine. The other names are descriptive bar UI which is merely a suggested use of the layer - it behaves exactly the same as the others and is named UI purely because it is the top-most layer, where the UI would most frequently be placed.
The scene also manages the sending of commands to entities, which can target specific entities by UID or groups of entities based on their command category mask. See commands for further details.
Once a scene is set up it needs to be updated once a frame, passing in the current frame time. This will in turn update the logic of all entities in the scene. This is done with
scene.update(elapsedTime);
The scene class inherits sf::Drawable
, and so can draw the entire graph in one call:
renderTarget.draw(scene);
The scene manages its view via a Camera
component. By default the scene uses its own internal camera, fixed so that the world coordinates 0,0 are positioned in the top left corner, and 1920,1080 in the bottom right. This means that the scene can be drawn right away without setting up any special cameras. The scene's active camera can be set by providing a pointer to a Camera
component attached to an entity in the scene using Scene::setActiveCamera()
. Camera
components assume the transform (translation and rotation) of their parent entities making it easy for the view to follow players or other in-game objects. See the camera page for more details on their properties and uses. Being able to set the active camera is useful when drawing split screen views, for example:
scene.setActiveCamera(playerOne->getCamera());
renderWindow.draw(scene);
scene.setActiveCamera(playerTwo->getCamera());
renderWindow.draw(scene);
This assumes the view of each camera is already set up to cover the appropriate sections of the render target.
When rendering with the scene full screen post process effects can easily be applied by creating an effect which inherits xy::PostProcess
. xygine comes with some example post process effects including Bloom and Chromatic abberation. When using a single camera, such as the default view or an in-scene camera, post process effects can be added to the scene's render queue in the order in which they should be applied:
xy::PostProcess::Ptr pp = xy::PostProcess::create<xy::PostBloom>();
scene.addPostProcess(pp);
pp = xy::PostProcess::create<xy::PostChromeAb>();
scene.addPostProcess(pp);
pp = xy::PostProcess::create<MyEffect>();
scene.addPostProcess(pp);
...and so on. The post processes are unique_ptr
s, of which the scene object takes ownership. This invalidates pp
as soon as it is added, allowing it to be reassigned a new effect. Effects in the render queue are then automatically applied every time the scene is drawn.
Using multiple cameras is a little more complicated, however. Because the scene needs to be rendered multiple times for each camera it is necessary to avoid repeating the post process for each pass. Instead of adding post effects to the scene's render queue, post process objects can easily be used on their own via their apply()
function, which takes sf::RenderTexture
as an input and sf::RenderTarget
(either another texture or the render window itself) as an output. A multiple camera scenario might then look like:
renderTexture.clear();
scene.setActiveCamera(playerOne->getCamera());
renderTexture.draw(scene);
scene.setActiveCamera(playerTwo->getCamera());
renderTexture.draw(scene);
renderTexture.display();
postBloomEffect.apply(renderTexture, renderWindow);
Again, post process effects can be chained using a pair of sf::RenderTexture
s to 'ping pong' the output, before finally drawing to the renderWindow.
postEffectA.apply(renderTextureA, renderTextureB);
postEffectB.apply(renderTextureB, renderTextureA);
postEffectC.apply(renderTextureA, renderWindow);
It is important to note that when doing this no post processes should be added to the scene itself. This will likely give poor visual and performance results.