In CosPlay, a scene is defined by CPScene class:
A scene can be adaptive or static in terms of its dimensions. When a scene dimension is set it becomes static for the lifetime of the scene and its scene objects can rely on this fact. However, if a scene dimension is not set, it will adapt to the terminal's window dimension on each frame update. Make sure that all scene objects in the adaptive scene take this into account in their rendering routines.
It is important to note that unlike many other game engines the scene objects in CosPlay do not form any hierarchy withing the scene - scene objects are just a flat collection. Any relationship between scene objects in the same scene can be resolved in any supported Scala way - CosPlay does not place any restrictions on that.
A scene has a strong encapsulation contract in relation to its scene objects:
In other words, once a scene is created its constituent scene objects can only be changed in the game loop by one of its own scene objects. This contract is necessary to ensure proper state management of the scene objects. Note that scene objects can add new and remove existing, not active, scenes as well.
When starting a game via one of the CPEngine.startGame(...) methods you need to pass at least one scene:
val scene = new CPScene(...) try CPEngine.startGame(scene) // At least one scene is required. finally CPEngine.dispose() sys.exit(0)
When creating a scene at least one scene object is required. There are two main ways to create a new scene in CosPlay:
val bgPx = CPPixel('.', C_GREY1, C_GREY2) val dim = CPDim(100, 50) val spr1 = CPAnimationSprite(...) val spr2 = CPAnimationSprite(...) val myScene = new CPScene("id", Option(dim), bgPx, spr1, spr2)
val bgPx = CPPixel('.', C_GREY1, C_GREY2) val dim = CPDim(100, 50) object MyScene extends CPScene("id", Option(dim), bgPx): private val spr1 = CPAnimationSprite(...) private val spr2 = CPAnimationSprite(...) addObjects(spr1, spr2)Note that in this case you need to use protected
addObjects(...)method to add scene objects to the scene.
A scene object is defined by CPSceneObject class and is the main CosPlay type that a game developer interacts with while building a game:
There are two main ways to create a scene object:
Hello CosPlay!at the top left corner of the scene in black using system font:
object HelloSprite extends CPSceneObject("id"): override def getX: Int = 0 override def getY: Int = 0 override def getZ: Int = 0 override def getDim: CPDim = CPDim(15, 1) override def render(ctx: CPSceneObjectContext): Unit = ctx.getCanvas.drawString( 0, 0, 0, "Hello CosPlay!", CPColor.C_BLACK )
You would typically implement CPSceneObject class directly when none of the existing sprites fit your needs.
Hello CosPlay! sprite can be implemented quicker using existing CPLabelSprite:
val spr = CPLabelSprite(0, 0, 0, "Hello CosPlay!", C_BLACK)
Scene objects get updated roughly 30 times per second (CosPlay maintains 30 FPS as its target frame rate). There is no need to update ASCII graphics on ANSI terminal with a higher frame rate which leaves more CPU time to the game logic.
Here is what happens inside the game loop. On each frame update game engine creates a new instance
ctx of CPSceneObjectContext trait that defines all in-game services available to scene objects and performs three passes through the list of scene objects in the current scene:
At the start of each frame, this callback is called on all scene objects in the scene regardless of whether they are visible or in camera frame. On this callback the scene object may read the keyboard input, update its internal state like position, dimension, or Z-index, send and receive messages, perform network sync, etc. Scene object should not do any rendering in this callback.
This callback is called only after all scene objects received the
CPSceneObject.update(ctx) callbacks and only for visible and in camera frame scene objects. Scene objects should only render in this callback using canvas object obtained via
ctx.getCanvas() method and should not do any state updates. Note that objects don't have to perform any clipping or worry about partially visible object - if this callback is called the object should render itself fully on a given canvas. Game engine will take care of proper clipping of the partially visible objects.
CPShader.render(ctx, ...) callback.
CPSceneObject.render(ctx) callbacks are called - this callback is called for scene objects that have one or more shaders attached to them regardless of whether they are visible or in camera frame.
After all three passes are complete, the game engine will render the difference between the last frame canvas and this frame canvas on the target terminal. And the game loop will begin again.