- DIY
- A
How I write an open source game in Java
About 10 years ago, I had the idea to write a game in Java, since I use this language at work. It was a kind of challenge. I wanted to try myself, see if it was possible. And spoiler - it is possible. But the project gave me more than I could have expected.
Information Systems Architect of the "Logistics" Department at KORUS Consulting
Hello everyone! My name is Alexander Nilov, I am an architect of the "Logistics" department at KORUS Consulting. But today we will not talk about work, but about my personal project - a 3D computer game.
I have been programming for quite a long time. About 10 years ago, I had the idea to write a game specifically in Java, since I use this language at work. It was a kind of challenge. I wanted to try myself, see if it was possible. And spoiler - it is possible. But the project gave me more than I could have expected.
In this article, I will delve into the details of game development and explain why it is worth engaging in such projects.
Not many games are written in Java. As a rule, so-called indie games are created in Java. Recently, this direction has become quite popular. For example, there is a multiplayer game Wurm Online with millions of players. But for the Java world, this is more of an exception than a rule.
In general, Java is a more versatile language - both for coffee machines and mainframes. But games, as a pet project, are interesting because they are, as it is called, on the cutting edge, i.e. the most advanced technologies are used here because everything must work as quickly as possible. There should be no bugs in the game because it will most likely be impossible to fix the first impression of a new player. If a person downloads the game and encounters bugs, they will simply delete it. That is, the development requirements are quite high here.
With these considerations in mind, I started the project about 10 years ago. The idea of an RPG game similar to Gothic or Oblivion crystallized right in the process. At first, the development was rather slow, for about five years I did almost nothing. But then I had some free time, and I took up the project more actively.
Engine
My game is based on the open-source engine jMonkeyEngine 3. According to the license terms, it can be used even in commercial projects. Games written on it have been posted on Steam for a small fee (about $1.57). However, I don't know of any super-famous commercial applications of this engine. It is not particularly widespread.
There are much more popular game engines, such as Unreal Engine or Unity Real-Time Development Platform. They have huge communities and good documentation, but they work in other languages – C++ and .NET. There is nothing like that in Java. jMonkeyEngine is actually the only one developing from non-commercial ones. Perhaps there are proprietary ones, but I did not initially consider them.
jMonkeyEngine has a fairly large community – it is supported, there is an active forum. However, the documentation for several versions lags behind the development of the code (writing documentation is usually not as interesting as writing code). Therefore, the forum is essentially the main source of information. It's easier to look at the code and talk there. Almost every day topics pop up on the forum: "How to do this..." or "Why is my grass flickering". At one time, I was surprised that the old-timers answer these questions in great detail, so their answers can really be used instead of documentation.
One of the main advantages of the engine is that it allows you to write for several platforms at once. Since this is Java, all the code is cross-platform. Developing a game on Linux, you can automatically get a working game on Windows and MacOS, as well as on Android tablets and smartphones, but with some reservations regarding testing – I will dwell on this in more detail later.
Honestly, I don't really like the direction this engine is taking. The core developers team often rejects cool ideas that community members come up with. In my opinion, considering that this is an open-source project without funding, if there is no development, the project may lose users (if another Java engine appears that offers the same functionality, it may attract users). However, in general, if we talk only about my project, there are no questions about the engine. Perhaps, I would like more features that would allow generating frames faster. But in general, everything that is needed is already implemented. You just need to learn how to use it.
Diving into technical details
I gradually studied the engine - started with simple tutorials, then moved on to solving tasks that interested me. The engine is responsible for rendering the content of the object graph and transmitting sound. In addition, it has a library that handles the physics of body collisions and detects collisions.
All other tasks need to be solved manually. If engines on other platforms allow you to lay some foundation - to start with something using their ready-made primitives, then there is no such thing here. It's like a Lego constructor - do what you want and how you want.
One of the engine's libraries allows procedurally generating a landscape consisting of squares - so-called tiles. When the camera approaches the edge tile, new ones are generated at the junction with it, and the most distant ones are unloaded. But this is just an illusion - to be able to move on such land, you need to add an invisible physical surface that repeats the bends of the landscape and endow the players with bodies present in the physics world (Collision shape). That is, both the game world and the characters are multidimensional - they exist simultaneously in several parallel dimensions.
Games themselves work on the same principle – in an infinite while=1 loop, they render so-called frames per second (FPS). The game itself is like a puppet theater made of an object graph. Only what the camera is currently filming exists. It creates the impression that there is land, characters, trees, sky, and clouds with the sun. What is outside its field of view, the engine does not render.
Adding an object to the field of view is done by manipulating the object graph. At the end of each iteration, the picture is rendered. And if some operations are slow (algorithms are unsuccessful or there are too many objects), you get low FPS. This applies to any games – both three-dimensional, as in my case, and two-dimensional, as on old consoles.
There are different ways to implement objects so that it is optimal in terms of performance. For example, you can draw three-dimensional pictures, or you can draw two-dimensional ones. The difference is not very noticeable to the eye, but it has a positive effect on performance.
There is a whole community of game developers on the internet for whom this is a hobby. Perhaps some of them get paid for it, but in their free time, they continue to pick up approaches and life hacks. Some time ago, I started sharing project screenshots on the engine's profile forum, and people started giving interesting advice, including optimization.
Vegetation
The engine allows you to set lakes and land with a certain relief (uneven ground). I picked parameters that make the forest look like nature near St. Petersburg.
I struggled for quite a while to realistically generate grass. It sounds simple – any kindergartener can draw grass on paper. But within the framework of the game, it turned out to be an extremely complex topic, on which several dissertations have even been written.
In general, there are many approaches – both complex and simple – a rectangle with a texture.
In modern games, as I understand it, a combined approach is used to make the grass look more realistic – a bush is created from several rectangles crossed at different angles.
I also use it. Perhaps in the future I will switch to something more complex. An example of a complex approach is rendering grass entirely on GLSL shaders.
What do I mean by a simple combined approach?
For the engine used, several examples of grass implementation were posted on the Internet. I tried to use these libraries, but they were too tied to their own internals, i.e. they had their own generation of land, trees, etc. It was impossible to take only the grass from there, and I did not want to drag the entire library to myself.
The libraries were poorly documented, so they could only be studied in detail through reverse engineering of the algorithms. You compile, connect the debugger to the code and see how and what works. Having studied several libraries in this way, I realized that it was easier to do it myself.
Grass generation is carried out as follows. At a height of 150 local units from the zero point (ground), I take a plane, on it I choose points with a random number generator. From each such point, I direct a ray downwards - this technique is called ray casting. If the intersection point of the ray and the ground is above the water level, i.e. not in the lake, then I plant a bunch of grass in it. To do this, I twist one of the standard bunches in three dimensions and scale it so that all the grass does not look the same.
Similarly, trees are planted, only there are much fewer of them on the map. I used tree models from one freely distributed library. I use only three tree models, but, like grass, each time I twist it in three dimensions and scale it. As a result, we see diversity in the frame.
Level of detail
From the point of view of performance, detailing is a very important point. If we render everything with 100% detail, the game will simply consume the computing resources of the video card and processor. But in fact, only those objects that are nearby should be 100% detailed. When we move away, say, 100 game meters, the detail can already be 50%, and even further - 25%, and so on. Setting the correct level of detail has a very good effect on performance. This also had to be tinkered with.
By the way, for the speed of processing the forest and grass, it is important that all materials are the same. Therefore, after planting vegetation, it is necessary to merge the textures using the engine's tools - this is another life hack from the forums that greatly saves performance. Roughly speaking, all the grass should be a single material, then the engine will be able to render it in one pass.
Weather
It took quite a long time to add rain. I also read life hacks on the forums.
In games, rain and snow are never calculated for the entire map - any modern hardware will simply die from such a task. Precipitation is created in a hemisphere attached to an invisible node above the player. When the player walks around the map, the hemisphere follows him. The camera kind of rotates inside this sphere. This creates the impression that there is precipitation everywhere on the map (although they are rendered on a small piece of it). Hypothetically, if you move the camera far away, you will see how the hemisphere moves above the player.
Models and licenses
There are a large number of open source models that can be used in your development. But there is a nuance - you need to monitor the licenses. My game is being developed under GPL v3. This license requires the project code to be opened if the sources are used. But there are many other licenses. Some of them allow you to distribute code suitable for commercial use, while others have certain restrictions that are incompatible with the GPL.
When using any third-party developments, you have to monitor the compatibility of licenses. At the same time, I try to avoid custom licenses because they are vague. It is unclear what the consequences of using models or textures with such restrictions might be.
I noticed that despite the fact that there are many objects that can be used simply by indicating authorship (and sometimes without indicating it), it is customary in the world of open-source games to preserve the source. When you take textures, models, sounds downloaded from any open source, a link to the source and the license is always indicated. Probably, this is done so that there are no questions later. For example, if you use freely distributed high-quality sounds in a game, and then post a playthrough of this game on YouTube, the service will most likely detect the use of the melody and may even block the video from public access without the necessary links.
Latest changes
In fact, I finished generating the forest. You can endlessly wander through the fictional forest like Karelia under beautiful music. Recently, I connected a library that allowed me to play the change of day and night with all the beautiful colors of sunrises and sunsets. Sometimes it rains in this forest.
Now I am finishing the part related to adding nature sounds. For sound, you could use the library that comes with the engine. Or you can use other libraries that allow you to render surround sound on the device (echo in the corridor, for example). The third way is to do it yourself from scratch. Specifically, in the case of sound, I had no questions about the engine - everything worked perfectly.
The game already has a simple artificial intelligence. Sometimes there is a so-called NPC (Non-Player Character) - an enemy who runs after the player and tries to beat him. He determines where the player is and tries to catch up with him, then the fight begins. The combat system consists of strikes that can be blocked or avoided (dodged). AI blocks incoming strikes with a certain probability.
Next, I plan to work on building models, possibly even entire villages, to add variety to the game.
Testing
Since many complex technologies are used to speed up rendering in games – the same multithreading – in the presence of errors or with the wrong approach, the game behaves differently on different hardware. And all this needs to be tested. I have to do this not only at home, but also with friends or on my child's computer. Only then do bugs begin to surface that are generally not so visible.
Above, I mentioned that the engine allows you to develop simultaneously for several platforms at once. So testing also has to be done on several platforms, because in theory everything is cross-platform, but in practice the behavior on Windows and Android is somewhat different.
At the moment, I am finishing debugging – the moment is approaching when I can say that there are no blocking and critical bugs in the game. In fact, the first release is just around the corner.
Why all this
Initially, the idea was to try to make a game in Java with an infinite procedurally generated world. And this is really possible, everything works, not inferior in speed to similar projects written, say, in C++. But now I see that this direction has huge potential – it has gone far beyond the original framework.
I am attracted by several points:
when working on such projects, you have to make a lot of architectural decisions, and it is interesting. The quality requirements for such projects are much higher than in commercial games. As a rule, the business still provides for the life cycle of the game - when there are bugs, they are sorted by criticality, somehow fixed. Here, however, there is zero bug tolerance. This is also found in some commercial projects, but not often. And in pet projects, it will not be possible to fix it later, this must be taken into account.
I had to test many different ideas and technologies, explore many topics directly or indirectly related to game design. For example, whole tomes have been written about how to endow NPCs with intelligence - you can apply a lot of different solutions, from very simple scripts to complex ones - LLM. And I continue to search and study available options. Of course, I would like to do more. But there is not always time for pet projects: you have to balance between work, personal life and a pet project, and the game is not a priority in this case.
However, the pet project has a positive effect on work. It can be quite attached to the resume. When you come for an interview, it is enough to give a link to the repository. Instead of asking you some questions, the interviewer can go and look at the code - understand whether he likes what you write or not.
In addition, the game turns out to be a "dual-use technology". On the one hand, I have fun, and on the other, I acquire certain useful skills. On my project, I debug some ideas that I then apply at work. For example, this is how I experimented with modularization - the game is divided into modules connected by interfaces. This gives a good structuring of the code. The code does not mix and does not intertwine, it is better read. In addition, individual parts of the project can be easily replaced - that is, rewrite the module or part of it, correcting a piece of logic. Subsequently, I successfully applied this approach in commercial development.
Similarly, I experimented with multithreading. The game requires significant computing resources that heavily load the processor. The same planting of grass is a rather "heavy" calculation. Before that, I had certain skills in working with multithreading in Java, but here I had the opportunity to hone them - in multithreaded mode, planting grass and trees is much faster. A large piece of work can be broken into sections, each of which is performed in a separate thread. This allows you to speed up processing several times.
Over the 10 years since the project started, my skills have grown significantly. Now I would do many things differently. There is a limitation on the available time. This time can be used to rewrite what was written poorly but works, or to add new features.
Overall, work on the game continues. The source code is posted, and I periodically receive comments from the community. If you are interested, come and get acquainted with the project. This will be valuable feedback for me. You can download the repository and build the game from the source code on your hardware. https://github.com/Arifolth/jme3rpg
Write comment