📜 ⬆️ ⬇️

Creating a scene system for the game engine

Foreword


I am currently working on my own game engine. Using the minimum number of third-party libraries, after the game loop was implemented (game loop), frame rendering, update function, texture downloads, etc., the main stuffing of the engine was ready. It's time to implement another important component - the scene.

Introduction


In this article, I assume that the engine is already equipped with a game cycle with “callback” functions. All code will be written in Java, but can be easily transferred to any other language that supports the garbage collection . Well, let's get started.

What is already there


As mentioned earlier, we already have a game cycle. Let it look something like this:

void awake() { RenderUtil.init(); //   OpenGL run(); } void run() { // game loop // ... //  input,  frame rate  . . // if (Window.isCloseRequested()) { //    stop(); return; } update(); render(); } 

I will provide implementation of the render () and stop () methods a bit later.
')

We define the scene


Before you start writing a class for a scene, you must decide what it will represent. In my case, this is an object that includes many game objects. Tear off for a second from reading and look around you: everything that you see (almost) we will call game objects, and where you are - a scene.

What do I mean by game object? This is an object that implements the "callback" function of the game cycle. For those familiar with Unity3D : an analogy with an object whose class implements MonoBehaviour.

Let this very game object be represented by an interface (or an abstract class - depending on the required functionality), which we will call GameListener (again, in this article I mean that this class is already implemented one way or another).
A primitive implementation of the interface can look like this:

 public interface GameListener { void start() ; // ,    void update(); //    void draw(); //  update() void destroy(); // ,   "" } 

The number of functions depends on the desired degree of control, for example, Unity3D has quite a lot of them .

Implement the Scene class


After determining the architecture and structure of our scene, you can finally begin to implement it.

 public abstract class Scene implements GameListener { ArrayList<GameListener> gameListeners = new ArrayList<>(); public abstract void initializeScene(); public final void AddToScene(GameListener gameListener) { gameListeners.add(gameListener); } public final void onInitializeScene() { if (gameListeners.isEmpty()) initializeScene(); } @Override public final void start() { for (GameListener gameListener : gameListeners) gameListener.start(); } @Override public final void update() { for (GameListener gameListener : gameListeners) gameListener.update(); } @Override public final void draw() { for (GameListener gameListener : gameListeners) gameListener.draw(); } public final void onDestroy() { for (GameListener gameListener : gameListeners) gameListener.destroy(); gameListeners.clear(); } @Override public final void destroy() {} 

Comments I will write down the points:


It is worth noting that all methods (except for initializeScene (), of course) are marked with the final keyword, thus, in the Scene class, the user of the engine can only add their game objects (this restriction is fine for me so far).

Transformations in the game cycle


Now it is necessary to carry out transformations in the game cycle. All of them, in fact, intuitive.

 Scene runningScene; void awake() { RenderUtil.init(); runningScene = SceneManager.getScene(0); run(); } void run() { if (Window.isCloseRequested()) { stop(); return; } runningScene.update(); runningScene.render(); } void stop() { runningScene.onDestroy(); } void render() { runningScene.draw(); } 

We can add all our created scenes to an array contained, for example, in some class called SceneManager . Then it will act as a controller for our scene system, introducing the methods getScene () , setScene (), and so on.

At this stage, the implementation of the system is very much like the “State” pattern. The way it is.

Scene change


To change scenes, we can define a similar instance of the Scene class in the SceneManager:

private static Scene currentScene;


Next, write setter setCurrentScene (Scene):

public static void setCurrentScene(Scene scene) { currentScene = scene; }


Then in the game loop we compare runningScene with currentScene and, if they do not match, change the scene:

 void run() { if (Window.isCloseRequested()) { stop(); return; } runningScene.update(); if (runningScene != SceneManager.getCurrentScene()) { runningScene.onDestroy(); runningScene = SceneManager.getCurrentScene(); } runningScene.render(); } 

It is important not to forget to call the onDestroy () method of the current scene to delete its game objects.

Implement additive loading


In the same Unity3D there is the possibility of “additive” loading of scenes. With this method, the objects of the “old” scene are not deleted (in our case, the onDestroy () method is not called), and the new scene is loaded “on top” of the old one.

This can be achieved, for example, by creating a container that stores a list of additively loaded scenes. Then along with the challenge

runningScene.update();


will need to say something like

for (Scene additive : additives)
additive.update();


and so on.

Call onDestroy () is necessary in case of restarting / changing the main scene (runningScene) or closing the game.

The architecture, the procedure for adding game objects and the scene itself remain the same.

Source: https://habr.com/ru/post/272161/


All Articles