June 21

Asteroids Pt. 1: Refreshing my basic JavaScript skills

So my current goals is to hopefully have a full React Native app done by the end of this summer. As soon as I started looking into tutorials I realized that the best start might not be going straight into React Native, but improve on my JavaScript. For those that don’t know, HTML is used for creating a web page, CSS is used to control the style and layout of your page, while JavaScript is used to make a page more interactive.

While we learned about HTML and CSS in my advanced programming class, we didn’t really learn much about JavaScript. Luckily I found this app for my phone called SoloLearn. It’s a cool app (that I’m not affiliated with by the way) that let’s you go through different courses for programming languages, has forums, quizzes against other people and coding challenges. I went through the HTML, CSS and JavaScript courses a few weeks ago, and it was a pretty good experience.

The beautiful certificate I received for completing the JavaScript course.

Sadly, I didn’t really use what I had recently learned, so I have to freshen up on my JavaScript again now. Since I’ve been watching a lot of TheCodingTrain recently I was inspired to learn how to use JavaScript with p5.js. Quoted straight from their website: “p5.js is a JavaScript library for creative coding, with a focus on making coding accessible and inclusive for artists, designers, educators, beginners, and anyone else!”. I decided that my first project should be making Asteroids in JavaScript using p5.js! Maybe not that surprising considering my background with game development.

Asteroids is an arcade game created by Lyle Rains, Ed Logg, and Dominic Walsh, and was released in 1979. In the game you control a small little ship. The goal is to dodge and destroy asteroids and saucers before they destroy you.

Asteroids. Picture taken from Wikipedia.

So, finally time to start some coding! Now for those that don’t know, the only thing JavaScript has in common with Java, is it’s name. While Java has strict rules on how to instantiate variables and how to create functions/methods, JavaScript is a lot more “flexible”. You don’t have to specify data types when creating variables in JavaScript. You may also just change the variables data type whenever you feel like it.

The following code is completely valid in JavaScript, while Java would throw errors over it:

var data;

data = 1;
data = "string";

Now when we first create a p5.js project we get a “sketch.js” file which looks like this:

function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}

In the setup() function we set up our canvas, which is what p5.js will draw our stuff on, and whatever other objects we might wanna set up in the start. The draw function is continously called so this is where we will make a update()/tick() function and render our objects.

Time to make the player! JavaScript has a few different ways to create objects, but coming from C# and Java, I decided to try JavaScripts “new” class for creating my player object called Ship:

class Ship {
  constructor(x, y, size) {
    this.pos = createVector(x, y);
    this.size = size;

    this.rotation = 0;
  }
}

In JavaScript you don’t give the constructor the class name, but rather just call it “constructor”. Also you can’t do a normal class variable in JavaScript classes. Only solution I found around this was to instantiate variables in the constructor. There might be another way though, but I’ll have to take a look at that later.

Next up, I need to render the ship. It was a small challenge learning how to do rotations in p5.js, but I eventually figured it out:

  render() {

    push();  //Makes so that translate and rotate functions only apply to this object

    translate(this.pos.x, this.pos.y);    //Moves the (0, 0) point to given coords
    rotate(this.rotation);                //Rotates the object in radians

    noFill();      //Draw shapes without fill
    stroke(255);   //Set the stroke color to white

    triangle(-this.size, this.size, 0, -this.size * 1.5, this.size, this.size) //Draws a triangle

    pop();  //Makes so that translate and rotate functions only apply to this object

  }

The push() and pop() functions let’s us manipulate the origin point and rotation of the ship without it affecting the rest of the objects. The rotate() function let’s us rotate something around the origin point of the canvas in p5.js, and combining it with the translate() function (which moves the origin point) I can make the ship rotate around itself!

Quickly adding some controls for rotation in the sketch,js file using p5.js’s built in event handler:

function keyPressed() {
  if (keyCode == LEFT_ARROW) {
    ship.rotate(-0.075);
  } else if (keyCode == RIGHT_ARROW) {
    ship.rotate(0.075);
  }

}

function keyReleased() {
  if (keyCode == LEFT_ARROW) {
    ship.rotate(0);
  }
  
  if (keyCode == RIGHT_ARROW) {
    ship.rotate(0);
  }
  
}

And adding this to the Ship class (NOTE: The ships tick() and render() functions are being called in the “sketch.js” file):

rotate(angularSpeed) {
    this.angularSpeed = angularSpeed;
}

tick() {
    this.rotation += this.angularSpeed;
}

And the ship can now rotate! Yay!

Animated GIF

Next I should make it so I can move the ship. For this I wanna make three vectors. One for position, one for velocity and one for acceleration. When the player press the Up Arrow, the ship should get some acceleration in the direction the ship is pointing. Then I will add that acceleration to the velocity vector, which we will add to the position vector. I’ll make a movement() function and add it to the tick() function for the Ship class:

movement() {
    
    //Acceleration
    this.acc = p5.Vector.fromAngle(this.rotation - PI / 2).normalize().mult(this.addedAcc);
    
    //Velocity
    this.vel.add(this.acc);
    
    //Prevents Ship from going too fast
    if(this.vel.x > this.maxVel) this.vel.x = this.maxVel;
    if(this.vel.y > this.maxVel) this.vel.y = this.maxVel;
    if(this.vel.x < -this.maxVel) this.vel.x = -this.maxVel;
    if(this.vel.y < -this.maxVel) this.vel.y = -this.maxVel;
    
    //Moves the ship according to the velocity
    this.pos.add(this.vel);
}

So I created the three vectors and an additional variable, "maxVel" for limiting the ships' speed in the Ship constructor. The most complicated part about this code is how I determine the acceleration. I first use p5.js' built in Vector function to create a vector from our ships angle. Since I want the ship to start with pointing up, but the angle initially would point right, I offset the rotation with Pi / 2 (which is 90 degrees in radians). Then since I just need the direction I take the normalized vector (a vector which makes the max length of the components 1) and multiply it with an "addedVec". The "addedVec" is set to a number when the player presses the Up Arrow, and set back to 0 when the key is released:

setAcceleration(acc) {
    this.addedAcc = acc;
}

Now the ship can move!

Animated GIF

Last two things I will do for now to make it more like the original game is adding friction to the ship... which is flying in space... and making it so that if the ships flies off one side, it pops up on the other side.

Making the ship go to the other side if it flies off one side is quite simple. I added this to the movement() function:

//Keeps the ship inside the canvas
    if(this.pos.x > width + this.size / 2){
      this.pos.x = 0;
    }else if(this.pos.x < 0 - this.size / 2){
      this.pos.x = width;
    }
    
    if(this.pos.y > height){
      this.pos.y = 0
    }else if(this.pos.y < 0){
      this.pos.y = height;
    }

Adding friction wasn't quite that bad either:

//Friction
let frictionVec = createVector(this.vel.x, this.vel.y);
    
if(this.vel.x != 0 || this.vel.y != 0) this.vel.add(frictionVec.normalize().mult(-this.friction));

Here I create a copy of the velocity vector and if the velocity is not 0, I add a certain percentage of the velocity in the negative direction. Also put into the movement() function.

Animated GIF

And that's it for this time! Next time I'll add a shooting mechanic to the ship and also some asteroids to shoot!

Full code for "sketch.js"
var ship;

function setup() {
  createCanvas(800, 640);

  ship = new Ship(width / 2, height / 2, 12);

}

function draw() {
  background(0);

  tick();

  ship.render();
}

function tick() {
  ship.tick();
}

function keyPressed() {
  if (keyCode == UP_ARROW) ship.setAcceleration(0.1);

  if (keyCode == LEFT_ARROW) {
    ship.rotate(-0.075);
  } else if (keyCode == RIGHT_ARROW) {
    ship.rotate(0.075);
  }

}

function keyReleased() {
  if (keyCode == UP_ARROW) ship.setAcceleration(0);
  
  if (keyCode == LEFT_ARROW) {
    ship.rotate(0);
  }
  
  if (keyCode == RIGHT_ARROW) {
    ship.rotate(0);
  }
  
}
[collapse]
Full code for "ship.js"
class Ship {

  constructor(x, y, size) {
    this.pos = createVector(x, y);
    this.size = size;

    this.acc = createVector(0, 0);
    this.vel = createVector(0, 0);

    this.rotation = 0;
    this.angularSpeed = 0;
    this.dir = createVector(0, 0);

    this.addedAcc = 0;
    this.friction = 0.015;
    this.maxVel = 10;
  }

  tick() {

    //Movement
    this.movement();

    this.rotation += this.angularSpeed;

  }

  movement() {
    
    //Acceleration
    this.acc = p5.Vector.fromAngle(this.rotation - PI / 2).normalize().mult(this.addedAcc);
    
    //Velocity
    this.vel.add(this.acc);
    
    //Prevents Ship from going too fast
    if(this.vel.x > this.maxVel) this.vel.x = this.maxVel;
    if(this.vel.y > this.maxVel) this.vel.y = this.maxVel;
    if(this.vel.x < -this.maxVel) this.vel.x = -this.maxVel;
    if(this.vel.y < -this.maxVel) this.vel.y = -this.maxVel;
    
    //Moves the ship according to the velocity
    this.pos.add(this.vel);
    
    //Friction
    let frictionVec = createVector(this.vel.x, this.vel.y);
    
    if(this.vel.x != 0 || this.vel.y != 0) this.vel.add(frictionVec.normalize().mult(-this.friction));
    
    //Keeps the ship inside the canvas
    if(this.pos.x > width + this.size / 2){
      this.pos.x = 0;
    }else if(this.pos.x < 0 - this.size / 2){
      this.pos.x = width;
    }
    
    if(this.pos.y > height){
      this.pos.y = 0
    }else if(this.pos.y < 0){
      this.pos.y = height;
    }

  }

  render() {

    push();  //Makes so that translate and rotate functions only apply to this object

    translate(this.pos.x, this.pos.y);    //Moves the (0, 0) point to given coords
    rotate(this.rotation);                //Rotates the object in radians

    noFill();      //Draw shapes without fill
    stroke(255);   //Set the stroke color to white

    triangle(-this.size, this.size, 0, -this.size * 1.5, this.size, this.size) //Draws a triangle

    pop();  //Makes so that translate and rotate functions only apply to this object

  }

  setAcceleration(acc) {
    this.addedAcc = acc;
  }

  rotate(angularSpeed) {
    this.angularSpeed = angularSpeed;
  }

}
[collapse]


Copyright 2020. All rights reserved.

Posted June 21, 2020 by Atle in category "JavaScript

Leave a Reply

Your email address will not be published. Required fields are marked *