On duty, there was a need to implement a cross-platform engine for casual games (for the most part, quests). In this article I will try to tell you about some non-trivial issues that we solved in the course of development.
Introduction
The engine must support at least three platforms: PC, MacOS X and iOS. It is used to create quests, so more emphasis is on visual beauty: particle systems, animation, video.
The basis of the engine are the following entities:
- the main class of the engine, which is responsible for creating the window, user input, getting / losing focus, managing all other parts of the engine;
- class rendering;
- resource manager - loading, storing, uploading resources (textures, fonts).
These three large entities themselves are completely self-sufficient and do not have external dependencies on the rest of the engine, so when we made an arcade for iOS, we just took them and wound the action wrapper on top.
')
A game (quest) part was created directly above the base. The main idea during its creation was the uniformity of management, creation and loading of game entities.
It was an introduction, now everything is in more detail.
Cross-platform implementation
This was our main task. Making 10 games a year and spending large forces on localization for ten languages of all this disgrace, we could not afford to add to this a separate porting of each game. Why separate? Because our company was divided into 5 working groups distributed in three cities, and each group had its own programmer who picked his own bike. In order to optimize the work, we temporarily transferred all the current games to the best available engine for moral preparation of people for the idea of a common engine and toolkit;
In order to simplify porting, we tried to localize all platform-specific code in as few classes as possible, and the main work is to be done with code common to all platforms. As a result, we have become platform-dependent classes: texture, rendering class, window manager, access to the file system and sound. In order for the rest of the code not to know about the platform used, we took the creation of low-level objects to the factory, which created the necessary object depending on the control directives. Creating low-level objects directly (new) was prohibited.
PC- and MacOS X-implementations were made using the Playground library, because we have been working with this library for a long time and can be sure that we will get a game that works for a very large number of users. For iOS, they implemented everything themselves on OpenGL ES. There we had no difficulties, except drawing into the texture and access to an arbitrary pixel of the texture.
Structuring game entities
I already wrote about this one post in the sandbox, but apparently it was not very.
The idea is that all game entities are in a common system and have one common interface. Any game can be divided into the engine and the game itself. So, the game itself and any entities inside it are inherited from one common object class defined in the engine and implementing all the basic operations: process, input, drawing, loading, unloading, managing children. Each object has a unique name that is generated or set by level designers. Having one common interface, any programmer can fix any code, because everything is the same everywhere. Having already made three games, we analyzed the game code and noticed that the number of unique methods for game objects that are not implemented in the base object is no more than one or two per class. I can not imagine how we lived before with the zoo classes and methods inside them.
The second side of this idea was to create a tree structure. Each object belongs to someone, someone created it, and someone will delete it. All objects are attached to one another. At the top of this hierarchy are two objects: the “root” and “storage”. All that is attached to the root is drawn, processed, handles input; everything that clings to the root automatically loads its resources; that which is uncoupled from it, unloads resources. The “vault” simply stores within itself what is not yet needed, but it can be useful and there will be no time to load; also in the “repository” are stored the elements, the state of which you need to remember and show the user in exactly the same form later. That is, dragging objects between these two super-objects, we control what is displayed on the screen. Access to any object is carried out by its unique name thanks to a recursive search procedure.
You can create an object and not attach it anywhere, but, first, this object will never be drawn or animated, which will be immediately noticed by the person who created it, and, second, it cannot be found using search procedures that will also be immediately noticed and eliminated.
Game editor
We write editors in C #. This is convenient, fast and level designers are happy with the standard Windows interface and hotkeys. And we write games in C ++. And here problems begin to arise ...
The dream of level designers is the editor combined with the game, that is: played the game - found the bug - switched to the editor mode - corrected the bug - switched to the game mode - went to play further. That is, the requirements for the editor were as follows: on the one hand, the game should be the editor at the same time, on the other hand, the editor should have standard Windows windows, controls and management principles.
At first we thought about various libraries of controls for OpenGL and DirectX, but the designers began to moan and whine that they didn’t like, not user friendly, and so on. We went to meet them - they are still five times more than programmers.
Then an idea was born that, having undergone many transformations, resulted in what we now have. In order not to bother you with the details I will tell you at once the final version.
The bottom line is that the game actually includes an editor, that is, the entire control code is in the game, more precisely in the engine, and the editor is just a beautiful control shell with which you can control this code. At the start of the game and the editor, they exchange the initial data with each other, and then at any time by pressing the button in the editor, the game enters the editing mode, providing all the functionality to the designer. The editor window also displays the loaded modules and other service information. At the same time, the editor does not store anything in himself, he only sends commands and receives answers. This was done to avoid duplication of information in the editor and the game and large hemorrhoids with the actualization of data in both places. This solution has a lack of a few additional clicks in some places to get relevant data, but we decided to suffer for now.
Data exchange between the game and the editor takes place with the help of WM_COPYDATA messages, which allows you to send data between applications. Data is packaged in XML. This is done to simplify future changes.
Conclusion
These were the main points that allowed us to implement a simple and portable game engine. There were a lot of little things that will be issued as a separate article.