Made an .io game in 10 days✈️
Tags: gamedev, javascript, node, webdev
Published: 2023-10-31 15:05:49

Hello Everyone!
Let's talk gamedev, better yet fast addictive .io games. And how I made
[StellarClash.io✈️](https://stellarclash.io) in 10 days.

**This is a real .io game!! You can open 2 tabs on your browser and see 2 planes!!**

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y19gqt4cra6re3gtirl4.png)


Here is the source [GitHub🐈](https://github.com/lil-shake-code/aerospace-io)

# Basic Structure of an .io Game
## Frontend - Gamemaker
You have to choose a frontend which is compatible with the HTML5 Canvas. You can directly use the canvas API if you like vanilla JS code but I used Gamemaker and it's really easy! 
Because the frontend stuff is mostly just
1. Drawing planes and Moving the Camera there
2. Drawing bullets 
3. Showing what is happening on the server

## Backend Nodejs Server
This is where all the game logic actually works.
And there is always websocket communication between client and server.

### Players
Every player is treated like a point particle and is spawned at a relatively low density area of the game. All players are saved to a players array

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6m2y73zi239zrqd9imj5.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ud7q3r76cxe85rsz6t92.png)

### Bullets  
Shooting is important and based on your level you shoot a specific number of bullets, bullets of specific damage. And on the client side the damage of the bullet appears as its size, so you have an idea how much you'll be hurt.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbuy57g2sz3cncow6j2u.png)

### Food ie Selenium
Some metal... these are like the slither io orbs. The gamemaker particle effects make it look a lot like that.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/st8mq5cwjxaqipprsfnx.png)



# Levelling system

The levelling system uses a formula.... This formula will need to be updated as the game gets better but here it is

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zsetjr5gkcgp1yivppe.png)



#The Game Loop in Nodejs

Here,
1. All the players are propelled forward according to their speed and direction

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lvcj0fvq1n671gi6bqt0.png)


2. All the bullets are propelled forward according to their speed and direction. If a bullet is hitting another player, the bullet is destroyed and health and kills of victim and killer are updated.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k7uxiici5llw2romta4k.png)


3. If players are close to the food, it is destroyed and added to their score.
4. Other behaviors like bots ...

#Bot Creation
So bots are created when there are less players and they are made to behave very similar to normal players.

```javascript
function createBots() {
  //go through the players and count how many are bots
  var botCount = 0;
  realPlayerCount = 0;
  for (var i in players) {
    var player = players[i];
    if (player.bot) {
      botCount++;
    } else {
      if (player.health > 0) {
        realPlayerCount++;
      }
    }
  }

  if (botCount < MAX_BOTS && realPlayerCount < MAX_BOTS) {
    console.log("creating a new bot");
    var spawnPoint = bestSpawnPoint(players, spawnPoints, "public");
    //for the name of the bot, 30% chance use the name from the list, 70% chance use empty string
    var botName = "";
    if (Math.random() < 0.3) {
      botName = botNames[currentBotName++];
      if (currentBotName >= botNames.length) {
        currentBotName = 0;
      }
    }

    var player = {
      clientId: clientId++,
      x: spawnPoint.x,
      y: spawnPoint.y,
      A: 0,
      N: 0,
      speed: 10,
      health: 100,
      maxHealth: 100,
      kills: Math.floor(Math.random() * 4),
      roomId: "public", ///BOTS WILL ONLY EXIST in public room
      username: botName,
      ws: null,
      bot: {
        recoil: 0,
        lifetime: 0,
      },
      skin: Math.floor(Math.random() * 7), //0-6
      lastHitTime: 0,
      recoil: 0,
    };
    player.shootingCharacteristics = getShootingCharacteristics(player.kills);

    player.speed = player.shootingCharacteristics.thrustSpeed;
    player.maxHealth = player.shootingCharacteristics.maxHealth;

    players[player.clientId] = player;

    //tell other players we created this guy
    var sendThis = {
      eventName: "create_player",
      clientId: player.clientId,
      roomId: player.roomId,
      x: player.x,
      y: player.y,
      username: player.username,
      skin: player.skin,
    };

    for (var j in players) {
      var otherPlayer = players[j];

      //check if same room
      if (otherPlayer.roomId != player.roomId) {
        continue;
      }

      if (otherPlayer.clientId != player.clientId) {
        //if ws is not null
        if (otherPlayer.ws) {
          otherPlayer.ws.send(JSON.stringify(sendThis));
        }
      }
    }
  }
}
setInterval(createBots, 1000);

```


# Bot Behavior
Inside the game Loop, bots are made to follow the closest player to them and once they are close enough start shooting. Note that there is some randomness allowed here to make it feel real.

```javascript
if (player.bot) {
      player.bot.lifetime++;
      //recoil
      if (player.bot.recoil > 0) {
        player.bot.recoil--;
      }
      //bot logic
      //try to chase the player closest to this bot and once you are close enough, shoot

      var closestPlayer = null;
      var closestDistance = Infinity;

      for (var j in players) {
        var otherPlayer = players[j];
        //if roomId is different, skip this player
        if (otherPlayer.roomId != player.roomId) {
          continue;
        }

        //if health is 0, skip this player
        if (otherPlayer.health <= 0) {
          continue;
        }

        if (otherPlayer.clientId != player.clientId) {
          var d = distance(player.x, player.y, otherPlayer.x, otherPlayer.y);
          if (d < closestDistance) {
            closestDistance = d;
            closestPlayer = otherPlayer;
          }
        }
      }

      if (closestPlayer) {
        //chase this player
        var dx = closestPlayer.x - player.x;
        var dy = closestPlayer.y - player.y;

        //if the player is close enough, shoot
        if (closestDistance < 100 + 5 * player.kills) {
          if (player.bot.recoil <= 0) {
            //rotate the bot to face the player
            player.A = Math.atan2(-dy, dx) * (180 / Math.PI);

            ///repeat x times

            for (var k = 0; k < player.shootingCharacteristics.spread; k++) {
              var bullet = {
                x: player.x,
                y: player.y,
                A: player.A + Math.random() * 10 - 5,
                speed: player.shootingCharacteristics.bulletSpeed,
                damage: player.shootingCharacteristics.damage,
                firedBy: player.clientId,
                roomId: player.roomId,
                lifetime: 120,
                username: player.username,
              };
              bullets[bulletId++] = bullet;
            }

            //recoil
            player.bot.recoil = player.shootingCharacteristics.recoilTime;
            player.recoil = player.shootingCharacteristics.recoilTime;
          }
        } else {
          //just make the bot move in the direction of the player 5 pixels
          var fakeN = 0.2 + Math.abs(Math.sin(player.kills)) / 2;
          player.x += (dx / closestDistance) * player.speed * fakeN;
          player.y += (dy / closestDistance) * player.speed * fakeN;

          //rotate the bot to face the player
          var newA =
            Math.atan2(-dy, dx) * (180 / Math.PI) + 5 * Math.random() - 2.5;
          //lerp player.A to newA
          player.A = (player.A + newA) / 2;
        }
        //check if player is out of bounds
        var correctedPosition = outOfBounds(player.x, player.y);
        player.x = correctedPosition.x;
        player.y = correctedPosition.y;
        continue;
      }
    }
```

#Deployment and public facing.
The nodejs websocket server is deployed on heroku for just 7$ a month!!! That is my game server, only 7$🤑🤑🤑💵💵

It is hosted in US so players like me from India can face some lag. But because of extreme lerping, it's smooth lag which kind of does not feel as bad.

The actual game is hosted with Firebase hosting. I bought the domain on Namecheap for like 42$ and then deployed using the firebase CLI.


#Future Plans
The next step would be to 
1. Improve the game - Adding more SFX and VFX to make it look better, and improve the levelling up and evolution
2. Develop a following for constant feedback. [iogames.forum ](iogames.forum) is the best place imo and then there are other io game sites. 
3. Iterate 1 and 2 so that players are funnelled to my twitter and iogames.forum where they get updates on the games and can interact with me and that will make the game better
4. Ads by Google - I was magically approved as a google H5 Games ad associate but I don't have the basic adsense approval on my site yet😶... so I have to figure some stuff out but hopefully I'll be able to earn a little from this!

Thank you for reading this post! Follow me on 
[Twitter🐦](https://twitter.com/lilshake139) for more updates!!