Latest Posts (20 found)
JSLegendDev 1 weeks ago

The Phaser Game Framework in 5 Minutes

Phaser is the most popular JavaScript/TypeScript framework for making 2D games. It’s performant, and popular games like Vampire Survivors and PokéRogue were made with it. Because it’s a web-native framework, games made with Phaser are lightweight and generally load and run better on the web than the web exports produced by major game engines. For that reason, if you’re looking to make 2D web games, Phaser is a great addition to your toolbelt. In this post, I’ll explain the framework’s core concepts in around 5 minutes. — SPONSORED SEGMENT — In case you want to bring your web game to desktop platforms, today’s sponsor GemShell , allows you to build executables for Windows/Mac/Linux in what amounts to a click. It also makes Steam integration easy. For more info, visit 👉 https://l0om.itch.io/gemshell You have a tool/product you want featured in a sponsored segment? Contact me at [email protected] A Phaser project starts with defining a config to describe how the game’s canvas should be initialized. To make the game scale according to the window’s size, we can set the scale property in our config. The mode property is set to FIT, so the canvas scales while preserving its own aspect ratio. As for keeping the canvas centered on the page, the autoCenter property is used with the CENTER_BOTH value. Most games are composed of multiple scenes and switching between them is expected during the course of gameplay. Since Phaser uses the object oriented paradigm, a scene is created by defining a class that inherits from the Phaser.Scene class. To be able to reference the scene elsewhere in our code, it’s important to give it a key. For this purpose and for being able to use the methods and properties of the parent class we need to call the super constructor and pass to it the key we want to use. The two most important methods of a Phaser scene are the and methods. The first is used for, among other things, creating game objects like text and sprites and setting things like scores. It runs once, every time the scene becomes active. The latter which runs once per frame is, for example, used to handle movement logic. Once a scene is created, we still need to add it to our game. This is done in the Phaser config under a property called scenes, which expects an array. The order of scenes in this array is important. The first element will be used as the default scene of the game. To switch scenes, we can call the method of the scene manager. Before we can render sprites, we need to load them. For this purpose, a Phaser scene has access to the method where asset loading logic should be placed. To load an image, we can use the image method of the loader plugin. Then, in the method, we can render a sprite by calling the method of the Game Object Factory plugin. The first two params are for specifying the X and Y coordinates while the third param is for providing the key of the sprite to render. Because we created our sprite game object in the method, we don’t have access to it in our method, that’s why you’ll often see the pattern of assigning a game object to an instance field so it becomes accessible to other methods of the scene. Finally, movement logic code is placed in the method which runs every frame. Rendering text is similar to sprites. Rather than the using the method, we use the method. If you want to hold data or define custom methods for a sprite game object, a better approach is to define a class that inherits from the Phaser.GameObject.Sprite class. Once the class is defined, we can use it in our scene’s code. While asset loading can be done in any Phaser scene, a better approach is to create a scene dedicated to loading assets, which then switches to the main game scene once loading is complete. This can be achieved like shown below : Another important aspect of any game is the ability to play animations. Usually for 2D games, we have spritesheets containing all the needed frames to animate a character in a single image. An example of a spritesheet In Phaser, we first specify the dimensions of a frame in the loading logic of the spritesheet so that the framework knows how to slice the image into individual frames. Then, we can create an animation by defining its starting and ending frames. To provide the needed frames we call the method of Phaser’s animation manager. Finally once the animation is created, it can be played by using the method of the sprite game object. If you want the animation to loop back indefinitely, add the repeat property and set it to -1. A game needs to be interactive to be called a “game”. One way to handle input is by using event listeners provided by Phaser. For keyboard input, we can use : And for handling mouse and touch input we can use . At one point, you might need to share data between scenes. For this purpose, you can use Phaser’s registry. Here is an example of its usage. To play sounds (assuming you have already loaded the sound first) you can use the method of the sound manager. You can specify the sound’s volume in the second param of that method. If you need to be able to stop, pause or play the same sound at a later time, you can add it to the sound manager rather than playing it immediately. This comes in handy when you transition from one scene to another and you have a sound that loops indefinitely. In that case, you need to stop the sound before switching over otherwise the sound will keep playing in the next scene. By default, Phaser offers an Arcade physics system which is not meant for complex physics simulations. However, it’s well suited for most types of games. To enable it, you can add the following to your Phaser config. You can add an existing game object to the physics system the same way you add one to a scene. This will create a physics body for that game object which is accessible with the body instance field. You can view this body as a hitbox around your sprite if you turn on the debug mode in your project’s config. Example of a Phaser game with debug set to true To create bodies that aren’t affected by gravity, like platforms, you can create a static group and then create and add static bodies to that group. Here’s an example : You can also add already existing physics bodies to a group. Now, you might be wondering what groups are useful for? They shine in collision handling logic. Let’s assume you have multiple enemies attacking the player. To determine when a collision occurs between any enemy and the player, you can set up the following collision handler : There are many concepts I did not have time to cover. If you want to delve further into Phaser, I have a project based course you can purchase where I guide you through the process of building a Sonic themed infinite runner game. This is a great opportunity to put in practice what you’ve learned here. If you’re interested, here’s the link to the course : https://www.patreon.com/posts/learn-phaser-4-147473030 . That said, you can freely play the game being built in the course as well as have access to the final source code. Original Phaser game live demo : https://jslegend.itch.io/sonic-ring-run-phaser-4 Demo of the version built in the course : https://jslegend.itch.io/sonic-runner-tutorial-build Final source code : https://github.com/JSLegendDev/sonic-runner-phaser-tutorial If you enjoy technical posts like this one, I recommend subscribing to not miss out on future releases. Subscribe now In the meantime, you can read the following : Phaser is the most popular JavaScript/TypeScript framework for making 2D games. It’s performant, and popular games like Vampire Survivors and PokéRogue were made with it. Because it’s a web-native framework, games made with Phaser are lightweight and generally load and run better on the web than the web exports produced by major game engines. For that reason, if you’re looking to make 2D web games, Phaser is a great addition to your toolbelt. In this post, I’ll explain the framework’s core concepts in around 5 minutes. — SPONSORED SEGMENT — In case you want to bring your web game to desktop platforms, today’s sponsor GemShell , allows you to build executables for Windows/Mac/Linux in what amounts to a click. It also makes Steam integration easy. For more info, visit 👉 https://l0om.itch.io/gemshell You have a tool/product you want featured in a sponsored segment? Contact me at [email protected] The Phaser Config A Phaser project starts with defining a config to describe how the game’s canvas should be initialized. To make the game scale according to the window’s size, we can set the scale property in our config. The mode property is set to FIT, so the canvas scales while preserving its own aspect ratio. As for keeping the canvas centered on the page, the autoCenter property is used with the CENTER_BOTH value. Scene Creation Most games are composed of multiple scenes and switching between them is expected during the course of gameplay. Since Phaser uses the object oriented paradigm, a scene is created by defining a class that inherits from the Phaser.Scene class. To be able to reference the scene elsewhere in our code, it’s important to give it a key. For this purpose and for being able to use the methods and properties of the parent class we need to call the super constructor and pass to it the key we want to use. The two most important methods of a Phaser scene are the and methods. The first is used for, among other things, creating game objects like text and sprites and setting things like scores. It runs once, every time the scene becomes active. The latter which runs once per frame is, for example, used to handle movement logic. Hooking Up a Scene to Our Game Once a scene is created, we still need to add it to our game. This is done in the Phaser config under a property called scenes, which expects an array. The order of scenes in this array is important. The first element will be used as the default scene of the game. Switching Scenes To switch scenes, we can call the method of the scene manager. Rendering Sprites Before we can render sprites, we need to load them. For this purpose, a Phaser scene has access to the method where asset loading logic should be placed. To load an image, we can use the image method of the loader plugin. Then, in the method, we can render a sprite by calling the method of the Game Object Factory plugin. The first two params are for specifying the X and Y coordinates while the third param is for providing the key of the sprite to render. Because we created our sprite game object in the method, we don’t have access to it in our method, that’s why you’ll often see the pattern of assigning a game object to an instance field so it becomes accessible to other methods of the scene. Finally, movement logic code is placed in the method which runs every frame. Rendering Text Rendering text is similar to sprites. Rather than the using the method, we use the method. Entity Creation If you want to hold data or define custom methods for a sprite game object, a better approach is to define a class that inherits from the Phaser.GameObject.Sprite class. Once the class is defined, we can use it in our scene’s code. Asset Loading While asset loading can be done in any Phaser scene, a better approach is to create a scene dedicated to loading assets, which then switches to the main game scene once loading is complete. This can be achieved like shown below : Animation API Another important aspect of any game is the ability to play animations. Usually for 2D games, we have spritesheets containing all the needed frames to animate a character in a single image. An example of a spritesheet In Phaser, we first specify the dimensions of a frame in the loading logic of the spritesheet so that the framework knows how to slice the image into individual frames. Then, we can create an animation by defining its starting and ending frames. To provide the needed frames we call the method of Phaser’s animation manager. Finally once the animation is created, it can be played by using the method of the sprite game object. If you want the animation to loop back indefinitely, add the repeat property and set it to -1. Input Handling A game needs to be interactive to be called a “game”. One way to handle input is by using event listeners provided by Phaser. For keyboard input, we can use : And for handling mouse and touch input we can use . Sharing Data Between Scenes At one point, you might need to share data between scenes. For this purpose, you can use Phaser’s registry. Here is an example of its usage. Playing Sound To play sounds (assuming you have already loaded the sound first) you can use the method of the sound manager. You can specify the sound’s volume in the second param of that method. If you need to be able to stop, pause or play the same sound at a later time, you can add it to the sound manager rather than playing it immediately. This comes in handy when you transition from one scene to another and you have a sound that loops indefinitely. In that case, you need to stop the sound before switching over otherwise the sound will keep playing in the next scene. Physics, Debug Mode, Physics Bodies and Collision Logic By default, Phaser offers an Arcade physics system which is not meant for complex physics simulations. However, it’s well suited for most types of games. To enable it, you can add the following to your Phaser config. You can add an existing game object to the physics system the same way you add one to a scene. This will create a physics body for that game object which is accessible with the body instance field. You can view this body as a hitbox around your sprite if you turn on the debug mode in your project’s config. Example of a Phaser game with debug set to true To create bodies that aren’t affected by gravity, like platforms, you can create a static group and then create and add static bodies to that group. Here’s an example : You can also add already existing physics bodies to a group. Now, you might be wondering what groups are useful for? They shine in collision handling logic. Let’s assume you have multiple enemies attacking the player. To determine when a collision occurs between any enemy and the player, you can set up the following collision handler : Project Based Tutorial There are many concepts I did not have time to cover. If you want to delve further into Phaser, I have a project based course you can purchase where I guide you through the process of building a Sonic themed infinite runner game. This is a great opportunity to put in practice what you’ve learned here. If you’re interested, here’s the link to the course : https://www.patreon.com/posts/learn-phaser-4-147473030 . That said, you can freely play the game being built in the course as well as have access to the final source code. Original Phaser game live demo : https://jslegend.itch.io/sonic-ring-run-phaser-4 Demo of the version built in the course : https://jslegend.itch.io/sonic-runner-tutorial-build Final source code : https://github.com/JSLegendDev/sonic-runner-phaser-tutorial

0 views
JSLegendDev 1 weeks ago

Learn Phaser 4 by Building a Sonic Themed Infinite Runner Game in JavaScript

Phaser is the most popular JavaScript/TypeScript framework for making 2D games. It is performant and popular games like Vampire Survivors and Pokérogue were made with it. Because it’s a web-native framework, games built with it are lightweight and generally load and run better on the web than web exports produced by major game engines. For this reason, if you’re a web developer looking to make 2D web games, Phaser is a great addition to your toolbelt. To make the process of learning Phaser easier, I have released a course that takes you through the process of building a Sonic themed infinite runner game with Phaser 4 and JavaScript. You can purchase the course here : h ttps://www.patreon.com/posts/learn-phaser-4-147473030 . Total length of the course is 1h 43min. More details regarding content and prerequisites are included in the link. That said, you can freely play the game being built in the course as well as have access to the final source code. Original Phaser game live demo : https://jslegend.itch.io/sonic-ring-run-phaser-4 Demo of the version built in the course : https://jslegend.itch.io/sonic-runner-tutorial-build Final source code : https://github.com/JSLegendDev/sonic-runner-phaser-tutorial Phaser is the most popular JavaScript/TypeScript framework for making 2D games. It is performant and popular games like Vampire Survivors and Pokérogue were made with it. Because it’s a web-native framework, games built with it are lightweight and generally load and run better on the web than web exports produced by major game engines. For this reason, if you’re a web developer looking to make 2D web games, Phaser is a great addition to your toolbelt. To make the process of learning Phaser easier, I have released a course that takes you through the process of building a Sonic themed infinite runner game with Phaser 4 and JavaScript. You can purchase the course here : h ttps://www.patreon.com/posts/learn-phaser-4-147473030 . Total length of the course is 1h 43min. More details regarding content and prerequisites are included in the link. That said, you can freely play the game being built in the course as well as have access to the final source code. Original Phaser game live demo : https://jslegend.itch.io/sonic-ring-run-phaser-4 Demo of the version built in the course : https://jslegend.itch.io/sonic-runner-tutorial-build Final source code : https://github.com/JSLegendDev/sonic-runner-phaser-tutorial

0 views
JSLegendDev 1 months ago

The Programmer's Roller Coaster (Comic)

Hope you enjoyed this little comic! I usually post articles about game development and tutorials. However, I wanted to try something different this time, as I think there’s a lot of potential in making comics that cover topics like software development or game development. I would like to eventually make an even more story-driven comic (I think the right term might be graphic novel). That said, I need to start small to gain stamina for a bigger project. I think the advice of “making small” game projects also applies to comics. Anyway, my plan is to continue posting comics alongside my other content. If you want to be notified when I post something new, I recommend subscribing. Subscribe now In the meantime, you can read some of my previous posts.

1 views
JSLegendDev 1 months ago

Why Text in Vampire Survivors Used to Look Like This

In 2022, Vampire Survivors, a game where you destroy hordes of enemies by just moving around, released. It ended up becoming extremely popular and spawning a whole genre by itself. At the time, I was working as a software developer for a company who’s product was a complex web application. Therefore, I became a web developer. I wasn’t really interested in game development as the working conditions and pay were known to be less than stellar. However, I quickly realized that the tools used to make web applications could also be used to develop games. Since I could make games with the skills and tooling I was already familiar with, I decided to try it out as a hobby to see if I’d enjoy it. As time passed, I got interested in the various ways one could use a web developer’s skill set to make games. After some research, I found out that Vampire Survivors was originally made in JavaScript (the programming language that is natively supported in all web browsers) using a game framework called Phaser . A game framework is essentially a game engine without the UI. While this is not the case for all game frameworks, it sure was for Phaser because it packed a lot of features out of the box. You didn’t have to reinvent the wheel. I wanted to give Phaser a try after seeing high profile games made with it, like Vampire Survivors and PokéRogue (A Pokémon Roguelike fan game). However, as I started my journey to learn this framework, I quickly gave up because the documentation was confusing. You also needed to write a lot more code to achieve the same results as the alternative I was already using called KAPLAY . I therefore, stuck with it leaving Phaser behind until some time had passed. As I got more comfortable in my game dev journey, I now wanted to make a small 2D RPG game with a combat system similar to Undertale. In my game, you would avoid projectiles and attack by stepping on attack zones. The player would be expected to learn to dodge various attack patterns. I also, wanted to publish the game on Steam. Prior to this, all games I made were mostly playable on the web. — SPONSORED SEGMENT — Speaking of publishing a game on Steam, since games made in JavaScript run in the browser you might be wondering how it’s possible to release them on Steam? For this purpose, most JavaScript-based games are packaged as desktop downloadable apps via technologies like Electron or NW.js. The packaging process consists in including a full Chromium browser alongside the game which results in bloated app sizes. However, technologies like Tauri offer a different approach. Instead of packaging a full browser alongside your app, it uses the Web engine already installed on your computer’s operating system. This results in leaner build sizes. That said, regardless of the technology used, you’ll have to spend a considerable amount of time setting it up before being able to export your game for Windows, Mac and Linux. In addition, extra work will be required for integrating the Steamworks API which allows implementing, among other things, Steam achievements and cloud saves which are features players have come to expect. If only there were a tool that made both the packaging process and the Steamworks API integration seamless. Fortunately, that tool exists, and is today’s sponsor : GemShell . GemShell allows you to package your JavaScript-based games for Windows, Mac and Linux in what amounts to a single click. It: Produces tiny executables with near-instant startup, avoiding the Chromium bloat by using the system’s WebView. Provides full access to Steamworks directly via an intuitive JavaScript API. Has built-in asset encryption to protect your code. Offers native capabilities allowing you to access the host’s file system. For more info, visit 👉 https://gemshell.dev/ To get the tool, visit 👉 https://l0om.itch.io/gemshell You have a tool/product you want featured in a sponsored segment? Contact me at [email protected] I got started working on my RPG project and things were progressing pretty smoothly until I ran into performance issues. By this point, I’d been developing the game in secret and wasn’t planning to reveal it yet. But I realized I could gather valuable performance feedback by sharing my progress publicly, so I decided to do just that. It turned out that, while KAPLAY was easy to learn to make games in, it was unfortunately not performant enough. FPS would tank in the bullet hell sections of my game. After doing all kinds of optimizations, I got the frame rate to be good enough but didn’t feel confident it would remain that way as I continued development. I initially thought of moving forward regardless but quickly changed my mind thinking of all the potential negative reviews I would get on Steam for poor performance. This led me to halt my game’s development. I needed to learn a more performant game framework or game engine. Unfortunately, this meant I’d have to restart making my game from scratch. At least, I could use the same assets. Among the options I was considering learning were Phaser and the Godot game engine. Since my game was made in JavaScript, I thought trying to learn Phaser again would save me time because I wouldn’t need to learn a different programming language and development environment. I would potentially also be able to reuse code I had already written for my game. Also, Phaser was the most mature and battle-tested option in the JavaScript game dev space as well as one of the most performant. Although I didn’t like how using Phaser would result in very verbose code, the perceived advantages in my situation, outweighed the cons. However, one thing that irked me with Phaser, looking at the many games showcased on its official website, was that all of them had blurry text or text with weird artifacts. This was also true for Vampire Survivors and for PokéRogue, if you looked closely enough. Text in Vampire Survivors. Text in Pokérogue. Weird outline around the text. Arrow placed by myself. Close up. While some may consider this a small detail, it annoyed me to no end. I almost gave up on Phaser again and even started to look for alternatives. Yet, what kept me going was that, in my previous attempt at learning Phaser, I had started working on a remake of my infinite-runner Sonic fan game which was originally made with KAPLAY. I remembered that I had pushed the project on GitHub and left it abandoned. Pulling the project again, I noticed that I had already made significant progress and that the font used didn’t produce any weird artifacts. I thought that I could get around the artifact problem by just carefully selecting which font to use for my games. Because of this, I continued working on the project, learning Phaser quickly in the process. There’s something to be said about how quickly you can learn a new technology by rebuilding a project you’ve already created. Since you already know exactly what the end result should look like, you can focus on understanding the key concepts of the technology you want to learn. Because each step in rebuilding the project is concrete, you know exactly what to search for. It’s now just a matter of translating between how things are done in the technology you already know VS the one you’re trying to learn. The rebuild of my Sonic fan game was nearing completion when I needed to display text elsewhere in the game at a different font size. To my dismay, when rendering the font at a smaller size, the artifact problem, which I thought was gone (at least with the font I was using), reared its ugly head again. This was a catastrophe. I was already knee-deep with Phaser and didn’t want to switch again. None of the Phaser games I knew of had clean text, so I assumed it was something inherent to the framework. I couldn’t believe I had forgotten to check how text was rendered at different sizes before committing to Phaser. In hindsight, it was a pretty stupid move, and I felt like I had wasted a lot of time for little benefit. Artifacts present in my Sonic game. At this point, the proper thing to do would have been to cut my losses and moved on to something else. However, afflicted by the sunk cost fallacy , I thought that there was no way I would give up now. Not after having spent this much time learning Phaser. I needed to fix this issue, come hell or high water. After some research, I figured out that the reason my text had artifacts, was because of my use of Phaser’s pixel art option. Since I was making a game with pixel art graphics, this option was needed so that my game sprites would scale without becoming blurry. Phaser would achieve this by applying a filtering method called Nearest-Neighbor. The issue, however, was that it wouldn’t only apply this to sprites, it also affected fonts, causing artifacts to appear around the text, since fonts don’t scale the same way. Since Vampire Survivors also used pixel art graphics, it now made sense why the text used to look so weird. The developer probably just applied the pixel art option and called it a day. By the way, the font used in the game is Phaser’s default font, Courier, which is also a font available on many electronic devices by default. It seems like the dev really didn’t care much about this aspect of the game’s presentation. Now that the game has been moved to Unity, text is rendered properly, though still using Courier, as it has become part of the game’s visual style. However, disabling the pixel art option wouldn’t solve the issue. Text would still render blurry by default, and the sprites would remain blurry as well. How the game looks without the pixel art option set to true. Fortunately, there was a fix for the font blurriness. The text method allowed setting the resolution of the rendered text. Setting the resolution to 4 made it render clearly. Still, this didn’t help in the end because I still needed the pixel art option for my sprites. How the game looks without the pixel art option + text resolution set to 4. A potential solution to my problem would have been to use a bitmap font, also known as a sprite font. As the name implies, instead of a .ttf font, which most people are accustomed to, a sprite font is stored as an image containing a sprite for each letter. This allows the font to render properly, scaling like other sprites when the pixel art option is enabled. That said, this solution was a non-starter because each character was a fixed image. I would lose out on the flexibility of a .ttf font that allows for text to be set to italic, bold, etc… I didn’t want to lose on that flexibility and didn’t want the hassle of converting my existing .ttf font. At this point, I couldn’t believe how much effort was needed just to do a simple thing like render text properly. In frustration, I decided to open the Godot game engine and figure out how to render text just to compare with what I currently had. As expected, I got the font to render nicely automatically. I was tired of having to do this much work for something that I would’ve gotten for free in Godot, this couldn’t go on. How text is rendered in Godot. I needed to take a break, so I stepped away from the computer, and the next day, I had an epiphany. Instead of using the pixel art option in Phaser, what if I could apply the Nearest-Neighbor filtering method to each sprite individually? I looked it up, and indeed, it was possible. Using this approach in conjunction with increasing the text resolution to 4 and voilà, I had both non-blurry pixel art and text that rendered clearly without weird artifacts. How the game looks with each sprites set to Nearest filtering individually + text resolution set to 4. My problem was fixed, but at what cost? I had wasted an enormous amount of time on something that was given for free in a game engine like Godot. I started to consider that going with Phaser was probably a huge blunder. Seeing how quickly I learned Phaser using this approach of remaking one of my previous projects, it hit me, what if I tried learning Godot the same way? Without waiting further, I jumped into Godot. As expected, it was very easy to learn considering I knew exactly what to search since my project’s requirements were crystal clear. It was only a matter of looking up how things were done in Godot for each piece of functionality I needed to implement. GDScript, the programming language used in that engine, was also very easy to pick up and felt like Python which I already had experience in. Godot version of my game. After completing the project, I now had a better view of Godot. Things were intuitive, the code was not verbose and it was an all around nice experience. Before trying the engine, I had the preconception that it no longer supported web export due to the transition from version 3 to 4. I’m happy to report that this is no longer the case. I’ve been using version 4.5, and exporting to the web is just as straightforward as exporting to desktop. Godot web export setup In conclusion, I think Godot is the right choice for my RPG project. I also want to gain mastery of the engine so I can eventually experiment with creating a game that uses 3D models for environments and props, but 2D sprites for characters. Godot makes developing this kind of game far less daunting. This style has been popularized recently by Octopath Traveler using the term HD-2D, although my real inspiration comes from the DS Pokémon era, which used a similar aesthetic without the over-the-top post-processing effects seen in Octopath Traveler. Screenshot from Octopath Traveler. Screenshot from Pokémon Diamond released on the Nintendo DS. That said, I’m now presented with two choices: Make multiple tiny games in Godot to become more proficient before restarting the development of my game. Jump into development headfirst and learn what I need along the way, just like I did for the Sonic project. I’m still thinking about it, but I’d appreciate your input. Anyway, if you want to try both the Phaser and the Godot versions of my Sonic game, they’re both available to play on the web. Here are the links: Phaser 4 version : https://jslegend.itch.io/sonic-ring-run-phaser-4 Godot 4 version : https://jslegend.itch.io/sonic-ring-run-godot-version I hope you enjoyed my little game dev adventure. If you’re curious about the small RPG I’m working on, feel free to read my previous post on the topic. If you’re interested in game development or want to keep up with updates regarding my small RPG, I recommend subscribing to not miss out on future posts and updates. Subscribe now In 2022, Vampire Survivors, a game where you destroy hordes of enemies by just moving around, released. It ended up becoming extremely popular and spawning a whole genre by itself. At the time, I was working as a software developer for a company who’s product was a complex web application. Therefore, I became a web developer. I wasn’t really interested in game development as the working conditions and pay were known to be less than stellar. However, I quickly realized that the tools used to make web applications could also be used to develop games. Since I could make games with the skills and tooling I was already familiar with, I decided to try it out as a hobby to see if I’d enjoy it. As time passed, I got interested in the various ways one could use a web developer’s skill set to make games. After some research, I found out that Vampire Survivors was originally made in JavaScript (the programming language that is natively supported in all web browsers) using a game framework called Phaser . A game framework is essentially a game engine without the UI. While this is not the case for all game frameworks, it sure was for Phaser because it packed a lot of features out of the box. You didn’t have to reinvent the wheel. I wanted to give Phaser a try after seeing high profile games made with it, like Vampire Survivors and PokéRogue (A Pokémon Roguelike fan game). However, as I started my journey to learn this framework, I quickly gave up because the documentation was confusing. You also needed to write a lot more code to achieve the same results as the alternative I was already using called KAPLAY . I therefore, stuck with it leaving Phaser behind until some time had passed. As I got more comfortable in my game dev journey, I now wanted to make a small 2D RPG game with a combat system similar to Undertale. In my game, you would avoid projectiles and attack by stepping on attack zones. The player would be expected to learn to dodge various attack patterns. I also, wanted to publish the game on Steam. Prior to this, all games I made were mostly playable on the web. — SPONSORED SEGMENT — Speaking of publishing a game on Steam, since games made in JavaScript run in the browser you might be wondering how it’s possible to release them on Steam? For this purpose, most JavaScript-based games are packaged as desktop downloadable apps via technologies like Electron or NW.js. The packaging process consists in including a full Chromium browser alongside the game which results in bloated app sizes. However, technologies like Tauri offer a different approach. Instead of packaging a full browser alongside your app, it uses the Web engine already installed on your computer’s operating system. This results in leaner build sizes. That said, regardless of the technology used, you’ll have to spend a considerable amount of time setting it up before being able to export your game for Windows, Mac and Linux. In addition, extra work will be required for integrating the Steamworks API which allows implementing, among other things, Steam achievements and cloud saves which are features players have come to expect. If only there were a tool that made both the packaging process and the Steamworks API integration seamless. Fortunately, that tool exists, and is today’s sponsor : GemShell . GemShell allows you to package your JavaScript-based games for Windows, Mac and Linux in what amounts to a single click. It: Produces tiny executables with near-instant startup, avoiding the Chromium bloat by using the system’s WebView. Provides full access to Steamworks directly via an intuitive JavaScript API. Has built-in asset encryption to protect your code. Offers native capabilities allowing you to access the host’s file system. Text in Vampire Survivors. Text in Pokérogue. Weird outline around the text. Arrow placed by myself. Close up. While some may consider this a small detail, it annoyed me to no end. I almost gave up on Phaser again and even started to look for alternatives. Yet, what kept me going was that, in my previous attempt at learning Phaser, I had started working on a remake of my infinite-runner Sonic fan game which was originally made with KAPLAY. I remembered that I had pushed the project on GitHub and left it abandoned. Pulling the project again, I noticed that I had already made significant progress and that the font used didn’t produce any weird artifacts. I thought that I could get around the artifact problem by just carefully selecting which font to use for my games. Because of this, I continued working on the project, learning Phaser quickly in the process. There’s something to be said about how quickly you can learn a new technology by rebuilding a project you’ve already created. Since you already know exactly what the end result should look like, you can focus on understanding the key concepts of the technology you want to learn. Because each step in rebuilding the project is concrete, you know exactly what to search for. It’s now just a matter of translating between how things are done in the technology you already know VS the one you’re trying to learn. The rebuild of my Sonic fan game was nearing completion when I needed to display text elsewhere in the game at a different font size. To my dismay, when rendering the font at a smaller size, the artifact problem, which I thought was gone (at least with the font I was using), reared its ugly head again. This was a catastrophe. I was already knee-deep with Phaser and didn’t want to switch again. None of the Phaser games I knew of had clean text, so I assumed it was something inherent to the framework. I couldn’t believe I had forgotten to check how text was rendered at different sizes before committing to Phaser. In hindsight, it was a pretty stupid move, and I felt like I had wasted a lot of time for little benefit. Artifacts present in my Sonic game. At this point, the proper thing to do would have been to cut my losses and moved on to something else. However, afflicted by the sunk cost fallacy , I thought that there was no way I would give up now. Not after having spent this much time learning Phaser. I needed to fix this issue, come hell or high water. After some research, I figured out that the reason my text had artifacts, was because of my use of Phaser’s pixel art option. Since I was making a game with pixel art graphics, this option was needed so that my game sprites would scale without becoming blurry. Phaser would achieve this by applying a filtering method called Nearest-Neighbor. The issue, however, was that it wouldn’t only apply this to sprites, it also affected fonts, causing artifacts to appear around the text, since fonts don’t scale the same way. Since Vampire Survivors also used pixel art graphics, it now made sense why the text used to look so weird. The developer probably just applied the pixel art option and called it a day. By the way, the font used in the game is Phaser’s default font, Courier, which is also a font available on many electronic devices by default. It seems like the dev really didn’t care much about this aspect of the game’s presentation. Now that the game has been moved to Unity, text is rendered properly, though still using Courier, as it has become part of the game’s visual style. However, disabling the pixel art option wouldn’t solve the issue. Text would still render blurry by default, and the sprites would remain blurry as well. How the game looks without the pixel art option set to true. Fortunately, there was a fix for the font blurriness. The text method allowed setting the resolution of the rendered text. Setting the resolution to 4 made it render clearly. Still, this didn’t help in the end because I still needed the pixel art option for my sprites. How the game looks without the pixel art option + text resolution set to 4. A potential solution to my problem would have been to use a bitmap font, also known as a sprite font. As the name implies, instead of a .ttf font, which most people are accustomed to, a sprite font is stored as an image containing a sprite for each letter. This allows the font to render properly, scaling like other sprites when the pixel art option is enabled. That said, this solution was a non-starter because each character was a fixed image. I would lose out on the flexibility of a .ttf font that allows for text to be set to italic, bold, etc… I didn’t want to lose on that flexibility and didn’t want the hassle of converting my existing .ttf font. At this point, I couldn’t believe how much effort was needed just to do a simple thing like render text properly. In frustration, I decided to open the Godot game engine and figure out how to render text just to compare with what I currently had. As expected, I got the font to render nicely automatically. I was tired of having to do this much work for something that I would’ve gotten for free in Godot, this couldn’t go on. How text is rendered in Godot. I needed to take a break, so I stepped away from the computer, and the next day, I had an epiphany. Instead of using the pixel art option in Phaser, what if I could apply the Nearest-Neighbor filtering method to each sprite individually? I looked it up, and indeed, it was possible. Using this approach in conjunction with increasing the text resolution to 4 and voilà, I had both non-blurry pixel art and text that rendered clearly without weird artifacts. How the game looks with each sprites set to Nearest filtering individually + text resolution set to 4. My problem was fixed, but at what cost? I had wasted an enormous amount of time on something that was given for free in a game engine like Godot. I started to consider that going with Phaser was probably a huge blunder. Seeing how quickly I learned Phaser using this approach of remaking one of my previous projects, it hit me, what if I tried learning Godot the same way? Without waiting further, I jumped into Godot. As expected, it was very easy to learn considering I knew exactly what to search since my project’s requirements were crystal clear. It was only a matter of looking up how things were done in Godot for each piece of functionality I needed to implement. GDScript, the programming language used in that engine, was also very easy to pick up and felt like Python which I already had experience in. Godot version of my game. After completing the project, I now had a better view of Godot. Things were intuitive, the code was not verbose and it was an all around nice experience. Before trying the engine, I had the preconception that it no longer supported web export due to the transition from version 3 to 4. I’m happy to report that this is no longer the case. I’ve been using version 4.5, and exporting to the web is just as straightforward as exporting to desktop. Godot web export setup In conclusion, I think Godot is the right choice for my RPG project. I also want to gain mastery of the engine so I can eventually experiment with creating a game that uses 3D models for environments and props, but 2D sprites for characters. Godot makes developing this kind of game far less daunting. This style has been popularized recently by Octopath Traveler using the term HD-2D, although my real inspiration comes from the DS Pokémon era, which used a similar aesthetic without the over-the-top post-processing effects seen in Octopath Traveler. Screenshot from Octopath Traveler. Screenshot from Pokémon Diamond released on the Nintendo DS. That said, I’m now presented with two choices: Make multiple tiny games in Godot to become more proficient before restarting the development of my game. Jump into development headfirst and learn what I need along the way, just like I did for the Sonic project. Phaser 4 version : https://jslegend.itch.io/sonic-ring-run-phaser-4 Godot 4 version : https://jslegend.itch.io/sonic-ring-run-godot-version

1 views
JSLegendDev 1 months ago

Making a Small RPG

I’ve always wanted to try my hand making an RPG but always assumed it would take too much time. However, I didn’t want to give up before trying so I started to think of ways I could still make something compelling in 1-2 months. To help me come up with something, I decided to look into older RPGs as I had a hunch they could teach me a lot about scoping because back in the 80s, games were small because of technical limitations. A game that particularly caught my attention was the first Dragon Quest. This game was very important because it popularized the RPG genre in Japan by simplifying the formula therefore, making it more accessible. It can be considered the father of the JRPG sub-genre. What caught my attention was the simplicity of the game. There were no party members, the battle system was turn based and simple and you were free to just explore around. I was particularly surprised by how the game could give a sense of exploration while the map was technically very small. This was achieved by making the player move on an overworld map with a different scale proportion compared to when navigating towns and points of interest. In the overworld section, the player appeared bigger while the geography was smaller, allowing players to cover large amounts of territory relatively quickly. The advantage of this was that you could switch between biomes quickly without it feeling jarring. You still had the impression of traversing a large world despite being small in reality. This idea of using an overworld map was common in older games but somehow died off as devs had less and less technical limitations and more budget to work with. Seeing its potential, I decided that I would include one in my project even if I didn’t have a clear vision at this point. Playing Dragon Quest 1 also reminded me of how annoying random battle encounters were. You would take a few steps and get assaulted by an enemy of some kind. At the same time, this mechanic was needed, because grinding was necessary to be able to face stronger enemies in further zones of the map. My solution : What if instead of getting assaulted, you were the one doing the assault? As you would move on the map, encounter opportunities signified by a star would appear. Only if you went there and overlapped with one would a battle start. This gave the player agency to determine if they needed to battle or not. This idea seemed so appealing that I knew I needed to include it in my project. While my vision on what I wanted to make started to become clearer, I also started to get a sense of what I didn’t want to make. The idea of including a traditional turn based battle system was unappealing. That wasn’t because I hated this type of gameplay, but ever since I made a 6 hour tutorial on how to build one , I realized how complicated pulling one off is. Sure, you can get something basic quickly, but to actually make it engaging and well balanced is another story. A story that would exceed 1-2 months to deal with. I needed to opt for something more real-time and action based if I wanted to complete this project in a reasonable time frame. Back in 2015, an RPG that would prove to be very influential released and “broke the internet”. It was impossible to avoid seeing the mention of Undertale online. It was absolutely everywhere. The game received praised for a lot of different aspects but what held my attention, was its combat system. It was the first game I was aware of, that included a section of combat dedicated to avoiding projectiles (otherwise known as bullet hell) in a turn based battle system. This made the combat more action oriented which translated into something very engaging and fun. This type of gameplay left a strong impression in my mind and I thought that making something similar would be a better fit for my project as it was simpler to implement. While learning about Dragon Quest 1, I couldn’t help but be reminded me of The Legend of Zelda Breath of The Wild released in 2017. Similarly to Dragon Quest, a lot of freedom was granted to the player in how and when they tackled the game’s objectives. For example, in Breath of The Wild, you could go straight to the final boss after the tutorial section. I wanted to take this aspect of the game and incorporate it into my project. I felt it would be better to have one final boss and every other enemy encounter would be optional preparation you could engage with to get stronger. This felt like something that was achievable in a smaller scope compared to crafting a linear story the player would progress through. Another game that inspired me was Elden Ring, an open world action RPG similar to Breath of The Wild in its world structure but with the DNA of Dark Souls, a trilogy of games made previously by the same developers. What stuck with me regarding Elden Ring, for the purpose of my project, was its unique way it handled experience points. It was the first RPG I played that used them as a currency you could spend to level up different attributes making up your character or to buy items. Taking inspiration from it, I decided that my project would feature individually upgradable stats and that experience points would act as a currency. The idea was that the player would gain an amount of the game’s currency after battle and use that to upgrade different attributes. Like in Elden Ring, if you died in combat you would lose all currency you were currently holding. I needed a system like this for my project to count as an RPG. Since by definition an RPG is stats driven. A system like this would also allow the player to manage difficulty more easily and it would act as the progression system of my game. When I started getting into game development, I quickly came across Pico-8. Pico-8, for those unaware, is a fantasy console with a set of limitations. It’s not a console you buy physically but rather a software program that runs on your computer (or in a web browser) that mimics an older console that never existed. To put it simply, it was like running an emulator for a console that could’ve existed but never actually did. Hence the fantasy aspect of it. Pico-8 includes everything you need to make games. It has a built-in code editor, sprite editor, map editor, sound editor, etc… It uses the approachable Lua programming language which is similar to Python. Since Pico-8 is limited, it’s easier to actually finish making a game rather than being caught in scope creep. One game made in Pico-8 particularly caught my interest. In this game you play as a little character on a grid. Your goal is to fight just one boss. To attack this boss, you need to step on a glowing tile while avoiding taking damage by incoming obstacles and projectiles thrown at you. ( Epilepsy Warning regarding the game footage below due to the usage of flashing bright colors.) This game convinced me to ditch the turned based aspect I envisioned for my project entirely. Rather than having bullet hell sections within a turn based system like in Undertale the whole battle would instead be bullet hell. I could make the player attack without needing to have turns by making attack zones spawn within the battlefield. The player would then need to collide with them for an attack to register. I was now convinced that I had something to stand on. It was now time to see if it would work in practice but I needed to clearly formulate my vision first. The game I had in mind would take place under two main scenes. The first, was the overworld in which the player moved around and could engage in battle encounters, lore encounters, heal or upgrade their stats. The second, being the battle scene, would be were battles would take place. The player would be represented by a cursor and they were expected to move around dodging incoming attacks while seeking to collide with attack zones to deal damage to the enemy. The purpose of the game was to defeat a single final boss named king Donovan who was a tyrant ruling over the land of Hydralia where the game took place. At any point, the player could enter the castle to face the final boss immediately. However, most likely, the boss would be too strong. To prepare, the player would roam around the world engaging in various battle encounters. Depending on where the encounter was triggered, a different enemy would show up that fitted the theme of the location they were in. The enemy’s difficulty and experience reward if beaten would drastically vary depending on the location. Finally, the player could level up and heal in a village. I was now ready to start programming the game and figuring out the details as I went along. For this purpose, I decided to write the game using the JavaScript programming language and the KAPLAY game library. I chose these tools because they were what I was most familiar with. For JavaScript, I knew the language before getting into game dev as I previously worked as a software developer for a company who’s product was a complex web application. While most of the code was in TypeScript, knowing JavaScript was pretty much necessary to work in TypeScript since the language is a superset of JavaScript. As an aside, despite its flaws as a language, JavaScript is an extremely empowering language to know as a solo dev. You can make games, websites, web apps, browser extensions, desktop apps, mobile apps, server side apps, etc… with this one language. It’s like the English of programming languages. Not perfect, but highly useful in today’s world. I’ll just caveat that using JavaScript makes sense for 2D games and light 3D games. For anything more advanced, you’d be better off using Unreal, Unity or Godot. As for the KAPLAY game library, it allows me to make games quickly because it provides a lot of functionality out of the box. It’s also very easy to learn. While it’s relatively easy to package a JavaScript game as an app that can be put on Steam, what about consoles? Well it’s not straightforward at all but at the same time, I don’t really care about consoles unless my game is a smash hit on Steam. If my game does become very successful than it would make sense businesswise to pay a porting company to remake the game for consoles, getting devkits, dealing with optimizations and all the complexity that comes with publishing a game on these platforms. Anyway, to start off the game’s development, I decided to implement the battle scene first with all of its related mechanics as I needed to make sure the battle system I had in mind was fun to play in practice. To also save time later down the line, I figured that I would make the game have a square aspect ratio. This would allow me to save time during asset creation, especially for the map as I wanted the whole map to be visible at once as I wouldn’t use a scrolling camera for this game. After a while, I had a first “bare bones” version of the battle system. You could move around to avoid projectiles and attack the enemy by colliding with red attack zones. Initially, I wanted the player to have many stats they could upgrade. They could upgrade their health (HP), speed, attack power and FP which stood for focus points. However, I had to axe the FP stat as I originally wanted to use it as a way to introduce a cost to using items in battle. However, I gave up on the idea of making items entirely as they would require too much time to create and properly balance. I also had the idea of adding a stamina mechanic similar to the one you see in Elden Ring. Moving around would consume stamina that could only replenish when you stopped moving. I initially though that this would result in fun gameplay as you could upgrade your stamina over time but it ended up being very tedious and useless. Therefore, I also ended up removing it. Now that the battle system was mostly done, I decided to work on the world scene where the player could move around. I first implemented battle encounters that would spawn randomly on the screen as red squares, I then created the upgrade system allowing the player to upgrade between 3 stats : Their health (HP), attack power and speed. In this version of the game, the player could restore their health near where they could upgrade their stats. While working on the world scene was the focus, I also made a tweak to the battle scene. Instead of displaying the current amount of health left as a fraction, I decided a health bar would be necessary because when engaged in a fast paced battle, the player does not have time to interpret fractions to determine the state of their health. A health bar would convey the info faster in this context. However, I quickly noticed an issue with how health was restored in my game. Since the world was constrained to a single screen, it made going back to the center to get healed after every fight the optimal way to play. This resulted in feeling obligated to go back to the center rather than freely roaming around. To fix this issue, I made it so the player needed to pay to heal using the same currency for leveling up. Now you needed to carefully balance between healing or saving your experience currency for an upgrade by continuing to explore/engage in battle. All of this while keeping in mind that you could lose all of your currency if defeated in battle. It’s important to note that you could also heal partially which provided flexibility in how the player managed the currency resource. Now that I was satisfied with the “bare bones” state of the game, I needed to make nice looking graphics. To achieve this, I decided to go with a pixel art style. I could spend a lot of time explaining how to make good pixel art but, I already did so previously. I recommend checking my post on the topic. I started by putting a lot effort drawing the overworld map as the player would spend a lot of time in it. It was a this stage that I decided to make villages the places where you would heal or level up. To make this clearer, I added icons on top of each village to make it obvious what each was for. Now that I was satisfied with how the map turned out, I started designing and implementing the player character. For each distinct zone of the map, I added a collider so that battle encounters could determine which enemy and what background to display during battle. It was at this point that I made encounters appear as flashing stars on the map. Since my work on the overworld was done, I now needed to produce a variety of battle backgrounds to really immerse the player in the world. I sat down and locked in. These were by far one of the most time intensive art assets to make for this project but I’m happy with the results. After finishing making all backgrounds, I implemented the logic to show them in battle according to the zone where the encounter occurred. The next assets to make were enemies. This was another time intensive task but I’m happy with how they turned out. The character at the bottom left is king Donovan the main antagonist of the game. Further Developing The Battle Gameplay While developing the game, I noticed that it took too much time to go from one end of the battle zone to the other. This made the gameplay tedious so I decided to make the battle zone smaller. At this point, I also changed the player cursor to be diamond shaped and red rather than a circle and white. I also decided to use the same flashing star sprite used for encounters on the map but this time, for attack zones. I also decided to change the font used in the game to something better. At this point, the projectiles thrown towards the player didn’t move in a cohesive pattern the player could learn over time. It was also absolutely necessary to create a system in which the attack patterns of the enemy would be progressively shown to the player. This is why I stopped everything to work on the enemy’s attack pattern. I also, by the same token, started to add effects to make the battle more engaging and sprites for the projectiles. While the game was coming along nicely, I started to experience performance issues. I go into more detail in a previous post if you’re interested. To add another layer of depth to my game, I decided that the reward you got from a specific enemy encounter would not only depend on which enemy you were fighting but also how much damage you took. For example, if a basic enemy in the Hydralia field would give you a reward of a 100 after battle, you would actually get less unless you did not take damage during that battle. This was to encourage careful dodging of projectiles and to reward players who learned the enemy pattern thoroughly. This would also add replayability as there was now a purpose to fight the same enemy over and over again. The formula I used to determine the final reward granted can be described as follows : At this point, it wasn’t well communicated to the player how much of the base reward they were granted after battle. That’s why I added the “Excellence” indication. When beating an enemy, if done without taking damage, instead of having the usual “Foe Vanquished” message appearing on the screen, you would get a “Foe Vanquised With Excellence” message in bright Yellow. In addition to being able to enter into battle encounters, I wanted the player to have lore/tips encounters. Using the same system, I would randomly spawn a flashing star of a blueish-white color. If the player overlapped with it, a dialogue box would appear telling them some lore/tips related to the location they were in. Sometimes, these encounters would result in a chest containing exp currency reward. This was to give a reason for the player to pursue these encounters. This is still a work in progress, as I haven’t decided what kind of lore to express through these. One thing I forgot to show earlier was how I revamped the menu to use the new font. That’s all I have to share for now. What do you think? I also think it’s a good time to ask for advice regarding the game’s title. Since the game takes place in a land named Hydralia . I thought about using the same name for the game. However, since your mission is to defeat a tyrant king named Donovan, maybe a title like Hydralia : Donovan’s Demise would be a better fit. If you have any ideas regarding naming, feel free to leave a comment! Anyway, if you want to keep up with the game’s development or are more generally interested in game development, I recommend subscribing to not miss out on future posts. Subscribe now In the meantime, you can read the following :

0 views
JSLegendDev 1 months ago

5 Pixel Art Tips for Programmers

Programmers are known to not have a strong suit for art related disciplines, pixel art is no exception. In this post, I’ll share 5 tips for making good pixel art from the point of view of a programmer. This is to help other solo devs coming from a programming background to make better pixel art for their games. Now, you might be wondering if I’m qualified to share tips related to pixel art? Below are two pixel art asset packs I made. If you like how they look, then the question is answered. https://jslegend.itch.io/kirby-like-platformer-asset-pack https://jslegend.itch.io/samurai-arena Here are all the asset packs I’ve made so far : Kirby like platformer asset pack (FREE) Mario-like Platformer Asset Pack ($4) 2D Pixel Art Samurai Arena ($4) How do you know if a sprite looks good or not? You might have assumed that you just need to look at it and your brain determines whenever it’s appealing or not. Maybe the colors clash or the pixels are not placed symmetrically, etc… all things that you should detect naturally. However, it’s not that simple, an art piece can go against commonly held rules and still look good. It can look good to one person but not to another. While it’s true that whether a sprite or any piece of art looks good is subjective to some extent, it’s undeniable that some of them are widely considered more appealing. This subjective nature of art is what a lot of programmers struggle with. You might be used to having a compiler compile and run your code. If your code successfully runs then you know that at least your code works. With art however, you need to acquire good taste that will effectively act as your own internal compiler when doing pixel art. It will guide you during your art process and allow you to realize when things don’t work (ex: Color clashes, etc…). The question now becomes, how do you develop good taste in pixel art? The simplest way is to look at well regarded pixel art online. It can come from games where the art is praised or from pixel artists online. When observing each piece, try to notice the details. If you do this enough, you’ll build an internal understanding of what makes good pixel art. https://x.com/nekocrocodile/status/1894101890406776904 https://x.com/ChrisPariano/status/1886151340541227153 Tip #2 : Don’t Draw! Negotiate Your Pixels Instead If you come to pixel art with the mindset that you’re going to draw what you want like you would on pen and paper, you’re going to get frustrated. Pixel art at low resolutions is all about negotiating with your canvas. It’s basically a puzzle game. You need to figure out where to place your pixels to represent what you want while still compromising on certain details. The sooner you view pixel art like this, the less you’re going to struggle drawing the sprites you need for your games. This is because you’re now more flexible and ready to adapt when representing certain things that are challenging at low resolutions. You can either do the hard work of figuring out how to represent an object or character in lower resolutions or you can go look at the million of available game sprites that already solved what you’re trying to represent. That’s why I recommend using references extensively. For example, if you want to make a top-down action adventure game a la Zelda there is no shame in looking up link’s sprites to see how he’s represented or how his sword attack animations are made. In case you didn’t know, the most comprehensive website for looking up sprites from various retro games is The Spriters Resources . Tip #4 : Stick to a Limited Color Palette but Expand It if Needed Sticking to a limited color palette is very important in pixel art due to the low amount of space available. Every color you choose will have a big impact on the appeal of the final sprite. If you don’t want to deal with color theory, you can always pick an existing color palette. There is this website called Lospec with plenty of color palettes you can pick from. On this website, you can often see pixel art examples using the listed color palette. This will help you determine if a given palette will likely help you achieve your desired aesthetic. Another option consists in finding a pixel art piece you like online and picking the same colors to make your palette. Finally, you might feel too constrained working with a limited amount of colors. Sometimes, the colors you have in your palette are not enough. In that case, I recommend adding colors to it. In fact, it’s much easier to expand a given color palette one color at a time then to not limit yourself to one by choosing colors on the fly. When needing a new color, you can more easily compare your new color choice with an existing palette and see if it fits or change your choice otherwise. I often hear the phrase “Constraints breeds creativity” and I agree because when constrained you often end up finding creative solutions. However, another lesser known benefit of constraints is that they allow you to hide your flaws. The less constraints you have, the more ambitious you get, the higher the likelihood of going out of your comfort zone and finding yourself in an area where you do not have enough skill to pass the “professional” quality bar. For example, if you’re someone with very limited coloring skills. I would recommend sticking to a prefined color palette of max 4 colors. This way, you can more easily reach a “professional” looking result compared to if you had to pick your own colors. Well chosen constraints allows you to put forward your strengths while hiding your flaws . Another way to put it would be that constraints reduces the likelihood of shooting yourself in the foot. While I hope these tips will help you make better pixel art, it’s no secret that practice remains a big part of what you need to do to achieve good results. If you’re interested in reading more posts like this. I recommend subscribing to my Substack as to not miss out on future releases. Subscribe now In the meantime, you can check out my previous posts. Programmers are known to not have a strong suit for art related disciplines, pixel art is no exception. In this post, I’ll share 5 tips for making good pixel art from the point of view of a programmer. This is to help other solo devs coming from a programming background to make better pixel art for their games. Now, you might be wondering if I’m qualified to share tips related to pixel art? Below are two pixel art asset packs I made. If you like how they look, then the question is answered. https://jslegend.itch.io/kirby-like-platformer-asset-pack https://jslegend.itch.io/samurai-arena Here are all the asset packs I’ve made so far : Kirby like platformer asset pack (FREE) Mario-like Platformer Asset Pack ($4) 2D Pixel Art Samurai Arena ($4)

0 views
JSLegendDev 2 months ago

What Caused Performance Issues in My Tiny RPG

In a previous post, I mentioned having strange performance issues regarding a tiny RPG I was secretly working on. The crux of the matter was that the game (built using web technologies) would run noticeably less smoothly when wrapped as a desktop app on my machine than when running in Firefox. I initially shared the project’s executables on the KAPLAY Discord server (KAPLAY being the library I used to make the game in JavaScript) and none reported the performance issues I had. Seeing this, I decided to make a Substack post inviting my wider audience to try the game out and report its performance. This led to someone sharing the post on Hacker News which resulted in a large influx of views and feedback. In this post, I would like to explain what went wrong (as best as I understood it myself). First of all, I have mostly fixed the performance of my game. It now runs much more smoothly and I have updated the executables on itch. I would still greatly appreciate feedback regarding how these run on your machines. (Download the ones tagged with -v2) Here’s the link : https://jslegend.itch.io/small-rpg-performance-playtest To build executables for Windows, Mac and Linux, I use either NW.js or GemShell. Similar to Electron but easier to set up. It packages a Chromium instance for rendering. Results in bloated executables but the same rendering engine is used across platforms. Similar to Tauri in how it uses the operating system’s webview to render the app rather than packaging a Chromium instance. This results in leaner executables but different web engines with varying performance differences are used on different platforms. However, contrary to Tauri, GemShell is a one click tool that generate executables for all three platforms. I wrote a post about it recently that delved into more detail. Since I wanted to build executables quickly, I decided to try out GemShell for this project. Executables that I distributed to get feedback were made with this tool. KAPLAY is a library based on the concept of game objects and components. A game object is created from a list of components, many of which, are ready made and offer functionality out of the box. This makes the library very easy to understand and productive. The issue is that it’s by default less performant than other alternatives (Phaser, Excalibur, etc…) I use it because it’s so fast to prototype game ideas in. However, I used it for this project because it was the tool I knew best that would allow me to immediately start working on the game and focus on game design. In hindsight, I should have probably started invested in learning other more performant alternatives to a sufficient level so that I could easily pivot away if the needs of my project demanded it. This was often reported among people for who the game didn’t perform well. In KAPLAY, there is an option to cap the FPS to a maximum amount. It’s used when first initializing the library. The idea behind this option is to enforce a consistent frame rate resulting in more consistently smooth gameplay. However, setting this to 60 in my game resulted in the game eventually slowing down to a crawl all while the debug fps count still displayed 60 fps. I conclude that this happened when the machine running the game wasn’t able to sustain 60fps or more at all times. The fix was simple, remove it. Why did this option behave this way? Probably a bug a in the library. A maintainer is currently working on it. Since GemShell was used to make executables, the underlying web engine used to render the game on a Mac is Webkit and not Chromium. While I knew that Webkit was less performant than Chromium, I didn’t think it would be that noticeable. The apparent solution to this would be to move off of GemShell and use NW.js instead. However, it turns out that MacOS does certain things to not allow Webkit to use its full potential. The dev behind GemShell apparently has a fix for this situation. Below is what they shared on their Discord, which you can join if you’re interested in the tool here . This would be great since it would be nice to have the best of both worlds, more consistent performance across platforms while still having lean executables. Since I’m still not done with the development of the game, I can afford to let the dev cook, as we say! This is the strangest performance issue I experienced. While I could conceive of the game performing better on Firefox VS Safari (since it uses Webkit), I was caught off guard by Chrome performing poorly than Firefox. Chrome is supposed to be more performant due to the Chromium Web engine. The Chrome profiler seemed to indicate that the way I was rendering text in my game probably needed to change. KAPLAY Game Objects and Text Rendering In KAPLAY, if you look at the examples provided in its playground , you’ll find that it’s often shown that to render text you first, create a game object using a text component and then pass the text to that component. However, it turns out that this is inefficient since game objects have a greater cost in terms of performance. For rendering simple text it’s far better to draw it directly in the draw loop. Here’s a simple example. The same logic applies to drawing static images like backgrounds. Instead of doing : I needed to do : Additionally, to not disable batching it was important that all drawSprite calls be placed together in the draw loop before rendering text with drawText calls. Doing this led to better performance on Chrome and Safari. However, there was one last obvious performance improvement I needed to do. In the battle section of my game the player must avoid getting hit by a horde of projectiles thrown at them. A common optimization technique used in this case was to reuse bullets that leave the screen rather than creating new ones. This technique is known as object pooling. I had planned on implementing something like this eventually but didn’t do it since I was developing the game using Firefox as my preview, and the performance was great. There weren’t that many projectiles in the first place so I felt that this would be useful later. However, considering that Chrome and Safari were struggling performance wise probably due to their garbage collector working differently, I resigned myself to implement it now. As expected the game performed much better on both Chrome and Safari. For Chrome, I now had a constant 60fps on my machine (Macbook Air M3, 16 GB RAM) but for Safari it was more around 45-60fps. To stress test my pooling system, I added a bunch of projectiles. Here is footage. I’m happy that I can now resume development and focus more on game design. However, what this adventure taught me is that I should probably invest my time learning other frameworks and game engines. While I eventually ended up fixing the performance issues in my game, I can’t help but think of scenarios where problems could arise later that are unfixable due to limitations of the tools I’m using. In that case, I would have to halt development to learn a new tool at a proficient level on top of having to reimplement the game which would take a lot of time and result in my project getting significantly delayed. If I start learning something else right now, I can at least go faster if I eventually need to reimplement the game. Finally, if you’re interested in keeping up with the game or like technical posts like this one. I recommend subscribing to not miss out when new posts are published. Subscribe now In the meantime, here are few of my previous posts that could interest you!

0 views
JSLegendDev 2 months ago

I'm Making a Tiny RPG and I Need Feedback Regarding Performance

This past month, I’ve been working secretly on a small RPG game. While the game is not ready at all and I didn’t plan on talking about it, I’m now kind of forced to. I’ve been using JavaScript + the KAPLAY game library to make this game but I’ve been experiencing performance issues. However, it seems that others aren’t experiencing them so now I wonder, is it just my machine? I’m using a Macbook Air M3 with 16GB of RAM. Normally, things should be smooth and they are when playing the game in the browser via Firefox. However, since I’m making this game with Steam in mind, it’s especially important that the game performs well when wrapped as a desktop app. For this reason, I decided to reach out to you, my audience, for feedback. I have included a build of the unfinished game for Windows, Mac and Linux. It would be very nice if you could try it out on your machine. Additionally, recording gameplay and sharing the link in the comment section of this post would be greatly appreciated. Here is the link to the game in its unfinished current form : https://jslegend.itch.io/small-rpg-performance-playtest Below is a gameplay video to give you an idea of how the game is supposed to be played. You can move around with arrow keys and enter battle by overlapping with a star in the overworld. Performance issues, if any, occur mostly during battles. Thanks in advance! UPDATE 7/11/2025 : It seems that this post was shared on Hacker News and is getting more attention than usual! If you’re new to my Substack and are potentially interested in updates regarding this project, I recommend subscribing. Subscribe now In the meantime, you can read some of my previous posts!

0 views
JSLegendDev 2 months ago

Export Web Games for Desktop in One Click

In a previous post , I tackled the various options one could use to make their web games playable offline as installable desktop apps. This would enable using web technologies to make games that could be sold on a platform like Steam. I also mentioned that the options available could be divided into two broad categories. Those who packaged a web engine with the executable and those who instead, used the default one available on the user’s system. I concluded, that the first category of options led to a more reliable experience because the same web engine was used on all operating systems. This however, led to more memory and storage usage making the resulting executables bloated. This was the case for Electron and NW.js which packaged a specific Chromium instance alongside the app. An empty project could easily exceed 100 MB in size. The second category led to more lean executables but required the dev to test on all operating systems as there could be differences in how the game was rendered since each used a different underlying web engine. Examples that fit in this category are Tauri, Neutralino and Wails. Finally, I concluded that the easiest option was to use NW.js since it was like Electron but easier to set up and that the bloated nature of this solution was not a big problem considering that it was expected for games to use more resources than normal apps. However, I recently discovered a new tool called GemShell which allows you to export for all 3 platforms in what amounts to one click! In this post, I’d like to present it. GemShell is available on itch.io under two versions. The Lite version which is free and the Pro version which is paid. Let’s go over the Lite version first. When you open the app, you’re first greeted with a section where you can provide the folder of your game’s build. For example, if you’re using a build tool like Vite, what you need to provide is the folder that is generated after running the command . Otherwise, if you’re not using a build tool, your web export is essentially your source code. Once the relevant folder is provided. You can go to the next section where you’ll be able to configure your game’s metadata like its title, window dimensions, author, whenever you want the window to be resizable, etc… Once the metadata is configured, you can choose for which platforms you want to build executables. Finally, you can reach the last section where you can build executables in one click. Once building is completed, you can either launch the build for your current operating system or open the folder containing all the generated builds. GemShell will put all the builds relevant to your game in a folder called GemShell-Builds which will be created in the Desktop directory of your computer (if it doesn’t already exist). What’s in The Pro Version? Since the developer of GemShell provided me with a pro version of the software, I can show you what it contains compared to the lite version. First, you now have the ability to set an icon for your executables other than the default using any image you want. However, the app recommends using a 1024x1024 png image. Second, you can toggle off the splash screen that appears before your game starts. Other pro features include : Performance Mode : Which claims to add a 15-40% FPS boost for 3D games. Since I only make 2D games and know almost nothing when it comes to 3D, I didn’t see a difference with/without this option toggled. Asset compression : As the name implies, it compresses your assets. I have tested this feature on a small pixel art based game I was working on and didn’t notice a difference between the Pro and Lite build. They both clocked in at 5.4 MB which is already incredibly lean. I assume that this would come in handy when using bigger assets. Code Minification : This compresses the code. However, build tools like Vite already do minifcation when making your web build. I assume this feature is useful for projects not relying on NPM and build tools. Preload Critical Assets : Claims to increase game start speed by 30-50% by downloading big files in parallel. I think this feature is mostly targeted to devs making muliplayer games where the desktop app is a client of some sort. Personally, I have not noticed a difference but remember that the games I make use pixel art based assets which are usually small in size. I would also like to note that regardless of if you use the Lite or Pro version, they both load extremely quickly when I tested them. The only difference is due to the splash screen taking 2 seconds before the game actually starts which is near instantaneous. Finally, a feature for detecting duplicate assets and removing console.log statements. While not available yet, there seems to be security features planned for future updates. One of the main reasons someone might want to package their web game as a desktop app is to have the ability to save user data permanently and reliably. This would allow a player to save their progress and play again later. Instead of writing data to an external file, you can simply use localStorage or IndexedDB. GemShell uses a hidden ID file set in your initial game’s folder. Therefore, data is not lost when updating your game’s build. An existing player can carry over their progress from one version to the next. If you’re interested in this tool, I recommend trying the Lite version with some of your projects to see if it fits your needs. In addition, if you have issues or would like certain features to be added, the dev has a Discord server where you can provide feedback. GemShell : https://l0om.itch.io/gemshell GemShell’s Discord server : https://discord.gg/b24q5B8ZAY Now you might be wondering if I still recommend using NW.js or do I now recommend switching to GemShell? It’s undeniable that GemShell is extremely convenient compared to the setup needed for NW.js which itself was simpler compared to other alternatives I explored in the previous post. If we were to only consider how quickly you could make builds for desktop platforms, GemShell takes the cake. However, it’s important to consider that GemShell is built upon Neutralino, a technology that uses the system’s webview to render the game. The web engine used is different on each operating system, therefore, you don’t have a guarantee that things will render the same way and perform the same way on all three platforms. This isn’t a theoretical problem. Let’s consider the KAPLAY library, the one I’m using to make most of my games. It turns out that this library doesn’t run as well on Safari. Since Safari uses the default web engine available on Mac called Webkit, this means that the Mac version of my game built with GemShell will perform significantly worse than on Windows which uses a Chromium based web engine name Edge. On one hand KAPLAY is known to not be the most performant web game framework, but Webkit is also weaker for canvas based apps compared to Chromium. This led me to stick with NW.js for my current project since it packages a Chromium instance with the build. I can rest assured that the game will have the same performance on all three major operating systems. This wouldn’t have been a major issue if I had used a more performant framework like Phaser or a rendering library like Pixi.js. While not mandatory, if you want to put your game on Steam, you might want to integrate features like Steam achievements. To achieve this with NW.js, you can use the steamworks.js node package since NW.js gives you access to a Node based environment. As for GemShell there isn’t a straightforward way to achieve this, at least, currently. To conclude, if you want : an extremely convenient way of packaging web games as desktop apps. builds that are very lean and start up quickly. don’t really care if there are differences in rendering and performance between platforms. are using a framework/library that is known to work well under all major web engines (Chromium, Gecko, Webkit, WebkitGTK). don’t care about integrating with the Steam SDK to implement features like Steam achievements. pick GemShell. If you want : to be sure that the game renders and performs the same way across platforms or/and are using a game framework that perform noticeably worse on web engines used as default webviews of some operating systems. to integrate with the Steam SDK to implement Steam achievements in your game. don’t care if the builds take more storage and consume more memory compared to leaner alternatives. don’t mind taking the time to do some setup work before being able to generate builds. Speaking of NW.js, I have a 1-hour long course on Patreon you can purchase that teaches : How to use Vite and NW.js together, so that you can develop for the web (therefore benefiting from hot reloading) while still being able to build an executable that will work as is. We can achieve this by using Vite’s environment variables allowing us to gate code which will run in the desktop version exclusively. This is useful when it comes to writing and reading files from/to the disk which is code that should run only in the desktop version. How to use NW-Builder to create a script to build executables programmatically rather than having to manually download binaries from the NW.js website. How to make a space shooter game using KAPLAY. If you’re interested, check it out here . If you enjoyed this post and want more like it, I recommend subscribing so you won’t miss out on future publications. Subscribe now In a previous post , I tackled the various options one could use to make their web games playable offline as installable desktop apps. This would enable using web technologies to make games that could be sold on a platform like Steam. I also mentioned that the options available could be divided into two broad categories. Those who packaged a web engine with the executable and those who instead, used the default one available on the user’s system. I concluded, that the first category of options led to a more reliable experience because the same web engine was used on all operating systems. This however, led to more memory and storage usage making the resulting executables bloated. This was the case for Electron and NW.js which packaged a specific Chromium instance alongside the app. An empty project could easily exceed 100 MB in size. The second category led to more lean executables but required the dev to test on all operating systems as there could be differences in how the game was rendered since each used a different underlying web engine. Examples that fit in this category are Tauri, Neutralino and Wails. Finally, I concluded that the easiest option was to use NW.js since it was like Electron but easier to set up and that the bloated nature of this solution was not a big problem considering that it was expected for games to use more resources than normal apps. However, I recently discovered a new tool called GemShell which allows you to export for all 3 platforms in what amounts to one click! In this post, I’d like to present it. GemShell is available on itch.io under two versions. The Lite version which is free and the Pro version which is paid. Let’s go over the Lite version first. When you open the app, you’re first greeted with a section where you can provide the folder of your game’s build. For example, if you’re using a build tool like Vite, what you need to provide is the folder that is generated after running the command . Otherwise, if you’re not using a build tool, your web export is essentially your source code. Once the relevant folder is provided. You can go to the next section where you’ll be able to configure your game’s metadata like its title, window dimensions, author, whenever you want the window to be resizable, etc… Once the metadata is configured, you can choose for which platforms you want to build executables. Finally, you can reach the last section where you can build executables in one click. Once building is completed, you can either launch the build for your current operating system or open the folder containing all the generated builds. GemShell will put all the builds relevant to your game in a folder called GemShell-Builds which will be created in the Desktop directory of your computer (if it doesn’t already exist). What’s in The Pro Version? Since the developer of GemShell provided me with a pro version of the software, I can show you what it contains compared to the lite version. First, you now have the ability to set an icon for your executables other than the default using any image you want. However, the app recommends using a 1024x1024 png image. Second, you can toggle off the splash screen that appears before your game starts. Other pro features include : Performance Mode : Which claims to add a 15-40% FPS boost for 3D games. Since I only make 2D games and know almost nothing when it comes to 3D, I didn’t see a difference with/without this option toggled. Asset compression : As the name implies, it compresses your assets. I have tested this feature on a small pixel art based game I was working on and didn’t notice a difference between the Pro and Lite build. They both clocked in at 5.4 MB which is already incredibly lean. I assume that this would come in handy when using bigger assets. Code Minification : This compresses the code. However, build tools like Vite already do minifcation when making your web build. I assume this feature is useful for projects not relying on NPM and build tools. Preload Critical Assets : Claims to increase game start speed by 30-50% by downloading big files in parallel. I think this feature is mostly targeted to devs making muliplayer games where the desktop app is a client of some sort. Personally, I have not noticed a difference but remember that the games I make use pixel art based assets which are usually small in size. I would also like to note that regardless of if you use the Lite or Pro version, they both load extremely quickly when I tested them. The only difference is due to the splash screen taking 2 seconds before the game actually starts which is near instantaneous. Finally, a feature for detecting duplicate assets and removing console.log statements. While not available yet, there seems to be security features planned for future updates. How is Data Saved? One of the main reasons someone might want to package their web game as a desktop app is to have the ability to save user data permanently and reliably. This would allow a player to save their progress and play again later. Instead of writing data to an external file, you can simply use localStorage or IndexedDB. GemShell uses a hidden ID file set in your initial game’s folder. Therefore, data is not lost when updating your game’s build. An existing player can carry over their progress from one version to the next. Try it Out If you’re interested in this tool, I recommend trying the Lite version with some of your projects to see if it fits your needs. In addition, if you have issues or would like certain features to be added, the dev has a Discord server where you can provide feedback. GemShell : https://l0om.itch.io/gemshell GemShell’s Discord server : https://discord.gg/b24q5B8ZAY GemShell VS NW.js Now you might be wondering if I still recommend using NW.js or do I now recommend switching to GemShell? It’s undeniable that GemShell is extremely convenient compared to the setup needed for NW.js which itself was simpler compared to other alternatives I explored in the previous post. If we were to only consider how quickly you could make builds for desktop platforms, GemShell takes the cake. Issue #1 However, it’s important to consider that GemShell is built upon Neutralino, a technology that uses the system’s webview to render the game. The web engine used is different on each operating system, therefore, you don’t have a guarantee that things will render the same way and perform the same way on all three platforms. This isn’t a theoretical problem. Let’s consider the KAPLAY library, the one I’m using to make most of my games. It turns out that this library doesn’t run as well on Safari. Since Safari uses the default web engine available on Mac called Webkit, this means that the Mac version of my game built with GemShell will perform significantly worse than on Windows which uses a Chromium based web engine name Edge. On one hand KAPLAY is known to not be the most performant web game framework, but Webkit is also weaker for canvas based apps compared to Chromium. This led me to stick with NW.js for my current project since it packages a Chromium instance with the build. I can rest assured that the game will have the same performance on all three major operating systems. This wouldn’t have been a major issue if I had used a more performant framework like Phaser or a rendering library like Pixi.js. Issue #2 While not mandatory, if you want to put your game on Steam, you might want to integrate features like Steam achievements. To achieve this with NW.js, you can use the steamworks.js node package since NW.js gives you access to a Node based environment. As for GemShell there isn’t a straightforward way to achieve this, at least, currently. Conclusion To conclude, if you want : an extremely convenient way of packaging web games as desktop apps. builds that are very lean and start up quickly. don’t really care if there are differences in rendering and performance between platforms. are using a framework/library that is known to work well under all major web engines (Chromium, Gecko, Webkit, WebkitGTK). don’t care about integrating with the Steam SDK to implement features like Steam achievements. to be sure that the game renders and performs the same way across platforms or/and are using a game framework that perform noticeably worse on web engines used as default webviews of some operating systems. to integrate with the Steam SDK to implement Steam achievements in your game. don’t care if the builds take more storage and consume more memory compared to leaner alternatives. don’t mind taking the time to do some setup work before being able to generate builds. How to use Vite and NW.js together, so that you can develop for the web (therefore benefiting from hot reloading) while still being able to build an executable that will work as is. We can achieve this by using Vite’s environment variables allowing us to gate code which will run in the desktop version exclusively. This is useful when it comes to writing and reading files from/to the disk which is code that should run only in the desktop version. How to use NW-Builder to create a script to build executables programmatically rather than having to manually download binaries from the NW.js website. How to make a space shooter game using KAPLAY.

0 views
JSLegendDev 3 months ago

You Can Now Make PS2 Games in JavaScript

I recently discovered that you could make PS2 games in JavaScript. I’m not even kidding, it’s actually possible. I was working on a project and had my phone near my desk when I received a notification. Upon further inspection, it came from itch.io which was a platform where I usually published most of my web games. Under my relatively popular Sonic infinite runner game which was made in JavaScript and developed a year ago, I received a comment from someone with the username Dev Will which claimed they had made a PS2 version of my game and provided the GitHub repo of the source code. At first, I thought that it was cool that someone took the time to remake my game for an old console that had a reputation to be hard to develop for and probably required them to write a lot of C or C++. Out of curiosity, I opened up the GitHub repo and was astonished to see that the project was not using even a bit of C++ or C but was entirely in JavaScript! If making PS2 games were easier than I thought since I could use a higher level language like JavaScript, I could probably try making one in a reasonable amount of time and play it on a retro handled or an actual PS2. How cool would that be? This is where I knew I had to drop everything I was doing to investigate how this was possible. Since the dev behind the project was Portuguese speaking (I assume they were either from Brazil or Portugal), they wrote the Readme of the repo in Portuguese which was a language I did not understand. Fortunately, I was still able to decipher most of what was written because I had done 3 years of Spanish in school and spoke French natively. Since Portuguese is a romance language like Spanish and French, I was fortunately not totally lost. Anyway, The readme said that the engine used to make the PS2 version of my game was called AthenaEnv with a conveniently placed link towards it so I could learn more. As with the Sonic Infinite Runner PS2 project, this engine was also open source and its repo had a very detailed readme written in English. To summarize, Athena was not what we commonly refer to as a game engine but an environment that also offered a JavaScript API for making games and apps for the PS2. It embedded a slightly modified version of QuickJS which was a small and embeddable JavaScript engine. This explained how Athena was able to run JavaScript code on the PS2. Therefore, Athena was the PS2 native program written in C that took your JavaScript code, passed it through the QuickJS engine to interpret it and finally, ran the relevant logic on the system. What made it compelling was not that it just ran JS on the PS2 but that it offered an API suitable for game development. It covered : Rendering : Allowing you to display sprites, text, shapes, etc… on the screen and animate them using a game loop. Asset loading : Allowing you to load images, sounds, fonts, etc... Input handling : Allowing you to receive player input from a controller, multiple ones or even from a mouse and keyboard since the PS2 supported these input methods. File handling : Allowing you to write save files among other things. Sound playback : For playing Sound. and the list goes on. I noticed however, that the level of abstraction offered by the API was similar to something like p5.js, the HTML canvas API or Raylib. That meant that you’d still needed to implement collision detection, scene management, etc… yourself. Now, that I got familiar with Athena, I wanted to try to run the Sonic infinite runner “port” on an emulator. According to the project’s Readme. I needed to install PCSX2 which is the most popular emulator for the PS2. Then, go into the settings and under the emulation tab, check the box “Enable host filesystem”. Once this was done, I would need to open an athena.elf file and the game would start. After installing and configuring the emulator, I was ready to run the game. However, there was a problem. I could not find the athena.elf file in the repo. It was nowhere to be found. This is where I remembered to look at the “releases” section of the repo because a lot of open source projects put executables there, especially if it’s a mobile or desktop app project. As expected, the zip attached in that section contained the athena.elf file but not only. It also contained an assets folder, a main.js file, an athena.ini file and src folder containing the rest of the game’s code. The athena.ini file allowed you to configure the entry point of the project. Here, the entry point was set to main.js which explained how Athena would know what JavaScript to run. You could also configure if you wanted to show Athena’s logo before your game started by setting the boot_logo property to true. It now became evident why we needed to check the “Enable host filesystem” check box earlier. This was so that the emulator could allow Athena to access the assets folder and the source code that were essential for our game. Anyway, I opened the athena.elf file in PCSX2 and surprisingly, the game actually ran with no issues. It was amazing to see that a game I wrote for the web was ported to the PS2 and I was there able to play it with a controller. Now, the game looked a bit blurry which was expected since this was supposed to emulate a PS2 which had a small resolution. Fortunately, I was able to make things more comfortable by upping the resolution in the graphics settings of the emulator. The dev process also seemed quite straightforward. You would only need to open the folder containing all the relevant files (athena.elf, main.js, etc…) in a code editor like VSCode and open athena.elf in the emulator. Now, you could make changes to your JS code and once you were ready to test, you would go under the PCSX2 system tab and click on reset. This would restart the emulator and you could see the latest changes. While not as seamless as in web development with hot reloading, it still was a relatively fast iteration cycle. It’s at that moment, that I knew had to make a post about it and share this awesome project with you. However, I still felt uneasy about one thing. Nowadays, people download PS2 games as .iso files. For most games, you only need one .iso file that you then open in your emulator. Less technical people can therefore more easily enjoy these older titles. However, to run the Sonic infinite runner game “port”, I needed to not only check a box in the settings but also needed the entire project’s folder containing the Athena executable and the source code. I wondered if instead, there was a way to distribute the game as a single .iso file. This is were I simply went back to the itch.io comment section and asked if it was possible. After a thorough back and forth that continued on Discord, the process to convert my files into a single iso, I could distribute, was now clear. To make an iso you needed the following files : athena.elf : Which is the Athena executable. athena.ini : For configuring the project’s entry point. A JS file acting as the entry point of the codebase. The rest of your source code if your code is more than one file, oftentimes it’s in a folder called src. Two files one named ATHA_000.01 and the other SYSTEM.CNF needed to make the iso bootable. As an aside, in case you want to also get into JavaScript PS2 game development, you can check this template I made containing all of the files needed . Once you had all the files, you had to make a zip archive containing them all. One issue I had, was that if I created a zip out of the folder containing the files, the resulting .iso would not work. However, if I selected the files one by one and then created the zip, I would experience no issues. This is something to keep in mind. Now, the only step left was to convert the zip into an iso. As I was using a Mac, the only reliable way I’ve found, was to use the website mconverter.eu and let them do the conversion. However, the issue with this website is that you’re limited in the number of conversions you can do per day before they ask you to pay. Additionally, if your zip archive is above a certain size, you’ll also have to watch an ad before you can do the conversion. If you end up finding a better way using either a CLI tool, a downloadable app or some other website, feel free to share it in the comment section. Once you had the iso, you could open it up in the emulator like you would do with other PS2 games. You also didn’t need to check the “Enable host filesystem” option anymore since all the relevant files needed were included in the iso. If the game booted correctly, then you now had a single file you could distribute which was very convenient. It was now time to get my feet wet. Before attempting anything too complicated, my goal was to create a simple “Hello World” example where I would : Load some assets (In my case a font and an image). Set up a game loop that would run every frame. Animate a sprite using that game loop. Render text. Handle player input so I could move a sprite around. Before I could achieve any of these sub-goals, in main.js, I first defined a few constants that I would end up needing. This is where I learned that you could get the screen’s width and height by first using the Screen module available globally like all Athena provided modules (Meaning that no import statements were needed) and then calling the getMode method. Then, to have a stable frame rate and accurate FPS counting, I needed to call the methods setVSync() and setFrameCounter() With the setup completed, I wanted to load the font I used in my Sonic game and a Spritesheet of Sonic so that I could later animate it. I could achieve the following by creating an instance of the Font and Image classes offered by Athena. While I planned on handling player input later, I still needed a way to get the player’s controller so that my code could know when a given button was pressed. This was made possible by using Athena’s Pads module. Before I could create a game loop, I needed to first write the setup code required to animate my spritesheet. Since all the frames where contained within a single image, I had to find a way to tell Athena what part of the image was to be rendered. To achieve this, I first spent some time to get familiar with the shape of the sprite object created earlier. It turned out that we could set the width and the height of the sprite by modifying the properties of the object with the same names. It also turned out that you could tell Athena what portion of the image to draw by setting the startx, endx, starty, endy properties. For example, if you had the following values : startx = 0, endx = 32, starty = 0 and endy = 44 you would get the first frame rendered. This is because in the spritesheet, every frame has a width of 32 and a height of 44. Also, the origin (0,0) corresponds to the top-left corner of the spritesheet. Now that I knew how to display a single frame within a wider image, I used the following logic to setup Sonic’s run animation. I first created an object called spritePos to set the position of the sprite on the screen. This was needed to be able to move it around when the player would press directional buttons on the D-pad. More on that later. Then I would set the sprite’s width and height to correspond to the width and height of a single frame which was 32x44 pixels. Since I wanted the sprite to appear big enough, I multiplied the width and height by a value defined by the SCALE constant we set earlier in our code. The next step consisted in creating an array called runAnimFrames which would describe each frame of Sonic’s run animation using an object with the startx, endx, starty and endy properties. We then had a frameIndex variable which would determine the current frame to display. The frameDuration constant would be used to set how long in miliseconds to display each frame. The lower the number the higher the frame rate of the animation because we would flip through all the frames faster. Finally, I initialized a timer coming from a custom Timer class that I added in my src folder and imported here. The full code is available in the template mentioned earlier. The timer would end up being crucial to know when it was time to move on to displaying another frame. Now that we had our animation logic setup done, it was time to render the animation. For this purpose, I needed a game loop that runs every frame. In Athena, we could achieve this by calling the display method available under the Screen module. In an if statement we would check if the timer had exceeded the time allocated to displaying the current frame. If it was the case we would move on to the next frame by incrementing the frameIndex as long as it was within the bounds of the runAnimFrames array, otherwise, we would set it back to 0 to display the first frame. This was to achieve a looping animation. Then, on every iteration of the game loop we would set the sprite’s startx, endx, starty, endy properties to correspond to the ones of the current frame. Finally, to render the sprite, we needed to call its draw method and pass to it the coordinates where you wanted to display it on the screen. Now that I had a game loop, I could finally handle user input by making sure that the sprite would move in different directions depending on which button was pressed. This could be easily achieved with a few if statements. You might be wondering where is deltaTime? For those unfamiliar, deltaTime is a value representing the time elapsed between the current frame and the previous frame in a game. It’s often used to make the movement of objects, frame rate independent. Meaning that if your game runs at a lower or higher frame rate, an object, like a character, will still move at the same rate. To achieve frame rate independence, you would usually multiply your movement code by deltaTime. The reason it was absent here, is because when creating a game loop using the display method, this matter is taken care of under the hood. Now that I could move Sonic around, I still needed him to face the correct direction because at this point, he would look right even If I moved him to the left. To implement this, I decided to go with a common technique in pixel art based games, which consisted in mirroring (or flipping) the sprite. To achieve this in Athena, you simply needed to provide a negative width or height to the sprite depending on what axis you wanted the mirroring to take effect on. For flipping a sprite horizontally, providing a negative width was enough. However, an issue arose! If you flipped the sprite, it would not flip in place since it would flip according to the sprite’s origin which was its top-left corner. This meant that it would move the sprite to the left after mirroring. To fix this issue, you only needed to subtract an offset to the x coordinate of the flipped sprite that corresponded to its width. Now that the issue was solved, I created variable called spriteIsFlippedX to know when to flip or unflip the sprite. The logic can be see below : Now, when you moved sonic to the left, he would face left and face right when moved to the right. There was still one thing I wanted to try out before wrapping up my Hello World example and that was text rendering. The first thing I wanted to render onto the screen was an FPS counter. It turned out that the FPS counter in the PCSX2 emulator is not accurate, however, Athena provides the getFPS() method available on the Screen module to accurately determine the frame rate. To display some text, you needed to first create a font object using the Font constructor. It would take either a path to a font that can be in a .ttf format or the string “default” if you wanted to use the default font available on the system. Once created, the font object had a print method that you could use within the game loop to tell the PS2 what to render and where on the screen. Finally, my Hello World example was finished. Now that you’ve been introduced to Athena, you might be tempted to try it out for yourself. In that case, I really recommend looking at the Sonic infinite runner Athena port’s code as you’ll learn a lot about concepts that I did not have time to cover here. Link to the repo here : https://github.com/DevWill-hub/Sonic-Infinite-Runner-PS2 Link to my Athena template : https://github.com/JSLegendDev/Athena-PS2-Template Link to the Athena project : https://github.com/DanielSant0s/AthenaEnv Additionally, I recommend joining the official Athena discord where you’ll be more likely to receive help when stuck. You can join here : https://discord.gg/cZUH5U93US Before wrapping up this post, you might have found strange that nothing was mentioned about 3D considering that the PS2 was mostly known for its 3D games. This is for 2 reasons. First, I’m a novice in terms of 3D game develoment, I have never done it before. Second, to my understanding, Athena has both 2D and 3D capabilities but version 4 which has more of a 3D focus is currently in development. I thought it would have been preferable to wait until v4 was stable before diving into PS2 3D gamedev in JavaScript. However, there are a few 3D demos you can check if you’re interested. Links down below. https://github.com/ps2devnoob/3D-Robot-DEMO-PS2 https://github.com/ps2devnoob/BurnTrack-PS2-DEMO https://github.com/ps2devnoob/AthenaENV-PS2-Samples To conclude, Athena is a cool project allowing you to make real PS2 games in JavaScript. If you learned something new and enjoy technical posts like this one, I recommend subscribing to not miss out on future releases. Subscribe now In the meantime, if you feel inclined, you can read the post below. I recently discovered that you could make PS2 games in JavaScript. I’m not even kidding, it’s actually possible. I was working on a project and had my phone near my desk when I received a notification. Upon further inspection, it came from itch.io which was a platform where I usually published most of my web games. Under my relatively popular Sonic infinite runner game which was made in JavaScript and developed a year ago, I received a comment from someone with the username Dev Will which claimed they had made a PS2 version of my game and provided the GitHub repo of the source code. At first, I thought that it was cool that someone took the time to remake my game for an old console that had a reputation to be hard to develop for and probably required them to write a lot of C or C++. Out of curiosity, I opened up the GitHub repo and was astonished to see that the project was not using even a bit of C++ or C but was entirely in JavaScript! If making PS2 games were easier than I thought since I could use a higher level language like JavaScript, I could probably try making one in a reasonable amount of time and play it on a retro handled or an actual PS2. How cool would that be? This is where I knew I had to drop everything I was doing to investigate how this was possible. The AthenaEnv Project Since the dev behind the project was Portuguese speaking (I assume they were either from Brazil or Portugal), they wrote the Readme of the repo in Portuguese which was a language I did not understand. Fortunately, I was still able to decipher most of what was written because I had done 3 years of Spanish in school and spoke French natively. Since Portuguese is a romance language like Spanish and French, I was fortunately not totally lost. Anyway, The readme said that the engine used to make the PS2 version of my game was called AthenaEnv with a conveniently placed link towards it so I could learn more. As with the Sonic Infinite Runner PS2 project, this engine was also open source and its repo had a very detailed readme written in English. To summarize, Athena was not what we commonly refer to as a game engine but an environment that also offered a JavaScript API for making games and apps for the PS2. It embedded a slightly modified version of QuickJS which was a small and embeddable JavaScript engine. This explained how Athena was able to run JavaScript code on the PS2. Therefore, Athena was the PS2 native program written in C that took your JavaScript code, passed it through the QuickJS engine to interpret it and finally, ran the relevant logic on the system. What made it compelling was not that it just ran JS on the PS2 but that it offered an API suitable for game development. It covered : Rendering : Allowing you to display sprites, text, shapes, etc… on the screen and animate them using a game loop. Asset loading : Allowing you to load images, sounds, fonts, etc... Input handling : Allowing you to receive player input from a controller, multiple ones or even from a mouse and keyboard since the PS2 supported these input methods. File handling : Allowing you to write save files among other things. Sound playback : For playing Sound. Now, the game looked a bit blurry which was expected since this was supposed to emulate a PS2 which had a small resolution. Fortunately, I was able to make things more comfortable by upping the resolution in the graphics settings of the emulator. The dev process also seemed quite straightforward. You would only need to open the folder containing all the relevant files (athena.elf, main.js, etc…) in a code editor like VSCode and open athena.elf in the emulator. Now, you could make changes to your JS code and once you were ready to test, you would go under the PCSX2 system tab and click on reset. This would restart the emulator and you could see the latest changes. While not as seamless as in web development with hot reloading, it still was a relatively fast iteration cycle. It’s at that moment, that I knew had to make a post about it and share this awesome project with you. However, I still felt uneasy about one thing. Nowadays, people download PS2 games as .iso files. For most games, you only need one .iso file that you then open in your emulator. Less technical people can therefore more easily enjoy these older titles. However, to run the Sonic infinite runner game “port”, I needed to not only check a box in the settings but also needed the entire project’s folder containing the Athena executable and the source code. I wondered if instead, there was a way to distribute the game as a single .iso file. This is were I simply went back to the itch.io comment section and asked if it was possible. After a thorough back and forth that continued on Discord, the process to convert my files into a single iso, I could distribute, was now clear. Making an .iso File To make an iso you needed the following files : athena.elf : Which is the Athena executable. athena.ini : For configuring the project’s entry point. A JS file acting as the entry point of the codebase. The rest of your source code if your code is more than one file, oftentimes it’s in a folder called src. Two files one named ATHA_000.01 and the other SYSTEM.CNF needed to make the iso bootable. Load some assets (In my case a font and an image). Set up a game loop that would run every frame. Animate a sprite using that game loop. Render text. Handle player input so I could move a sprite around. Now that I knew how to display a single frame within a wider image, I used the following logic to setup Sonic’s run animation. I first created an object called spritePos to set the position of the sprite on the screen. This was needed to be able to move it around when the player would press directional buttons on the D-pad. More on that later. Then I would set the sprite’s width and height to correspond to the width and height of a single frame which was 32x44 pixels. Since I wanted the sprite to appear big enough, I multiplied the width and height by a value defined by the SCALE constant we set earlier in our code. The next step consisted in creating an array called runAnimFrames which would describe each frame of Sonic’s run animation using an object with the startx, endx, starty and endy properties. We then had a frameIndex variable which would determine the current frame to display. The frameDuration constant would be used to set how long in miliseconds to display each frame. The lower the number the higher the frame rate of the animation because we would flip through all the frames faster. Finally, I initialized a timer coming from a custom Timer class that I added in my src folder and imported here. The full code is available in the template mentioned earlier. The timer would end up being crucial to know when it was time to move on to displaying another frame. Creating our Game Loop Now that we had our animation logic setup done, it was time to render the animation. For this purpose, I needed a game loop that runs every frame. In Athena, we could achieve this by calling the display method available under the Screen module. In an if statement we would check if the timer had exceeded the time allocated to displaying the current frame. If it was the case we would move on to the next frame by incrementing the frameIndex as long as it was within the bounds of the runAnimFrames array, otherwise, we would set it back to 0 to display the first frame. This was to achieve a looping animation. Then, on every iteration of the game loop we would set the sprite’s startx, endx, starty, endy properties to correspond to the ones of the current frame. Finally, to render the sprite, we needed to call its draw method and pass to it the coordinates where you wanted to display it on the screen. Handling Player Input Now that I had a game loop, I could finally handle user input by making sure that the sprite would move in different directions depending on which button was pressed. This could be easily achieved with a few if statements. You might be wondering where is deltaTime? For those unfamiliar, deltaTime is a value representing the time elapsed between the current frame and the previous frame in a game. It’s often used to make the movement of objects, frame rate independent. Meaning that if your game runs at a lower or higher frame rate, an object, like a character, will still move at the same rate. To achieve frame rate independence, you would usually multiply your movement code by deltaTime. The reason it was absent here, is because when creating a game loop using the display method, this matter is taken care of under the hood. Mirroring a Sprite Now that I could move Sonic around, I still needed him to face the correct direction because at this point, he would look right even If I moved him to the left. To implement this, I decided to go with a common technique in pixel art based games, which consisted in mirroring (or flipping) the sprite. To achieve this in Athena, you simply needed to provide a negative width or height to the sprite depending on what axis you wanted the mirroring to take effect on. For flipping a sprite horizontally, providing a negative width was enough. However, an issue arose! If you flipped the sprite, it would not flip in place since it would flip according to the sprite’s origin which was its top-left corner. This meant that it would move the sprite to the left after mirroring. To fix this issue, you only needed to subtract an offset to the x coordinate of the flipped sprite that corresponded to its width. Now that the issue was solved, I created variable called spriteIsFlippedX to know when to flip or unflip the sprite. The logic can be see below : Now, when you moved sonic to the left, he would face left and face right when moved to the right. Rendering Text There was still one thing I wanted to try out before wrapping up my Hello World example and that was text rendering. The first thing I wanted to render onto the screen was an FPS counter. It turned out that the FPS counter in the PCSX2 emulator is not accurate, however, Athena provides the getFPS() method available on the Screen module to accurately determine the frame rate. To display some text, you needed to first create a font object using the Font constructor. It would take either a path to a font that can be in a .ttf format or the string “default” if you wanted to use the default font available on the system. Once created, the font object had a print method that you could use within the game loop to tell the PS2 what to render and where on the screen. Finally, my Hello World example was finished. Now that you’ve been introduced to Athena, you might be tempted to try it out for yourself. In that case, I really recommend looking at the Sonic infinite runner Athena port’s code as you’ll learn a lot about concepts that I did not have time to cover here. Link to the repo here : https://github.com/DevWill-hub/Sonic-Infinite-Runner-PS2 Link to my Athena template : https://github.com/JSLegendDev/Athena-PS2-Template Link to the Athena project : https://github.com/DanielSant0s/AthenaEnv Additionally, I recommend joining the official Athena discord where you’ll be more likely to receive help when stuck. You can join here : https://discord.gg/cZUH5U93US How About 3D? Before wrapping up this post, you might have found strange that nothing was mentioned about 3D considering that the PS2 was mostly known for its 3D games. This is for 2 reasons. First, I’m a novice in terms of 3D game develoment, I have never done it before. Second, to my understanding, Athena has both 2D and 3D capabilities but version 4 which has more of a 3D focus is currently in development. I thought it would have been preferable to wait until v4 was stable before diving into PS2 3D gamedev in JavaScript. However, there are a few 3D demos you can check if you’re interested. Links down below. https://github.com/ps2devnoob/3D-Robot-DEMO-PS2 https://github.com/ps2devnoob/BurnTrack-PS2-DEMO https://github.com/ps2devnoob/AthenaENV-PS2-Samples

0 views
JSLegendDev 4 months ago

How do Devs Make Levels Without Game Engines?

The story of how I started game development is quite unusual, which led me to not using game engines and allowed me to get familiar with alternative tooling. When I reached the age to go to university, I chose computer science as my major not to learn to make games but with the aim of studying AI. However, by the time I ended up graduating, my interest for AI had completely vanished. The math needed for it did not interest me. Instead, I used to spend my time building web related projects which I then put on my resume and was able to land a job as a software developer. In that job, we used a JavaScript and TypeScript based stack. It was during that time that I came across a nice little library called Kaboom.js. It would end up later as KAPLAY. This library would allow you to make 2D games quickly using either JavaScript or TypeScript. As someone with no real experience in game dev, this was right up my alley as I already had the prerequisites to learn this library quickly. Considering that this was a library and not a game engine, the development setup was quite familiar with what I was used to in my day job (at the very least the frontend portion of it). You would write your code in an editor and look at the preview in your browser. My first few games were simple, they would take place in a single scene and I would hard code the positions of game objects into my code. As my projects got bigger, I started to use a feature provided by Kaboom.js allowing me to describe the layout of a level using an array of strings. For each character, you could tie a specific game object that would be spawned according to where the character was located in the string array. While this was much better than hard coding coordinates, it became tedious when I wanted to make maps with multiple layers since each layer was represented with its own array of strings. Therefore, I wouldn’t know how the map would really look like unless I ran the code. It also felt annoying to reposition objects by adding spaces in a string or removing them. Dissatisfied with the string-based approach, I decided to search how others tackled the problem. I first got familiar with how game engine users did things. For Unity, Godot or Unreal they were all provided a built-in editor where they could easily place objects around and run their games. It dawned upon me why people preferred to use ready made game engines due to how convenient this was but I didn’t feel like spending the time to learn an engine just for this. However, if I couldn’t find a solution that was better then what I had, I would probably bite the bullet and start learning game dev from scratch with one of the major engines. I then came across developers with an approach that seemed a bit crazy. They just decided to invest time in building their own tooling for map/level making. As a programmer, while I appreciate the exercise, I felt that this was not worth my time unless, I had really no options and with the goal of making an editor that was general enough that I could sell as a product to other developers. This is where I wondered: Has no one made a general-purpose editor for map-making that can be used by anyone working with libraries and game frameworks rather than engines? This prompted me to do some research, I came across 3 options : Ogmo Editor I first found out about Ogmo when I tried learning the HaxeFlixel game framework. In the official tutorial, they used Ogmo to design the tutorial game’s map. Since, I didn’t end up pursuing HaxeFlixel much further. I kind of forgot about Ogmo and moved on. As for LDTK, it was an editor made by the developers behind the popular game Dead Cells. It looked nice, but I ended up using Tiled because it was the most popular. According to their home page, Tiled was used to design the levels of popular indie games like Shovel Knight and Axiom Verge amongst others. However, the reason that really pushed me over to use it over other options, was the fact that someone had written a plugin to integrate maps made with Tiled with the library I was using. At first, Tiled’s interface looked ugly and not very intuitive but ultimately the way it worked was relatively simple. You first started by creating a map by setting the size of each tiles and its width and height. You then, imported your tileset and finally created a first tile layer on which you could draw using tiles from that tileset. An arbitrary number of layers could then be created to appear on top of the first one allowing you to design complex looking maps. In the inspector, you could rename layers and change their order, which would determine the order in which they would be rendered. In addition to being able to create tile layers, you could create a second type of layer called an object group. This layer type could be used to place collider shapes which you could use in your code to determine which parts of the map were walls. Alternatively, you could also place pins that could be used to position players, enemies and other game objects in your map. Finally, you would be able to export your map into various formats depending on your needs. After having learned how to use Tiled, I had finished creating a test map and was eager to test the plugin that would allow me to render it in my game. Unfortunately, I realized that the plugin didn’t work! I concluded that the plugin expected an older deprecated version of the library explaining why it didn’t work. Kaboom.js (now KAPLAY) also didn’t have an official way of importing maps made with Tiled and still doesn’t. I therefore felt pretty much stuck. I tried reading the code of the plugin but didn’t understand much of it. I was about to give up feeling frustrated of not being able to achieve my goals because the people maintaining the software I was using did not take into account my use case and why would they? They maintained the library for free. They wrote the plugin for free. It was being dependent to them that felt annoying. I did not want to wait for someone else to do the work before I could continue my projects. I also wasn’t your average software user anymore. Software is often thought as a black box. Developers know the internals and users just use the software from the outside via an interface. However, I had a computer science degree, I knew how to code, why should I wait and not fix my pain point myself. That’s because jumping into a new codebase as a dev is often still intimidating. You don’t have the context that led to various decisions around the code and you need to get familiar with a lot at once which always made me wary of trying to fix things myself for open source software I was using. I really needed a good reason to justify the effort. However, this time was different, I was motivated and didn’t even need to jump into a codebase. What I needed to do was to learn how the Tiled export file worked so I could parse it in my game code and render the map. I was initially intimidated by the various file formats you could use as exports. However, I was relieved to discover that you could export your map as a JSON file. For those unfamiliar, JSON is a relatively simple format composed of an object with an arbitrary amount of key value pairs where keys are called properties. It’s inspired by the way objects are created in JavaScript and that’s why it’s called JavaScript Object Notation (JSON). It’s an incredibly common format used a lot in web development. For example, it’s often used by backends and third party APIs to send data upon being requested by frontends or users of those APIs. Knowing that we could export the map as a JSON file allowed me to answer a big question I had in mind. How to import a map made with Tiled in my codebase? As with regular API requests, I simply needed to use the JavaScript function to fetch the file and then convert its response using the method which allowed me to load the JSON into an actual JavaScript object I could use in my code. With this out of the way, the next step was to figure out how the export file was structured so I could know how to render the map. At first the , , and properties stood out. They would allow me to compute how many tiles per row and per columns needed to be rendered. In the end, however, only the and the properties where really needed since I would render the tiles from left to right, top to bottom. I could get by only knowing how many tiles were required per row. When I ended up rendering the expected number of tiles per row, I would move down by a tile span to start rendering the next row below. Since the and properties had the same values due to the tiles being squares, I only needed to use one to know where to place the next tile and by how much to move down to render the next row. However, the most important property was the one called . As the name implied, its value contained an array of objects containing all the needed data for each layer. A tile layer was represented with an object that had the property. Its value was an array of numbers. At first I did not understand what it meant but then it became obvious. In Tiled, each tile in the imported tileset will be assigned a number starting with 1 and then increasing from left to right, top to bottom. The array assigned to the “data” property would contain 0 if no tile needed to be displayed therefore denoting an empty space. Otherwise, the number would determine which tile from the tileset should be rendered for a given tile in the map. If I wanted to render a tile layer in my game, I would simply need to iterate through that array. Each iteration, I would compute the position where the tile needed to be by computing the result of the previous tile position + the tilewidth. Then, I would determine, which tile from the tileset needed to be drawn there. If the number was 0, nothing would be rendered and we would proceed to the next iteration. While writing the rendering logic for tile layers, I experienced a problem with the way Tiled numbered tiles in the tileset compared to Kaboom.js. In Tiled, numbering starts from 1 since 0 is an empty tile. However, in Kaboom.js, numbering starts from 0. I was able to get around this issue relatively easily by just making sure that when I needed to draw a tile, I would subtract the number in the data array by 1 to get the correct tile to display. As for when 0 occurred, as mentioned previously, I would just skip to the next element of the array after having updated the next tile position as to avoid the issue of trying to display a tile numbered 0 - 1 = -1, which wasn’t a valid index. Finally, because I knew how many tiles were needed per row due to having access to the and properties, I was able to determine when to move to the next row despite the array being one dimensional. After having tested my code, I was able to successfully render the map onto the game’s canvas and felt overjoyed and relieved that it wasn’t as complicated as I thought it was going to be. Finally, the last step was to render the object group layers which I would use to implement walls, obstacles and where to spawn game objects like enemies. The object used to describe an object group layer in the “layers” array had a different shape than the one for tile layers. Rather than having a property, we had an property, which instead of referencing an array of numbers, was referencing an array of objects. Each of these objects would have important properties like , and , which was all I needed to determine where to place them in my game. I would therefore, simply iterate through the array and create the needed game objects with the needed width and height and place them according to the x and y coordinates provided. Since I set them up to be invisible, the player wouldn’t be able to see them, however, since Kaboom offered a debug mode, you could see their outline by pressing the f1 key. I had finally completed the task I set out to do, and now had a more flexible way of making levels for my games. I still use this workflow to this day. To conclude, game devs not using game engines, either write their own map editor or use an already existing one like Tiled to design and make their levels more efficiently. If you’re interested in using JavaScript and the same library (now called KAPLAY) to make games, I have plenty of tutorials you can watch on my channel . I also want to mention, that I made an exclusive step-by-step tutorial available on Patreon where I teach you how to render maps made in Tiled in your KAPLAY games in a performant and modular fashion so that you can use the same code in other KAPLAY projects. This 40 minutes tutorial, will tackle more nuanced topics. For example, how to render polygonal colliders (useful for slopes) which differ from regular rectangular colliders. In addition, I have also bundled my paid mario-like asset pack as part of this tutorial. You can access the tutorial here . If you liked this post and want to see more of it, I recommend subscribing as to not miss out on future posts. Subscribe now In the meantime, you can read this next. The story of how I started game development is quite unusual, which led me to not using game engines and allowed me to get familiar with alternative tooling. When I reached the age to go to university, I chose computer science as my major not to learn to make games but with the aim of studying AI. However, by the time I ended up graduating, my interest for AI had completely vanished. The math needed for it did not interest me. Instead, I used to spend my time building web related projects which I then put on my resume and was able to land a job as a software developer. In that job, we used a JavaScript and TypeScript based stack. It was during that time that I came across a nice little library called Kaboom.js. It would end up later as KAPLAY. This library would allow you to make 2D games quickly using either JavaScript or TypeScript. As someone with no real experience in game dev, this was right up my alley as I already had the prerequisites to learn this library quickly. Considering that this was a library and not a game engine, the development setup was quite familiar with what I was used to in my day job (at the very least the frontend portion of it). You would write your code in an editor and look at the preview in your browser. My first few games were simple, they would take place in a single scene and I would hard code the positions of game objects into my code. As my projects got bigger, I started to use a feature provided by Kaboom.js allowing me to describe the layout of a level using an array of strings. For each character, you could tie a specific game object that would be spawned according to where the character was located in the string array. While this was much better than hard coding coordinates, it became tedious when I wanted to make maps with multiple layers since each layer was represented with its own array of strings. Therefore, I wouldn’t know how the map would really look like unless I ran the code. It also felt annoying to reposition objects by adding spaces in a string or removing them. Dissatisfied with the string-based approach, I decided to search how others tackled the problem. I first got familiar with how game engine users did things. For Unity, Godot or Unreal they were all provided a built-in editor where they could easily place objects around and run their games. It dawned upon me why people preferred to use ready made game engines due to how convenient this was but I didn’t feel like spending the time to learn an engine just for this. However, if I couldn’t find a solution that was better then what I had, I would probably bite the bullet and start learning game dev from scratch with one of the major engines. I then came across developers with an approach that seemed a bit crazy. They just decided to invest time in building their own tooling for map/level making. As a programmer, while I appreciate the exercise, I felt that this was not worth my time unless, I had really no options and with the goal of making an editor that was general enough that I could sell as a product to other developers. This is where I wondered: Has no one made a general-purpose editor for map-making that can be used by anyone working with libraries and game frameworks rather than engines? This prompted me to do some research, I came across 3 options : Ogmo Editor

0 views
JSLegendDev 4 months ago

The Struggle of Packaging a JavaScript Game for PC

The story begins after I’d spent some time making web games in JavaScript. At the time, I was looking at the various monetization options that were available. Unfortunately, I came to realize that the only “realistic” option was to make casual games for web game portals and monetize via ads. Something I did not find enticing. I also wanted to make deeper games which required saving user data. While you can save your player’s data in the user’s browser, this wasn’t reliable as the data could get deleted when the browser cache was cleared. I also didn’t want to pay for servers just to save user data for a single player game. I also didn’t want to switch to other tools for 2 reasons : The first, was that I was getting more and more proficient and comfortable with making games in JavaScript using the KAPLAY game library. Every other option I looked into would’ve taken me longer to achieve the same result than in KAPLAY. I also didn’t want to start from scratch learning a new set of tools as it would hinder my game dev progress. I wanted to just build games. As for the second reason, I felt that making games in JavaScript wasn’t a disadvantage since they would immediately and smoothly run in the browser allowing me to reduce considerably the friction to get new players. My strategy was therefore, making the game available on the web as a demo to gauge interest while putting the full release on Steam. This is where things got complicated. How would I convert my web game into something that can be installed and played offline on one’s PC? After doing some research, I came across 5 technologies I could potentially use : Electron is a technology that allowed developers to build desktop apps using HTML, CSS and JavaScript. Which was perfect since, my games are based on these technologies. To achieve this, Electron would package your app with a whole Chromium browser. Your app would run in that browser. That meant every time you installed an app made with Electron, you were also installing a Chromium browser. Since, Chromium also consumed a lot of memory when running, apps made with it gained a reputation of being bloated. Some users started complaining but still, it did not prevent Electron to gain market share. However, this is irrelevant for games since people are more forgiving when it comes to resource usage. Most major apps built today that are cross platform (meaning works on Mac, Windows, and Linux) are likely made with Electron. This is the case for VSCode (arguably the most popular code editor), Discord, Slack and many others. You might be wondering why it’s this popular? This can be boiled down to three points. Web developers can use a familiar set of tools to build cross-platform desktop apps rather than having to learning tools specific to each operating system. You have access to most packages and libraries you can find on NPM which means you can build apps really fast. The fact that it packages a specific Chromium version with your app implies that you can rest assured that your app will render and behave the same way regardless of the user’s operating system. This means less testing is required and therefore less headache on your part. Despite all of these conveniences, when trying Electron, I found the dev experience quite annoying. First of all, the way Electron is designed, makes you separate your app into two portions. You could view this as your “backend” and “frontend”. The “frontend” portion, runs JavaScript that can run in the browser and interacts with the HTML. The “backend” portion, runs JavaScript within Node.js and does not have access to the UI. If you check my previous post : How to Start Making Games in JavaScript with No Experience , I explain in more details how JavaScript runs in two major environments. I recommend checking it out for more details (Video version is also available here .) but to summarize, JavaScript could only be used in the browser before Node.js was invented. With the advent of Node.js, you could now use JavaScript for backend applications that could run directly on a computer rather than within a browser. While Electron’s approach of separating these two environments makes sense for more professional desktop app development, it does make wrapping a web game as a desktop app more tedious. The reason is that to communicate between your “frontend” JS and your “backend” JS within an Electron app, you would need to use a IPC brigde to share only what is needed between the two. I mostly wanted to do simple things like write my user’s save data in a file on their computer and read that same file. I found doing this in Electron more complex than what other alternatives would end up offering. I didn’t know this at the time. However, The reason I left Electron was due to the whole packaging and building executables process which I found cumbersome. I tried using Electron Forge which was supposed to help you set up a full build pipeline. I tried using this tool because it was the one linked in the official Electron tutorial docs. What happened after trying to set it up, is that I could only build the app for the platform I was developing on. Since I was developing on a Mac and I’m making a game, having at least a Windows executable is mandatory. (Most gamers use Windows and even Linux since the advent of the Steam Deck and not Macs. I use a Mac mainly for the development experience which I find nicer than on Windows.) Now, Electron Forge is supposed to allow you to build for the 3 major operating systems if you’re on a Mac. However, I experienced all kinds of issues for the Windows build as I needed to install WINE (which is a compatibility layer for running Windows software on Unix-like systems). In the end, I was left with two choices, either continue to try to make it work (maybe I would have succeeded with other tools like Electron Builder) or look for alternatives. I decided to look for alternatives and only come back if nothing else suited my needs. The second option I explored was Tauri. It was gaining popularity online and I found the way it approached desktop app development unique. Instead of wrapping a browser with your app like Electron did, it would simply use the webview available on the user’s operating system. At first, this sounded great, you get apps that are less bloated in size and trying it out, I found it easy to read/write files to the filesystem. This was due to Tauri having a JavaScript API you could use for simple things like that. However, for anything more complex, you would have to write Rust. Rust is a programming language that’s quite complicated. It’s mainly used for lower level development like operating system kernels. I didn’t want to learn Rust as I had no use for it. Initially, I thought that just using the JS API would be enough. Unfortunately, I didn’t consider something very important. This whole exercise I was engaged with consisted in packaging a web game to put it on Steam, therefore integrating with the Steam SDK would have been highly desirable. With Electron, I could have achieved this using the steamwork.js package but with Tauri I couldn’t use this package since the “backend” portion of the app required me to write Rust for anything more complicated than what the JS API offered. That was a deal breaker. The fact that it used the default webview to render the app also meant that it wouldn’t render the same across platforms since every operating system had a webview based on a different engine. For Windows, it’s based on Edge which is now Chromium based. For Mac, it’s based on WebKit which is what is used under the hood for Safari. For Linux, it’s based on WebKitGTK which is probably similar to WebKit, but I’m not sure. Regardless, the way a Tauri app was rendered would force me to do extensive testing on all three platforms to make sure the game was rendered the same way which would just add to my workload compared to something like Electron. That was another deal breaker. The last deal breaker was that, when I last used Tauri, you could only generate the executable for the current platform you were developing on. Therefore, I would have to run a Linux distro to build for Linux, run MacOS to build for Mac and run Windows to build for Windows. It was time to look for something else. While I was looking up other alternatives, I came across Neutralinojs and Wails. While they didn’t end up fitting my needs in much the same way as Tauri, I thought it would be interesting to mention them. Neutralinojs is like Tauri but without Rust. It has a JavaScript API for doing simple things like writing to a file, reading a file, etc… For anything more complex, you could hook up a separate backend in any language you want using web sockets. Wails is also like Tauri but with Golang instead of Rust. Due to Go’s portability, it was a breeze to set up and to build executables even though the last time I tried it, I was not able to build for Linux, just Windows and MacOS. Golang is also much more approachable as a language than Rust. It doesn’t have a JS API you can use unfortunately. Integrating your game with the Steam SDK is a big question mark since Golang isn’t very used in game dev. Maybe there is a dedicated Go module for this? After getting a bit frustrated with all the options I tried, I was about to go back to Electron when suddenly, I had an epiphany. Why not look up what existing JavaScript based games on Steam where using? I proceeded to go on the SteamDB website which compiled Steam related data. Among the data gathered was a list of engines and tools used by released Steam games. The most popular JavaScript related tool was NWJS with over 5700 games using it. For reference: There are over 55000 games made with Unity. There are over 16900 games made with Unreal. There are over 2600 games made with Godot. The info mentioned above can be found here . I was initially caught off guard by this data because it seemed to indicate, at first glance, that JavaScript was really popular on Steam. This was unexpected because JavaScript is still viewed as a niche language amongst game developers. After doing some research, I figured out the reason NW.js was used as much as seen on SteamDB was because RPG Maker and Construct were both popular (the former more than the latter) no-code game engines built on web technologies (JavaScript). They would use NW.js to allow their users to export their games as desktop executables. This implicitly meant that this technology was suitable to use since it was battle tested with so many examples in the wild. I had initially written off NW.js as a deprecated (even though still maintained) version of Electron since it was created before it and used the same principle of bundling a Chromium instance with your app. However, after having tried it. It’s really simple to use compared to Electron. First of all, it does not make a separation between the JavaScript that runs in the browser and the one that runs in Node.js. There is no “backend” and “frontend” to worry about and no IPC bridge to set up between the two. Writing logic to save data to some file and reading that file back is really simple. Secondly, building executables for all 3 platforms is also very easy. When you go on the official NW.js website, you download the needed pre-built binaries for the platforms you need to build for. Then, you place it in a folder and create a new folder at the same level as the executable with the name . Finally, you put your source code in that folder. When you click on the executable, it runs your app using the source code located in the folder. That’s it. Now, you can run a command to merge the folder with the executable so it appears as a single .exe that you click and run. What’s left is just to rename the executable with the name of your game and maybe also change the icon with a third-party tool. You might be wondering, doesn’t this process make it easy for someone to just pirate your game? The answer is yes but it doesn’t matter. The Love2D game framework also works similarly. It didn’t prevent Balatro, a popular game using this framework to make millions even though the source code was easily accessible. After trying all of the technologies mentioned previously, I came to the conclusion that NW.js is the easiest technology by far for wrapping a JavaScript based game for desktop. You’re thinking of giving it a try? In that case, I have an exclusive 1-hour tutorial teaching you how to use NW.js to make a space shooter game for Mac, Windows and Linux. In this tutorial, I’ll teach you : How to use Vite and NW.js together, so that you can develop for the web (therefore benefiting from hot reloading) while still being able to build an executable that will work as is. We can achieve this by using Vite’s environment variables allowing us to gate code which will run in the desktop version exclusively. This is useful when it comes to writing and reading files from/to the disk which is code that should run only in the desktop version. How to use NW-Builder to create a script to build executables programmatically rather than having to manually download binaries from the NW.js website. How to make a space shooter game using KAPLAY. If you’re interested, check it out here . If you enjoyed this post and want more like it, I recommend subscribing so you won’t miss out on future publications. Subscribe now Meanwhile, you can check these out. The story begins after I’d spent some time making web games in JavaScript. At the time, I was looking at the various monetization options that were available. Unfortunately, I came to realize that the only “realistic” option was to make casual games for web game portals and monetize via ads. Something I did not find enticing. I also wanted to make deeper games which required saving user data. While you can save your player’s data in the user’s browser, this wasn’t reliable as the data could get deleted when the browser cache was cleared. I also didn’t want to pay for servers just to save user data for a single player game. I also didn’t want to switch to other tools for 2 reasons : The first, was that I was getting more and more proficient and comfortable with making games in JavaScript using the KAPLAY game library. Every other option I looked into would’ve taken me longer to achieve the same result than in KAPLAY. I also didn’t want to start from scratch learning a new set of tools as it would hinder my game dev progress. I wanted to just build games. As for the second reason, I felt that making games in JavaScript wasn’t a disadvantage since they would immediately and smoothly run in the browser allowing me to reduce considerably the friction to get new players. My strategy was therefore, making the game available on the web as a demo to gauge interest while putting the full release on Steam. This is where things got complicated. How would I convert my web game into something that can be installed and played offline on one’s PC? After doing some research, I came across 5 technologies I could potentially use : Electron Web developers can use a familiar set of tools to build cross-platform desktop apps rather than having to learning tools specific to each operating system. You have access to most packages and libraries you can find on NPM which means you can build apps really fast. The fact that it packages a specific Chromium version with your app implies that you can rest assured that your app will render and behave the same way regardless of the user’s operating system. This means less testing is required and therefore less headache on your part. There are over 55000 games made with Unity. There are over 16900 games made with Unreal. There are over 2600 games made with Godot. How to use Vite and NW.js together, so that you can develop for the web (therefore benefiting from hot reloading) while still being able to build an executable that will work as is. We can achieve this by using Vite’s environment variables allowing us to gate code which will run in the desktop version exclusively. This is useful when it comes to writing and reading files from/to the disk which is code that should run only in the desktop version. How to use NW-Builder to create a script to build executables programmatically rather than having to manually download binaries from the NW.js website. How to make a space shooter game using KAPLAY.

0 views
JSLegendDev 4 months ago

How to Start Making Games in JavaScript with No Experience

It’s been a while since I started making web games in JavaScript. In this post, I’d like to share tips that would be helpful for beginners wanting to do the same. This might sound obvious, but I really recommend learning to program before learning game dev. For JavaScript, that means learning the fundamentals of the language and how it integrates with HTML and CSS. Considering that JavaScript is primarily used on the web to make websites and web apps, I recommend that your first few projects be web related and not game related. Game development has a lot of domain specific knowledge to grasp. A beginner would easily be overwhelmed with having to learn programming, programming in JavaScript, HTML and CSS to make the web page on which the JavaScript will run and game development all at once. You don’t have to learn everything in JavaScript either, just the core fundamentals. You’ll learn more obscure features during practice when building projects. For some time, JavaScript was relegated to a scripting language used for making web pages built using HTML and CSS, interactive. However, when the Node.js runtime was released, it allowed devs to run JavaScript outside of the browser on their machines, similarly to other languages like C#, Python, etc… This enabled JavaScript to be used for more than just websites. There were now two major environments where JavaScript could run, in the browser and in Node.js. When creating a server side or command line application, you would write JavasScript that would run directly on the user’s machine with Node.js. However, even for JavaScript that was intended to run on a web page, you would still use Node.js, not to run the code but to install tools useful for transforming your JavaScript before it runs on a web page. This is due to an innovation that was brought with Node.js: The ability to download packages directly from a package manager called NPM (stands for Node Package Manager). The days where you needed to import a library by linking it using a script tag in your HTML were over. These tools, called bundlers would bundle the libraries you installed via NPM alongside your JavaScript code and compile them into a single compact JavaScript file that you could run within a web page. Developers would also write a more ergonomic version of JavaScript that had features not supported by browsers since they relied on the bundler to transpile whatever they wrote into JavaScript that was browser supported. For example, React, arguably the most popular library for building web UIs, allows you to author UI components in an easy fashion by using an HTML-like syntax within your JavaScript called JSX. However, JSX is not valid in JavaScript. That’s why the bundler will transform your JavaScript code using JSX into JavaScript that doesn’t so it can run in the browser while retaining the same functionality. At the end of the day, what can be done with JSX can still be done in regular JS but in a more verbose way. That’s why we let the bundlers deal with it to make our lives easier. All this to say, that today, most JavaScript developers, use a dev setup built around using Node.js and NPM even if the intended code is to run in the browser and not in Node.js. For gamedev, that implies installing Node, using a build tool like Vite, installing your game frameworks/libraries via NPM, and compiling/transpiling your code to a version that can run on the web. This is often called “your build”. This build is what you deploy on your website or on platforms like itch.io. You get a more streamlined experience from this setup since your Node.js based project keeps track of your libraries’ versions via a file called package.json (updating a library is one command away), you have access to hot reloading (meaning every time you modify your code, the change is immediately reflected which is a game changer for gamedev since this allows you to iterate quickly) and a local server is spun up automatically for you so that you can preview your project easily. The old way of doing things is still available if you wish to avoid that complexity. For example, you can download the JavaScript file for the library, link it to you HTML using a script tag and finally install an extension like live server in VSCode to benefit from a local server and hot reloading. However, you’d still need to manually keep track of what version of the library you’re using and manually have to download new versions and replace the existing one in your project’s folder which can become tedious. While using JavaScript for 2D games is viable, for 3D, it’s a different story. It’s very hard to compete with modern engines like Unity and Unreal which are more suited for creating 3D games and abstract away a lot of the complexity that comes with 3D. However, there are still 3D focused libraries like Three.js but there are more suited for 3D experiences that lives on a web page rather than full fledge games. I concede that it’s still possible to make a good looking 3D game if you’re able to come up with a unique art direction. As an idea you could try replicating the HD-2D artstyle of Square Enix. Putting 2D sprites into a low poly 3D world and add post processing effects. Games made in JavaScript are rendered within the HTML canvas element of a web page. By default, you have access to the canvas API allowing you to render graphics. For those unfamiliar, it’s similar to PyGame or Love2D where you have to basically write most things from scratch. While this is very good for learning and you’ll learn knowledge that is transferable to other lower level game dev environment, I don’t think it’s the way to go for beginners. At least, it depends on why you’re doing game dev. If you like the technical challenges that comes with making a game, using no libraries could be more fulfilling but unfortunately time consuming. However, if you care about results, meaning having finished games, it would be wiser to use a framework or a library that offers a lot out of the box. As a beginner you’ll be more likely to finish projects which will in turn increase your motivation and increase the likelihood that you stick with game dev long term. However, as opposed to using an engine like Unity, Godot, Unreal, using a frameworks still allows to you architect your codebase with a greater degree of freedom and prevents you from spending too much time learning how specific game engine workflows and UIs work. For JavaScript, I recommend going with KAPLAY due to it’s simplicity and intuitive API. I have a video explaining the library in 5 minutes that you can watch next. For something more established, Phaser is the dominant player and is more performant even though it has a steeper learning curve. Manually placing objects in your game via code will get tedious quickly and make you wish you used a proper game engine. Fortunately, there is a solution. You can use a map editor like Tiled or LDTK to create your game’s levels/maps visually like you would do in game engine. I really recommend investing the time to learn how to use one. When getting started, you might be tempted to follow a project based tutorial. There’s nothing wrong with that. Just stick with it from start to finish. Don’t hop between different tutorials. Doing this will only slow down your progress. Once you have completed following along, you can start building an original project that heavily leverages what was taught in the tutorial. It’s at that stage that you actually learn. Before that, you’re only getting exposed to various concepts without them being consolidated in your mind. You can check some of my tutorials here . Your game will sooner or later need graphics. At first, there is nothing wrong with using ready made asset packs. However, I think it’s worth the investment to learn how to make good pixel art since you’ll be able to make the sprites you need without having be dependent on an asset pack existing for your particular use case. A nice intermediate step between using asset packs and making your own sprites from scratch is to modifiy existing asset packs. This is actually very helpful in gradually developing an understanding of what makes good pixel art. Learning to modify asset packs well, is also very useful when you need to use multiple ones for a single project as it will allow you to make everything look consistent. In terms of software I use is Aseprite. It’s the most popular option. However, it doesn’t really matter what software you use as long as it works for you. You can check some of the pixel art I make on my itch.io page. Pixel art is an art form where you have the highest likelihood of making something that passes the professional quality bar in a reasonable amount of time. That’s why it’s my go-to art style. I recommend checking my pixel art for programmers video for more tips. This is now the standard advice parroted online but by making small games, you’re more likely to finish a project. This will in turn motivate you to continue game development and increase your skills for the next project, so on and so forth. If you lack ideas, try remaking existing simple games like pong, duck hunt, etc… I have tutorials on my channel you can follow. I recommend publishing your games not only on your own website (if you have one) but on platform like itch.io where people interested in games congregate. For itch.io, you might find it hard to find players if you just upload your game. That’s why I recommend joining a game jam as you’ll be more likely to get feedback on your games that way. Unfortunately, ways to successfully monetize web game development are rather limited. However you’re not limited to the web, you can transform your JavaScript web game into a desktop app that can be sold on Steam. The simplest way I’ve found of achieving this was to use the NW.js technology which is similar to the more popular Electron but much simpler to use. I have an exclusive step by step tutorial on Patreon teaching you how to make a downloadable desktop space shooter game with KAPLAY and NW.js for Mac, Windows and Linux. Check it out here! Hope these tips where useful in your JavaScript game development journey. If you’re interested in more content like this, consider subscribing to not miss out when I post something new. Subscribe now

0 views
JSLegendDev 5 months ago

The KAPLAY Game Library in 5 Minutes

Hi, in this post, I’ll try to explain what is KAPLAY and what are its major concepts in 5 minutes. Let’s get started! First of all, KAPLAY is an open source library for making games in JavaScript and TypeScrip t. It has built-in support for rendering, asset loading, input handling, animations, audio playback, collision detection and physics. Contrary to Phaser, KAPLAY isn’t OOP (Object-Oriented-Programming) based, instead it offers an entity component system (ECS) where you build game objects using components. Each component enables the game object to have access to certain features, properties and methods. In KAPLAY, a game object is created by passing an array of components to the add function. For example, using the sprite component will allow the game object to be rendered as a sprite. The area component will create a hitbox useful for collision detection. It can be seen using KAPLAY’s debug mode by pressing the key or the keys on a Mac. Finally, the pos component is used to set its position on the canvas. KAPLAY offers a bunch of ready-made components, increasing development speed. However, you can create your own components if you wish to. Additionally, passing a JavaScript object to the array of components, allows us to define custom properties and methods which removes the need to create classes like in the OOP (Object-Oriented Programming) paradigm. As for animations, KAPLAY offers an intuitive animation API. To load a spritesheet use the loadSprite function, pass to it a name you want to use to refer to the spritesheet, the path where the image is located and finally an object telling KAPLAY how to slice it into single frames. This object, should have the sliceX property determining the number of frames per row, the sliceY property determining the number of frames per column and an anims property containing an object for configuring animation data. To define an animation, you can pass a key object pair to the anims object. The key determines the name of the animation. The value can be an object with the from property containing the number of the frame where the animation starts, the to property for determining the last frame of the animation and optionally, a loop property containing a boolean where you can set the animation to loop indefinitely and a speed property for setting the speed at which the animation will play. Finally, to play an animation, you can either use the play method available on any game object that uses the sprite component or pass the animation you want to play as a second param to the sprite component where the game object is defined. Another important concept of KAPLAY are scenes. To create a scene you need to call the scene function and pass a string defining the scene’s name and a function that will run the scene’s logic. You can use the go function to navigate from one scene to another and even pass data to other scenes. KAPLAY also offers functions for handling user input with support for controller, mouse and keyboard. You can define virtual buttons in the KAPLAY context where your game is initially configured. Once done, you can use the onButtonPress , onButtonDown , onButtonReleased methods for handling input. For example, the onButtonPress function will be provided a function which will run when any button is pressed. The name of the button being pressed will be passed to that function which you can use to determine whenever to run some logic. In addition to components and JavaScript objects, you can pass a string to the array of components to give an identifier to a game object. These are called tags and they’re very useful. A prime example of their use, is for collision detection. Using KAPLAY’s onCollide function you can use tags to handle collisions between two games objects or a game object and a group of game objects or two groups. Tags are also useful for querying game objects. Using KAPLAY’s get function you can provide it with a tag and it will return all game objects having that tag. KAPLAY also has a physics system included which is very easy to use. You can set the gravity per scene by using the setGravity function. To make a game object susceptible to gravity, use the body component. As for a game object acting as a platform, you can within the body component, pass an object with an isStatic property set to true to prevent it from falling off or moving. Additionally, using this component enables the game object to use KAPLAY’s built in jump method. Therefore, you don’t have to implement a jump mechanic yourself. Another very useful KAPLAY concept is the ability of creating child game objects using the add method available on the parent. You can use the pos component to offset the child’s position vis-a-vis the parent. This makes implementing certain mechanics easier. Sometimes, you need to run logic every frame. For this purpose, you can create an update loop by using the onUpdate function. Alternatively, you can attach an update loop to a specific game object so that if it ends up destroyed, the update loop is cancelled. Finally, KAPLAY can be installed like any JavaScript library using a script tag or with NPM but you can also use its web based editor called KAPLAYGROUND which is free. There are many KAPLAY concepts I haven’t covered here but I hope it gave you a quick overview of the library and helped you determine if you want to invest your time learning it. If you want to dive deeper, check my project based tutorials using KAPLAY on YouTube . You can also check the API reference and examples in the KAPLAY docs . Additionally, I offer exclusive KAPLAY tutorials on Patreon. Link is here . Subscribe now

0 views
JSLegendDev 8 months ago

My Sonic TypeScript KAPLAY Game Dev Tutorial Now Available as a Nicely Formatted PDF

I have made available a PDF version of my Sonic TypeScript KAPLAY tutorial. With the PDF, you get : Nice formatting, making the text very easy to read. Everything in a single 34 pages document. Syntax highlighting for code. Easy to navigate table of contents. Here is a preview of the PDF. Buy it for 5$ I have made available a PDF version of my Sonic TypeScript KAPLAY tutorial. With the PDF, you get : Nice formatting, making the text very easy to read. Everything in a single 34 pages document. Syntax highlighting for code. Easy to navigate table of contents.

0 views
JSLegendDev 8 months ago

A List of Games Made With KAPLAY

KAPLAY is a recent JavaScript/TypeScript library for making games. It originated from the now defunct Kaboom.js library. (In fact, it’s a direct fork of Kaboom) Since the library is new, it is often overshadowed by more popular libraries/frameworks like Phaser, Pixi.js, Three.js, etc… While writing about the merits of KAPLAY might convince some to give it a try, I think listing interesting games made with it, might be more effective. Here is a list of games made with KAPLAY in no particular order. This is a remake of the old MS-DOS game called Pharaoh’s Tomb. Clickery Hexagone is a very polished clicker style game. The game is relatively popular on Newgrounds (a Web game portal). A short mini-golf game set in the ocean. This game could easily be on Steam considering how much attention to detail there is. A clone of the classic game Duck Hunt. I made this one. A Plant VS Zombie-like game developed as part of the Earth Journalism Network. Not common to see games used for journalism. I hope this list gave you an idea of what you can achieve with KAPLAY. If you’re aware of other games you think should be showcased here, don’t hesitate leaving a comment below.

0 views
JSLegendDev 8 months ago

How to Build a Sonic Themed Infinite Runner Game in TypeScript With KAPLAY - Part 2/2

In the previous part of the tutorial, we finished implementing Sonic’s movement and jumping logic. We also implemented platforms and background infinite scrolling. In this part, we will finish what remains to be implemented. Implementing Rings for Sonic to Collect Implementing a Scoring System Adding Enemies Implementing Collision Logic With Enemies Finishing The Scoring UI Implementing The Game Over Scene In the file, add the following code : Import the needed assets in . The function creates a ring game object. The component adds an method to that game object (used later in the tutorial) to destroy it once it leaves the screen. “ring” is a tag used to identify the game object in collision logic which we will later cover. Multiple game objects can have the same tag. In , add the following logic in the game scene to spawn rings. We create a recursive function called . When called, it first creates a ring by calling . In KAPLAY, you can set an loop specific to a game object that will be destroyed if the game object is destroyed. In that update loop, we make the ring move to the left at the same rate as the game’s speed. This will give the illusion that Sonic is approaching the ring while in reality, it’s the contrary. We use the method to destroy the ring when it exits the screen to the left. Using KAPLAY’s function we’re able to get a random number between 0.5 and 3 representing the time to wait before spawning another ring. KAPLAY’s function is used to only call the function once the wait time is elapsed. Implementing a Scoring System Now that the rings are spawned we need to write the logic for Sonic to collect them. Which implies needing to keep track of the score. In , under our game scene, add the following code : In addition to creating variables related to the score, we created a game object acting as our score UI. Using the component, we’re able to display text on the screen. The second param of that component is used to set the font and sizing needed. Finally, we use the component to make sure the score UI is always displayed on top of other game objects by setting it’s layer to 2. You should have the following result. Now, let’s update the score every time Sonic collides with a ring. Add the following code in our game scene : We used Sonic’s built-in method which takes as the first param the tag of a game object you want to check collisions with. The second param is a function that will run in case a collision does occur. Here, we play the “ring” sound and then destroy the ring game object Sonic collided with. Finally, we increment the score and change the score UI’s text to reflect the new score. If you run the game now, you should see the score updating every time Sonic collides with a ring. Adding Enemies The code needed for adding enemies to our game is going to be very similar to the one for adding rings. The only difference is that, contrary to rings, if Sonic touches an enemy, it’s game over. However, if Sonic jumps on that enemy, the enemy gets destroyed. In , add the following code : Here, we defined a function for creating our enemy, the “Motobug”. We used components that should now be familiar to you. However, you might have noticed that we pass an object to the area component. This is something you can do to define a custom hitbox shape. Here, we’re setting the shape of the hitbox to be a rectangle using KAPLAY’s Rect constructor. It allows you to set the hitbox’s origin relative to the game object. If you pass k.vec2(0,0), the origin will be the same as the game object’s. The second and third param of the constructor are used to set the width and the height of the hitbox. Once we will add enemies to the game, you’ll be able to use the debug mode to view how our hitbox configuration for Motobug is rendered. Add the following code to : The logic for spawning “Motobugs” is mostly the same compared to the one for “rings”. However, the “Motobug”s update loop is slightly different. When the game’s speed is inferior to 3000 we make the “Motobug” move faster than the scrolling of the platforms so that it appears as moving on the platforms towards Sonic. Otherwise, it would look like Sonic is the one moving towards stationary “Motobugs”. However, when the game’s speed gets really fast, it isn’t possible to really tell the difference. In that case, we simply make the “Motobug” move at the same rate as the scrolling platforms. At this point, you should see enemies spawn in your game. Implementing Collision Logic With Enemies At the moment, if Sonic collides with an enemy, nothing happens. Likewise, if he jumps on one. Let’s add the following code in : If you run the game now, you should be able to jump on enemies and if Sonic hits an enemy while grounded, you will be transitioned over to an empty game over screen. You’ll notice that we added logic to multiply the player’s score if they jump on multiple enemies before hitting the ground. We’re also registering the current player score in local storage so we can display it later in the game over scene. Since our game is very fast paced, it’s hard for players to keep track of how many rings they’re collecting. They would have to look up to the top left of the screen while risking not seeing an enemy in time to avoid it/jump on it. To mitigate this and to give the player a better sense of what they’re doing, I opted to display the number of rings collected after every collision with a ring or a jump on an enemy. This will also make combos easier to understand. Add the following code in to implement this feature : Now, if you run the game, you should see a +1 appear every time Sonic collides with a ring and a +10, x2, x3, etc… when he jumps on one or many “Motobugs”. An important concept present in the code above, is that game objects can have child game objects assigned to them in KAPLAY. This is what we do here : Instead of calling the function to create a game object, we can call the method to create a child game object of an existing game object. Here, we create the as a child of Sonic so that its position is relative to him. Finally, for our game over screen, let’s display the player current VS best score and allow them to try the game again if they wish to. In the game over scene code in , add the following : While it should be relatively easy to figure out what the code above does, I’d like to explain what we do here : Using KAPLAY’s function we’re able to get the data we previously set in local storage. However, when the player plays the game for the first time, they will not have a best score. That’s why we set to be 0 if returns null which is possible. We do the same with currentScore. Now, if you run the project, you should have the following game over screen appear after getting hit by an enemy. After, 1 sec you should be able to press the “Jump” button (in our case click or press the space key) to play the game again. Deployment Assuming you want to be able to publish the game in web portals like itch.io, you can make a build by creating a vite.config.ts file at the root of your project’s folder and specifying the base as . Now, run the command and you should see a folder appear in your project files. Make sure your game still works by testing the build using . Finally, once ready to publish, zip your folder and upload it to itch.io or to other web game platforms of your liking. Hope you enjoyed learning how to make games in TypeScript with KAPLAY. If you’re interested in seeing more web developement and game development tutorials from me. I recommend subscribing to not miss out on future releases. Subscribe now If you’re up for it, you can check out my beginner React.js tutorial. In the previous part of the tutorial, we finished implementing Sonic’s movement and jumping logic. We also implemented platforms and background infinite scrolling. In this part, we will finish what remains to be implemented. Table of Contents Implementing Rings for Sonic to Collect Implementing a Scoring System Adding Enemies Implementing Collision Logic With Enemies Finishing The Scoring UI Implementing The Game Over Scene

0 views
JSLegendDev 8 months ago

How to Build a Sonic Themed Infinite Runner Game in TypeScript With KAPLAY - Part 1/2

In this tutorial, I’ll teach you how to make a simple Sonic themed infinite runner game in TypeScript using the KAPLAY game library. You can play the game here . The final source code for it is available here . In this game, you need to collect as many rings as possible. Jumping on enemies grants you a bonus of 10 rings. Continually jumping on enemies without touching the ground gives you a multiplier on each enemy. The first enemy grants you +10, the second +(10 x 2), the third +(10 x 3), etc… The challenge comes from the game getting progressively faster, making it harder to avoid getting hit by enemies. I really recommend playing the game to have a better idea of what we’re going to build. Since this tutorial is published in parts, I recommend subscribing to not miss out on future releases! I expect that you have basic familiarity with TypeScript or are, at the very least, competent in JavaScript. We’re going to write the game in TypeScript, but it’s going to be very easy TypeScript code. Since we will rarely, if ever, need to write custom types. You should be familiar with Node.js and NPM and have them installed on your machine before following this tutorial. I do not expect you to know KAPLAY. What is KAPLAY? Project Setup Initializing The Canvas Creating Scenes Loading Assets Implementing an Infinite Scrolling Background What are Game Objects and Components in KAPLAY? Implementing Movement With an onUpdate Loop Implementing an Infinite Scrolling Platform Implementing The Sonic Game Object How Animations Work in KAPLAY How Hitboxes Are Created in KAPLAY How to Add Methods to a Game Object Adding Sonic to Our Game Creating a Static Body as The Floor Placing Sonic in The Game Scene KAPLAY is an open source library for making games in JavaScript and TypeScript. Compared to the more popular Phaser JavaScript game framework, its API is a lot simpler making it easier for beginners to pick up. It also offers a lot of premade functionality out of the box. You spend therefore, less time reinventing the wheel. For those familiar with the Godot game engine, you might be accustomed to the concept of nodes which acts as building blocks containing out of the box functionality. KAPLAY has a somewhat similar concept to nodes called components. You can check the official KAPLAY website for more info here . Create a folder called . within using your terminal of choice and run : This command will scaffold a TypeScript project within the existing folder using the popular build tool Vite. Once the command runs, you’ll be presented with the following : Pick “Vanilla”. You’ll be presented with another menu to chose between JavaScript and TypeScript. Select TypeScript. Now let’s install the KAPLAY library. We will install the specific version called v3001.0.16. Run the following : This version is the latest stable version available at the time this tutorial was written. As long as you stick with this specific version, you shouldn’t have issues following this tutorial years into the future. Once installed, take a look at your file in your project’s folder. You’ll notice that KAPLAY was added to your dependencies. When we used the command, Vite created a bunch of files. Make sure that you have the following project structure. Remove everything within the folder as we’re going to write everything from scratch. Once everything is removed in , create two files within. One named and the other . You should have the following project structure. Finally, the last step of our setup is to bring in our game assets. Copy the contents of the folder provided in the game’s final source code available on GitHub. The repo can be accessed by clicking here . After having done so, you should have the following files in your public folder. Now that our setup is complete, we can initialize an HTML canvas in which the game will be rendered. This will be done through KAPLAY by initializing our context which will also allow us to use KAPLAY functions. In , write the following code : Our context is created by calling the function and passing an object containing various options we want to configure for our game. We first set the width and the height of the canvas. We set the property to , allowing our game canvas to scale regardless of the screen size while still retaining its aspect ratio. We set the property to so that KAPLAY functions can only be used via the constant . This will lead to more legible code since you’ll be able to easily detect if a KAPLAY function is in use by noticing . We pass an object to the property to define the action of our game. Since, we want to make the game playable on both desktop and mobile, we set the custom action to be mapped to the space key or the left mouse click. is an arbitrary name and you could have decided on something different. You can also add an arbitrary amount of actions depending on your game’s needs by adding a new property. For example : The property is used to convert touch input to mouse clicks so that the game is playable on mobile. The property is used to toggle on/off KAPLAY’s debug mode. This mode can be accessed on the webpage the game is running on by pressing the key or the keys on a Mac. It will display an fps counter along with the game objects’ hitboxes which makes debugging easier. We will use it later in this tutorial. Here, we’re setting to so that the debug mode is accessible. One thing to note is that by default, debug mode is available so you don’t need use this property initially. The reason I included it here, is that when you ultimately deploy your game, you might not want your players to access this mode. Therefore, setting this property to before deployment is a good idea. Finally, to make the game display sharply regardless of the screen we set the property to correspond to the pixel ratio of the current screen. You’ll notice that not using this property will make the game render slightly blurry on certain screens. We then export the constant containing a reference to the context, so that we can use it elsewhere in our code. Our game will have two scenes. The first, is where the game takes place while the second, is a game over screen displaying the player’s score. In KAPLAY, you can create mulitple scenes using the function. In our file let’s import the KAPLAY context and create our scenes. Here we created two scenes. The function takes, as the first param, the name you want to use to refer to that scene. The second param is a function that will run when the game enters that scene. Finally, we also need to use the function to tell KAPLAY which scene to go to when the game starts. This function is also used to navigate to other scenes when called within a specific scene. We will write the logic for each scene later, now let’s run our project to see if we have correctly initialized our canvas. In your terminal, run the command : When you open the localhost address in your browser, if everything went well, you should see a checkered canvas like shown below. If you try to resize your window, you’ll also notice that the canvas seems to always preserve its aspect ratio. This is exactly what we want so that the game is playable on any device. In let’s load the assets we need for the background and the platforms. This will be a good introduction to how assets are loaded in KAPLAY. Add the following code : Here we use KAPLAY’s function. It takes, as the first param, a string which corresponds to the name you want to use to refer to that specific sprite/image. For the second param, the function expects the path to the asset. Note that we don’t need to add the folder to the path since Vite makes sure to make whatever is in that folder accessible as if it was placed at the root of the project. In our game, the city background must scroll indefinitely. Let’s implement this in our game scene. Add the following code in : Let’s break it down. To achieve an infinite scrolling background, we need need to display the same background image twice. The trick is to reposition the first image behind the second one once it leaves the screen. The second image becomes the first and the first becomes the second. The same logic is repeated indefinitely. I previously wrote a post specific to infinite backgrounds with easy to understand visuals. You can check it out below. In the code above, we first set a constant holding the width of the background image when scaled by 1.5, which corresponds to 1920 x 1.5 = 2880 since the original image has a width of 1920. This is needed so that we can position the second copy right after the first one without leaving any gaps. We will display the background at 1.5 its original scale so that it looks nice within our game. We then set an array containing the two copies. Each copy is created using KAPLAY’s function which creates a game object, one of KAPLAY’s major concepts. A game object represents an entity rendered on the canvas and is composed of components. These components determine various methods and features that it has access to. KAPLAY provides these components out of the box but you can also create custom ones if you wish to (We won’t in this tutorial). The function used to create game objects, takes an array of KAPLAY components. Let’s break down the 4 we use here : comp : Renders a game object as a visible sprite/image. comp : Sets the position of a game object on the canvas using coordinates. In KAPLAY and in many other game libraries, the coordinate increases the further you go right and the coordinate increases the further you go down. The origin is set at the top-left corner of the canvas. comp : Sets the opacity of a game object using a value between 0 and 1. The closer the value is to 0 the more it becomes transparent. comp : Sets the scale of a game object. Is often used alongside the comp to change the sprite/image’s size on the canvas. As you can see, the second copy uses the same components. However, we set the position differently so that it is rendered right behind the first one without leaving any gaps. While creating game objects using the function will render them on the canvas, no movement will occur without an update loop. The update loop runs every frame and is used to update our game’s logic. If your game runs at 60fps, the loop will run 60 times per second. In KAPLAY, we use the function to set our update loop. We first check if the background image second copy’s position is < 0. In that case, we can assume that the first copy is offscreen and therefore, we can safely attempt to move it behind the second copy. This is achieved by setting its position using the KAPLAY method made available due to using the comp when the game object was defined. Using the array method we delete the first element from the array and return it. Finally, we push it back to the array so that the first copy is now placed as the second element of the array making it become the second copy. Another if statement is used to make sure the result of is not before pushing it to the array again. Otherwise, TypeScript will complain and rightly so. Regardless of all of the above, on each iteration of the loop, we move both copies to the left by using the KAPLAY method on the first copy and the method on the second copy to follow the first copy. Both these methods exists because we used the component when creating each respective game object. The method takes two params. The first is for setting the velocity of the game object on the axis while the second sets the velocity on the axis. By setting the velocity to -100 and the velocity to 0, we’re moving only to the left at a velocity of 100 pixels/second. If you’re accustomed to other game engines/frameworks, you might have noticed the absence of (corresponds to the time elapsed since the last frame in our game loop) being used to make our movement frame rate independent. This is because the method takes care of it under the hood. Now, looking at your browser tab (Assuming that you have localhost running) you should see the city background scrolling indefinitely. To make the background less distracting during gameplay, I opted to set its opacity to 0.8 instead of 1. Now, if you go back to your , we can set the background of the whole webpage to be black. This will result in the city background becoming darker and therefore less distracting during gameplay. We provide to the property an array of three elements each representing one of the RGB color channels. Each element’s value can vary between 0 to 255. This is what’s used to represent color. If you check your browser tab, you should notice that the city background is now darker. Implementing an Infinite Scrolling Platform Our game is based on one big illusion. Sonic isn’t actually running. The platform on which Sonic stands isn’t actually moving. The only things moving are the background and the platform’s image which are made to scroll indefinitely. If we were to remove all of the game’s graphics, we would be left with a game object representing the player that can jump up and land on a static floor. Our goal at the moment, is to implement infinite scrolling platforms the same way we did for the city background. The only difference is that the scrolling speed will increase the further the game progresses. In add the following code : We first set a variable that is incremented every second using KAPLAY’s function. This is similar to JavaScript/TypeScript’s function. The first param is for setting the time between each call and the second param is the function that will be called. Similarly to the city background, we create two copies of the platforms’ image so that we can achieve infinite scrolling. corresponds to the final width of the platforms’ image after scaling it twice. This is because we need to make the platforms bigger in our game to make it look good. We use this constant to know where to place the second copy behind the first one without leaving any gaps. The update logic is also similar, the only difference this time is that the function takes the variable as the velocity allowing for it to increase the platforms speed through time. If you run the project, you should notice the platforms scrolling slowly and then gradually increase in speed as time passes. Now that we have our game scene mostly done, we’re ready to work on adding Sonic to our game. Sonic, the rings he must collect and the enemies are all entities. Let’s create a file named in the folder. Then, let’s create a game object that will hold Sonic’s logic. In add the following code. Let’s break it down. We first import the KAPLAY context to be able to use the library’s functions in this file. We then import 2 types. and . As you can see, KAPLAY offers TypeScript types you can use, making development in TypeScript smoother. We then create a function who’s sole purpose is to create and return our Sonic game object. You can think of it as our constructor. We enable the function’s caller to set the position of the game object before it’s created. Positions in KAPLAY are Vec2 s (stands for vector 2). It’s a data structure offered by KAPLAY which has two elements, and . We use the , , , , and components to compose our game object. Note that generally the comp can be passed the and coordinates as two distinct params or be passed a containing both at once. In our code above, we opted for the latter while with our background and platforms game objects, we opted for the former. In the case of the comp, we not only display a but set an animation. You might be wondering where does this “sonic” sprite and “run” anim comes from since we haven’t loaded anything like that at the moment? Indeed, we put the cart before the horse in this case. Let’s add the asset loading logic in . It will be a great opportunity to explain how animations work in KAPLAY. In main.ts, add the following : The function can take an object as an optional third param used for telling KAPLAY how to split an image into individual frames. When an image contains multiple sprites, it is often referred to as a spritesheet. In our case, Sonic’s spritesheet looks like this. We have a total of 16 frames which can be viewed as a grid of 2 rows and 8 columns. The object passed to must first tell KAPLAY how to slice the image using the and properties. corresponds to the number of columns while corresponds to the number of rows. Then, an object can be configured to set the animations we need and tell KAPLAY which animation is composed of which frames in our spritesheet. If we take a look at the definition for the animation, the property holds the number of the starting frame of the anim while the property holds the number of the last frame. When KAPLAY slices the image into individual frames, it assigns a number to each of them starting from 0 and counting from left to right, top to bottom. The property set to makes the anim run indefinitely unless manually stopped while the property is used to set its frame rate. The frame rate values used in the code above are arbitrary. I came up with these values based on what looked good during gameplay after tweaking values. For more details on how animations work in KAPLAY check the post below. Looking back at the components used for creating the Sonic game object in , you’ll notice that the comp takes as the first param the name of the image you want to display while the second param can be used to set the default running animation. We use the comp to increase Sonic’s sprite size by 3. Which is what looked good during gameplay. The comp is very useful because it allows us to easily create a hitbox for our player enabling us to call methods like , , etc… useful for dealing with collisions. In KAPLAY, just passing the comp to the components array is enough to create a box surronding the player’s sprite if the comp is also used. You might need in some cases to configure the position, width and height of the hitbox but in our game, this isn’t necessary at the moment. We will tackle this later. Now that we have used the comp, we can use the comp to set the origin of our game object. By default, game objects are rendered starting from their top-left corner. This might not be very intuitive for game objects representing characters, that’s why I like using the comp in those cases to set the center as the origin instead. Finally, the comp is used to give the game object a physics body allowing them to be affected by gravity. Using the comp is crucial since it will give us access to the method which allows the game object to jump. In our comp usage we set the game object’s jump force to be 1700. This is an arbitrary value which I arrived at after extensive testing during gameplay. I explained earlier that a game object is composed of components. However, the function which takes in an array of components can also be passed, to that same array, a JS object that you can use to set custom properties and methods for the resulting game object. We have an object containing two custom methods. The first one is used to set the player controls while the second is used to set what happens when Sonic hits the ground after jumping. In these methods, we can use the keyword, to access the sonic game object. In TypeScript, we need to specify what is. This is done by creating a param and typing it using the type definition imported from KAPLAY. When TypeScript is compiled to vanilla JS, this param will be removed. Taking a closer look at our custom function, we can see that we call an KAPLAY function. As the name implies, this is used for handling when a specific button is pressed. It takes, as the first param, the name of the “button” you want to listen on. The second param is the function that will fire when that “button” is clicked. If you remember, the “jump” “button” was defined in our KAPLAY context. This is where you need to define all the “buttons” you need for your game. Within our we check if the player is on top of a static game object using the method which is available because Sonic has a comp. At the moment, we haven’t defined any static game objects but we will create one later. When the player is indeed grounded, we can set the jump animation to play, make the player jump with the method and finally play a jump sound using KAPLAY’s function which is used to play audio. Unfortunately, it’s very easy to confuse the method which is used on a game object for playing animations VS the function which is used for sound. By the way, we need to load the jump sound in for it to work here. Add the following : The reason why we make sure the player is grounded before allowing any jump logic to run is so that the player can’t jump indefinitely making them able to fly away. Our second custom method is used to set event logic. In KAPLAY, when a game object has the component, you have access to the event method. That method runs a function every time the player hits the ground (meaning collides on top of a static game object). When the player jumps, the jump animation plays indefinitely. We want to switch back to the run animation as soon as the player lands back. The code above achieves this. To reiterate, and are custom methods we decided to create on our Sonic game object. They could have been named differently. We could have decided to only have one method that does all of the required logic. This is up to you when making your own KAPLAY games. Let’s create a floor on which to place Sonic. Add the following code in main.ts : To create a static game object, you simply need to create a game object with an and a component. Within the comp, set the property to . We use the comp to create a rectangular shape and we used the comp set to 0 to make it invisible. We also don’t assign the game object to a constant since we don’t need a reference. If you look at the webpage where the game is running (assuming your project is running), you will see that nothing has changed. However, if you activate the debug mode by pressing the key (or keys on a Mac), you’ll see an outline representing the hitbox game object we just created. For fun, you can try to put the opacity back to 1 and you’ll see a white rectangle at the bottom of the canvas. Placing Sonic in The Game Scene In , add the following : We call and set a position using the KAPLAY function, assign the result to a constant called Sonic which enables us to call our custom methods and , enabling our core player game logic. Everything should work now, but wait! Why is Sonic hanging in the air? That’s because we haven’t set our game’s gravity. In KAPLAY, gravity is set per scene using the function and passing to it a number determining how strong it will be. Add the following code in our “game” scene definition : I came up with 3100 after trial and error. You need to test during gameplay to see if the jump is floaty or not and that’s how I came up with this value. Now, if you run the project. You should see Sonic initially fall on the platforms and immediately start running. You can now jump and when you do, Sonic should curl into a ball and you should hear a jump sound. However, as soon as he hits the ground, the run animation should be the one playing. Up to this point we covered a lot of KAPLAY specific concepts and we made quite a bit of progress on our Sonic game. However, there’s still work to do. In the next part of the tutorial we will : Add rings for Sonic to collect. Implement a scoring system. Add Motobug enemies that Sonic can jump on for more rings. Implement a combo system allowing Sonic to earn extra rings by continuously jumping on Motobugs before hitting the ground again. Update : Part 2 is now available! In this tutorial, I’ll teach you how to make a simple Sonic themed infinite runner game in TypeScript using the KAPLAY game library. You can play the game here . The final source code for it is available here . In this game, you need to collect as many rings as possible. Jumping on enemies grants you a bonus of 10 rings. Continually jumping on enemies without touching the ground gives you a multiplier on each enemy. The first enemy grants you +10, the second +(10 x 2), the third +(10 x 3), etc… The challenge comes from the game getting progressively faster, making it harder to avoid getting hit by enemies. I really recommend playing the game to have a better idea of what we’re going to build. Since this tutorial is published in parts, I recommend subscribing to not miss out on future releases! Prerequisites I expect that you have basic familiarity with TypeScript or are, at the very least, competent in JavaScript. We’re going to write the game in TypeScript, but it’s going to be very easy TypeScript code. Since we will rarely, if ever, need to write custom types. You should be familiar with Node.js and NPM and have them installed on your machine before following this tutorial. I do not expect you to know KAPLAY. Table of Contents What is KAPLAY? Project Setup Initializing The Canvas Creating Scenes Loading Assets Implementing an Infinite Scrolling Background What are Game Objects and Components in KAPLAY? Implementing Movement With an onUpdate Loop Implementing an Infinite Scrolling Platform Implementing The Sonic Game Object How Animations Work in KAPLAY How Hitboxes Are Created in KAPLAY How to Add Methods to a Game Object Adding Sonic to Our Game Creating a Static Body as The Floor Placing Sonic in The Game Scene If you try to resize your window, you’ll also notice that the canvas seems to always preserve its aspect ratio. This is exactly what we want so that the game is playable on any device. Loading Assets In let’s load the assets we need for the background and the platforms. This will be a good introduction to how assets are loaded in KAPLAY. Add the following code : Here we use KAPLAY’s function. It takes, as the first param, a string which corresponds to the name you want to use to refer to that specific sprite/image. For the second param, the function expects the path to the asset. Note that we don’t need to add the folder to the path since Vite makes sure to make whatever is in that folder accessible as if it was placed at the root of the project. Implementing an Infinite Scrolling Background In our game, the city background must scroll indefinitely. Let’s implement this in our game scene. Add the following code in : Let’s break it down. To achieve an infinite scrolling background, we need need to display the same background image twice. The trick is to reposition the first image behind the second one once it leaves the screen. The second image becomes the first and the first becomes the second. The same logic is repeated indefinitely. I previously wrote a post specific to infinite backgrounds with easy to understand visuals. You can check it out below. In the code above, we first set a constant holding the width of the background image when scaled by 1.5, which corresponds to 1920 x 1.5 = 2880 since the original image has a width of 1920. This is needed so that we can position the second copy right after the first one without leaving any gaps. We will display the background at 1.5 its original scale so that it looks nice within our game. We then set an array containing the two copies. Each copy is created using KAPLAY’s function which creates a game object, one of KAPLAY’s major concepts. What are Game Objects and Components in KAPLAY? A game object represents an entity rendered on the canvas and is composed of components. These components determine various methods and features that it has access to. KAPLAY provides these components out of the box but you can also create custom ones if you wish to (We won’t in this tutorial). The function used to create game objects, takes an array of KAPLAY components. Let’s break down the 4 we use here : comp : Renders a game object as a visible sprite/image. comp : Sets the position of a game object on the canvas using coordinates. In KAPLAY and in many other game libraries, the coordinate increases the further you go right and the coordinate increases the further you go down. The origin is set at the top-left corner of the canvas. comp : Sets the opacity of a game object using a value between 0 and 1. The closer the value is to 0 the more it becomes transparent. comp : Sets the scale of a game object. Is often used alongside the comp to change the sprite/image’s size on the canvas. Implementing an Infinite Scrolling Platform Our game is based on one big illusion. Sonic isn’t actually running. The platform on which Sonic stands isn’t actually moving. The only things moving are the background and the platform’s image which are made to scroll indefinitely. If we were to remove all of the game’s graphics, we would be left with a game object representing the player that can jump up and land on a static floor. Our goal at the moment, is to implement infinite scrolling platforms the same way we did for the city background. The only difference is that the scrolling speed will increase the further the game progresses. In add the following code : We first set a variable that is incremented every second using KAPLAY’s function. This is similar to JavaScript/TypeScript’s function. The first param is for setting the time between each call and the second param is the function that will be called. Similarly to the city background, we create two copies of the platforms’ image so that we can achieve infinite scrolling. corresponds to the final width of the platforms’ image after scaling it twice. This is because we need to make the platforms bigger in our game to make it look good. We use this constant to know where to place the second copy behind the first one without leaving any gaps. The update logic is also similar, the only difference this time is that the function takes the variable as the velocity allowing for it to increase the platforms speed through time. If you run the project, you should notice the platforms scrolling slowly and then gradually increase in speed as time passes. Implementing The Sonic Game Object Now that we have our game scene mostly done, we’re ready to work on adding Sonic to our game. Sonic, the rings he must collect and the enemies are all entities. Let’s create a file named in the folder. Then, let’s create a game object that will hold Sonic’s logic. In add the following code. Let’s break it down. We first import the KAPLAY context to be able to use the library’s functions in this file. We then import 2 types. and . As you can see, KAPLAY offers TypeScript types you can use, making development in TypeScript smoother. We then create a function who’s sole purpose is to create and return our Sonic game object. You can think of it as our constructor. We enable the function’s caller to set the position of the game object before it’s created. Positions in KAPLAY are Vec2 s (stands for vector 2). It’s a data structure offered by KAPLAY which has two elements, and . We use the , , , , and components to compose our game object. Note that generally the comp can be passed the and coordinates as two distinct params or be passed a containing both at once. In our code above, we opted for the latter while with our background and platforms game objects, we opted for the former. In the case of the comp, we not only display a but set an animation. You might be wondering where does this “sonic” sprite and “run” anim comes from since we haven’t loaded anything like that at the moment? Indeed, we put the cart before the horse in this case. Let’s add the asset loading logic in . It will be a great opportunity to explain how animations work in KAPLAY. How Animations Work in KAPLAY In main.ts, add the following : The function can take an object as an optional third param used for telling KAPLAY how to split an image into individual frames. When an image contains multiple sprites, it is often referred to as a spritesheet. In our case, Sonic’s spritesheet looks like this. We have a total of 16 frames which can be viewed as a grid of 2 rows and 8 columns. The object passed to must first tell KAPLAY how to slice the image using the and properties. corresponds to the number of columns while corresponds to the number of rows. Then, an object can be configured to set the animations we need and tell KAPLAY which animation is composed of which frames in our spritesheet. If we take a look at the definition for the animation, the property holds the number of the starting frame of the anim while the property holds the number of the last frame. When KAPLAY slices the image into individual frames, it assigns a number to each of them starting from 0 and counting from left to right, top to bottom. The property set to makes the anim run indefinitely unless manually stopped while the property is used to set its frame rate. The frame rate values used in the code above are arbitrary. I came up with these values based on what looked good during gameplay after tweaking values. For more details on how animations work in KAPLAY check the post below. Looking back at the components used for creating the Sonic game object in , you’ll notice that the comp takes as the first param the name of the image you want to display while the second param can be used to set the default running animation. We use the comp to increase Sonic’s sprite size by 3. Which is what looked good during gameplay. How Hitboxes Are Created in KAPLAY The comp is very useful because it allows us to easily create a hitbox for our player enabling us to call methods like , , etc… useful for dealing with collisions. In KAPLAY, just passing the comp to the components array is enough to create a box surronding the player’s sprite if the comp is also used. You might need in some cases to configure the position, width and height of the hitbox but in our game, this isn’t necessary at the moment. We will tackle this later. Now that we have used the comp, we can use the comp to set the origin of our game object. By default, game objects are rendered starting from their top-left corner. This might not be very intuitive for game objects representing characters, that’s why I like using the comp in those cases to set the center as the origin instead. Finally, the comp is used to give the game object a physics body allowing them to be affected by gravity. Using the comp is crucial since it will give us access to the method which allows the game object to jump. In our comp usage we set the game object’s jump force to be 1700. This is an arbitrary value which I arrived at after extensive testing during gameplay. How to Add Methods to a Game Object I explained earlier that a game object is composed of components. However, the function which takes in an array of components can also be passed, to that same array, a JS object that you can use to set custom properties and methods for the resulting game object. We have an object containing two custom methods. The first one is used to set the player controls while the second is used to set what happens when Sonic hits the ground after jumping. In these methods, we can use the keyword, to access the sonic game object. In TypeScript, we need to specify what is. This is done by creating a param and typing it using the type definition imported from KAPLAY. When TypeScript is compiled to vanilla JS, this param will be removed. Taking a closer look at our custom function, we can see that we call an KAPLAY function. As the name implies, this is used for handling when a specific button is pressed. It takes, as the first param, the name of the “button” you want to listen on. The second param is the function that will fire when that “button” is clicked. If you remember, the “jump” “button” was defined in our KAPLAY context. This is where you need to define all the “buttons” you need for your game. Within our we check if the player is on top of a static game object using the method which is available because Sonic has a comp. At the moment, we haven’t defined any static game objects but we will create one later. When the player is indeed grounded, we can set the jump animation to play, make the player jump with the method and finally play a jump sound using KAPLAY’s function which is used to play audio. Unfortunately, it’s very easy to confuse the method which is used on a game object for playing animations VS the function which is used for sound. By the way, we need to load the jump sound in for it to work here. Add the following : The reason why we make sure the player is grounded before allowing any jump logic to run is so that the player can’t jump indefinitely making them able to fly away. Our second custom method is used to set event logic. In KAPLAY, when a game object has the component, you have access to the event method. That method runs a function every time the player hits the ground (meaning collides on top of a static game object). When the player jumps, the jump animation plays indefinitely. We want to switch back to the run animation as soon as the player lands back. The code above achieves this. To reiterate, and are custom methods we decided to create on our Sonic game object. They could have been named differently. We could have decided to only have one method that does all of the required logic. This is up to you when making your own KAPLAY games. Adding Sonic to Our Game Let’s create a floor on which to place Sonic. Creating a Static Body as The Floor Add the following code in main.ts : To create a static game object, you simply need to create a game object with an and a component. Within the comp, set the property to . We use the comp to create a rectangular shape and we used the comp set to 0 to make it invisible. We also don’t assign the game object to a constant since we don’t need a reference. If you look at the webpage where the game is running (assuming your project is running), you will see that nothing has changed. However, if you activate the debug mode by pressing the key (or keys on a Mac), you’ll see an outline representing the hitbox game object we just created. For fun, you can try to put the opacity back to 1 and you’ll see a white rectangle at the bottom of the canvas. Placing Sonic in The Game Scene In , add the following : We call and set a position using the KAPLAY function, assign the result to a constant called Sonic which enables us to call our custom methods and , enabling our core player game logic. Everything should work now, but wait! Why is Sonic hanging in the air? That’s because we haven’t set our game’s gravity. In KAPLAY, gravity is set per scene using the function and passing to it a number determining how strong it will be. Add the following code in our “game” scene definition : I came up with 3100 after trial and error. You need to test during gameplay to see if the jump is floaty or not and that’s how I came up with this value. Now, if you run the project. You should see Sonic initially fall on the platforms and immediately start running. You can now jump and when you do, Sonic should curl into a ball and you should hear a jump sound. However, as soon as he hits the ground, the run animation should be the one playing. Conclusion Up to this point we covered a lot of KAPLAY specific concepts and we made quite a bit of progress on our Sonic game. However, there’s still work to do. In the next part of the tutorial we will : Add rings for Sonic to collect. Implement a scoring system. Add Motobug enemies that Sonic can jump on for more rings. Implement a combo system allowing Sonic to earn extra rings by continuously jumping on Motobugs before hitting the ground again.

0 views
JSLegendDev 8 months ago

Where to Find Inspiration For Making Small Games

In my opinion, the most approachable way to get into game development is to make small games. However, making the usual Pong or Flappy bird clone is boring. In this post, I’ll share where I find inspiration for building interesting small games. PICO-8 is a fantasy console, meaning a console that doesn’t actually exist. In practice it’s a virtual machine that has a bunch of artificial limitations (number of colors it can display, resolution, limited sprites, etc…). There are two components to it. The first one is for developers where you can develop your game entirely in PICO-8 using the lua programming language. It has a built-in code editor, sprite editor, level editor and sound editor. Since this fantasy console is so limited in what it can do, developers can easily avoid making games that are too big in scope. The second component is the console aspect, where you can play games made in PICO-8. While I think limitations breeds creativity, I think PICO-8’s limitations are a bit too much for me as a developer, however, I think it’s worth playing games made in it. You’ll get very inspired by how creative these small games are. I recommend installing the P8GO app which allows you to discover PICO-8 games in a TikTok style app, allowing you to cover a lot of games quickly. There are a lot of games released in the past that are easy to remake today. I recommend looking up and trying old arcade, NES and Gameboy games. A good exercise is to try remaking them while simplifying or improving the user experience since many of these games were obtuse as gaming was relatively young back then. A lot of the established quality of life features we’re used to today weren’t common. In the startup world, you’ll often see successful businesses emerge by taking one of the many features an establish company already has and focusing exclusively on building that one feature as their product. You can take the same approach in game dev. In The Legend of Zelda Link’s Awakening, there is a part of the game where you need to do a trading sequence. Meaning, exchange an item with an NPC to get another item you can exchange with another NPC until you obtain the item you need. What if you made a game that was exclusively about trading things with NPCs to get an item the player needs? While that might not sound particularly fun, you can always take inspiration from things outside of game dev to spice up your game concept. For example, Rayan Trahan, a popular YouTuber, made a series called the penny challenge. He starts with a penny and needs to cross through America using only that as his starting budget. The YouTuber then proceeds to buy, sell and trade items enabling them to gradually increase their wealth so they can pay for transportation and other expenses to achieve their goal. Now, let’s modify the game idea a bit. What if you made a game where the player starts in a small island/town and wants to leave that town. To do so, they must buy an expensive ticket for a cruise that will enable them to leave. However, they’re broke and need to explore the town for items, and trade them with NPCs, until they make enough money to buy the ticket and leave. Add to this, challenging aspects, like some NPCs only being interested in certain items, and you’ve got a nice little game. You can add a bit of randomness with which NPC wants what and this will make each run different allowing your game to be highly replayable. In the popular game franchise Pokémon, you often need to pass by a Pokémart to buy various items useful in your adventure. What if you made a game solely about managing a Pokémart? You don’t have to use the Pokémon IP, you could replace it with your own monster catching IP. Instead of selling Pokéballs, you would sell CaptureCubes or something to that effect. The whole game would take place within the mart. Every time a new customer enters, they can either buy from your inventory or you can buy from them. Which could be your only way of getting certain items in demand by other customers. Your goal is to stay in business since you have expenses, while keeping customers happy by having the items they need so that they keep coming to your mart. I could go on, but I think you get the point. In the first example of the previous section, I mentioned taking inspiration from a YouTuber’s video series. This is an example of taking inspiration from outside of gaming and I’d argue that the truely novel ideas can be found there. What if you made a game about managing a library? A game about being a moderator of a social media website? A game about being a lumberjack? A game about managing a retro handled company? What if you made a game based on a book, how would you adapt it so that the game is fun? The list goes on. In the end, there are a lot of ways to find good small game ideas. In this post, I shared what the inspiration sources I use. If you want to learn game development, web development or game developement using web dev tools, I recommend subscribing to not miss out on future posts and tutorials. Thanks for reading! Subscribe now In the meantime, you can check out my previous content.

0 views
JSLegendDev 8 months ago

My React.js Beginner Tutorial is Now Available as a Nicely Formatted PDF

I recently published a beginner React.js tutorial on building a game search app as a series of free substack posts. However, I would like to announce that you can now purchase the tutorial as a nicely formatted PDF. The advantage of the PDF is that you have everything in one place and it’s nicely formatted with an easy to navigate table of contents. It’s 5$ and you can buy it here . Here is a preview showcasing how the PDF looks like.

0 views