DEV Community

Cover image for Having Fun with Browser Games & Web databases
William Sayama
William Sayama

Posted on • Updated on

Having Fun with Browser Games & Web databases

Introduction

This post introduces how to spin up a phaser.io game integrated with a Kintone Web Database.

GIF of the invaders game on a Kintone Web Database

There's no need to set up a node.js environment, to get an AWS serverless environment ready, or to install the latest .NET framework.

If you have a browser and a text editor, you can follow along easily.
And no, you won't be needing a credit card, so you can put that away.

What is Kintone?

Kintone is a cloud platform for teams with a visual web database builder. Kintone's REST APIs and JavaScript APIs enable further extensions to be built on top of the platform.
https://www.kintone.com/

What is Phaser.io?

Phaser is a free 2D game framework for making HTML5 games for desktop and mobile.
https://phaser.io/

Prerequisites

A Kintone Cloud Environment

Kintone is a cloud service that has flexible web database features.
We will be creating a web database on Kintone, and then building a phaser.io game on top of that.

You can obtain a Kintone environment by either applying for a free Kintone Developer License, or requesting for a free trial through Kintone.com. The former is more handy, as the developer license will be available to you indefinitely.

Phaser.io Files

In this example, we will be using the Phaser 2 library and an example game listed on their website, Invaders. I understand that Phaser 3 is available, but to be honest, I couldn't find any good game examples that use Phaser 3.

The Phaser Version 2 Library

This library can be obtained from the below page.
https://phaser.io/download/release/2.13.3
Download the js file to your local computer.

A screenshot showing which js file to download from the phaser.io website to obtain the phaser2 library

The Invaders Game Example Code

This code is available on the below page.
https://phaser.io/examples/v2/games/invaders#download
Save the zip file on to your local computer and extract the phaser-examples-master folder.

A screenshot showing which zip file to download from the phaser.io website to obtain the invader game related files

Running the Invaders Game on Kintone

Follow these 5 steps to start running the Invaders game on Kintone

1. Create a Kintone App

Web databases in Kintone are called "Apps".
Once you log in, click on the + sign on the Apps widget to start creating a new App. Select "Create App from Scratch", and give a name to your App.
On the form editor, drag and drop 2 number fields into the form. Name them "Scale" and "Health", and also set their field codes as "Scale" and "Health".

Alt Text

Save the form, and click on the blue Activate App button.
Your web database is now ready, and you will be directed to a page that holds a list of your data. Except that you don't have any data yet, so it will contain no data.

Screenshot of Kintone, with a list view and no data

Which is where we move on to our next step.

2. Input Some Data

On this List view page, click on the + button to start adding data to the App.
You'll notice that the input form is the form you created in the previous step. Let's place in the value 0.2 for the Scale field, and the value 5 for the Health field. Click on Save to save this input data.

GIF of filling in record values into Kintone

Click on the + button again to input some new data. Let's place in 1 and 10 for Scale and Health.
Repeat this process again, and place in 0.7 and 2 for Scale and Health.

OK, once we are done here, click on the name of your App to navigate to the List view. Previously, we saw this page had not data. Now that you've put new data inside, it should now look a bit more lively.

Screenshot of Kintone, with 3 records of data inside the record list view

We will be utilizing this data in a later step.

3. Create a Custom View

Access the App's setting page by clicking the cog wheel.
The view tab allows you to set new views by limiting the fields you want to see, and by setting filter conditions. Kintone also allows a "Calendar view and" a "Custom view".
In this example, we will be using a Custom view, which is a view defined by HTML elements.

Click on the + button to add a new view, and select "Custom view". In the "HTML Code" option, insert the following html:

<div id="phaser-example"></div>
Enter fullscreen mode Exit fullscreen mode

Screenshot of Kintone, with HTML inserted into the HTML field

Click Save, and then the Update App button to apply these new settings.

4. Make Edits to the Invaders JavaScript File

We need to modify the invaders.js file before applying it to the Kintone App.
Open up the invaders file in your local editor. This file can be found in the phaser-examples-master folder under examples > games.

Update the preload() Function

By default, the Invaders code is loading images in this method by stating a local path. As we are activating this game on the browser, we need to change these paths to a valid path. Unfortunately, Kintone doesn't have a stable place to host image files (I hope this gets updated soon!), so upload the images onto an image hosting service such as https://imgur.com and reference these images in the preload() function:

game.load.image('bullet','{path to bullet.png}');
game.load.image('enemyBullet', '{path to enemy-bullet.png}');
game.load.spritesheet('invader', '{path to invader32x32x4.png}', 32, 32);
game.load.image('ship', '{path to player.png}');
game.load.spritesheet('kaboom', '{path to explode.png}', 128, 128);
game.load.image('starfield', '{path to starfield.png}');
//game.load.image('background', '{path to background2.png}'); <-- this file is not used
Enter fullscreen mode Exit fullscreen mode

If you are using imgur, the link should be in the format of https://i.imgur.com/{randomstring}.png

The name and locations of the images within the downloaded folder may be a bit confusing - use the below table as reference:

Image variable Image Location
bullet Alt Text assets/games/invaders/bullet.png
enemyBullet Alt Text assets/games/invaders/enemy-bullet.png
invader Alt Text assets/games/invaders/invader32x32x4.png
ship Alt Text assets/games/invaders/player.png
kaboom Alt Text assets/games/invaders/explode.png
starfield Alt Text assets/games/invaders/starfield.png

Update the Phaser.Game method

State "Phaser.CANVAS" for the third parameter of the Phaser.Game method so that images can be loaded and used from an external server.

var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });

Enter fullscreen mode Exit fullscreen mode

Update the restart() Function

This Invaders example doesn't reset the score when you die and restart the game. It's not a large matter, but it was sort of bugging me, so add the following code at the end of the restart() function in the invaders.js file so that the score resets to 0 when you restart your Invaders game:

score = 0;
scoreText.text = scoreString + score;
Enter fullscreen mode Exit fullscreen mode

5. Add JavaScript files to Your Kintone App

Now this is the final step for getting the Invaders file to work on top of Kintone.

Access the App's settings page by clicking on the cog wheel. Click on the "App Settings" tab, and select the "JavaScript and CSS Customization" settings. This page allows you to upload JavaScript and CSS files that are to be run when pages of the Kintone App run.

Under the "Upload JavaScript for PC" settings, click on "Upload file", and upload the phaser.js library, and the Invaders file that we've been working on. Make sure that the phaser.js library is located above the Invaders file, as Kintone will read these files in order from the top, and the Invaders file will be using methods that exist within the phaser.js library.

Alt Text

Once uploaded, click on Save, and Update changes to apply these new changes to the App.

You should be navigated to the Custom view page, where the Invaders game should load.

A screenshot of the invaders game working on Kintone

Use the arrow keys to move the player's ship, and the spacebar to shoot bullets! Pew pew!

Further Configurations to the Invaders Game

Before integrating the game with the data in our Kintone database, we'll make some further updates to the code.

Scaling the Alien Size

The createAliens() function contains the code for, well, creating the Aliens.

for (var y = 0; y < 4; y++)
{
    for (var x = 0; x < 10; x++)
    {
        var alien = aliens.create(x * 48, y * 50, 'invader');
        alien.anchor.setTo(0.5, 0.5);
        alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);
        alien.play('fly');
        alien.body.moves = false;
    }
}
Enter fullscreen mode Exit fullscreen mode

Scale the alien size by adjusting the scale property of the aliens:

for (var y = 0; y < 4; y++)
{
    for (var x = 0; x < 10; x++)
    {
        var alien = aliens.create(x * 48, y * 50, 'invader');
        alien.anchor.setTo(0.5, 0.5);
        alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);
        alien.play('fly');
        alien.body.moves = false;
        alien.scale.x = 0.2; //<--- 
        alien.scale.y = 0.2; //<---
    }
}
Enter fullscreen mode Exit fullscreen mode

This should make the aliens 1/5 of the default size.

Adding HP to the aliens

Aliens in this Invaders game die with just one hit of the laser. This is due to alien.kill() method being called within the collisionHandler() function. Phaser has a health component (Phaser.Component.Health) that adds an HP component to the game's characters, so that the kill method is called when their HP reaches or goes below 0.

To configure the aliens to have HP, first replace the alien.kill() method with alien.damage() method within the collisionHandler() function:

    //  When a bullet hits an alien we kill them both
    bullet.kill();
    //alien.kill(); //<---
    alien.damage(1); //<---
Enter fullscreen mode Exit fullscreen mode

Next, configure the aliens' health parameters in the createAliens() function:

for (var y = 0; y < 4; y++)
{
    for (var x = 0; x < 10; x++)
    {
        var alien = aliens.create(x * 48, y * 50, 'invader');
        alien.anchor.setTo(0.5, 0.5);
        alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);
        alien.play('fly');
        alien.body.moves = false;
        alien.scale.x = 0.2;
        alien.scale.y = 0.2;
        alien.health = 5; //<--- 
    }
}
Enter fullscreen mode Exit fullscreen mode

With this, aliens will start off with an HP of 5, which will be reduced by the integer stated in alien.damage() every time the player's bullet hits them. When the alien's HP reaches 0 or below, the alien's kill method will be called.

Integrating Databases with the Invaders Game

OK so now that we've got our Invaders game running on the browser on top of Kintone, we'll get right to the question - why are we using a Kintone database to do this?
This is because we want to relate the data inside the database with the Invaders game that we are playing.

Let's add some final touches.

Add an Immediate Function and a Kintone Event

As a best practice to writing code on Kintone, we will wrap the current code in an immediate function, and also set a Kintone List View event. This will ensure that the code will run only when the record list view (including the custom view) is displayed.

(function () {
    "use strict";
    kintone.events.on('app.record.index.show', function (event) {

    /////////////////////////////////////
    //  The code we've written so far  //
    /////////////////////////////////////

    });
})();
Enter fullscreen mode Exit fullscreen mode

When using "use strict", undeclared variables cause errors.
For some reason, enemyBullets, live and bullet are not declared in the original code, so declare them in the list of variables under the preload() function:

var player;
var aliens;
var bullets;
var bulletTime = 0;
var cursors;
var fireButton;
var explosions;
var starfield;
var score = 0;
var scoreString = '';
var scoreText;
var lives;
var enemyBullet;
var firingTimer = 0;
var stateText;
var livingEnemies = [];
var enemyBullets; //<---
var live; //<---
var bullet; //<---
Enter fullscreen mode Exit fullscreen mode

Set the Code to Run Only on the Custom View

The Invaders game code will run in any view since we are currently using Kintone's Record List View event.

To ensure that the game code will only run when we are in the Custom view, add the following:

(function () {
    "use strict";
    kintone.events.on('app.record.index.show', function (event) {

      if (event.viewType != "custom") {return;} // <---
Enter fullscreen mode Exit fullscreen mode

Update the createAliens() Function

Replace the double loop that created the aliens to the below code.

for (var i=0; i<event.records.length;i++)
{
  var alien = aliens.create(i * 48,  50, 'invader');
  alien.anchor.setTo(0.5, 0.5);
  alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);
  alien.play('fly');
  alien.body.moves = false;
  alien.scale.x = event.records[i].Scale.value;
  alien.scale.y = event.records[i].Scale.value;
  alien.health = event.records[i].Health.value;
}
Enter fullscreen mode Exit fullscreen mode

The event variable is an object made available by Kintone when the Record List event was triggered. It includes data of the Record List, including an array of record data. As we have set the field codes for the two number fields to be Scale and Health, the values for these fields can be found in each content of the array. For example, event.records[0].Scale.value will have the numerical value stored in the Scale field of the 1st record.

The code above will create multiple aliens equal to the number of records in the record list (taken from event.records.length). Also, the size and health changed from being predefined numbers to values from the Kintone database records.

Update the collisionHandler() Function

The explosion animation that is displayed when the player's bullets hit the enemies are a bit off when the aliens are scaled down/up.
To fix this bug, the explosion.reset(alien.body.x, alien.body.y); method in the collisionHandler() function needs to be altered:

explosion.reset(alien.body.center.x, alien.body.center.y);
Enter fullscreen mode Exit fullscreen mode

Save the File and Re-Upload It to the Kintone App

Now that the code is further altered, save your invaders.js file, access the JavaScript and CSS Customization settings of the Kintone App, and replace the old file with the latest saved file.
Click Save, and Update App to apply the changes.

Try out the updated game in your Custom view

Screenshot of the invaders game working on Kintone with several alines with different sizes

Screenshot of the Kintone list view with 10 records with different values inside

Summary

This post went through how to set up an Invaders game related to a web database, just using the browser and a Kintone cloud environment. By doing this, users who are not able to code can join the game development process by updating the Kintone database properties and adding in new data, that will be reflected upon the Invaders file.

Feel free to copy the code and test it out on your environment - I will be happy to see enhanced versions of the example code in this post!

The resulting invaders code used

(function () {
    "use strict";
    kintone.events.on('app.record.index.show', function (event) {

      if (event.viewType != "custom") {return;}

      var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });

      function preload() {

        game.load.image('bullet','{path to image}');
        game.load.image('enemyBullet', '{path to image}');
        game.load.spritesheet('invader', '{path to image}', 32, 32);
        game.load.image('ship', '{path to image}');
        game.load.spritesheet('kaboom', '{path to image}', 128, 128);
        game.load.image('starfield', '{path to image}');
        game.load.image('background', '{path to image}');

      }

      var player;
      var aliens;
      var bullets;
      var bulletTime = 0;
      var cursors;
      var fireButton;
      var explosions;
      var starfield;
      var score = 0;
      var scoreString = '';
      var scoreText;
      var lives;
      var enemyBullet;
      var firingTimer = 0;
      var stateText;
      var livingEnemies = [];
      var enemyBullets;
      var live;
      var bullet;

      function create() {

          game.physics.startSystem(Phaser.Physics.ARCADE);

          //  The scrolling starfield background
          starfield = game.add.tileSprite(0, 0, 800, 600, 'starfield');

          //  Our bullet group
          bullets = game.add.group();
          bullets.enableBody = true;
          bullets.physicsBodyType = Phaser.Physics.ARCADE;
          bullets.createMultiple(30, 'bullet');
          bullets.setAll('anchor.x', 0.5);
          bullets.setAll('anchor.y', 1);
          bullets.setAll('outOfBoundsKill', true);
          bullets.setAll('checkWorldBounds', true);

          // The enemy's bullets
          enemyBullets = game.add.group();
          enemyBullets.enableBody = true;
          enemyBullets.physicsBodyType = Phaser.Physics.ARCADE;
          enemyBullets.createMultiple(30, 'enemyBullet');
          enemyBullets.setAll('anchor.x', 0.5);
          enemyBullets.setAll('anchor.y', 1);
          enemyBullets.setAll('outOfBoundsKill', true);
          enemyBullets.setAll('checkWorldBounds', true);

          //  The hero!
          player = game.add.sprite(400, 500, 'ship');
          player.anchor.setTo(0.5, 0.5);
          game.physics.enable(player, Phaser.Physics.ARCADE);

          //  The baddies!
          aliens = game.add.group();
          aliens.enableBody = true;
          aliens.physicsBodyType = Phaser.Physics.ARCADE;

          createAliens();

          //  The score
          scoreString = 'Score : ';
          scoreText = game.add.text(10, 10, scoreString + score, { font: '34px Arial', fill: '#fff' });

          //  Lives
          lives = game.add.group();
          game.add.text(game.world.width - 100, 10, 'Lives : ', { font: '34px Arial', fill: '#fff' });

          //  Text
          stateText = game.add.text(game.world.centerX,game.world.centerY,' ', { font: '84px Arial', fill: '#fff' });
          stateText.anchor.setTo(0.5, 0.5);
          stateText.visible = false;

          for (var i = 0; i < 3; i++) 
          {
              var ship = lives.create(game.world.width - 100 + (30 * i), 60, 'ship');
              ship.anchor.setTo(0.5, 0.5);
              ship.angle = 90;
              ship.alpha = 0.4;
          }

          //  An explosion pool
          explosions = game.add.group();
          explosions.createMultiple(30, 'kaboom');
          explosions.forEach(setupInvader, this);

          //  And some controls to play the game with
          cursors = game.input.keyboard.createCursorKeys();
          fireButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

      }

      function createAliens () {

        for (var i=0; i<event.records.length;i++)
        {
          var alien = aliens.create(i * 48,  50, 'invader');
          alien.anchor.setTo(0.5, 0.5);
          alien.animations.add('fly', [ 0, 1, 2, 3 ], 20, true);
          alien.play('fly');
          alien.body.moves = false;
          alien.scale.x = event.records[i].Scale.value;
          alien.scale.y = event.records[i].Scale.value;
          alien.health = event.records[i].Health.value;
        }

        aliens.x = 100;
        aliens.y = 50;

          //  All this does is basically start the invaders moving. Notice we're moving the Group they belong to, rather than the invaders directly.
          var tween = game.add.tween(aliens).to( { x: 200 }, 2000, Phaser.Easing.Linear.None, true, 0, 1000, true);

          //  When the tween loops it calls descend
          tween.onLoop.add(descend, this);
      }

      function setupInvader (invader) {

          invader.anchor.x = 0.5;
          invader.anchor.y = 0.5;
          invader.animations.add('kaboom');

      }

      function descend() {

          aliens.y += 10;

      }

      function update() {

          //  Scroll the background
          starfield.tilePosition.y += 2;

          if (player.alive)
          {
              //  Reset the player, then check for movement keys
              player.body.velocity.setTo(0, 0);

              if (cursors.left.isDown)
              {
                  player.body.velocity.x = -200;
              }
              else if (cursors.right.isDown)
              {
                  player.body.velocity.x = 200;
              }

              //  Firing?
              if (fireButton.isDown)
              {
                  fireBullet();
              }

              if (game.time.now > firingTimer)
              {
                  enemyFires();
              }

              //  Run collision
              game.physics.arcade.overlap(bullets, aliens, collisionHandler, null, this);
              game.physics.arcade.overlap(enemyBullets, player, enemyHitsPlayer, null, this);
          }

      }

      function render() {

          // for (var i = 0; i < aliens.length; i++)
          // {
          //     game.debug.body(aliens.children[i]);
          // }

      }

      function collisionHandler (bullet, alien) {
          //  When a bullet hits an alien we kill them both
          bullet.kill();
          //alien.kill();
          alien.damage(1);

          //  Increase the score
          score += 20;
          scoreText.text = scoreString + score;

          //  And create an explosion :)
          var explosion = explosions.getFirstExists(false);
          //explosion.reset(alien.body.x, alien.body.y);
          explosion.reset(alien.body.center.x, alien.body.center.y);
          explosion.play('kaboom', 30, false, true);

          if (aliens.countLiving() == 0)
          {
              score += 1000;
              scoreText.text = scoreString + score;

              enemyBullets.callAll('kill',this);
              stateText.text = " You Won, \n Click to restart";
              stateText.visible = true;

              //the "click to restart" handler
              game.input.onTap.addOnce(restart,this);
          }

      }

      function enemyHitsPlayer (player,bullet) {

          bullet.kill();

          live = lives.getFirstAlive();

          if (live)
          {
              live.kill();
          }

          //  And create an explosion :)
          var explosion = explosions.getFirstExists(false);
          explosion.reset(player.body.x, player.body.y);
          explosion.play('kaboom', 30, false, true);

          // When the player dies
          if (lives.countLiving() < 1)
          {
              player.kill();
              enemyBullets.callAll('kill');

              stateText.text=" GAME OVER \n Click to restart";
              stateText.visible = true;

              //the "click to restart" handler
              game.input.onTap.addOnce(restart,this);
          }

      }

      function enemyFires () {

          //  Grab the first bullet we can from the pool
          enemyBullet = enemyBullets.getFirstExists(false);

          livingEnemies.length=0;

          aliens.forEachAlive(function(alien){

              // put every living enemy in an array
              livingEnemies.push(alien);
          });


          if (enemyBullet && livingEnemies.length > 0)
          {

              var random=game.rnd.integerInRange(0,livingEnemies.length-1);

              // randomly select one of them
              var shooter=livingEnemies[random];
              // And fire the bullet from this enemy
              enemyBullet.reset(shooter.body.x, shooter.body.y);

              game.physics.arcade.moveToObject(enemyBullet,player,120);
              firingTimer = game.time.now + 2000;
          }

      }

      function fireBullet () {

          //  To avoid them being allowed to fire too fast we set a time limit
          if (game.time.now > bulletTime)
          {
              //  Grab the first bullet we can from the pool
              bullet = bullets.getFirstExists(false);

              if (bullet)
              {
                  //  And fire it
                  bullet.reset(player.x, player.y + 8);
                  bullet.body.velocity.y = -400;
                  bulletTime = game.time.now + 200;
              }
          }

      }

      function resetBullet (bullet) {

          //  Called if the bullet goes out of the screen
          bullet.kill();

      }

      function restart () {

          //  A new level starts

          //resets the life count
          lives.callAll('revive');
          //  And brings the aliens back from the dead :)
          aliens.removeAll();
          createAliens();

          //revives the player
          player.revive();
          //hides the text
          stateText.visible = false;

          score = 0;
          scoreText.text = scoreString + score;

      }
  });
})();
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
tmgos profile image
Thalia

Definetly like games and apps such as this one It's such an immerse world to delve into which can take up so much of your time when you get into it..

Collapse
 
jessiccaa profile image
jessiccaa • Edited

Code Vein does indeed offer a multiplayer component, which can add a whole new layer of enjoyment to the game. It's not just about tackling the challenging enemies and exploring the post-apocalyptic world solo; you can team up with friends or other players to take on the adventure together. So, yes, in a nutshell, Code Vein does have multiplayer features that can enhance your gaming experience and make it even more enjoyable. If you're interested in diving deeper into the specifics of how it all works, you can check out this link: is code vein multiplayer? The multiplayer aspect in Code Vein allows you to embark on co-op journeys, which can be a blast if you prefer to tackle the game's formidable bosses with some backup. It's a great way to strategize, share your experiences, and help each other out when the going gets tough.

Collapse
 
lindamarine profile image
lindamarine

Yes, I really like the games and apps like this. I still remember when I have to create the very first app for my university project and how much issues I have to face. But time passes a friend help me and submitted the assignment of the app.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.