First Steps in Game Dev With NativeScript

Let's have some fun with NativeScript! In this post we will start to build a labyrinth-like game. We will use TypeScript for this project, but as always, plain old Javascript is an option. Check this github repo for the end result.

Project Setup

For brevity, assume we have already completed the NativeScript setup. Run these 3 commands to set up our new project:

tns create ns-game

cd ns-game

tns install typescript

The default template creates files we don’t need. Let’s get rid of the main-view-model.js and main-page.js files and create an empty main-menu.ts file instead.

The “tns install typescript” command we executed installed the necessary hooks that will transpile the TS files when we build the project. If you are using plain Javascript, you don't need this command.

As our last bit of clean up, replace the body of the main-page.xml file with the below code:

Let’s run the project for the first time. Switch to your command line program and enter the following command: “tns run android/ios” The results of this command are below:

Getting the Physics Right

Now we need to bring in a physics engine that will drive our game. We are going to use the nativescript-physics-js plugin which contains a NativeScript render for the PhysicsJS library.
Install the plugin from NPM using the CLI:

The #container element will be used by the physics renderer to display the scene. The #meta label is optional - it will show the current FPS. The styling for those elements is done in the app.css file:

#container {

width: 300;

height: 300;

background-color: lightgreen;

}

#meta {

font-family: monospace;

font-size: 10;

horizontal-align: right;

vertical-align: top;

margin: 5;

}

Next, let's bootstrap the scene in the pageLoaded event of the page (main-page.ts).

Finally, create a function that adds an actual body (in this case, a ball) to the world (we should call it after all the initialization):

export functionpageLoaded(args) {

// … initialization

// Add the ball

addBall(world, 50, 150);

}

functionaddBall(world, x: number, y: number){

varball = Physics.body('circle', {

label: "ball",

x: x,

y: y,

radius: 15,

label: "ball",

styles: { image: "~/images/ball.png"}

});

ball.restitution = 0.3;

world.add(ball);

}

The ball is a “circular” body. The image we pass in the styles object is used by the NS renderer to load an image for the body. Restitution is the “bounciness” of the body. We want the ball to feel “heavy” so give it a relatively low value. We also assign a label “ball” as a handy reference for later.

Add Interaction

While a bouncing ball is cool, we can make it cooler by being able to control the direction of the ball by rotating the phone. Again, we are going to use a plugin to attach to accelerometer events:

tns plugin add nativescript-accelerometer

Now, update the code so the accelerometer will influence the direction of the gravity vector. In main-page.ts, initialize the ‘constant-acceleration (a.k.a. “gravity”) behavior.

Nothing fancy here - attach to accelerometer event 1 second after the app has started and update the gravity vector every time the accelerometer reports an event. For extra credit, use a constant for gravity_scale.

const gravity_scale = 0.003;

We can fine-tune it later for the best experience. Lets enjoy the interaction for now:

Add Some Game Logic

With very little code, and by using pre-existing libraries, we now have a fairly realistic ball controlled by moving the mobile device. Now comes the interesting part - defining the other game elements.

Start with adding walls on to the stage. Add the following code to main-page.ts:

Walls are rectangular bodies with 'static' treatment - they cannot be moved. Again - the styles object is used by the renderer to set the color of the body.

Next: define the target - the place we want to push the ball into:

functionaddTarget(world, x: number, y: number){

world.add(Physics.body('circle', {

label: 'target',

treatment: 'static',

x: x,

y: y,

radius: 20,

styles: { image: "~/images/target.png"}

}));

}

Notice we give it a label just as we did with the ball. We are going to use these labels to detect the winning condition: the ball hits the target. The engine provides us with several ways to detect collisions. We need to detect the collision when the “ball” and the “target” bodies collide (add the code in the world initialization):

varquery = Physics.query({

$or: [

{ bodyA: { label: 'ball'}, bodyB: { label: 'target'} }

, { bodyB: { label: 'target'}, bodyA: { label: 'ball'} }

]

});

world.on('collisions:detected', function(data, e) {

if(Physics.util.find(data.collisions, query)) {

world.pause();

alert("You Win!!!")

}

});

Now we have all the methods we need to define the first level of the game:

// After physics word initialization

addWall(world, 0, 150, 20, 300);

addWall(world, 300, 150, 20, 300);

addWall(world, 150, 0, 300, 20);

addWall(world, 150, 300, 300, 20);

addWall(world, 150, 250, 10, 200);

addTarget(world, 225, 225);

addBall(world, 50, 250);

Let’s play:

With very little source code, we have been able to make a labyrinth-style game. Further, this game is available in native iOS and Android without any extra steps. In future articles, we will update the game as follows:

Enterprise

Stay connected with NativeScript

I agree to receive email communications from Progress Software or its Partners, containing information about Progress Software’s products. Consent may be withdrawn at any time.

We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.

Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.