<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Cubos Engine - Demo</title><link href="https://cubos.riscadoa.com/" rel="alternate"/><link href="https://cubos.riscadoa.com/feeds/demo.atom.xml" rel="self"/><id>https://cubos.riscadoa.com/</id><updated>2025-06-19T22:00:00+01:00</updated><entry><title>Roll Racers</title><link href="https://cubos.riscadoa.com/blog/roll-racers/" rel="alternate"/><published>2025-06-19T22:00:00+01:00</published><updated>2025-06-19T22:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-06-19:/blog/roll-racers/</id><summary type="html">&lt;p&gt;We once again made a game in a week for the Coffee N' Jam with Cubos, and this time, with a lot of physics!&lt;/p&gt;
</summary><content type="html">&lt;section id="the-theme"&gt;
&lt;h2&gt;The Theme&lt;/h2&gt;
&lt;p&gt;From 2nd of May to the 10th of May, &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/francisco-catarino/"&gt;&amp;#64;fkatar&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/pedro-simoes/"&gt;&amp;#64;PedroSimoes24&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; participated in the Coffee N' Jam, a game jam organized by GameDev Técnico, with our engine, &lt;strong&gt;Cubos&lt;/strong&gt;.
The goal was to create a game in a week, this time with theme &amp;quot;Blackout&amp;quot; - the huge power outage in the Iberian Peninsula had happened the prior week, and just like during the Covid lockdown, people rushed to the supermarkets to buy toilet paper.&lt;/p&gt;
&lt;p&gt;We thought it would be funny to make a game about racing to the supermarket to buy toilet paper - and that's what we ended up doing.
Our priorities were to test new features of the engine, and to make a fun local multiplayer game.
This idea was a great opportunity to fully use the physics and collision plugins in a game for the first time.&lt;/p&gt;
&lt;p&gt;The game is called &lt;em&gt;Roll Racers&lt;/em&gt;, and you can play it on its &lt;a href="https://riscadoa.itch.io/roll-racers"&gt;itch.io page&lt;/a&gt;.
It has a web build and a downloadable version for both Windows and Linux.
You can also check out its source code in our &lt;a href="https://github.com/GameDevTecnico/cubos-demo"&gt;demo repository&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/cover.png" /&gt;
&lt;figcaption&gt;The game supports up to 4 players, each having a different car model.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/image1.png" /&gt;
&lt;figcaption&gt;A player holding toilet paper, racing back to the start line to score points.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We had a lot of fun making the game, and we fixed a ton of bugs and missing features in the engine as we went along.
Since the jam was a week long, we had some more time to work on the game than in the &lt;a href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/"&gt;previous jam&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="voxel-modeling"&gt;
&lt;h2&gt;Voxel Modeling&lt;/h2&gt;
&lt;p&gt;One major problem we had was that we didn't have any artists in the team, only programmers.
We ended up going full programmer art mode, and we used, as usual, &lt;a href="https://ephtracy.github.io/"&gt;MagicaVoxel&lt;/a&gt; for this purpose.
&lt;a href="https://cubos.riscadoa.com/blog/author/pedro-simoes/"&gt;&amp;#64;PedroSimoes24&lt;/a&gt; focused on making the buildings and the road models, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; focused on the cars and some tiles and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; made a few props.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="map-generation"&gt;
&lt;h2&gt;Map Generation&lt;/h2&gt;
&lt;p&gt;To make our jobs easier, the map is divided into tiles. Each tile is 68x68 voxels wide. Tiles can be road tiles, building tiles or park tiles.
&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt; worked on the map generation, which is done procedurally. First, road tiles are placed in a river-like fashion, and then, building tiles are placed around them.&lt;/p&gt;
&lt;p&gt;To make the map prettier we also added some park tiles with trees and bushes, which are randomly placed adjacent to the road tiles.
For an extra challenge, we added a 'river' tile where the road has a big hole in the middle, and the players have to jump over it by going fast enough on the ramps.&lt;/p&gt;
&lt;p&gt;We also have special start and end tiles, the end tile being the supermarket, where the toilet paper is placed.
The map regenerates every time a round ends, i.e., when a player brings the toilet paper back to the start line.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Supermarket" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/supermarket.png" /&gt;
&lt;figcaption&gt;Supermarket tile, where the toilet paper can be picked up.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="physics-and-collisions"&gt;
&lt;h2&gt;Physics and Collisions&lt;/h2&gt;
&lt;p&gt;For this game, we decided to go all in on physics. &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; developed physically realistic cars.
The cars are essentially composed of a box shape, representing the chasis, and then four empty child entities representing each of the wheels, with origin on the point where the suspension connects to the car. When turning, these wheel entities are rotated on the y-axis.
Then, the forces for each individual wheel are calculated and applied on the connection point with the chasis, for realistic behaviour. These forces include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The suspension force, which lifts the car up and makes it react realistically to the ground bellow.&lt;/li&gt;
&lt;li&gt;The steering force, which simulates the cars steering with tire friction and splip angle.&lt;/li&gt;
&lt;li&gt;The acceleration force, which applies acceleration and breaking to the cars. The acceleration forces are applied to different wheels, depending if the car is front wheel drive or rear wheel drive, affecting it's handling.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/going-over-sidewalk.png" /&gt;
&lt;figcaption&gt;Since each wheel is simulated independently, the cars tilts when going over the sidewalk.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;However, if the cars are not on the ground, we do not want to calculate any forces besides gravity. Therefore, we perform a raycast from the wheel origin to the ground.
In case it hits bellow the max distance, we calculate the forces, and particularly, we use it's hitpoint for determining the strength of the suspension force.&lt;/p&gt;
&lt;p&gt;As mentioned previously, the map is divided into tiles. As it's much fast and accurate than trying to put collision shapes manually,
we decided to use voxel collision shapes for every tile. This worked surprisingly well, and the framerate didn't drop as much as we where expecting given how many voxel shapes we had,
and the little optimization we currently have for collisions. It's worth noting that we were taking advantage of the collision layers and masks to determine which shapes should detect collision between themselves, which lead to better performance in this scenario.&lt;/p&gt;
&lt;p&gt;But this is where things went a little wrong... As we tried to put the car on the map, we noticed that the car would just completly fall through the tile.
After finding the root issue, &lt;a href="https://cubos.riscadoa.com/blog/author/tiago-antunes/"&gt;&amp;#64;fallenatlas&lt;/a&gt; started working quickly on fixing it. The issue had two parts. The first was related to the component matching on the penetration constraint solving, where we were missing some checks for certain components, leading to the solving being done in the opposite way, effectively pulling bodies to each other, instead of pushing them apart.
The second issue was related to the local contact points calculation for voxel shapes, in the narrow phase, where the points were always relative to the center of the entity rather than relative to their relative box center (since the voxel shape is composed of multiple boxes). This lead to the penetration distance being miscalculated in the penetration constraint solving, and therefore, the impulses applied to each entity were incorrect.&lt;/p&gt;
&lt;p&gt;Since, we were at it, we wanted to use as much physics features as we wanted, so &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; experimented with using the new Distance Constraint to attach the paper roll to the car, and having it be dragged along.
However, the team felt like it was too hard to steal the toilet paper, since it moved around too much, and discarted this idea.&lt;/p&gt;
&lt;p&gt;On the last day, a comical situation happened when we where putting ramps on the map. We (&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt;) tried to rotate one of the ramps, by rotating it, and then putting it as a child entity of another entity with the same rotation applied to it. This lead to a huge framerate drop, which took as a couple of hours to figure out.
The exact reason for why this happened is still unknown, but we'll be investigating it in the future.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/image2.png" /&gt;
&lt;figcaption&gt;A player can steal another player's toilet paper by hitting them with their car.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="ui"&gt;
&lt;h2&gt;UI&lt;/h2&gt;
&lt;p&gt;This game was also the first time UI was used on a demo. &lt;a href="https://cubos.riscadoa.com/blog/author/tomas-gouveia/"&gt;&amp;#64;tomas7770&lt;/a&gt; picked up this task, and made multiple menus and screens for the game.
We wanted to briefly explain the game mechanics to the players, so we added a title screen with some information.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Title screen" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/title-screen.png" /&gt;
&lt;figcaption&gt;The &amp;quot;Press Space/A ...&amp;quot; text color is animated, fading in and out.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Additionally, the players can choose how many players will play the game by pressing the spacebar or the &amp;quot;A&amp;quot; on the gamepad for each of them.
Instructions were added to the top of the screen.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Start screen" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/start.png" /&gt;
&lt;figcaption&gt;After advancing, the players must choose the number of players.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In game, it was important to show the players their score and how it changed over time, to make the game more competitive.
We decided to add a score indicator to the top of the screen, which shows the score of each player.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Score indicators" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/mid-game.png" /&gt;
&lt;figcaption&gt;Points are gained by holding the toilet paper.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Finally, at the end of the game, we wanted to reward the players with a scoreboard, showing the score of each player.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img alt="Scoreboard" src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2025/scoreboard.png" /&gt;
&lt;figcaption&gt;The text color helps associate the players with their scores.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The UI ended up working well during the jam. There were only three issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our UI and ImGui were not compatible - this was easily fixed by ensuring they were drawn in the correct order.&lt;/li&gt;
&lt;li&gt;There is no support for text wrapping or newlines, so we had to create a different text element for each line.&lt;/li&gt;
&lt;li&gt;The text contents only updated when its respective component was reinserted - simply modifying it wasn't enough. We're still unsure on how to handle these scenarios, but the most likely solution is to make the component unmodifiable and thus force the user to reinsert it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="audio"&gt;
&lt;h2&gt;Audio&lt;/h2&gt;
&lt;p&gt;On the last day of the jam &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; decided to focus on adding audio to the game.
Previously, when making &lt;a href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/"&gt;Ondisseia&lt;/a&gt;, we had a lot of trouble with adding sound effects.
They didn't play reliably and the audio system was a bit of a mess.&lt;/p&gt;
&lt;p&gt;To use audio in &lt;em&gt;Roll Racers&lt;/em&gt;, we ended up fixing all of the problems we previously had, which means that we can now rely on it for future projects.
We added a few sound effects to the game, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Car engine sound, which changes pitch and gain based on RPM and gear.&lt;/li&gt;
&lt;li&gt;Pickup sound when picking up toilet paper.&lt;/li&gt;
&lt;li&gt;Steal sound when stealing toilet paper from other players.&lt;/li&gt;
&lt;li&gt;Fireworks sound when winning the game.&lt;/li&gt;
&lt;li&gt;Power cut sound when the race starts.&lt;/li&gt;
&lt;li&gt;Honking sound when honking the horn.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the audio still sounded a bit janky, it was very satisfying to hear sound being used so extensively in a Cubos game for the first time.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="ecs-troubles"&gt;
&lt;h2&gt;ECS Troubles&lt;/h2&gt;
&lt;p&gt;At the end of the jam, the game's performance was very bad.
A bit of profiling quickly showed that a lot of time was being spent iterating over tens of thousands of relation tables - the ones introduced in the &lt;a href="https://cubos.riscadoa.com/blog/hello-relations/"&gt;relations development note&lt;/a&gt;.
This was a waste of time, as only less than 15 actually contained entities.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; quickly fixed this by adding logic to clean up unused relation tables every frame.
This immediately improved performance by a lot - on one machine, the framerate went from 40FPS to stable 240FPS.
In the future, this logic should probably be improved to only clean up the tables when they're not used for a while, to avoid wasting time recreating tables constantly.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="whats-next"&gt;
&lt;h2&gt;Whats Next?&lt;/h2&gt;
&lt;p&gt;We are very happy with the results of this jam - this demo is and will continue to be a great testbed for the engine, as it really pushes it to its limits.
After the jam ended, we met to discuss what went wrong and what issues we should prioritize fixing.
One of the biggest problems continues to be the lack of proper tooling for the engine - this will be the focus for the next few months.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Coffee N' Jam"/></entry><entry><title>The "Ondisseia" of Cubos</title><link href="https://cubos.riscadoa.com/blog/the-ondisseia-of-cubos/" rel="alternate"/><published>2025-05-16T10:00:00+01:00</published><updated>2025-05-16T10:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2025-05-16:/blog/the-ondisseia-of-cubos/</id><summary type="html">&lt;p&gt;Cubos participation in the Azul Game Jam - the achievements, lessons taken, and much more.&lt;/p&gt;
</summary><content type="html">&lt;section id="the-jam"&gt;
&lt;h2&gt;The Jam&lt;/h2&gt;
&lt;p&gt;Around a month ago, &lt;a href="https://cubos.riscadoa.com/blog/author/miguel-canais/"&gt;&amp;#64;mcanais&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/nuno-baptista/"&gt;&amp;#64;NunoBaptista&lt;/a&gt;, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-subtil/"&gt;&amp;#64;rsubtil&lt;/a&gt; joined forces to participate in the &lt;a href="https://www.linkedin.com/company/azulgamejam"&gt;Azul Game Jam&lt;/a&gt;.
This was the first edition of a Portuguese on-site game jam, where the aim was to join the Portuguese game dev community at the &lt;a href="https://lisboainnovation.com/hubs/aihub/"&gt;AI Hub&lt;/a&gt;, to make games during 48 hours.
We were a team of 4 - 3 programmers, 1 artist, and a lot of ideas.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosInAzul4.jpg" /&gt;
&lt;figcaption&gt;Cubos team participating in the Azul Game Jam - photo by Gonçalo Goulão.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The game is playable on the browser, and can be found at &lt;a href="https://riscadoa.itch.io/ondisseia"&gt;itch.io&lt;/a&gt;.&lt;/p&gt;
&lt;section id="game-development"&gt;
&lt;h3&gt;Game Development&lt;/h3&gt;
&lt;p&gt;It is challenging to work with such a barebones engine, but also really satisfying to have so much control over what is made.
This year's theme was waves, and we decided to make a sea battle game, where up to 4 players fight until the last standing.
The movement was really inspired on &lt;a href="https://cubos.riscadoa.com/blog/coffee-n-jam-2024/"&gt;Scraps vs Zombies&lt;/a&gt; demo, functioning in a grid wise manner,
where the player moves a tile at the time, in for possible directions.
To fulfill the jam theme, we've included a cellular automata wave that sweeps across the scene.
The player's boat slows down when sailing against this wave, or can catch up speed while riding it.
The waves are colossal, sometimes sweeping over the islands on the map.
With this game mechanic, we'd hoped to add an interesting twist to the genre of Battle Royale.&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/OndisseiaGameplay.gif" /&gt;
&lt;section id="voxel-art-nunobaptista"&gt;
&lt;h4&gt;Voxel Art &lt;span class="m-text m-dim author-dim classifier"&gt;(&lt;a href="https://cubos.riscadoa.com/blog/author/nuno-baptista/"&gt;&amp;#64;NunoBaptista&lt;/a&gt;)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;As an artist, I produce the voxel assets, including the animations, the tiles of the map, and the wave.
Currently, we don't have a voxel editor inside Cubos, so we used &lt;a href="https://ephtracy.github.io/"&gt;MagicaVoxel&lt;/a&gt; for this purpose.
For the animations, we've used a process very similar to pixel art animation, with a model for each frame.
Though not strictly necessary, the boat had several animated parts: the sail and the flag, plus the oarsmen and the oars.
The oarsmen also served as a game design purpose -to indicate the number of lives the player had, each time the player took damage,
one of the sailors would fly out of the boat. Some decoration was created to bring life to the game's world.
Plants, houses, and shipwrecks were designed and modeled, but in the end, never made it to the final cut.
Currently, each model has to be imported by hand, and even the meta files and ID have to be manually assigned.
The 2-day Jam was a very limited time frame for this level of polish.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/Deco.png" /&gt;
&lt;figcaption&gt;Models to populate the game world.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="playtest"&gt;
&lt;h4&gt;Playtest&lt;/h4&gt;
&lt;p&gt;The game was playtested during the jam's showcase, and it was well received by the community present.
A real sense of competition sprout in the players, and fierce fights unfold before our eyes.
The game was praised for its engaging gameplay and innovative art style. Some even comment that it locked like pixel art.
The voxel resolution and the camera perspective might have helped in this regard.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosInAzul5.jpg" /&gt;
&lt;figcaption&gt;Cubos showcasing its game in Azul Game Jam - photo by Gonçalo Goulão.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Few days later the Cubos team took the opportunity to showcase the game in a &lt;a href="https://www.eventbrite.pt/cc/game-break-3434859"&gt;Game Break&lt;/a&gt; event, at &lt;a href="https://unicornfactorylisboa.com/hubs/"&gt;Gaming Hub&lt;/a&gt;.
Developers from Portugal and beyond gather at these Game Break events every mouth at Gaming Hub to showcase their latest work, discuss ideas and network.
Ondisseia was again showcased in this last april edition, and again, the feedback was really positive. Some suggestions were made about the aiming system -
it seemed to some to be too complicated. Only when the player is sideways to other player, over a minimum of 5 chunks, is it possible to shoot arrows.
It was also suggested to use a free movement instead of the grid base one.
Maybe the most interesting suggestion was to try and make the game run in mobile (browser).&lt;/p&gt;
&lt;img class="m-image" src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/CubosGameBreak9.jpg" /&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="android-features"&gt;
&lt;h2&gt;Android Features&lt;/h2&gt;
&lt;p&gt;Cubos uses &lt;a href="https://emscripten.org/"&gt;Emscripten&lt;/a&gt; to export to WebGL.
We already had a build from Emscripten for Azul Game Jam, but the formatting was incompatible with itch.io. This build was made for a single web page and not to be embedded on Itch.io. Therefore, we strived to simplify the HTML and customize the CSS classes to our liking.&lt;/p&gt;
&lt;p&gt;There were problems with the way Emscripten controls the canvas. Apart from the usual fullscreen bottom, there was a Resize canvas and  Lock/hide mouse pointer, for resizing and hiding the mouse in fullscreen, respectively. We've opted to rewrite these functionalities in a new .JavaScript, but we aim to integrate these better in the future.&lt;/p&gt;
&lt;p&gt;After this step, we had a functional WebGL build on Itch.io, though no UI and functionalities for mobile devices had yet been included. The game would open, the sound would play, but there wasn't any interactivity.&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/AzulAndroid1.jpg" /&gt;
&lt;figcaption&gt;Screenshot of early test of the game in mobile.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To support mobile devices better, new UI  elements were created with HTML and CSS. Still, there wasn't any way to connect them with the game code.  For this purpose, this JavaScript Function was created to send the bottom's interaction to the game:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;simulateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;KeyboardEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isDown&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;keydown&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;keyup&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;which&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;keyCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;bubbles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;cancelable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;This combined with this HTML button:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;shoot&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ontouchstart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;simulateKey(32, true)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ontouchend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;simulateKey(32, false)&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;space&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;Allowed to interact with the game&lt;/p&gt;
&lt;figure class="m-figure"&gt;
&lt;img src="https://cubos.riscadoa.com/images/blog/demo/azul-jam-2025/AzulAndroid2.jpg" /&gt;
&lt;figcaption&gt;Screenshot of the game in a mobile browser.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Despite this progress, a new problem emerged; now we were stuck with these inputs on the PC.
To solve this issue, we added a function to hide the mobile control when no longer necessary:&lt;/p&gt;
&lt;pre class="m-code"&gt;&lt;span class="c1"&gt;// Detect if it&amp;#39;s a touch device (Android or otherwise)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;ontouchstart&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxTouchPoints&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Detect Android specifically&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isAndroid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;Android&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isAndroid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// If it&amp;#39;s Android, display the touch controls&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;touch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;controls&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;visible&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;fullscreen&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibility&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="n"&gt;hidden&lt;/span&gt;&lt;span class="err"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;
&lt;p&gt;With this final touch, we were able to hide the extra UI when the game was running on a PC browser and enable the UI in a mobile device browser.
After the jam, we integrated these changes into the main branch of Cubos, so that all samples can be easily ported to mobile devices.&lt;/p&gt;
&lt;/section&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Azul Game Jam"/></entry><entry><title>Coffee N' Jam 2024</title><link href="https://cubos.riscadoa.com/blog/coffee-n-jam-2024/" rel="alternate"/><published>2024-05-21T09:00:00+01:00</published><updated>2024-05-21T09:00:00+01:00</updated><author><name>Cubos Team</name></author><id>tag:cubos.riscadoa.com,2024-05-21:/blog/coffee-n-jam-2024/</id><summary type="html">&lt;p&gt;We made a game in a week with Cubos, and managed to win two awards!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;From 26th of April to 3rd of May, &lt;a href="https://cubos.riscadoa.com/blog/author/ricardo-antunes/"&gt;&amp;#64;RiscadoA&lt;/a&gt; and &lt;a href="https://cubos.riscadoa.com/blog/author/joao-manita/"&gt;&amp;#64;joaomanita&lt;/a&gt; participated in the Coffee N' Jam, a game jam organized by GameDev Técnico, with our engine, &lt;strong&gt;Cubos&lt;/strong&gt;.
The goal was to create a game in a week, with the theme &amp;quot;Apocalypse&amp;quot; - and we had a blast!&lt;/p&gt;
&lt;p&gt;The game jam was a great opportunity to test the engine in a real game development scenario, and we learned a lot from it.
The game is called &lt;em&gt;Scraps vs Zombies&lt;/em&gt;, and you can download it from its &lt;a href="https://riscadoa.itch.io/scraps-vs-zombies"&gt;itch.io page&lt;/a&gt;.
You can also check out its source code in our &lt;a href="https://github.com/GameDevTecnico/cubos-demo"&gt;demo repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We managed to win the &amp;quot;Best Gameplay&amp;quot; and &amp;quot;People's Choice&amp;quot; awards, which we're very proud of!&lt;/p&gt;
&lt;div class="m-image"&gt;
&lt;a href="https://riscadoa.itch.io/scraps-vs-zombies"&gt;&lt;img src="https://cubos.riscadoa.com/images/blog/demo/coffee-n-jam-2024/cover.png" /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;We found a lot of bugs and missing features during the jam. Additionally, we had performance become a problem for the first time.
One particular area that is in dire need of development is Audio, which we've been ignoring so far - people expect to hear something when they play a game!
We'll be working on fixing these issues in the next release, and we'll also be profiling the engine to find out where the bottlenecks are.&lt;/p&gt;
</content><category term="Demo"/><category term="Demo"/><category term="Game Jam"/><category term="Coffee N' Jam"/></entry></feed>