(https://pixabay.com/en/technology-computer-black-code-1283624/)
Short intro
I've had so many interesting problems, challenges and moments of frustration with making the game with javascript, that I just have to share them. If you don't care about programming or javascript, feel free to skip this post and return for the next one, which should be more game development oriented.
This is probably the longest post I have made so far.
Standard coding style and linting
Description of linting from
wikipedia:
Lint-like tools generally perform static analysis of source code.
Lint as a term can also refer more broadly to syntactic discrepancies in general, especially in interpreted languages like JavaScript and Python. For example, modern lint checkers are often used to find code that doesn't correspond to certain style guidelines. They can also be used as simple debuggers for common errors, or hard to find errors such as heisenbugs.
When you program, you always have a big variety of possibilities, which you can use to manage obstacles and construct the code. I am quite organized programmer when it comes to the style of the code and I love to have a standard approach to programming, whether it's my style or someone else's. So for me setting up a standard for code organizing, comes quite naturally. I don't have to specifically force myself to be excited about it.
In a company that has multiple developers, I would argue it's almost always a good approach to have a coding standards that everybody applies in their code. I happen to work in a company, where you might end up working in somebody else's codebase at anytime and it's very useful if people write the code with the same set of rules. I know I have suffered from this on multiple occasions.
Well with all this nice introduction talk to code organization, I'm not developing my game in a company am I? Nor are there even any other developers involved with the project really. So we have quite a different starting point here. Still there are some good points in having a standard coding rules.
Since I have been doing the game alone, I can admit it has not always been a smart choice to be very strict and enthusiastic regarding standard coding style. The pros and cons I would consider are something like this:
Personal projects
Pros:
- When you look at old code, you understand it faster.
- With some personalities, it can give you a piece of mind :)
- If you setup linter only with syntax checking it will very likely just simply save time for you, as it spots errors, that would otherwise cause syntax errors or difficult to spot bugs.
Cons:
- Sometime (e.g. when copy pasting) you might have to format the code manually.
- It takes time to setup and maintain. Especially the more extensive the style rules are.
Company environment
Pros
- Other people can look at your code, or you can look at other peoples code and understand the functionality much faster
- Code reuse and sharing is easier
- Teaching of solutions is more understandable
- Easier to read source code of the company's code and thus save time
- Fixing bugs on other peoples code is sometimes a lot more easier
Cons
- Somebody has to push it internally and enforce it to be used inside the company
- You might end up in disagreements regarding used coding styles
- Somebody has to also look after other peoples codebase and make sure they follow the standard (linters can be used automatically, but if you really want code structure to be organized well, it is not always possible with automated tools).
Environment for multiple developers
I also made the whole project from the "open source and multiple developers"-perspective, so the base for the project would allow for simultaneous development. When I did this I was kind of hoping and dreaming. Realistically I did take into consideration it's very possible, there are not other people involved with it (and I even more assumed that). I still went to the path of setting the project for making it possible for multiple developers to develop the game engine.
This was a mistake. I have learned a lot through it too, so it wasn't a total waste of time, but later on when I realized how hard it is to make a game, I realized I should have cut the corner with this one.
I believe it was a good end goal, but I started it too early. For example CONTRIBUTING.md and gitter channels have been useless so far. Also I have fairly good documentation for the project and way less would have been sufficient for myself. Now if I would do 100% proper job, I would need to keep them up-to-date, which I don't cause it is not a wise option at this stage. And the end situation is that, I have out-dated and crappy documentation, since I'm fairly sure, no one else will start to develop it. Thus everybody will be less eager to do that, if there were somebody interested.
Though I do need to take into account the experience I've gained from doing all this. The project was always more emphasized as a hobby to learn and try out things, rather than something that brings me any real value. So fortunately, I've always been fairly realistic with this.
ES6 / ES 2015
ES6 is a synonym for ES 2015, both mean a specific version of javascript. Javascript took a big leap in new features and changes in that version, so I decided to hop on to that train. I regret that decision, even though I do want to take into account the massive amount of experience I have gained through the regrettable choice nonetheless.
I decided to choose the newer version of javascript, because I was always quite realistic with the project, in the sense that I knew it will take years to make it ready. During that time, the newer javascript of version would be usable by almost all the browsers. This was a wise theory and there was no issue with this. It seems that I was right on, with this theory, but there were other things I didn't take into account properly. The actual issue came with all the tools and other things that made the whole workflow more difficult to develop with and slowed the development down. One of the biggest strengths of browser environment is that you can code things basically live, if you wish to.
Description of transpiler from
wikipedia:
A source-to-source compiler, transcompiler or transpiler is a type of compiler that takes the source code of a program written in one programming language as its input and produces the equivalent source code in another programming language
Choosing ES6 before it was actually supported by any browser fully, meant that I needed a
transpiler (transpiler changes code written in ES6 to earlier ES5 and thus lets you use the code in browsers that don't support the new version yet). Having a transpiler, means having a whole set of workflow just to convert your code to something the browser can use. This was supposed to be a short and simple step, but it wasn't (more on the next chapter). What was the biggest issue with ES6 for me was the new features of exporting and importing modules, which ended up being the weakest standard in ES6.
There were also good new features in ES6 that I really liked, like
arrow functions and
default parameters. With default parameters I had some learning curve, as I was used to dealing with object literals and in general quite simple way of coding, I tried to use them originally with object parameters as such:
function custom(value, options = {property1: { property2: 2 }, property3: 3}) {}
Well I can tell you that doesn't work. It does not in the end make sense to even allow that, because then the function definitions might become really chaotic and long. So I had to use and try it out for a while, to actually realize it's just simply not how you should do things. You can always define another class / function that acts as an interface and use that, like so:
function custom(value, options = {}) {
options = createOptionsProperty(options);
}
And the code will be much cleaner and clearer.
Javascript modules with webpack
So this was the thing that was really the clearest mistake I did. I really don't know did waste more of my time than other bad or questionable choices, but this one was just clearly a mistake, that I would have wanted to avoid (both by draining me mentally and using up my time).
Since I jumped on the ES6 train, I needed to have a workflow, where I write ES6 code and it gets changed to ES5 code. This would probably not have been an issue, if I would not have been using all of the new ES6 features, especially the import and export keywords. These keywords enable a standard way to split the whole structure to smaller files and modules. So in one file, you can import other files and modules to it by importing them at the start of the file, basically like in most other programming languages. Sounds nice? Sure, but there already were other non-standard approaches to importing and exporting. I could have used those, but I just wanted to be too stubborn and "on the frontier of javascript"-hippie.
There are tons of guides how to do transpiling, how hard could it be? First I went for jspm, since that was easy to setup. I did that for a long while in the beginning, until I reached a state where I wanted to have an option to bundle a javascript file without transpiling from ES6 to ES5. Since the game was expected to be ran natively with ES6 and not ES5. Well this proved to be an overwhelming task. On top of that, I discovered later, that the browsers were actually slower with native ES6 functions, than the transpiled version. They might still be, not sure of the present situation. Of course I decided during this to try out different libraries, to see how they work and which one would work best. Mostly I tried webpack, jspm and browserify. I setup the workflow with each one of them, but always encountered an issue, that was hard to get around.
I calculated the amount that I spent on trying to achieve the whole workflow and it was around 50 hours and that is a huge amount of time, when we are talking of one hobbyist free-time game developer. In the end I ended up switching my whole code base to use global module format. Months later when I again had the energy and time to get into the module workflow, I switched back to using the import and export keywords and succeeded with it. So I ended up using more than 50 hours for a task (probably around 80).
When I was using global module format, I had a complete workflow built with gulp and it worked nicely, but using a proper module format and workflow makes the whole package easier to handle and also the source maps work much better. Getting the modules working well would not have been in any way essential to the project, but because of my initial choices, I simply had to be able to do it. I needed to get piece of mind with it and to get the advantages it provided me (like properly working source maps).