Recreating a game project in a 3D HTML5 WebGL environment. Research and comparison of 3D capable HTML5 WebGL game engines.

Mikko Järvilä

Bachelor’s thesis April 2021 Information and Communications Technology Degree Programme in Information and Communications Technology

Description

Author(s) Type of publication Date Järvilä, Mikko Bachelor’s thesis April 2021 Sivumäärä Language of publica- 110 tion: Number of pages PermissionEnglish for web 110 publication: x Title of publication Recreating a Unity game project in a 3D HTML5 WebGL environment. Research and comparison of 3D capable HTML5 WebGL game engines.

Degree programme Information and communications technology Supervisor(s) Paavo Nelimarkka, Pasi Manninen Assigned by Zaibatsu Interactive Oy Abstract The objective stemmed from the assigner’s desire to find an alternative for Unity game en- gine -based projects in a 3D HTML5 environment. The goal was to find an engine candidate that provides the necessary functionalities, tools and performance required to recreate ex- isting Unity-based hyper-casual productions within the HTML5 environment in 3D. This was done by firstly selecting a larger group of candidates based on a set of criteria provided by the assigner, for example restrictions regarding the scripting language, availa- bility of the code and the general LTS (long-term support) expectable from the cho- sen candidate, and then by comparing and weighing the aspects of each candidate, as well as comparing them with each other, and then selecting the most promising engine solution from among them. With the chosen engine solution, a demo project was then created. The goal of the demo project was to recreate an existing Unity-based hyper-casual game devel- oped by the assigner with the chosen engine. The point of this was to gauge the practical capabilities, as well as differences, of the chosen engine compared to Unity. As could be expected, some discrepancies between the advertised and researched capabil- ities of the chosen engine solution and its practical usage could be found in the process of recreating the chosen hyper-casual game. Certain development hampering key differences also arose during the re-creation portion, which impacted the performance and visual fi- delity of the remade game compared to the original hyper-casual Unity production. While capable in its own terms and environment, certain aspects of the chosen engine left open to discussion and consideration whether it truly should be used in the fast-paced de- velopment cycles of hyper-casual games.

Keywords/tags (subjects) Babylon.js, , Comparison Hyper-Casual, JavaScript, TypeScipt, Unity, Multiplatform.

Miscellaneous (Confidential information)

Kuvailulehti

Tekijä(t) Julkaisun laji Päivämäärä Järvilä, Mikko Opinnäytetyö, AMK Huhtikuu 2021 Sivumäärä Julkaisun kieli 110 Englanti Verkkojulkaisulupa myön- netty: x Työn nimi Recreating a Unity game project in a 3D HTML5 WebGL environment. Research and comparison of 3D capable HTML5 WebGL game engines.

Tutkinto-ohjelma Tieto- ja viestintätekniikka Työn ohjaaja(t) Paavo Nelimarkka, Pasi Manninen Toimeksiantaja(t) Zaibatsu Interactive Oy Tiivistelmä Työn lähtökohdat perustuivat toimeksiantajan toiveeseen ja tarpeeseen löytää vaihtoehto Unity -pelimoottorille 3D HTML5 -kohdeympäristöön toteutettaville projekteille. Päämää- ränä oli löytää pelimoottoriehdokas kyseiseen ympäristöön toiminnallisuuksilla, työkaluilla sekä suorituskyvyllä jotka vaaditaan uudelleentuottamaan olemassa olevia hyperkasuaali- tuotantoja kyseissä HTML5 -ympäristössä. Työn ensiaskel oli kerätä valikoima toimeksiantajan kandidaatille antaman kriteeristön täyttäviä moottoriehdoikkaita näihin kriteereihin lukeutuessa mm. kandidaatin käyttämä ohjelmointikieli, tämän lähdekoodin saatavuus ja uudelleenkäytettävyys, sekä kandidaa- tilta odotettava pitkäaikaistuki. Tämän jälkeen kandidaatteja kriteeristön valossa tutkien, punniten sekä toisiinsa vertaillen, näistä valittiin yksi kriteerit parhaiten täyttävä pelimoot- tori. Tämä jälkeen kyseistä moottoria käyttäen luotiin tämän käytännön kyvykkyyksiä ha- vainnollistava peliprojekti, jonka tarkoituksena oli uudelleen luoda toimeksiantajan ole- massa oleva, Unity-pohjainen hyperkasuaalipeliprojekti. Uudelleenluomisprojektin tarkoi- tuksena oli myös peilata valikoituneen kandidaatin eroja Unity-pelimoottoriin nähden. Kuten odottaa saattoi, esitutkimuksesta ja -testauksesta huolimatta eriäväisyyksiä valikoi- tuneen pelimoottorin käytännön ja tämän mainostettujen ominaisuuksien välillä löytyi pro- jektin tekovaiheiden aikana, kuten myös erinäisiä uudelleenkehittämistyötä haittaavia avaineroja valitun moottorin sekä Unityn välillä. Vaikkakin kykeneväinen omassa ympäristössään ja omilla ehdoillaan, tietyt ominaisuudet valitussa moottorissa jättivät avoimeksi kysymyksen siitä, tulisiko kyseistä moottoria kui- tenkaan hyödyntää lähtökohtaisesti nopeatempoisessa hyperkasuaalipelikehityksessä.

Avainsanat (asiasanat) Babylon.js, Pelimoottori, Vertailu, Hyper-Casual, JavaScript, TypeScipt, Unity, Monialustainen.

Muut tiedot (salassa pidettävät liitteet)

4 Contents

1 Introduction ...... 8

2 Engine Candidates and Criteria for Selection ...... 10 2.1 Babylon.js ...... 12 2.2 Three.js ...... 16 2.3 PlayCanvas ...... 18 2.4 Turbulenz ...... 19 2.5 Candidate Selection ...... 22

3 From Unity to Babylon.js ...... 23 3.1 Highway Chase ...... 23 3.2 A brief summary of relevant Unity Engine components ...... 29 3.2.1 GameObjects ...... 29 3.2.2 Components ...... 32 3.2.3 Meshes, Materials, Textures and Shaders ...... 32 3.2.4 Prefabs ...... 34 3.2.5 Scenes ...... 34 3.3 Babylon.js ...... 35 3.3.1 Nodes ...... 35 3.3.2 Node Properties ...... 36 3.3.3 Scenes ...... 37 3.3.4 Editor ...... 38 3.3.5 Playground ...... 39

4 Recreating Highway Chase with Babylon.js ...... 40 4.1 Tools Used in the Project...... 40 4.2 Starting the Project ...... 42 4.3 Loading and Using Assets – Asset Manager and Meshes ...... 46 4.4 Loading and Using Assets - Materials and Textures ...... 50 4.5 Loading and Using Assets – Setting Up the Game World ...... 52 4.6 Physics and Movement ...... 58 4.7 Collisions ...... 66

5 4.8 The Police ...... 68 4.9 GUI ...... 71 4.10 Particle Effects ...... 79 4.11 Winning and Losing ...... 87 4.12 Shadows and Lighting ...... 92 4.13 Optimizations ...... 95 4.13.1 Rendering Pipeline ...... 98

5 Results and Comparison ...... 100 5.1 Development Process ...... 100 5.2 Handling of Assets and Meshes...... 101 5.3 Handling of Gameplay Elements ...... 103 5.4 Visual Elements ...... 103 5.5 Project Size ...... 103 5.6 Performance ...... 104 5.7 General Notes ...... 106

6 Conclusion ...... 107

References ...... 109

6 Figures

Figure 1. Highway Chase gameplay screenshot ...... 22

Figure 2. Highway Chase explosion screenshot ...... 23

Figure 3. Highway Chase Lose State ...... 25

Figure 4. Unity GameObject with Components ...... 27

Figure 5. Unity parent-children hierarchy view ...... 28

Figure 6. Unity Mesh, Material, Texture and Shader components ...... 30

Figure 7. Highway Chase Car Prefabs ...... 31

Figure 8. Babylon.js Node structure ...... 33

Figure 9. Node Properties ...... 34

Figure 10. The BabylonJS Editor ...... 35

Figure 11. Babylon.js Playground ...... 36

Figure 12. Visual Studio Code with Live Server Extension ...... 38

Figure 13. Index file with BJS relations and references ...... 40

Figure 14. Loaded car mesh ...... 45

Figure 15. Loaded car mesh with material ...... 48

Figure 16. Game view with assets and materials ...... 55

Figure 17. Rudimentary UI ...... 76

Figure 18. Smoke trail created with the Solid Particle System ...... 81

Figure 19. Particle explosions ...... 83

Figure 20. Win scenario ...... 86

Figure 21. Lose scenario ...... 89

7

Figure 22. Game with shadow generation enabled ...... 92

Figure 23. Rendering pipelines enabled ...... 96

8 1 Introduction

We live in a time of video games. Games, in one form or another, are nearly every- where and are played by a wide array of people. While industry giants mass-produce grand AAA-titles and console and PC games make the headlines with much-discussed hit blockbuster games following one another, there is an aspect and genre of games that often gets ignored by the mainstream media, but one that is widely played and financially booming. This genre of gaming is called hyper-casual, and it is mostly fo- cused on smartphone platforms. While grand e-sports tournaments are being held and played, with players and teams competing for millions of euros, what does the audience do with their spare time between matches besides browsing the social me- dia? They play games on their smartphones. What do people do while they are wait- ing for a bus at a bus stop, with their smartphones in hand? They are either browsing social media or playing games. Whenever there is some form of idle waiting or some- thing plain uninteresting happening, these days people tend to reach for their smart devices and either browse the social media or play games on them. Sometimes even both. Most of the time, these games tend to be hyper-casual in nature.

The reason for bringing up hyper-casuals has a lot to do with the premise of this the- sis you are currently reading, despite its title. As a starting point, the assigner of the thesis, Zaibatsu Interactive, has expressed the need find a HTML5 3D capable game engine as an alternative for Unity. Most of the games developed by Zaibatsu are in some form or another either mobile-based and/or hyper-casual in nature and the platform of development for a vast majority of them is the Unity Engine. While it can generally be said that the Unity Engine excels in such development and mobile builds, the HTML5 builds generated by it can be on the larger and more cumbersome side. This can be considered an issue with lightweight and hyper-casual games, as any unnecessary load times or poor performance can lead to a potential gamer-cus- tomer with a short attention span just outright discarding the game. Size and perfor- mance are also an issue with the so-called instant apps. Therefore, the goal of this thesis is to search for, study, research and ultimately test in practice a suitable 3D HTML5 replacement for Unity for browser and instant-based development and porting.

9 The research portion focuses on studying, comparing, and choosing a suitable engine candidate from among the most noteworthy candidates around today, based on a set of criteria given by the assigner of the thesis. After a candidate has been selected, its mettle will be put to test in the form of a small demo , it being a remake of a hyper-casual game developed by Zaibatsu Interactive in the past. The remake will then be compared to the original game and the chosen engine judged by its capabili- ties of recreating the original game not only by size and performance, but also by its capabilities of handling development features, functionalities, tools, and aspects compared to the Unity engine.

Hyper-Casual Games What then are hyper-casual games? In most cases the term references games that are incredibly easy to learn and play. They are games that are ”snackable”, meaning that they are satisfying and playable in short sessions, without the player having to commit to anything long-term in terms of gameplay. They require little time and even less attention yet being addictive in nature. The skill-curve in such games is of- ten very low, sometimes even non-existent. While not exactly on the tapestry, hyper- casual games are everywhere. (Karnes 2020).

10 2 Engine Candidates and Criteria for Selection

Even though Unity is generally considered to be a capable and a reliable game en- gine, it does have its drawbacks especially when it comes to HTML5 builds, as they tend to be on the larger side file size-wise and suffer from at times mediocre perfor- mance. Therefore, the assigner of this thesis feels that the company has the need to find a proper replacement for Unity when it comes to web-based, 3D capable pro- jects, as especially noted in the summary of their recent company-wide technological survey.

The purpose of this section of the thesis is to list the criteria hailing directly from the assigner of the thesis and, as stated in the introduction portion, to analyze and weigh each of the chosen, preselected candidates based on this given list of criteria, leading to the ultimate selection of the most suitable candidate, with which the practical portion of this thesis will be carried out with.

The main list of criteria for the candidate as expressed by the presentative of the as- signer is as follows:

The candidate uses JavaScript or TypeScript as its programming language. The programming language must one that the assigner personnel are already familiar with to ensure a smooth transition into the use of the chosen candidate without re- quiring any extraneous, additional company resources in the form of workload, time, or training.

The candidate is already well-known and popular. A widely known and widely used candidate usually ensures that there is more sup- port to be found for its use in the form of practical guides and examples on a practi- cal basis. This helps to provide more potential solutions for the developers in the form of actual, practical guidelines and support should ever run into demanding or problematic situations while using the candidate.

11 The candidate is being actively developed. It is generally unwise and even unprofitable to put resources into a technology or a solution that is on its way to becoming irrelevant due to the lack of active develop- ment and support. Even though predicting the future usually tends to be rather diffi- cult, the gauging of this aspect is quite possible by digging into the development his- tory of each candidate, as well as paying attention to the activity of the developers in matters regarding the development of said candidate.

The source code of the candidate is available and acquirable. The assigner is able to acquire and transfer the source code of the candidate into their own Git environment as well as to their clients if the need arises. Even though this may seem obvious, some frameworks are built solely on web-interfaces, making it needlessly difficult to acquire the written code or handle version management.

The candidate is embeddable. The candidate must be capable of so-called “lightweight” -solutions for the sake of embedding the content created with the solution into web pages.

The candidate must work on all platforms and browsers. The candidate must be capable of producing content and products that support and work on multiple different platforms without any major extraneous steps or script translations from the developer. Such platforms should include, for example, a vast majority of browsers, Android, iOS, and .

Drawing from the list of criteria above, the main focus of choosing the most suitable engine from among the candidates should be placed on the candidate’s multiplat- form capabilities and it being able to produce lightweight solutions. The engine also has to be capable and powerful enough to stand on par with Unity, and preferably surpassing it when it comes to performance and file size.

For the purposes of limiting the scope of the thesis, several engine candidates have been pre-scouted and selected based on the aforementioned criteria, with the

12 weight being placed on the overall suitability and availability of the potential candi- dates. Somewhat surprisingly, the pool of outright suitable engines was rather shal- low, and only four fit the criteria closely enough in order to warrant further study and comparison.

For the sake of brevity and in order to avoid unnecessary workload in favor of start- ing the demo phase after candidate selection, this section will not provide an in- depth description of each candidate, but rather summarize the reasons for picking each of them, bringing out the main points, functionalities, and capabilities of each, along with some background information. The additional, more technical and in- depth description will be provided in a later section of the thesis, after the chosen candidate has been confirmed as the technology to be used in the practical demo portion of the thesis.

2.1 Babylon.js

Babylon.js is a TypeScript-based HTML5 engine that also supports JavaScript to an ex- tent in which its practical to describe Babylon.js as JavaScript HTML5 engine. The source code of Babylon.js itself is written with TypeScript, but then compiled into Ja- vaScript. As for its multiplatform availability, the code the user creates with Baby- lon.js, is interpreted natively by all web-browsers providing support for HTML5 and WebGL for 3D rendering, as is quite common with many other such engines and li- braries. As for its availability, the Babylon.js project is fully open-source, using the Apache 2.0 license, with the source code being available on the project’s GitHub -repository in its entirety. The code can also be acquired via NPM or CDN. (Babylon.js license 2020; Babylon.js GitHub 2020.)

Having originally started as a spare-time project between two French Microsoft em- ployees David Rousset and David Catuche, with the support from the artist Michael

13 Rousseau in 2013, the project first saw the public light of day in the form of a presen- tation at the WebGL Conference in Paris in 2015. (Rousseau 2015; Rousset 2015.) Having mentioned the years 2013 and 2015, the Babylon.js project is relatively new, but considering the fact that most of the modern HTML5 game engines, such as PixiJS or , have also been conceived around the same early 2010 era, this shouldn’t be seen as an actual lack of project maturity due to its active development, with the engine having a decent number of contributors according to the Bablylon.js GitHub repository statistics at the time of writing. (Babylon.js contributors 2020.)

Feature-wise Babylon.js engine provides a plethora of them, most being amazingly well on par with the Unity engine, considering the development environment, with one of the main weights being on relative ease-of-use, especially once the user has gained some summary experience regarding the use of the engine. (Babylon.js En- gine specifications 2020.) As for the main features the engine itself, Babylon.js lists its complete scene graph capabilities, a full-featured viewer, transparent WebGL 1.0 / WebGL 2.0 / WebGPU support, a native collisions engine, an animations engine and a physics engine that is based on oimo.js, ammo.js and cannon.js integrations. (ibid.) As for the rendering capabilities of the engine, alongside with the usual lighting and texture options, such as diffuse, ambient, specular lighting and textures, opacity, emissive, mirror bump, lightmap and reflection textures, Babylon.js also boasts node materials and PBR (Physically Based Rendering), a procedural textures library as well as a materials library. (ibid.)

Babylon.js also has a desktop editor that has many of the features used by the Unity editor, such as providing a live scene preview, management of assets, the current scene graph, an inspector, the ability to create and edit particle systems, premade particle system sets, animations, textures, materials, sounds along with many other functionalities. The editor is also available as an online version, though notedly mainly only for demo purposes, as the use of desktop version is recommended in or- der to ensure the access to the user’s local file system for the purposes of saving the project. (ibid.)

14

The documentation for the engine is also wide-ranging and provides a wide array of examples and tutorials alongside with the usual, expected technical documentation. (Babylon.js documentation 2020.)

Babylon Native One of the main aspects that makes Babylon.js a prime candidate for the purposes of the thesis, is its Babylon Native library. As described on the project’s introductory web page,

“The holy grail of software development is to write code once and have it work absolutely everywhere: on any device, on every platform. This is the inspiration behind Babylon Native. This exciting new addition to the Babylon platform allows anyone to take their Babylon.js code and build a native application with it, unlocking the power of native technolo- gies.” (Babylon Native introduction 2020),

Babylon Native, while still being in development, promises and provides the inte- grated ability to use the rendering code written with Babylon.js seamlessly with the Babylon Native, making it reusable across platforms. (Babylon Native introduction 2020; Babylon Native readme 2020.) Once the rendering code is written with Baby- lon.js, the Babylon Native Runtime is capable of running it on platform native graphics , such as OpenGL on Android platforms, Metal on MacOS/iOS platforms, and DirectX on Windows platforms. (ibid.) With this all being said, the Babylon Native is still in development and currently avail- able only as a public preview. However, quite a few of the core features are available at the time of writing, with some of the other major features being already partially implemented. The features currently available and supported include Babylon Native development on Windows 10 and macOS, building and running the libraries and demo apps for An- droid, iOS, macOS, Win32 and UWP as well as loading and executing JavaScript code on all of the supported platforms, to name some. The project currently also partially

15 supports Babylon Native development on Linux. (ibid.) Another notable aspect of Babylon Native, as well as Babylon.js, is its ability to cus- tomize and optimize its package size. With Babylon.js it is possible to pick and choose the parts you need using ES6 package and Babylon Native has also taken componen- tization into consideration and allows the optimization of the package size even fur- ther. (ibid.)

As an added bonus, it is possible to combine Pixi.js and Babylon.js with relative ease, enabling the use of Pixi.js for UI purposes, for example. (How to combine Babylon.js and Pixi.js 2020.) Another notable aspect of Babylon.js is its compatibility with Unity in the form of its Unity Exporter toolkit, meaning that it is actually possible to outright export projects or parts of them made with Unity with the toolkit enabled and export the supported components into Babylon.js projects. There are however some limitations to this when it comes to exporting certain, Unity-based components that Babylon.js does not support outright. However, in essence, it is possible to create Babylon.js game projects using the Unity editor itself with the toolkit enabled. (Introduction to Unity exporter 2020.) It is however worth noting that the exporter toolkit is currently a few versions behind the most recent Babylon.js version. This does not make the exporter incompatible with the most recent version, but it does mean that the exporter doesn’t necessarily provide or support the most recent features of the engine outright.

Some people have gone as far as to name Babylon.js as “the best JavaScript 3D games engine out there in the wild” (noeticsunil 2019.), when comparing modern 3D HTML5 engines, which is a claim that should not be made or taken lightly, but is one that does have a ring of truth to it after researching and seeing the possibilities and the raw potential that Babylon.js presents.

Key points of Babylon.js  Supports both TypeScript and JavaScript  Popular  Actively developed  Embeddable

16  Multiplatform capable  Customizable package size  Free & open source  Unity compatible with the Unity Exporter toolkit  Babylon Native

2.2 Three.js

Much like Babylon.js, Three.js is a widely known and quite popular HTML5 WebGL engine. Initially released ten years ago back in 2010, Three.js saw its first release in the April of 2010 by Ricardo Cabello on GitHub. Cabello had originally developed the code in ActionScript, but later ported it into JavaScript one year prior to its original GitHub release, in an attempt to pursue platform independence. Cabello claims that one of the deciding factors for him to port the code into JavaScript was the release of Chrome and the raise in JavaScript performance it brought with it. He sum- marizes the creation of the project stemming from his need to have a 3D library that suited his needs, and after developing such a library, he realized that it could be ben- eficial for more people than not just him. Originally being a graphics artist, Cabello drew from this, and therefore the initial focus of Three.js was indeed on the visual, 3D capabilities of the engine rather than, say, it being a HTML5 game engine out-of- the-box. (White Paper for Three.js 2012.)

Just like Babylon.js, Three.js supports both JavaScript and TypeScript, with the latter support requiring just a minimal addition into the TypeScript compiler configuration files in order for TypeScript to be enabled. (TypeScript setup n.d.)

Three.js falls under the MIT license, making it available for free, and the source code for the project can be readily found for downloading on the project’s GitHub page. (Three.js license. 2020.) Further inspecting the repository, one quickly notices that the Three.js also has a hefty number of contributors, which is to be expected from a project that is almost over ten years old. (Three.js contributors 2020.)

As for the technical capabilities, the claimed aim of the Three.js project is to create a 3D library possessing a default WebGL renderer that is lightweight and easy to use.

17 Included with this, the library also provides Canvas 2D, SVG and CSS3D renderers. (Three.js Fundamentals N.d.; Three.js documentation N.d.; Blue, Levy N.d. Discover Three.js.) The visual performance and capabilities of Three.js are quite evident when browsing through the multiple graphics-related examples and demos that are found on the project’s home page, making the WebGL renderer capabilities of Three.js hard to pass by without serious consideration.

Outright comparing Three.js to Babylon.js makes it clear that Three.js was indeed originally designed with a more general-purpose web development in mind, being more focused with animations and visual aspects and lacking in the side of physics, actually requiring a separate plugin, Physijs, for proper functionalities on that front when it comes to a physics engine. However, the Physijs plugin is also built on top of ammo.js, and somewhat partially on cannon.js, them being libraries that also Baby- lon.js’s physics engine originally draws from. (Chandler Prall, Physijs N.d.) Three.js also sports an editor for desktop, made using NW.js and OrnaOrg, with NW.js being based on Node.js and , and with OrnaOrg being a CSS-related parser and a .

While being seemingly lightweight and capable, the concise documentation regard- ing Three.js feels sparse and strewn around. The home site offers fundamentals, but when first trying to find up-to-date information and technical information, the home site takes a while to get used to while looking for desired information and statistics. However Three.js claims ease-of-use with being approachable for those not so well- versed in graphics and this does seem to be the case once one takes the time to get around the veritable jungle of documentation, this being apparent in the sheer num- ber of users and content produced with it.

Despite being somewhat lacking in out-of-the-box features and utilities when com- pared to Babylon.js, Three.js does have a plethora of various plugins due to its popu- larity and widespread use, definitely making it a notable candidate for a game engine for the purposes of the practical section of this thesis.

18 Key points of Three.js  Supports both JavaScript and TypeScript  Easy to use and to get into  Long-lived and popular with an active user and developer base  Capable of lightweight solutions  Has a multitude of plugins available, making it quite versatile  Free & open source

2.3 PlayCanvas

Much like the previous two candidates, the PlayCanvas project saw its start in the early 2010’s, in the April of 2011 by Will Eastcott and Dave Evans. The deciding factor for starting the project was the release of ’s 4.0, which provided WebGL support by default. While being originally closed-source, PlayCanvas project however eventually open-sourced its PlayCanvas engine, and it is currently readily available on their GitHub page under the MIT-license. (Eastcott, Will; Nyman, Robert 2014; PlayCanvas license 2021.)

PlayCanvas is JavaScript-based, but in the recent years has made accommodations to provide TypeScript support as well, as the ongoing trend appears to be. Like previously stated, the focus of PlayCanvas indeed lies in game development and therefore does provide a multitude of tools and functionalities to support this direc- tion. Much like Babylon.js, the PlayCanvas editor claims to be easy to use and pro- vides object, entity, and scene management somewhat comparable to the Unity en- gine, with lighting, camera, particle, and physics management options, to name some. While being powerful and capable, the main drawback of the candidate is its lack of a local desktop editor, with its editor being a cloud-hosted solution that runs solely on a browser. This does however enable the possibility of real-time collaboration devel- opment, as this enables multiple people to work on the same project simultaneously via the browser editor. (PlayCanvas Features 2021.)

As for the documentation and examples available for the engine users, PlayCanvas does have an easy-to-read online user manual, but the manual does seem to provide

19 only the most summary examples and documentation regarding each chosen aspect (PlayCanvas Developer Resources 2021).

The assigner has done some marginal experimentation with PlayCanvas in the early 2017, but were back then put off by the challenges posed by its restrictions when it came to licensing and subscriptions, in the context of limitations regarding storage, private projects, and team management options. While PlayCanvas has grown especially feature- and performance-wise since its 2017 iteration, it still does indeed pose the same problems for Zaibatsu that they ran into back in 2017, with the lack of a local desktop editor and the local management and storage of code written with the editor, and having to rely on the organizational li- censing and pricing plans. (PlayCanvas pricing 2021.) Many major companies and institutes do however prefer to use PlayCanvas, with the project been backed by major corporations such Mozilla, Activision and ARM, to name some, which lends trustworthiness to PlayCanvas and poses it as a popular, stable and reliable candidate with expectable long-term support (PlayCanvas GitHub 2021).

Key points of PlayCanvas  Supports both JavaScript and TypeScript  Collaborative online editor, enabling several people to work on the same pro- ject at the same time  Wide userbase  Backing from major organizations such as Mozilla

2.4 Turbulenz

The oldest engine discussed in this thesis, Turbulenz began back in 2009, dating its origins back into a time when HTML5 and WebGL were still works in progress. (Handrahan 2012). Despite being released in 2009, the source code for it did not be- come available as open source until 2013, and then under the MIT license. The source code can be found and downloaded on the engine’s GitHub page. (Austin

20 2013; Turbulenz license 2014.) Turbulenz is developed by a company of the same name based in the United King- dom, with founding member James Austin working as the CEO. Austin had previously worked at EA as a director of technology, with many of the other company members being industry veterans as well, hailing from companies such as Lionhead Studios, Google and Square Enix. (Handrahan 2012.)

The main design goals of the TypeScript-based engine are stated as being perfor- mance, modularity and customizability, with the users being able to “build any kind of game without any limitations in an efficient manner and with an end product that performs optimally when loading and during play” (Turbulenz GitHub 2021). The doc- umentation claims that the core focus while writing the engine code has been set to base upon these three goals, with additional attention for the code’s simplicity, fault tolerance and it being as data-driven as possible (ibid).

As for the features, the engine separates itself into two parts in its documentation; the runtime API and the offline tools, with the runtime API portion of the engine be- ing focused in the content that is executed on the final user machine and the offline portion containing tools that are being used in the development process. In addition to this categorization, the runtime API is also divided into two parts: a low-level API and a high-level API. As their names imply, the low-level API is a set of interfaces that deals with features and components that functionally resemble OpenGL and other such APIs, therefore being focused on the core portions of the engine functionality and the high-level API handling things such as the scene graph, a set of different ren- ders, lighting, cameras, resource management and server requests. Additionally, the low-level API interface separates its components into different modules that it calls “Devices”, such as GraphicsDevice, MathDevice, PhysicsDevice, SoundDevice, NetworkDevice and InputDevice. As their names imply, these Devices form and provide the core functionalities of the engine, with the functionalities and modules of high-level API being built upon these Devices. As for the aforementioned offline tools, they are as well divided into two; code tools and asset tools, with code tools for example providing debugging and the generation of HTML files to launch

21 applications during the development process and the asset tools focusing on the con- version and handling of different file types and the compression and generation of cube- and mipmaps, for example. (ibid.)

On a more concrete note, Turbulenz offers a wide array of features when it comes to elements one expects to have when building a game using a game engine. Its 3D physics are based on the Bullet Physics Library, optimized for JavaScript implementa- tions, and the physics-related features are in general akin to those found in Unity, for comparison. Rigid bodies, collision, constraints, ray, and convex sweep queries are all to be found within the physics capabilities of Turbulenz. Along with these, there are plenty of lighting and camera-related options. Turbulenz also provides the Turbulenz Service API, that enables the use of user profiles, game profiles, multiplayer with ses- sion matchmaking, global leaderboards, badges, notifications and even a payments API. (ibid.)

As for the documentation regarding the engine and its features, the GitHub page pro- vides ample documentation with in-depth explanations of the features and technolo- gies that come with the engine (ibid). Sadly, the Turbulenz engine does not provide a desktop editor like some of the previ- ous candidate entries, running on localhost and relying on browser-based develop- ment. While not being a major issue, if one is looking for a Unity-grade candidate with a visual editor, this can be a slight let-down.

The single, most major drawback of the Turbulenz as a candidate is its lack of recent development engine-wise. However, the Turbulenz game team going by the name of Wondergames have published games made with the Turbulenz engine that are still active and developed, most notable being the game ‘Boundless’, signifying that the engine is still a capable candidate, even if one that should be considered and ap- proached with some reservations. (Boundless 2021.)

Key points of Turbulenz  Supports both JavaScript and TypeScript  Well-built and optimized engine  Free & open source

22  Well-documented with several examples

2.5 Candidate Selection

Considering the features, functionalities, and other aspects in the light of the given engine candidate criteria, the strongest contenders were Babylon.js, PlayCanvas, and Three.js. When it came to Turbulenz, despite it being a spot-on engine in certain aspects, es- pecially design-wise, its lack of active development made it an inferior choice when lined up with the other three candidates, just from the criteria standpoint alone. Turbulenz was, however, an interesting counterpoint in terms of research and study of engine structures and would have definitely merited an in-depth demo tryout, if only it were in active development with some strong, reliable long-term support. This, however, was not the case, and also ended up being the deciding factor of it be- ing eliminated from the list of engine candidates considered for the practical portion of the thesis.

For the remaining three engines, it was clear that Three.js, while being a graphically capable engine, was less focused on game creation and required some extra steps to be an out-of-the-box game engine with all the desired functionalities one expects from a game engine. This made it less suitable for the purposes of this thesis and the needs of the assigner when compared to the other two remaining candidates, Baby- lon.js and PlayCanvas. While the engine was lightweight and beginner-friendly, with several handy features, but due to the aforementioned reasons, it had to be dropped as a candidate.

As for PlayCanvas, despite its sheer potential and features, the engine did still suffer from same issues and restrictions that the assigner faced back in 2017 when experi- menting with the engine. This makes it an impractical choice for their purposes and expectations when it comes to the game engine to be chosen, however capable and tempting a candidate it may be.

23 The last candidate left standing, so to speak, was Babylon.js. While not being immac- ulate and outright superior compared to the other engines presented in this thesis, it did however fit the list of criteria given by the assigner, with its plethora of features and promising performance and multiplatform capabilities.

This, by the process of analyzation and elimination, made Babylon.js the most suita- ble solution when compared to the other three candidates and made it the engine the practical portion of this thesis was carried out with.

3 From Unity to Babylon.js

In this section, the components and the elements of the practical demo portion of the thesis will be discussed, starting with the game to be recreated using the selected HTML5 engine, Babylon.js. The game in question is named Highway Chase.

3.1 Highway Chase

Highway Chase is a hyper-casual mobile-based game developed by the assigner, Zai- batsu Interactive, over a two-month period between 16.12.2019 and 31.01.2020. An in-house project, most of the coding was handled by a single person, with some support from other in-house developers. All of the graphical assets were produced in-house as well, with various car models, particle effects and UI elements being cre- ated by the company graphic artists. As the title of the thesis and the heading of the chapter may suggest, the game was created using the Unity development platform, with the editor version having been 2019.2.0f1. Being a mobile game, Highway Chase utilizes touch controls, with the movement of the player being handled by tap and drag controls. This is to say that the player ob- ject moves according to the touching finger’s horizontal position, with forward move- ment being completely automated.

24 Gameplaywise the game itself is fairly simple, as hyper-casual games often tend to be. In the game you play as a car driver who has just robbed a bank and has the po- lice chasing after them. The goal of the game is to navigate one’s way through sev- eral lanes of ongoing traffic while avoiding other cars and obstacles, such as concrete roadblocks, with a police car chasing after them. Additional police cars are also amidst the traffic and join in on the chase, should the player manage to lose their “tail”.

Being a level-based game like most hyper-casual titles, the win condition of the game is for the player to reach a finish line waiting at the end of each stage, and the failure conditions being that either the player gets caught and collides with a police car or that the player collides with an obstacle on the road, crashing and exploding and therefore stopping their journey tragically short. The player can also collide with the other cars driving on the road, them serving as slowing agents rather than outright stopping obstacles. If the player collides with a certain number of other cars and gets slowed down enough, the police is able to gain up to the player and “bust” them, as the game states. The police cars, like the player car, are able to hit other cars and road obstacles, but instead of slowing the speed of the police cars, the police vehi- cles are instead instantly destroyed after colliding with another car. This sets a small, hidden respawn timer that spawns another police car after the player after one chas- ing police car has been destroyed.

The game also features, again as several other hyper-casual titles do, collectibles in the form of coins that when in this instance driven into, get collected by the player and increase their score to ensure a satisfying incrementation of the score-related numbers appearing on the screen. Appearing on the UI is also a progress bar, another common sight in hyper-casual games, that gets filled as the player progresses through each level, based on the dis- tance between the player and the finish line. In addition to these two elements, the game also has a five star “wanted” -indicator; a metric that increases the more may- hem the player causes, with mayhem in this case consisting of colliding with other cars.

25

Figure 1. Highway Chase gameplay screenshot.

Visually the game is simplistic, clear, and bright, again keeping in with general hyper- casual lines, with clear visuals and an aesthetic that can be considered being easy on the eyes in order to keep the player focused on the action. The lighting is also soft and the shadows pleasing to the eye. As for the environments themselves, there is very little additional screen clutter in the form of non-essential gameplay elements. In this case, several small, unassuming pine trees can be seen on both sides of the road. The terrain on both sides of the road is also molded into hilly shapes in order to break the monotony of a plain, flat landscape with some elevational changes. In addition to these, when it comes to visual effects, the game features exhaust fumes coming from each car, blinking red-blue lights on top of the police car and showy explosion effects upon either hitting another car, driving into an obstacle or the police car reaching the player car.

26

Figure 2. Highway Chase explosion screenshot.

From these gameplay objects, elements, and visual themes, several points which should be present in the Babylon.js demo re-creation project of the game can be drawn, in order to be able to draw proper comparisons between the Unity engine and Babylon.js:

 Lighting, including shadows  Gameplay objects  Physics & collision handling  UI elements  Particle effects  Controls

In addition to these, other features to keep in mind while comparing the two prod- ucts together can be derived from the list above:

27  Materials  Textures

Also, while mostly code-related, the game is also state-based, meaning that the game follows a state structure with the following general states:

1. Start State 2. Game State 3. Win State 4. Lose State

The Start State is principally a start screen prompting the player to press “play”, upon which the game automatically transitions into the Game State, which is the actual gameplay portion of the game. From this state the game can either transition to the Win State if the player manages to finish the current level, or into the Lose State if they collide with a stopping, game-ending obstacle or the police manages to reach them. If the Win State triggered, the game tallies the player’s score and increases the level index, presenting them a new level with another Start State in between. If, however, the player triggers the Lose State, the game presents them with a Lose State screen and loops them back into the start of the current level, with another Start State in between.

28

Figure 3. Highway Chase Lose State.

In addition to these four states, hyper-casual games often have states such as a Pause State and a Settings State. It should be noted that the different state terms and structures used in the Zaibatsu SDK that Highway Chase uses are a little different, but for the sake of simplification, the state mechanics can somewhat safely be described in such a manner without go- ing too far off-tangent. While not considered crucial and not necessarily implemented in the demo portion since it doesn’t truly gauge the Babylon.js engine in any way, the state structure is however worth mentioning, as nearly all hyper-casual games follow a similar looping state pattern.

29 3.2 A brief summary of relevant Unity Engine components

As stated earlier, Highway Chase is created using the Unity Engine, specifically with the editor version 2019.2.0f1. Widely popular, especially in hyper-casual mobile game development (Dillet 2018.), Unity is a multiplatform game engine that is in itself written with C++, and with ma- jority of the actual game scripting being done in C#.

For the sake of keeping the summary concise, one should immediately start off by looking at how Unity handles things when it comes to aspects relevant to the com- parison and conversion process.

3.2.1 GameObjects

First of all, it should be noted that Unity is heavily, if not entirely reliant on crucial building blocks called GameObjects. (Important Classes – GameObject 2019.) A GameObject, while being a building block, acts also as a Component container, be- ing capable of consisting several different Components. GameObjects can be parented, set as children, and used in conjunction with other GameObjects. A simple, blank wall object is as much a GameObject as a player character GameObject with several Script Components, Physics Components, and children objects, is. (Using Components 2019.) A GameObject can consists of any number of Components, but each and every GameObject harbors a Transform Component. This Transform Component is respon- sible for the Position, the Rotation, and the Scale of the GameObject. It is impossible to create a GameObject without it having a Transform Component, because without one it would not have a position, size or any sort of axial heading or affinity in the eyes of the game engine. Shortly put, without a Transform Component a GameOb- ject would not exist.

30

Figure 4. Unity GameObject with Components.

A crude but effective example of GameObjects, Components and the parent-children hierarchy would be a comparison to a car: It could be said that a car firstly consists of a chassis. Attached to this chassis are the

31 engine, the steering wheel, and the tires. In this comparison the chassis GameObject (abbreviated as “GO” from here on out) would be the first one in the hierarchy, with all the other GOs being parented under it. The engine has a Script Component at- tached to it, in which it tells the Transform Component of the chassis GO to move forward. This would cause all of the children GOs to move with the chassis GO due to the parent-children hierarchy, effectively moving the whole car just by moving the Transform Component of the parent GO. Mayhap needlessly complicated, but in turn the steering wheel GO would have an- other Script Component attached to it, referencing and telling front wheel GOs to change their rotation according to the given input, i.e. turning the wheel and the wheels into the direction one wants to drive to. This, however, would not actually turn the car in any direction in the case of this example, since this would only rotate a child GO, which can be rotated independently in the hierarchy, without affecting the rotation of the parent GO in any manner.

Figure 5. Unity parent-children hierarchy view.

32 3.2.2 Components

As stated and grazed in the previous example, Unity GameObjects house elements called Components. Unity Components come in many shapes and sizes and make the GameObjects into nearly whatever the developer wants them to be if used correctly. As already made clear, the most common Component is the Transform Component, which is automatically added to any GameObject created in the Unity editor. Other common Components to be found in GameObjects are different Collider, RigidBody, Animator, Audio, Light, and Script Components. These handle everything from the GameObject’s behavior when exposed to Unity’s physics to attaching differ- ent scripts onto the GameObjects. Another central Component in any GameObject that has any visual representation in any manner, is the Renderer Component. The most common Renderer Component in 3D projects the Mesh Renderer, that works in conjunction with the Mesh Filter Com- ponent. The Mesh Renderer is responsible for taking the mesh geometry indicated to it by the Mesh Filter Component and rendering it at the position declared by the GameObject’s Transform Component. (Mesh Renderer 2019.)

3.2.3 Meshes, Materials, Textures and Shaders

As aptly stated in the Unity Manual itself: “To draw something in Unity, you must provide information that describes its shape, and information that describes the appearance of its surface. You use meshes to de- scribe shapes, and materials to describe surfaces.” (Materials Introduction 2019.), Meshes define and make up the shape of an object, whereas Materials can be thought as the paint on top of the shapes, in a simplistic sense. Textures, on the other hand, are bitmap images that define the surface of a Material in various ways. These ways can include things such as reflectivity, bumpiness, and the general way how they work with Shaders in conjunction with lighting and color- ing effects to create the visual representation of an object. (Textures 2019). Keeping in line with the previous metaphor, if Material is the paint on top of a Mesh, then a Texture is the type, shade and/or texture of the said paint.

33 Shaders themselves are, in essence, small scripts that house the algorithms and cal- culations that are responsible for handling the way a material is shown to the world and how it reacts to the world and lighting elements around it. They also calculate and handle different Material configurations. (Meshes, Materials, Shaders and Tex- tures 2019).

Figure 6. Unity Mesh, Material, Texture and Shader components. Note the selected Texture in the Material Component.

34 3.2.4 Prefabs

Prefabs are another key element of the Unity engine. Simply put, Prefabs are a way to store GameObjects along with their various Components as assets that can be re- used anywhere in the Unity environment, without the developer having to build the same specific GameObject structure from scratch each time the same specific set of GameObjects is required. Like GameObjects, that they essentially are but not limited to, Prefabs can also be nested to create more complex GameObject structures at rel- ative ease. (Prefabs 2019). For example, let us say that a developer creates a house GameObject with several different Components such as Scripts, Meshes and Materials, and takes a liking to the house they have made and wishes to reuse it in the current project, and in their other projects as well. Instead of duplicating the same GameObject structure end- lessly, they can create a specific prefab of it and store it within the project’s files, making reusing and modifying the set of objects that form the house a simplicity it- self, as prefabs can just be dragged into the scene view or opened separately in the editor where they can be modified independently without affecting the base prefab in any way. They can also be easily packaged and exported for use in other Unity pro- jects.

Figure 7. Highway Chase Car Prefabs.

3.2.5 Scenes

Where do all of the previously discussed GameObjects with their components then reside? The answer is in Scenes, which are as integral to the Unity engine as are GameObjects. A Scene can be thought of as a collection of GameObjects that in turn form and make up the pieces that form the game itself. Environments, obstacles,

35 decorations and menus are all things that live within Scenes. Depending on the type of the game, it can have just a singular scene, or utilize several scenes in the form of different levels, for example. Each scene also contains its own light source and a camera by default. (Scenes 2019).

3.3 Babylon.js

In places, the ways that Babylon.js engine handles things are quite similar to the Unity engine, however with some crucial differences. The most notable of which are the differences in GameObjects, Components and the lack of Prefabs.

3.3.1 Nodes

Instead of relying on GameObjects, Babylon.js is fully Node-based. These are how- ever quite similar to the GameObjects of Unity, in the sense that they follow the same parent-children hierarchy model as Unity’s GameObjects, with many of the same rules and restrictions applying to them as well. (Babylon.js – Class Node 2021). The most notable difference however is that while Unity’s GameObjects have sepa- rate Filter and Rendering Components for the Meshes, in Babylon.js the Mesh is the direct component that is being manipulated and processed. Empty nodes can how- ever be created and utilized, which is particularly useful when parenting different Nodes and Meshes. While the difference between GameObjects and Nodes might seem large on paper, in practice it is not a major one, and boils down to how each can be utilized and ma- nipulated in their own respective engine environments.

36

Figure 8. Babylon.js Node structure.

3.3.2 Node Properties

In place of Unity’s Components, Babylon.js has something called Node Properties. While not necessarily as multifaceted and flexible when it comes to usage, they how- ever offer many of the same basic tools and properties that exist in Unity’s Compo- nent selection. The properties range from Transform handling to physics options to a Node’s material settings, to name some.

37

Figure 9. Node Properties.

3.3.3 Scenes

Much like Unity, Babylon.js is scene-based meaning that, much like in Unity, a scene in Babylon.js is responsible for holding all of the game’s objects and instances within it, acting as a container for them. Swapping and handling multiple scenes is also pos- sible. As later code examples will show, scenes and references to them are prevalent in the Babylon.js code environment, even when dealing with a single-scene project. (Babylon.js – Scene 2021).

38 3.3.4 Editor

While Unity, being a program, comes with its own default editor, Babylon.js does not offer one right out of the box, but rather relies on its built-in Debug Layer View, with most of the developing happening in the code editor of one’s choice. Babylon.js does however have a desktop editor, BabylonJS Editor, and while it does offer a more pleasing view of the Scene being edited and streamlines some aspects of Babylon.js development, it is however not required for development. The way the editor works is quite similar to Unity, offering a developer the tools for creating and editing basic geometric meshes, lights, cameras, particle systems, along with material and texture handling, to name some. One thing worth noting about the editor is the fact that it defaults to using Type- Script scripts, with no option for JavaScript. If one is not as comfortable with Type- Script as they are with JavaScript, the editor may take some time getting accustomed to. Also, if one is coming straight from working with the Unity editor, the BabylonJS Editor may be the cause of some grey hairs, since while being seemingly deceptively similar to the Unity editor, the BabylonJS editor does suffer from some pitfalls if one expects it to behave in a manner similar to the more advanced Unity editor.

Figure 10. The BabylonJS Editor.

39 3.3.5 Playground

A fantastic aspect of Babylon.js is the Babylon.js Playground. An online live editor that supports both Java- and TypeScript, the Playground is an invaluable tool for pro- totyping, gaining aid in any possible problem situations and especially learning the functionalities of the engine, as most of the documentation comes with links to the Playground, demoing the features and showing them in action. The Playground is also a handy tool for seeking answers to code-related problems on the various Baby- lon.js forums and sites, as developers can copy and paste their problematic code on the Playground, save a direct link to it, and them paste it for the people wanting to help them. It is safe to say that if one has a problem with the Babylon.js engine or some functionality related to it and one does not find a direct answer to it in the offi- cial documentation, the answer can most assuredly be found with little to no search- ing on the forums and the plentiful Playground links to be found on them. The Play- ground also offers an option to locally save and load any code created with it for fur- ther usage. (Babylon.js – Playground 2021). Albeit somewhat beside the topic, it is also worth noting that the developers of the engine are very active on the said various forums, with many of the answers to vari- ous problems coming directly from the lead developer himself.

Figure 11. Babylon.js Playground.

40 4 Recreating Highway Chase with Babylon.js

As discussed before, the focus of the re-creation part of this thesis is placed upon the aforementioned comparable aspects, such as the handling and the behavior of game- play objects, lights, physics, particles and other visual effects, in order to effectively draw comparisons between the original Unity product and the remade Babylon.js version. On the code front, while Babylon.js does support TypeScript, the project will be coded using JavaScript. This is in part due to personal preference and previous en- gagements with JavaScript. Also, if tread carefully, a general opinion of more people still using JavaScript rather than TypeScript, especially when it comes to this coding environment, might perhaps be voiced. Following the logic behind the argument, this in turn leads to more code examples being available if, and when, the need to find them should arise during the project. While the code used in the project is funda- mentally self-produced, there are however certain premade and pre-set functionali- ties that use set code patterns that are utilized in the project. As for any extraneous plugins, the core idea of the project is to produce everything with the base tools and plugins offered in conjunction with Babylon.js. While other plugins and libraries could be used, the point here is to gauge Babylon.js as Babylon.js, without any external, ad- ditional technologies that might produce a distorted view of the game engine.

4.1 Tools Used in the Project

While a plethora of various tools and platforms exist, the project is made with the well-known saying “keep it simple” in mind. This is partly due to scope and time re- straints of the project, as well as a personal preference. With minimal additional tools it is possible to ensure a quick and an efficient process when it comes to coding, debugging and testing the project, with minimal possible platform or version mis- matches. The code editor used in the project is Visual Studio Code, a highly capable yet light- weight program that comes with a set of excellent developing features right out of the box, with the addition of having several useful extensions (Visual Studio Code

41 2021). As for the extensions, the Live Server extension by Ritwick Dey is being used. The extension in question enables one-click local development server creation and running with live reloading and can be launched straight from the Visual Studio Code editor (Live Server 2021). The use of the Live Server extension saves a considerable amount of time, and even more so the unnecessary hassle of setting up Node and lo- cal servers. A truly invaluable tool indeed.

Figure 12. Visual Studio Code with Live Server Extension.

In addition to Visual Studio Code, the project requires some usage of Blender when it comes to converting mesh file formats. Blender is a widely used open-source 3D graphics editor and toolset (Blender 2021). Many, if not all, of the original meshes and models used in both the base Highway Chase and the remake in question, were in fact originally created using Blender. The development and testing of the project utilizes the Firefox Browser by the Mozilla Foundation. This is again due to personal preference and not by any perfor- mance-related design or deliberation. The browser version at the time of writing is 87.0, 64-bit, but at the start date of this project, the version was 82.0.

42 4.2 Starting the Project

Getting started with Babylon.js is relatively simple. As stated previously, all one needs is a code editor and a development server, with these being described in the previous section. As the development is naturally browser-based, a HTML index page file is also needed, and it serves as a base for the JavaScript files that will form the application itself. It is also still worth noting at this point that the online Playground tool can also be used to produce code and code sketches and import them into the project. The index file in question holds all the references to the scripts the project needs, as well as a reference to the project file(s) themselves, along with the usual setup options and parameters needed to set up and visualize a HTML page. In this instance the project uses the online sources of Babylon.js core engine files and acquires them via CDN (short for content delivery network or content distribution network). The files are available for local downloading as well, but since during the start of the project it was yet a tad unclear which files and plugins would actually be needed and used, this approach was chosen. For example, despite the Babylon.js site documentation and promotional texts mentioning that the Babylon.js engine comes with a built-in physics engine, this was not indeed the case, and a separate reference to a physics plugin was required. Apparently at one point in time the Babylon.js en- gine came with a built-in Ammo.js physics engine, but as the newer versions of the Babylon.js engine do not have these, there exists a need to separately reference the physics engines. Like with the Babylon.js engine files, this is again done via the CDN. At the start of the project all three of the physics engines supported by Babylon.js; Ammo.js, Cannon.js and Oimo.js, were acquired for the sake of testing and compar- ing them. Also among the references acquired via the CDN is the jQuery.pep.js reference. What this is in essence is a jQuery plugin that enables turning any DOM elements into drag- gable, interactable objects. What this means in practice, since mobile platforms have to be kept in mind, is that using this plugin enables touch screen controls and fluid mouse dragging for the project and its game objects. It should be noted at this point, that the project is done using the 4.2 version of the Babylon.js engine.

43

Figure 13. Index file with BJS relations and references.

Now that the index file has been set up, the way how a Babylon.js project actually structures itself can be examined. First off, a separate JavaScript file in which all of the actual game code is actually handed should be created. Once this is created, within it Babylon.js requires a reference to the canvas set up in the index file so that it knows where to draw and render everything it is required to draw and render. This is done by creating a variable for the canvas and using document.getElementByID() to get and assign the to the variable.

const canvas = document.getElementById("renderCanvas");

Once the canvas variable is set up, it can then be used to set up the actual engine portion and enable Babylon.js for the project. Firstly, another variable for the engine should be created, and then assigned using the Babylon.js command of new BABYLON.Engine() and giving it the canvas as a parameter, along with several other constructors, such as additional engine options and adaptation to current device’s viewport properties.

44 const engine = new BABYLON.Engine(canvas, true, { preserveDrawingBuffer: true, stenci l: true }, true);

After these two have been set up, it is time to create the scene the game will take place in. As discussed before, much like Unity, Babylon.js is scene-based, with a scene being the actual environment that game objects exist and interact each other with in. While Babylon.js supports multiple scenes, the project will utilize just a singu- lar scene, as the remake prototype will showcase the Babylon.js’s capabilities within a singular gameplay instance, i.e., a level. A scene is created by creating a function expression createScene() and within it, it is necessary to define the scene variable it- self and then pass the engine variable to it, which in turn indicates that the scene should be rendered by the passed engine variable. At the end of createScene, a way is needed to forward the scene in question for further use. This is done simply by re- turning the scene variable. In addition, it is extremely useful to set up and show the Babylon.js inspector layer within the scene at this point. This can simply be done with defining the scene the inspector layer should be shown and rendered in, and then doing so by typing the scene.debugLayer.show(). Doing this enables both the scene explorer pane and the inspector pane for the project.

const createScene = function () { let scene = new BABYLON.Scene(engine); scene.debugLayer.show();

return scene; };

It is also advisable to add a camera to your scene at this point, as the debugger will throw an error if one is not defined. This is done simply by creating a camera variable and using the desired camera type from the selection that Babylon.js offers. As the project will use a camera that follows the player when they move and drive around the level, one might think that the preset Follow Camera would be the best option, but as later testing showed, this is not however the case. Instead an Arc Rotate Cam- era, an orbiting satellite type camera with a separately set locked target pointing at a

45 defined target position, is the best option here. In this instance, the camera is given a name, an alpha value, a beta value, the position of the camera’s target and the scene the camera belongs in. In addition to these, the field of view and the radius of the camera are adjusted.

let followCamera = new BABYLON.ArcRotateCamera ("Follow Camera", -Math.PI/2, 0.7, 0, null, scene); followCamera.fov = 0.6; followCamera.radius = 70;

Along with the camera, this is also a fine place to add a light to the scene. This is done in a similar manner to adding the camera, with the given parameters being the name of the light, its position in the scene, and the name of the scene it belongs to. Its intensity should be, and also will be, adjusted from the default 0.7 to 2, since the light source will be placed into far left, as it is in Highway Chase as well, with the posi- tion it will be set at.

let light = new BABYLON.HemisphericLight("Light", new BABYLON.Vector3( -50, 20, 100), scene); light.intensity = 2;

Despite these few additions, the createScene function might look empty for now, but this is the place where nearly everything else that will make up the actual game itself will be placed.

Having created and returned a scene, the next step is to actually set up and manage the rendering of the scene. This is done simply by invoking the the runRenderLoop() function and passing the desired scene into it, with scene.Render() within it render- ing the scene within the render loop.

scene = createScene();

engine.runRenderLoop(function () { if (scene) {

46 scene.render(); } });

Also while not exactly strictly necessary, one could still also add a resize event han- dler to the script just in case.

window.addEventListener("resize", function () { engine.resize(); });

With these basic steps, the engine and the scene are both ready for further use. Nothing exists in the scene yet, so the camera does not have a target yet, nor does the created light shine upon anything. This will be fixed by loading the first assets into the scene.

4.3 Loading and Using Assets – Asset Manager and Meshes

Firstly, it should be mentioned that all of the meshes and materials are pre-existing and come from the original Highway Chase, which makes them nearly ready to use as-is in Babylon.js as well. The reason the previous sentence uses the word “nearly” lies in the file format of those meshes. As they are originally from a Unity project, they use the .fbx file format. Sadly, Babylon.js does not offer support for .fbx files and is unable to use the files in their original form. This problem is however easily remedied with the use of the Blender software and its inherent import/export func- tionalities. All one has to do is to open one of the original .fbx files and then navigate to the ‘file’-menu and choose the ‘export’-option. This offers two potential export options in this case; glTF 2.0 (.glb/.) and Wavefront (.obj), as these are the two file formats among the listed exportation formats that Babylon.js actually supports. Out of no particular reason the Wavefront (.obj) file format will be selected for the files that will be used in the project.

47 Loading assets in Babylon.js is relatively simple, once one internalizes the things that are happening while doing so. How they are manipulated and used after loading them can be more complex than expected, however. If outright compared to Unity, the adding and manipulating new objects can be a bit confusing at first, especially with the lack of prefabs. For asset loading, Babylon.js provides a handy Assets Manager set of functionalities. Firstly, in order to start using it, one must be created with BABYLON.AssetsManager(), and then pass the desired scene into it. When this is done, a Mesh Task can be created and added to the AssetsManager just created, us- ing assetsManager.addMeshTask(). To this should be passed the name of the task, the name of the mesh, which in this scenario will be left as “ ”, the path to the de- sired mesh to be loaded and finally the file name of the mesh itself. After this, as- setsManager.load() can be called to load the designated mesh into the scene.

let assetsManager = new BABYLON.AssetsManager(scene); let playerMeshTask = assetsManager.addMeshTask("Player Mesh Task", "", "./Models/", "player.obj"); assetsManager.load();

The reason for not specifying the actual mesh by name after naming the mesh task is because since the models/meshes used in the project have several child objects in them, i.e. each car has four wheels, each of the child objects has a different name. Just by calling the parent mesh by name causes for the child objects not to load with the rest of the task. Hence the “ ” is used to circumvent this.

48

Figure 14. Loaded car mesh.

As can be seen in Scene Explorer in the left corner of the figure 14 above, each one of the loaded mesh pieces is a separate node, despite being loaded in the same mesh task. This is where the Asset Manager’s functionality onSuccess() comes into play. After loading a mesh using the Asset Manager and mesh tasking, an “on success” check can be created for the mesh task in the form of a function. Within this function the loaded mesh and parent mesh parts can manipulated somewhat easily as needed.

playerMeshTask.onSuccess = function (task) { playerChassis = task.loadedMeshes[0]; lbwheel = task.loadedMeshes[1]; lfwheel = task.loadedMeshes[2]; rbwheel = task.loadedMeshes[3]; rfwheel = task.loadedMeshes[4];

lbwheel.parent = playerChassis; lfwheel.parent = playerChassis; rbwheel.parent = playerChassis; rfwheel.parent = playerChassis;

}

49 What the above code snippet does, is that it takes each one of the multiple meshes loaded in the mesh task and assigns them as parts of the car, and then parents them all under the playerChassis mesh. However, using the playerChassis as a parent ob- ject can cause several problems when it comes to mesh alignment and rotation, es- pecially if physics are involved, so therefore it is advisable to first create a separate object for under which all of the loaded meshes in playerMeshTask can be parented and under which the can be used more effectively. This root object will also serve as a potential physics root object should the physics be enabled later on and will effec- tively be the object the player is in control of and plays as.

Creating basic shapes in Babylon.js is nearly as easy as it is in Unity, and Babylon.js has several basic shape generation functionalities available. Shape creation can be handled with the BABYLON.MeshBuilder, providing it a desired mesh shape, name, size, and a scene. Additional options are available, but for this purpose the basic di- mension handling will be sufficient. Here a box that is approximately the same size as the loaded mesh will be created for the purposes of collision checking later on and then positioned in the origin point using BABYLON.Vector3(). The playerRoot object will also be set as invisible since visibility is not required for checking collisions.

let playerRoot = BABYLON.MeshBuilder.CreateBox("PlayerRoot", {height: 3, width: 5, depth: 2}, scene); playerRoot.position = new BABYLON.Vector3(0,0,0); playerRoot.isVisible = false;

After the playerRoot has been created, the meshes in the playerMeshTask can easily be manipulated by parenting the playerChassis under the freshly created playerRoot in the playerMeshTask.onSuccess function. For example, the rotation of the loaded car mesh object can be set by manipulating the parent playerRoot.

playerRoot.rotation = new BABYLON.Vector3(0, 4.714, 0);

50 4.4 Loading and Using Assets - Materials and Textures

Now that the basic principles of loading and manipulating assets and meshes have been figured out, it is time to see how Babylon.js handles loading, modifying, and setting materials, so that the newly loaded meshes can gain some color and texture. The process of loading materials and textures is quite similar to the way Babylon.js handles managing meshes, but with some key differences. While mesh loading is reli- ant on the Asset Manager, materials and textures on the other hand can easily be loaded without using a separate task for them. Instead, Babylon.js offers both BABYLON.StandardMaterial() and BABYLON.Texture() functionalities that are used in loading both materials and textures into a project. Like meshes, they require a name for both the material and texture and a path to their location, with the BABYLON.StandardMaterial() requiring a scene reference as well. After loading both, the texture needs to be set for the material by assigning the texture with loadedMa- terial.diffuseTexture = loadedTexture. DiffuseTexture is not the only adjustable tex- ture option here, but it is the only one really needed at this point. What this effec- tively does, is determining the way the assigned material reacts to light, with Baby- lon.js offering diffuse, specular, emissive, and ambient texture options (Babylon Ma- terials Introduction 2021). The loaded material’s color can also be set in the same manner, which in this case can be done by using the specularColor and setting a color value of BABYLON.Color3(0,0,0) for it. After the value has been set, it has to be ena- bled using the selected colorization type by typing useSpecularColors = true.

var carMaterial = new BABYLON.StandardMaterial("Player Material", scene, "./Models/Materials/Material_world.mat"); var carTexture = new BABYLON.Texture("./Models/Textures/mainTexture.png"); carMaterial.diffuseTexture = carTexture; carMaterial.specularColor = new BABYLON.Color3(0, 0, 0); carMaterial.useSpecularColors = true;

After the material and its texture has been set, it can then be assigned to the previ- ously loaded car mesh. This takes place within the onSuccess function of the mesh

51 task and since it is desirable to apply the material to each one of the loaded meshes, both to the chassis and the four wheels, it can easily be done so by looping through the list of loaded meshes within the task by using task.loadedMeshes.length and ap- plying the material to each mesh. As for the loaded material, it should be at this point noted, that this material file, Material_world.mat, contains material data for nearly everything that the project uses, despite it being named here as “carMa- terial”. This naming is mostly due to the fact that the game, after all, consists mainly of cars.

for(var i = 0; i < task.loadedMeshes.length; i++) { task.loadedMeshes[i].material = carMaterial; }

With that, the result is a parented set of meshes grouped under a manipulatable dummy parent node, with the means for manipulating the loaded meshes and a ma- terial with a texture applied to the loaded mesh.

Figure 15. Loaded car mesh with material

By utilizing these steps, the rest of the graphical assets can be loaded much in the same manner. This goes for the ground objects, other cars, obstacles, and coins.

52 4.5 Loading and Using Assets – Setting Up the Game World

While loading the other, non-player-controlled cars, obstacles and coins, the fact that they need to be positioned in such a manner that they align on and follow the lanes on the road as they do in the original game, has to be considered. Therefore, firstly the road mesh chunk should be loaded and then the corresponding material applied to it. In addition to the already demonstrated loading via Assets Manager and the handling of position, rotation and materials, this time in the onSuccess function of the mesh task the mesh.receiveShadows functionality is also used, setting is as true. This is quite self-explanatory, as enabling the option allows the selected mesh to have shadows drawn upon it. Also, as the road is a singular piece of mesh and hardly makes up for a whole high- way, it is necessary to figure out a way to fill out the whole road with the loaded road mesh pieces, preferably without having to load and task one’s way through the same mesh multiple times. Fortunately, Babylon.js provides a clone() functionality. This, as the name of the functionality states, can be used to clone an object, while preserving all the previously set attributes for it. By calculating the size of a singular terrain piece, the number of times needed to clone one in order to create a highway of sufficient length can be determined, as well as the correct position value to add to each piece with each time it is cloned, so that the blend together seamlessly and without overlapping. To aid in this cloning task, an array holding each of the terrain pieces should be created and each cloned piece added to it with each cloning loop. The number of nodes cluttering the Scene Inspector view can also be reduced by par- enting each successive piece to the terrain Root within the clone loop. Shortly put, a variable is created to tell the loop the desired number of times to cycle through itself and with each successive cycle, clone, position, and parent the terrain mesh piece, forming the titular highway for the titular game.

let terrainMeshTask = assetsManager.addMeshTask("Terrain Task", "", "./Models/Ter- rain/", "four_lane_road.obj"); let timesToAddTerrain = 20; let allTerrain = [];

53 terrainMeshTask.onSuccess = function (task) { terrain = task.loadedMeshes[0] terrain.parent = terrainRoot; terrain.position = new BABYLON.Vector3(0,0,-10) terrain.rotation = new BABYLON.Vector3(0, 1.57075, 0); terrain.material = carMaterial; terrain.receiveShadows = true;

for (var i = 0; i < timesToAddTerrain; i++) { allTerrain[i] = terrain.clone("Terrain"+i); allTerrain[i].parent = terrainRoot; for (var l = 0; l < allTerrain.length; l++) { allTerrain[i].position.z += 40; } } }

Now that the road is set up, the correct rough coordinates for each one of the four lanes can be figured out by using the debugger along with some trial and error. These values can then be assigned to variables and store them in an array for future use.

const fLeftLane = -7; const mLeftLane = -2.2; const mRightLane = 2.7; const fRightLane = 7.5; let lanes = []; lanes[0] = fLeftLane; lanes[1] = mLeftLane; lanes[2] = mRightLane; lanes[3] = fRightLane;

With the lane positions known, the root objects for the non-player, non-chasing cars can then be created. While there are eight different car models in total besides the player and police cars, there is little point in loading all eight, as they are all function- ally the same. Instead, it is sensible to settle just for four cars and begin by creating an array for the root objects. In a manner that might seem a tad convoluted a sepa- rate array for storing these for future use after they have been created will also be

54 constructed. As positions of the lanes are now known, this knowledge can then be immediately utilized in positioning the root objects for the cars. The knowledge can be implemented by using the Math.floor() and Math.random() functionalities in con- junction with the length of the lanes array. Simply put, as the loop goes through each root object to be created, each is set in a random horizontal position, with four possible positions coming from the lanes array containing the four predetermined values. In addition, BABYLON.scalar.RandomRange is used to pick a random value between two set numbers, randomizing the depth coordinate value, i.e., placing the created car in a random point along the highway. After this, the created root object is pushed into the previously mentioned array containing the created root objects. Note that the naming of the two root object arrays could be more distinctive, as they are nearly identical, but they can be considered to suffice well enough for the intents and pur- poses of this example.

let carRoot = []; let carRoots = []; let differentCars = 4;

for (var d = 0; d < differentCars; d++) { carRoot[d] = new BABYLON.MeshBuilder.CreateBox ("CarRoot" + d, {height: 3, width: 2, depth: 5}, scene); carRoot[d].position.x = lanes[Math.floor(Math.random() * lanes.length)]; carRoot[d].position.z = BABYLON.Scalar.RandomRange(50, 500); carRoot[d].position.y = 0; carRoot[d].isVisible = false; carRoots.push(carRoot[d]); }

After this is done, the next step is to create, load, and handle the mesh tasks of each of the objects separately, yet in the same way as it was done with the first mesh task.

let car1MeshTask = assetsManager.addMeshTask("Car 1 Task", "", "./Models/", "car1.obj"); let car2MeshTask = assetsManager.addMeshTask("Car 2 Task", "", "./Models/", "car2.obj"); let car3MeshTask =

55 assetsManager.addMeshTask("Car 3 Task", "", "./Models/", "car3.obj"); let car4MeshTask = assetsManager.addMeshTask("Car 4 Task", "", "./Models/", "car4.obj");

The onSuccess functions of each of the mesh tasks are nearly identical to the one the playerMeshTask possesses, with each mesh being parented in the same manner and with the loading of materials being exactly the same, the same applying to their ma- terials as well. With the mesh tasking and the setting of materials done, there now exists four cars with four different meshes and materials set at random positions throughout the lanes. This, however, is not nearly enough considering that the game is supposed to take place in an active, highly trafficked highway. To remedy this, the clone function- ality can once again be utilized to duplicate the existing car root objects. However, managing several objects outside of their task onSuccess functions can be trouble- some unless this is done within an assetsManager.onFinish function. What the function essentially is, is a section that runs and loads right after all of the project’s Asset Manager tasks have been completed and finished loading. Firstly, for simple storing and holding all of the created clone objects, another array should be created, along with a variable determining how many times the objects should be cloned, i.e., how many cars are required to be on the highway. With a number decided, another loop should be constructed within the assetsManager.on- Finish function, with each loop creating and adding another cloned car into the cre- ated array. This is done by taking a random index from the previously created car- Roots array, then cloning that particular car root object and after that adding the cloned instance into the created allCars array.

let timesToAddCar = 10; let allCars = [];

assetsManager.onFinish = function (tasks) { for (var i = 0; i < timesToAddCar; i++) { allCars[i] = carRoots[Math.floor(Math.random() * carRoots.length)].clone("CarClone"+i); }

56 While within the same function, additional looping can be done to adjust the posi- tions of the cloned car root objects by using the same positioning principles as with the original car root objects themselves. However, as the highway is starting to get cluttered with several cars, a way to avoid the overlapping of the created car objects should be conceived so that two cars will not spawn on top of each other. For this end, it would not be entirely unwise to create a grid array for the car spawn positions. This has to utilize several different aspects, ranging from the size of the spawned object to the size of the grid cells and both the depth and width of the grid cell placement. As the dimensions of the insertable cars are known, their width and depth can be used to determine the size of each cell of the grid. In addition, a buffer area should be determined for both the width and depth, so that the cells of the grid are far enough apart from each other, so that they and therefore the object meshes will not clip over each other.

let objectWidth = 2; let objectDepth = 5; let positionWidth = 0; let positionDepth = 0; let cellX = positionWidth + objectWidth + 2.8; let cellZ = positionDepth + objectDepth + 4;

With the basic dimensions of a grid cell set, the number of cells should then be deter- mined. As there are four lanes, the horizontal cell count would therefore be four. As for the depth value, determining it is largely based on the desired length of the stage. This can be derived from the number and size of the ground mesh pieces, as well as general intuition and testing. In this case, the value is initially set to 70 for depth-wise grid cells. After determining the number of cells, an array to hold them should then be constructed and the cells pushed into it. In addition, the array can then be pseudo-randomized using the sort() function.

let cellCountX = 4; let cellCountZ = 70; let gridSpots = []; for (let x = 0; x < cellCountX; x++) { for (let z = 0; z < cellCountZ; z++) {

57 gridSpots.push([x, z, Math.random()]); } }

gridSpots.sort((a, b) => { return a[2] - b[2]; })

After this, the position of the grid should be aligned to correspond with the lanes and the game world. This is possible by setting origin points for both the horizontal and depth values of the created grid array. While creating these origin points, variables for gridX and gridZ should also be created, as these are needed soon when placing objects within the grid cells.

let gridOriginX = lanes[0]; let gridOriginZ = 60; let gridX; let gridZ;

The next step would then be to position the objects within the created grid cells within the same function where the car roots are being added and cloned, as this is the best place to handle their positioning into the grid and into the game world. Both the x and z positions of the car objects are then set by first calculating and then ran- domizing them.

for (var i = 0; i < timesToAddCar; i++) { allCars[i] = carRoots[Math.floor(Math.random() * carRoots.length)].clone("CarClone"+i);

gridX = gridOriginX + gridSpots[i][0] * cellX; gridZ = gridOriginZ + gridSpots[i][1] * cellZ;

allCars[i].position.x = gridX - positionWidth / 2 + positionWidth * Math.random();

allCars[i].position.z = gridZ - positionDepth / 2 + positionDepth * Math.random(); }

58 The same basic loading, arraying, positioning, looping, and randomizing can then be done for both the coins and the road obstacles. This can be considered mostly a rep- etition of things already explained and demonstrated, so in order to avoid said un- necessary repetition, the portion describing the process is best left out.

After this is done, the game world, or the highway, is starting to look quite a bit live- lier than before.

Figure 16. Game view with assets and materials.

4.6 Physics and Movement

Now that the assets and materials have been loaded and the game world filled with them, it is time to focus on how they move and interact with each other and the world itself. Firstly, attention should be drawn to how Babylon.js handles updating, animating, and moving the different nodes, components, and values within the game. Wherein Unity has its Update() function, Babylon.js has a function called registerBe- foreRender(). While minute differences exist, it is safe to say that this can be consid- ered a somewhat direct equivalent to Unity’s previously mentioned Update() func- tion. The function contents within the registerBeforeRender() are run before each

59 frame rendered by the engine’s previously discussed driving runRenderLoop(), with the frames rendered approximately 60 times per second.

If one wanted to simply automate the movement of the player car object, one could plainly set the z position of the playerRoot object to update during each tick of a frame of the tick-based update function by adding the desired value to it during each tick. The playerRoot would update its position, adding the set value during to its z po- sition during each of these ticks. For example, a variable for controlling the player speed could be set, and then be used to translate the position of the playerRoot ob- ject by it during each tick. Respectively, the same can be done to use move the non- player -controlled objects in the game world.

scene.registerBeforeRender(function(){ playerRoot.position.z += playerSpeed; });

While simplistic, for a simple game such as this, this approach is not completely un- desirable. Extensive testing with physics engines and collisions were done over the course of the project, and yet the physics plugin was dropped as a functionality near the very end. This is in part due to the, frankly said, somewhat convoluted way the Babylon.js engine handles mesh and object collisions, especially with a physics plugin enabled. For whatever reason, the collision physics were often prone to downright not registering the collisions between objects, and even when they did so, they often miscalculated the bounding boxes, causing mesh clipping issues on collisions. Despite this, however, a few words should be said about the physics aspects of the Babylon.js engine.

Despite advertising a built-in physics engine, this feature was however stripped from the engine prior the current build the project is conducted with. Instead, Babylon.js now offers integrated support for the Cannon.js, Oimo.js, Ammo.js and the Energy.js physics plugins, with the last option not being yet publicly available. While all four have their benefits and downsides, Cannon.js was initially chosen for this project. This is partly due to the fact that it best supports the Physics Impostor types provided

60 by Babylon.js, and due to the fact, that while Oimo.js, Energy.js and Ammo.js are all JavaScript ports of engines, Cannon.js is actually entirely written with it. As stated in the section describing the tools used in the project, the usage of a phys- ics plugin requires a reference to one in the index file of the project. Once this has been set up, physics can be enabled for the project by simply defining desired the physics plugin with scene.enablePhysics(), which takes both the gravity vector and the physics engine as parameters. If the gravity vector is left as null, the hidden, de- fault value for it will be set as (0, -9.807, 0), which is the universal gravity value on Earth.

scene.enablePhysics(null, new BABYLON.CannonJSPlugin());

In addition to this, for collision checking to work, it must be enabled for the scene, as well as any potential objects one wishes for to check collisions, by using the collision- sEnabled and checkCollisions.

scene.collisionsEnabled = true;

The most troublesome part with physics-based collision in Babylon.js are, frankly said, the Physics Impostor bodies. These are in essence physics-enabling pseudo-col- liders that wrap themselves around a chosen mesh object. They come in set shapes as well as mesh imitators, depending on the physics plugin being used. To enable a Physics Impostor for a mesh, one needs to use the BABYLON.PhysicsImpostor() func- tionality and give it a mesh, an Impostor type, options such as mass, friction, and res- titution, along with scene that the Impostor belongs to as parameters. Sadly, modify- ing these colliders in any way is incredibly awkward and cannot be done freely or in any precise manner.

carRoot[d].physicsImpostor = new BABYLON.PhysicsImpostor(carRoot[d], BABYLON.PhysicsImpostor.BoxImpos tor, { mass: 1, friction: 0.01, restitution: 0 }, scene); carRoot[d].checkCollisions = true;

61 With Physics Impostors set for meshes and their collision checks enabled, they can then be checked and manipulated in various ways. The approach the project used was by using the mesh.physicsImpostor.registerOnPhysicsCollide() option, which takes two Physics Impostors and runs a function on the two, based on the mesh to be collided with and the colliding mesh.

barrierRoot.physicsImpostor.registerOnPhysicsCollide (playerRoot.physicsImpostor, function(main, collided) { if (collided.object.hasHit == false) { console.log(collided.object.name + "Goes Boom"); explode(collided.object); collided.object.hasHit = true; } });

Using Physics Impostors also naturally plays a part in moving the car meshes, or ra- ther their root objects, which themselves are meshes as well. This is done by giving the Impostors a linear velocity factor with the mesh.physicsImpostor.setLinearVeloc- ity, i.e., defining and moving the chosen Physics Impostor with a desired force. This force, or rather the movement of the mesh, is naturally dependent on the mass and the friction of the Impostor to be moved.

allCars[i].physicsImpostor. setLinearVelocity(new BABYLON.Vector3(0, 0, 15));

All in all, using a physics plugin in a project that is not truly reliant on physics but heavily reliant on collisions ended up causing more trouble than any benefit to be gained from using one, and was ultimately dropped from the project despite the in- tention of showcasing the physics interactions in Babylon.js in more depth.

Returning to the topic of character movement, as stated in the previous few para- graphs, the project handles mesh transformations, movement, and collisions without a physics plugin. Fortunately, this is relatively, and maybe even deceptively, simple in Babylon.js.

62 Firstly, in order to enable any player interaction with the game world via the mesh they will be moving around as, some input management and controls based on this management are needed. This is handled by creating an input map, and then initializ- ing an Action Manager for the scene with BABYLON.ActionManager() where the de- sired scene is passed as a parameter. By utilizing this Action Manager, the engine can then be made to listen for triggers and events, meaning key presses in this instance with both OnKeyDownTrigger and OnKeyUpTrigger.

let inputMap = [{}]; scene.actionManager = new BABYLON.ActionManager(scene); scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction (BABYLON.ActionManager.OnKeyDownTrigger, function (evt) { inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown"; })); scene.actionManager.registerAction(new BABYLON.ExecuteCodeAction (BABYLON.ActionManager.OnKeyUpTrigger, function (evt) { inputMap[evt.sourceEvent.key] = evt.sourceEvent.type == "keydown"; }));

With the Action Manager set up, player inputs can then be listened to within the reg- isterBeforeRender() function. As WASD and arrow key -based movement is nearly then norm in modern keyboard-oriented gaming, the model was chosen for the pro- ject as well. While w/up and s/down keys are useful in testing by increasing and de- creasing the playerSpeed on input, the actual game uses only a/left and d/right con- trols due to its simple nature.

if(inputMap["d"] || inputMap["ArrowRight"]){ keydown = true; playerRoot.position.x += playerSpeed; }

if(inputMap["a"] || inputMap["ArrowLeft"]){ keydown = true; playerRoot.position.x -= playerSpeed; }

63 As touch-based inputs are also required, the project needs mouse-based controls as well, as these translate nearly directly into touch controls on devices that support them. Babylon.js handles controls such as these in a fairly similar event-based man- ner than its Action Manager does. Firstly, a function for handling the mouse move- ment should be created. As the need for such movement is restricted to a single mesh, i.e., the playerRoot, at a time, a single mesh can be set to be required as a pa- rameter. What the function is then required to do, is for it to create a behavior with a set of events that can then be attached to a mesh, causing it to respond to mouse in- teraction. Within the function, Babylon.js’s BABYLON.PointerDragBehavior() should be utilized, with the required parameter being the plane in which the dragged object is moved around in. In addition to this, since manually handling the drag event is desired, pointerDragBehavior.moveAttached should be set to false, and as the movement should take place according to the world space, and with the dragged mesh’s own orientation being the guiding factor the pointerDragBehavior.useObjectOrientation- ForDragging should be set as true.

function attachPointerDragBehavior(mesh){ let pointerDragBehavior = new BABYLON.PointerDragBehavior({dragPlaneNormal: new BABYLON.Vector3(0, 1,0)}); pointerDragBehavior.moveAttached = false; pointerDragBehavior.useObjectOrienationForDragging = true;

As for the mesh dragging and manipulation itself, onDragStartObservable, onDragOb- servable and onDragEndObservable events should be added into the function, with onDragObservable being the section where sideways movement logic should be added into. This is done by utilizing the attachedNode.position functionality in con- junction with event.delta, i.e., the direction the cursor is moving at a given time dur- ing the drag event. Finally, the created behavior can then be set to be attached to a mesh.

pointerDragBehavior.onDragStartObservable.add((event)=>{ console.log("startDrag");

64 }) pointerDragBehavior.onDragObservable.add((event)=>{ console.log("drag"); pointerDragBehavior.attachedNode.position.x += event.delta.x * playerSpeed; }) pointerDragBehavior.onDragEndObservable.add((event)=>{ console.log("endDrag"); }) mesh.addBehavior(pointerDragBehavior);

After the logic is done, it can then be attached to the playerRoot mesh, enabling the mesh to be moved according to mouse and, in conjunction, touch inputs.

attachPointerDragBehavior(playerRoot);

As the created controls set no limit to how far to the left or to the right the player- Root mesh can actually be moved, something should be done to limit the range of the movement so that the player stays within the bounds of the road lanes. This could be done in several different ways, for example setting invisible colliders to the sides of the road if physics were being used, or as in this case, simply limiting the hor- izontal values the player can move between by utilizing the registerBeforeRender() function. While this could be done in a myriad of ways, ranging from horizontal value clamping with Scalar.Clamp() or Math.abs() to manipulating the input logic. However, in the case of this project the simplest solution can be considered as the best. Within the registerBeforeRender(), a simple check can be made to see if the player- Root is trying to move over the left side of the road or the right side of the road based on its horizontal position value, and if this is the case, setting the value to the wanted limit, depending on the side of the road. This way if the player is trying to move over the threshold value, the horizontal value gets set back to the desired threshold, without the playerRoot going over or under the value, depending on the side of the road.

65 if (playerRoot.position.x < -8 || playerRoot.position.x >= 8) { if (playerRoot.position.x < -8) { playerRoot.position.x = -8; }

else if (playerRoot.position.x > 8) { playerRoot.position.x = 8; } }

With that, most of the basic movement-related tasks have been handled. At this point, before moving on to other tasks, it would be a good moment to add a target for the previously set up camera that is supposed to be following the player as they move, seeing that the player is now actually capable of movement. This is done simply by setting the camera’s locked target. The target can be any mesh, but as the playerRoot is the one being moved and manipulated, it would be the most sensible target to add as the camera’s target.

followCamera.lockedTarget = playerRoot;

Another task that should be done before moving on to collision handling, would be to make the coins that are scattered throughout the road to rotate slowly in place. This can be achieved by looping through the created allCoins array within registerBe- foreRender() and then adding a small value to the y rotation of each coin. The value could be negative as well, depending on whether the coins should spin clockwise or counterclockwise.

for (var c = 0; c < allCoins.length; c++) { allCoins[c].rotation.y += 0.03; }

66 4.7 Collisions

Now that the project has all of the meshes that can be considered as obstacles and collectibles in place, it is time to focus on colliding with and collecting them. Without a physics engine in use, the mesh-to-mesh collisions are handled by Baby- lon.js’s mesh intersection checks, via the mesh.intersectsMesh(intersecting mesh, precision type) functionality. What this does in effect is that it checks whether the two defined meshes are inter- secting and detects this by using the set, boolean-based detection type. By default, Babylon.js automatically creates bounding boxes around meshes. These bounding boxes are usually fairly accurate and avoid a great deal of calculating, therefore in- creasing overall performance. If the boolean value is set to false, which is the default, the collision checking uses these general bounding boxes for the collision checking. If set to true, the detection and bounding box type switches over to one that more closely follows the actual contours of the mesh, but with the downside of more per- formance-costly calculations. As the project checks collisions based on the specifically created root objects that have been set into rectangle shapes that follow the general outlines of the car meshes, setting this boolean as true in unneeded. In addition to the set method of checking intersecting meshes, each of the project’s collision-related objects should also be fitted with a boolean check for whether one has been a part in collision or not. This is done simply by creating a hasHit variable and adding mesh.hasHit = false to each of the created meshes.

Naturally, the collision checking and the ensuing actions have to differ depending on the colliding objects, with a non-played controlled vehicle driving into a road barrier requiring a different set of follow-up actions than the player colliding into one. For the player driving into another car, the collision check should obviously be between the player mesh and the mesh the player collides with. As all of the non-player car objects are stored in an array, the check is rather simple and can be done by looping through the array to check which car is taking place in the collision. As an additional check in order to stop multiple instances of collisions within the same frame loop, the hasHit of the colliding object should also be taken into account, first by allowing

67 the collision event to happen if the hasHit is false, and then setting it to true during the collision event, preventing the same event triggering twice, even though the two meshes are still intersecting.

for (var i = 0; i < allCars.length; i++) { if (playerRoot.intersectsMesh(allCars[i], false) && allCars[i].hasHit == false) { allCars[i].hasHit = true; } }

The same basic principles and checks are also applied to checking if a non-player car collides with a road barrier obstacle, with both outcomes leading to setting the col- liding car’s hasHit value as true. This tracking and checking comes into play in the registerBeforeRender() function where, if a car is considered having been hit, the according actions are then taken, them in essence being lifting the car into the air while spinning and flying it off the road by adjusting its positional and rotational values.

if (allCars[i].hasHit == true) { allCars[i].rotation.y += 0.15; allCars[i].position.x -= 0.4; allCars[i].position.y += 0.4; }

As for player collision checking, should the playerRoot object collide with a road bar- rier or the chasing police car, its speed will set to and the appropriate actions be taken. Colliding with other cars will also cause the chasing police cars to speed up, and eventually reach the playerRoot. These appropriate actions are however dis- cussed in a later section. Should the player collide with a coin mesh, the check is again made from the player- Root’s perspective, with the array containing all of the coins being looped through

68 much like with the allCars array. Should a collision between a coin and the player- Root happen, a variable made for holding the player’s score is increased by one, the hasHit Boolean of the coin is set as true and then the coin is removed from the game using Babylon.js’s mesh.dispose() functionality. What this effectively does is remove it from the scene and node hierarchy altogether, freeing up resources.

if (playerRoot.intersectsMesh(allCoins[c], false) && allCoins[c].hasHit == false) { playerScore++; allCoins[c].dispose(); allCoins[c].hasHit = true; }

4.8 The Police

One might have wondered about the absence of a key driving factor in the game; the police cars chasing the player. As grazed upon earlier, the game can be lost in two ways; either the player collides with a road barrier, or the chasing police car manages to catch up and collide with them. This happens should the player collide with too many other cars, with the event causing the police car to gain speed, ultimately al- lowing the police car to reach the player. The police car is added into the game in the same manner as all of the other meshes, with a root object being created and the mesh file loaded and parented under this root mesh. What differs most from the other cars in the game is the way the police root object is being moved, as it should pursue the player according to their move- ments. While seemingly tricky to achieve, this is rather simple in practice, especially when utilizing both the BABYLON.Vector3.Lerp() and looAt() functionalities. Using the lerp with playerRoot.position, policeRoot.position and a policeSpeed variable, the position of the policeRoot mesh can be made to move towards the playerRoot via lin- early interpolating the value between the two positions, using the policeSpeed as the interpolation amount indicator between the two scalar values. Simply put, with the Lerp() the policeRoot strives to move to the position inhabited by the playerRoot,

69 with the speed value given to it, forming the illusion of the police car chasing the player.

policeRoot.position = new BABYLON.Vector3.Lerp (policeRoot.position, playerRoot.position, policeSpeed);

When the lookAt() is utilized as well, making policeRoot mesh to look at the player- Root mesh, this causes the policeRoot mesh to continuously to adjust its rotation to- wards the direction where the playerRoot is at any given moment. When combining these two functionalities, the policeRoot both follows and turns as the playerRoot moves and switches position.

policeRoot.lookAt(playerRoot.position);

Now, what happens should the police car collide with something? In the base game the police object is capable of being destroyed by collisions with both the other cars on the highway and the road obstacles. Once a police car is destroyed in such a man- ner, another one is shortly spawned in to take its place chasing the player. Like with making the policeRoot follow the player, this behavior of being destroyed and res- pawned can easily be achieved with a couple of tricks and boolean checks. Firstly, much like with the other meshes, intersectsMesh checks should be created for colliding with the other cars and the road barriers. Upon colliding with either, the hasHit of policeRoot should be toggled to true, as well as the other colliding mesh’s if the mesh in question is another non-player car.

if (policeRoot.intersectsMesh(allCars[i], false) && allCars[i].hasHit == false) { allCars[i].hasHit = true; policeRoot.hasHit = true; }

70 With the hasHit check being set to true, a similar instance of the police car flying into the air spinning can be created as with the non-player cars. In addition to this, within this same clause a respawn timer for the police car can be set and enabled. Instead of destroying and re-creating the object, a trick can be made where the said timer is set and activated with BABYLON.setAndStartTimer. As the timeout value given to the timer reaches zero, the position of the policeRoot is then reset, with its positional z value set behind the player, and the hasHit check of the policeRoot set to false once more. With the hasHit check being reset, the policeRoot also returns to its normal behavior of lerping and looking at the playerRoot mesh, effectively creating the illusion of a new police car zooming in from outside of the screen, when in fact it is the same object that just collided. A simple trick, but it should save some perfor- mance by not destroying and instancing a new mesh every time the police car col- lides with something.

else if (policeRoot.hasHit == true) { policeRoot.rotation.y += 0.15; policeRoot.position.x -= 0.4; policeRoot.position.y += 0.4; policeSpeed = 0.08;

if (isCounting == false) { isCounting = true; BABYLON.setAndStartTimer({ timeout: 3000, contextObservable: scene.onBeforeRenderObservable, onEnded: () => { if (isCounting == true) { policeRoot.position.x = playerRoot.position.x; policeRoot.position.y = playerRoot.position.y; policeRoot.position.z = playerRoot.position.z - 40; policeRoot.hasHit = false; isCounting = false; } }, }); } }

71 Should the police car collide with the playerRoot, the collision will be handled in the same way as them colliding with a road obstacle, and this is by setting a gameOver boolean variable value to true which, as the name might imply, sets in motion the game over event of the game, which will be discussed later on.

if (playerRoot.intersectsMesh(policeRoot, false)) { playerSpeed = 0; gameOver = true; }

4.9 GUI

As both the winning and losing are in a sense tied to the GUI, it is only sensible to dis- cuss and set one up before moving to actual win and lose conditions. Defining and setting up an GUI is in a way simpler in Babylon.js than it is in Unity, as it can be done with a single line of code. Basically, a variable could be set for the GUI, but as the GUI will have more than one element in it, a list may be the better option. The GUI itself is created by using the BABYLON.GUI.AdvancedDynamicTexture.CreateFullScreenUI(), which indeed does create a full screen UI texture layer with one singular command. This UI can then be set to always inhabit the topmost layer, or the foreground of the view by setting the .isForeground option as true.

UI = {}; UI.advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreen UI("UI"); UI.advancedTexture.isForeground = true;

Within this UI element all of the other UI elements can then be added. So, what to add then? If one inspects the UI of the original game, at least four differ- ent elements can be seen; a progress bar that indicates the player’s progress throughout the stage, a list of wanted stars that increase as the player causes may-

72 hem by colliding with other cars, a counter for all of the coins the player has col- lected and lastly a pause button responsible for pausing and resuming the game. In addition to these, separate UI screen events exist for winning and losing the game, with two separate loss screens with different text images, depending on whether the player lost by colliding with a road barrier or if the chasing police car caught them. These image screens will however be added later on during the handling of win and lose scenarios.

At this point it may be worth noting that while the original game uses a spritesheet, an image file containing several different images with added data indicating the posi- tion and dimension of each of the images on the image arc, the spritesheet in ques- tion could not be used in this project. While Babylon.js does support spritesheets, the spritesheet from the original Highway Chase would not yield its data, and there- fore some of the elements had to be lifted and edited from the original spritesheet as separate images. Also, the positioning and the angle of the progress bar related images made it unnecessarily hard to lift and reuse them from the original sprite- sheet, so these images had to be left unused and alternatives be created for the pro- ject. Also while the original UI also possess a durability bar for the car collisions as well as a level text, these are purely logic-based functionalities and due to time constraints and not truly gauging the Babylon.js engine in any way, and are therefore left out of the project.

As the progress bar is easily the most dynamic and meaningful out of the three core UI elements, it should be the one discussed and set up first. The creation of a progress bar element can be handled in several different ways, ranging from sliders and image sliders, which the project might have used if the origi- nal images used in the base game were available, to shaped GUI elements with an- other shaped GUI elements used to fill them, which is the option the project leaned towards. Firstly, a parent outer box for the progress bar should be created and initialized. This is done by creating a new rectangular object into the GUI and then setting its width,

73 height, and the general position. The general shape of the rectangle can also be mod- ified by smoothing out its corners by adjusting the corner radius parameter. As the UI should be readable in multiple different resolutions, any strict and hard-set values should be avoided when setting its dimensions, and instead prefer the use of per- cent-based values. The visibility of the object should also be set and, as there will be another bar filling within the one being created, its background should be set as transparent to ensure that a container-like visual aesthetic is gained. After setting and adjusting the values, the control of the progress bar parent should then be handed over to the general UI element created earlier by using .addControl(). This ensures that the created element scales, moves and positions itself in relation the general, parenting UI element.

UI.progressBar = new BABYLON.GUI.Rectangle("ProgressBar"); UI.progressBar.width = "30%"; UI.progressBar.height = "3%"; UI.progressBar.cornerRadius = 5; UI.progressBar.verticalAlignment = 0; UI.progressBar.top = "5%"; UI.progressBar.left = "0%"; UI.progressBar.isVisible = true; UI.progressBar.background = "transparent"; UI.advancedTexture.addControl(UI.progressBar);

With the parent container for the progress bar initialized, the inner bar can then be created in a similar manner, but with a few key exceptions when it comes to setting the values. As the inner bar will be nested within the previously created bar, its height should be set to 100%, so that it will fill cleanly within the parenting bar, and the initial width set as 0, since this after all is the portion of the progress bar that tracks the player’s progress, naturally starting from 0. With the control of this inner progress bar given to the outer, any potential overflow and misalignment issues are neatly handled, as the GUI hierarchy does not allow a child object to display itself over the parenting object outside of the transparent por- tions of the parent object.

UI.progressBarInner = new BABYLON.GUI.Rectangle("ProgressBarFill"); UI.progressBarInner.width = 0;

74 UI.progressBarInner.height = "100%"; UI.progressBarInner.thickness = 0; UI.progressBarInner.horizontalAlignment = 0; UI.progressBarInner.isVisible = true; UI.progressBarInner.background = "#ff3300"; UI.progressBar.addControl(UI.progressBarInner);

Now all the progress bar still needs is a logic to measure its growth and tracking. Firstly, a finish line should be added into the game world, so that the end point can be gauged based on its position. Like with all the other meshes, a simple texture can be created and shaped to form a rudimentary finish line that is then positioned across the road. This line can then be set at any position along the level, depending on the desired level length, as this will serve as the end point of the level.

let finishLine = BABYLON.MeshBuilder.CreateBox("FinishLine", {height: 1, width: 19, depth : 2}, scene); finishLine.isVisible = true; finishLine.Color3 = "white"; finishLine.position.z = 700;

As the level now has an end, it also needs a beginning. This could also be set using a mesh node indicating its position, or since the start’s depth point is already known as the playerRoot is initialized at that position, the plain depth coordinate of that spot can be used. The third piece needed is the current position of the playerRoot, as this is the object which’s progress the bar will track. This can easily be acquired by checking it during the registerBeforeRender() function, where it is constantly being updated. With these three factors, it is now possible to a simple function to determine the current fill amount of the progress bar at any given time during the stage. The func- tion will calculate the fill amount by using the position of the playerRoot, deducting the start point position from it, then doing the same for the position of the finish line and then finally dividing the current position with the calculated finish line position. After this, the remainder is set as the value of UI.progressBarInner.width.

75 function getCurrentFill(){ if (playerRoot.position.z < finishLine.position.z) { let currentOffset = playerRoot.position.z - 0; let maximumOffSet = finishLine.position.z - 0; let fillAmount = currentOffset / maximumOffSet; UI.progressBarInner.width = fillAmount; } }

The next UI element to be handled should be the so-called wanted stars. As previ- ously mentioned, these are images that pop up when the player collides with the other cars on the road. The base images for the stars are firstly lifted and edited from the original sprite- sheet as .png files in such a manner that they are as uniform in the base size and alignment as possible, and so that their backgrounds are transparent. As there are two types of wanted stars, “active” and “blank”, two different images have to be loaded into the project and set as a part of the UI. As with the previous UI elements, their alignment and size are then set. As a new parameter, the stretch type of the im- age can also be taken into consideration. Babylon.js offers several different pre-set stretch behaviors for GUI elements such as images, and to ensure that the image scaling does not get broken in any point or in any screen size, the .stretch option should be set as .STRETCH_UNIFORM.

UI.wantedStarBlank = new BABYLON.GUI.Image("BlankStar", "Sprites/WantedStarBlankWOBG.png"); UI.wantedStarBlank.alpha = wantedStarBlankAlpha; UI.wantedStarBlank.width = "4%"; UI.wantedStarBlank.height = "4%"; UI.wantedStarBlank.stretch = BABYLON.GUI.Image.STRETCH_UNIFORM; UI.wantedStarBlank.top = "-39%"; UI.wantedStarBlank.left = "-20%"; UI.advancedTexture.addControl(UI.wantedStarBlank);

As a total of ten star images are needed, five for the base blank stars and five for the stars that will appear on top of these blank, they all need to go through this process, with the horizontal position being adjusted for each so that they appear side to side

76 and that the “active” stars are correctly positioned over the corresponding blank stars. The next step after this is to create a logic for how the active stars actually acti- vate, as they are set as invisible at the start. The simplest way to achieve this would be to create a simple if-check by creating and utilizing a variable for counting the cars the player hits and determining the number of stars to be shown based on this varia- ble value.

if (carsHit >= 2 && carsHit < 5) { UI.wantedStar2.isVisible = true; } else if (carsHit >= 5 && carsHit < 7) { UI.wantedStar3.isVisible = true; } else if (carsHit >= 7 && carsHit < 9) { UI.wantedStar4.isVisible = true; } else if (carsHit >= 9) { UI.wantedStar5.isVisible = true; }

After the progress bar and the wanted stars have been set, the last item on the UI agenda is the visual tracking of the number of coins the player collects throughout the stage. Firstly, a coin image should be loaded to serve as a clear indicator for what the text block next to it will contain.

UI.coinIcon = new BABYLON.GUI.Image("CoinIcon", "Sprites/UICoinWOBG.png"); UI.coinIcon.width = "5%"; UI.coinIcon.height = "5%"; UI.coinIcon.stretch = BABYLON.GUI.Image.STRETCH_UNIFORM; UI.coinIcon.verticalAlignment = 0; UI.coinIcon.top = "10%"; UI.coinIcon.left = "20%"; UI.advancedTexture.addControl(UI.coinIcon);

77 Next, a text block will be created next to the coin image. It should be noted that while .addControl() can be used with GUI elements such as shapes, an image cannot be given the control of another element. Same goes for text blocks and therefore po- sitioning the coin text block to align next to the coin image in no matter which screen size is difficult at best. As the base game uses a custom font, SquadaOne, for the coin text, one should be loaded and assigned to the project as well. For this, a “Fonts” folder should be cre- ated in the project’s file structure and the font placed there. After this is done, the index file should be modified so that the font is imported into the project using @font-face within the