August 16

React Native: A full new application, Nature Tracker!

Hi again! Been a while since last time, but I promise you it has been worth it! So I knew that last time I said I would take a look at PHP and Laravel next, but turns out I lied… a little.

I did finish the PHP course on SoloLearn!

But my dad came to me with a pretty good app idea that I wanted to make for his birthday! The idea is an app where you can easily keep track of various stuff you find out in the nature. We both sat down and designed the app, and I got to work as soon as I got home!

After spending around 8 workdays on it, I ended up with this:

The app works like this: You have a big map (provided by Google Maps and react-native-maps) you can drag around or search for a specific location, and place markers on. To place a marker, you simply have to drag and drop one of the markers from the left side. Like this:

Now the user just need to put in a description of the marker (if they want to), and a marker is created!

If the user now taps the marker, it will show the marker name, description, how far away the marker is (it’s also updating the distance in real time!), the ability to delete it and also buttons to open Google Maps for plotting a route towards it!

The user can press the “Marks” button at the bottom to open a modal that shows a list of all the markers. The user can then press one of the markers to be brought to it on the map:

The settings screen allows the user to switch between English and Norwegian language, and adjust the icon sizes.

And that’s it! If you want to try out the app then it’s available on Google Play now!

There’s still some features that could be implemented, stuff that could be optimized, but overall I’m pretty happy with what I’ve accomplished in the 8 days I worked on the app. The challenges I’ve had with this app was learning how to deal with react-native-maps and Google Maps, localization and focusing more on performance based coding (using async functions, and just better code overall).

Thanks for reading!

August 2

React Native: ToDo App

So the time as finally come… To make a React Native app! This app will just be a “port” of my previous ToDo app, which means it will take advantage of the database (MySQL) and API (Node.js + Express) that I made previously. Let’s begin! Note: I’ve made some big refactor changes since the React.js, but the app’s logic still functions the same.

While React.js is considered a JavaScript library for making websites, React Native is seen as a framework for building native apps for both Android and iOS. React Native works a lot like React.js, but it has some differences. First order of business was to learn more about those differences. I scouted on YouTube to find some tutorial videos to watch. I found The Net Ninja React Native tutorial series and binged watched a lot of them. After a while I felt confident with making my ToDo app.

Two of the biggest differences between React and React Native is that React Native doesn’t use CSS, but something called styles and “StyleSheets” to mimic CSS. The other one is that React Native uses different tags than React. Example, instead of “div”, you have “View”. You also just can’t type out text to be rendered. You have to use the “Text” tag.

Now let’s really begin! This is how the final app looks like:

Since the app has a lot of similarities to the React app, I won’t be covering the parts that are the same. Just know that we have functions to communicate with an API. All of the code can be viewed through the GitHub page.

The only thing different in the App.js file is how the render function and a StyleSheet object in the bottom of the file, instead of a CSS file:


...

  render() {
    return (
      // Body container
      <View style={styles.body} >
        <StatusBar style="auto" />
        <Header />

        {/* Main  */}
        <ScrollView style={styles.wrapper} contentContainerStyle={{ paddingBottom: 80 }} showsVerticalScrollIndicator={false} >

          {/* Shows all todos from state.todos */}
          {this.state.todos.map(toDo => {
            return <ToDo key={toDo.id} id={toDo.id} desc={toDo.desc} completed={toDo.completed} setAsCompleted={this.setAsCompleted} remove={this.removeToDo} />
          })}

        </ScrollView>

        {/* Menu and button that allows user to add new ToDos */}
        <AddToDo addToDo={this.addToDo} />

        <Footer />

      </View>
    );
  }
}

const styles = StyleSheet.create({

  body: {
    flex: 1,
    backgroundColor: '#ccc',

  },

  wrapper: {
    width: '95%',
    height: 100,

    marginLeft: 'auto',
    marginRight: 'auto',

    marginTop: 'auto',
    marginBottom: 5,

  },
});

Here you can see that I use “View” tags instead of “div”. The main difference is that in React Native we can use a ScrollView for our list of ToDos. Every object we place in React Native is set in a kind off “fixed position”, but with ScrollView we can have a scrollable space, like this:

The styling syntax is also really similar to CSS. The main difference is that here we use “camelCase” notation instead of using hyphens. Styles also have a fewer properties and values to use, but they usually do the job very well. It’s also worth nothing that we can only choose “flex” or “none” values in the “display” property, unlike CSS which has many values to choose between. However, flex is extremely strong and… well, flexible.

Since we don’t have access to CSS we have to do animations a different way too. Here’s how I did it for my “widen animation” for the ToDo component:

First I create the animation object in my state and also an interpolate var. This transform the value from 0-1 to 0%-100%, so I can use it for the CSS styling.


    state = {
        widenAnim: new Animated.Value(0),
    };

    interpolateWidth = this.state.widenAnim.interpolate({inputRange:[0,1],outputRange:['0%','100%']});

Next I create a function for starting the animation and call it when the component mounts:


    widen = () => {
        Animated.spring(this.state.widenAnim, {
            toValue: 1,
            useNativeDriver: false
        }).start();
    };

    componentDidMount() {
        this.widen();
    }

The spring function creates a spring-like animation. It has a lot of different properties we can change, but that wasn’t needed for what I wanted.

Last piece of the puzzle is to connect our animation to our render function. To make the entire component be effected by the animation I have to wrap it all up in a “Animated.View” tag, and make a style which is connected to the animation values. This is how the render function ended up like:


render() {

        // Destructure props
        const { id, desc, completed } = this.props;

        // Decides if the text should be shown with a lined-through effect
        let descStyle = completed ? [styles.component_todo_desc, styles.lined_through] : styles.component_todo_desc;

        return (
            <Animated.View style={{ opacity: this.state.fadeInAnim, width: this.interpolateWidth }}>
                <TouchableOpacity style={styles.component_todo} onPressIn={() => this.props.setAsCompleted(id)}>
                    {/* Main component ToDo div */}
                    <View style={{ flexDirection: "row", }}>
                        {/* Checkmark */}
                        <View style={styles.component_todo_checkmark_box} />
                        {completed ? <Text style={styles.component_todo_checkmark_check}>&#10003;</Text> : <Text />}

                        {/* Show desc text */}
                        <Text style={descStyle}>{desc}</Text>
                    </View>

                    {/* Delete button */}
                    <TouchableOpacity style={styles.component_todo_delete_button} onPressIn={() => this.props.remove(id)}>
                        <Entypo name="cross" size={48} color='rgb(90, 0, 0)' style={{textAlign:'center'}} />
                    </TouchableOpacity>

                </TouchableOpacity>
            </Animated.View>
        )
    }

Also, to create a touchable component we need to use the tag “TouchableOpacity” instead of Button.

I also made a fade in animation to go along with the widen animation. This is how it ended up like:

And that’s it! A port of the React ToDo app over to React Native, using MySQL as a database and Node.js + Express as an API. My goal for the summer was to create a full stack React Native application, and even though I’m happy about the app there’s still a lot more I need to go through (like React Navigation, etc…), but that will have to be for another time!

Currently I’m looking at trying to create an API using PHP and the Laravel framework instead, so stay tuned for that!

Category: Blog | LEAVE A COMMENT
July 26

Full Stack: ToDo App with MySQL, Node.js, Express and React

So I had two goals for this summer, Build a full stack app and make a React Native app. Using a stack of MySQL, Node.js, Express and React I managed to build a full stack ToDo app! In this blog post I’ll show you how I did it!

The whole app works like this: I have a MySQL database where I store all my ToDos. Using Node.js and Express (a web application framework) I made a REST API that will allow my React front end to communicate with the MySQL database. And, of course, React as the front end.

Here’s how the React front end app ended up looking!

So, for the SQL server I could use either MySQL or MongoDB. I decided to use MySQL since I already have a lot of experience with it through school. MySQL is maintained by Oracle, and is both open-sourced and free to use! MySQL runs in the background of your computer and you can give it commands through the CMD to manage your database, but I strongly recommend downloading MySQL Workbench so you can get a nice GUI to use instead. I won’t go into depth about how I made my database here.

The ToDo table from MySQL Workbench

Now, for the API I decided to use Node.js and Express. They are also both free to use and very popular. For those that don’t know, Node.js is JavaScript runtime built using Chrome’s V8 JavaScript Engine. Basically, JavaScript usually only runs through a browser, but with Node.js we can run it without one. Express is a web app framework for Node.js. It makes creating APIs so much easier.

So first up is the entry point “server.js” file:

// Init
const express = require('express');
const bodyParser = require("body-parser");
const cors = require('cors');

// Set up a const for express and a port
const app = express();
const port = 3000;

// Init connection to database
const db = require('./db');

// Middleware
app.use(cors());
app.use(bodyParser.json());

// Routers
const todoRouter = require('./routes/todo.route');

// Use routers
app.use('/todo', todoRouter);

// Start listening/server
app.listen(port, () => console.log(`ToDo API listening on port ${port}...`));

First I import express itself, body-parser (used for taking the body information from a POST and making it accessible for the API) and CORS (which is used for being able to skip the Same-origin policy, which means we can connect with remote hosts to the API). Next is setting up express and a port to use with it. After that I set up something called “Middleware”. They are basically functions that gets called when the API receive a request and can manipulate the request object from the user and the response object from the API. We have to set both cors and body-parser as middleware for them to work. Next, I set up “routers” for my todos. I need to research routers a little more, but they let me organize my routes into a different file.

For connecting the API to my database I have a file called “db.config.js” which handles the settings:

module.exports = {
  HOST: "localhost",
  USER: "root",
  PASSWORD: "PASSWORD",
  DB: "tododb"
};

And then a “db.js” for connecting to the database:

const mysql = require("mysql");
const dbConfig = require("./config/db.config.js");

// Create a connection to the database
const connection = mysql.createConnection({
  host: dbConfig.HOST,
  user: dbConfig.USER,
  password: dbConfig.PASSWORD,
  database: dbConfig.DB
});

// open the MySQL connection
connection.connect(error => {
  if (error) throw error;
  console.log("Successfully connected to the database!...");
});

module.exports = connection;

Here I just create a connection using a MySQL library for Node.js, and export it so that the route files can use it.

Last up is the route file for ToDo. This is the code that’s gonna handle the HTTP request and responses for the ToDos. I’m not gonna show the entire file since it’s quite big, but all of the functions looks quite the same. It can be viewed through the GitHub page.

const express = require('express'),
    router = express.Router(),
    db = require('../db');

// Get one ToDo
router.get('/:todoId', function (req, res) {
    let sql = `SELECT * FROM todo WHERE id = ${req.params.todoId}`;

    db.query(sql, function (err, data, fields) {
        if (err) return res.status(400).send(err);

        if(Object.keys(data).length === 0) return res.status(404).send(`404: ToDo with id: ${req.params.todoId} not found.`);

        res.json({
            data,
            message: "ToDo retrieved successfully"
        })
    });
});

First, I’m importing express, the router and the db (database connection). I can now add functions that handles “Get”, “POST”, “Put” and “Delete” requests for my ToDos. In the code above I handle a Get request for getting a single specific ToDo from the database. Using “db” I can send a query to my database and use it in the callback function. From the callback function I can check if it’s empty (and in case return a HTTP status code 404: Not found) or send the data it found as a JSON object back to the React client.

And that’s it for the back-end. My API isn’t the best right now, but I’m planning to add JWT security and rewrite it so that it uses the information from the body of an HTTP request.

Now for the front-end. I’ve covered making React apps some times now, so for this blog post I’m only gonna focus on what’s related to the full-stack part.

First, in my constructor I add a var for my API Url:


  constructor(props) {
    super(props)

    this.apiURL = "http://localhost:3000";

    this.state = {
      todos: []
    }
  }
Then when my App starts I run a function called "getTodos" which sends a HTTP request to my API and (hopefully) receives some ToDos back, and sets the state:


// When the app starts, get todos from the database and set it to the state
  componentDidMount() {
    this.getTodos();
  }

  getTodos() {
    fetch(`${this.apiURL}/todo`)
      .then(res => res.json())
      .then((data) => {

        let todos = data.data;

        // Convert 'completed' from 0's to bools
        for (let i = 0; i < todos.length; i++) {
          todos[i].completed = (todos[i].completed === 0) ? false : true;
        }

        // Set todos in the state
        this.setState({ todos })
      })
      .catch(console.log)
  }

Since MySQL doesn't support straight up boolean values, I use 0's and 1's as false and true. That's why I convert the "completed" value from those 0's and 1's to bool's.

Now for adding a new ToDo:


  // Adds a new ToDo
  addToDo = desc => {
    fetch(`${this.apiURL}/todo?desc="${desc}"&completed=0`, { "method": "POST" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)

  }

I send a POST request with the new ToDos description in the query and a completed value of zero. The id is being set server side, so my client app doesn't need to set it itself. In the callback function I could receive the id back and just add a new ToDo object to my array, but I decided to take the lazy way and just refresh all ToDos from my database.

The delete and update (setAsComplete) functions are quite similiar:


// Marks a ToDo as completed
  setAsCompleted = id => {
    // Get the selected todo
    let editedToDo = this.state.todos.find(todo => todo.id === id);

    // Flip the completed value and return it as a number
    editedToDo.completed = editedToDo.completed ? 0 : 1;

    fetch(`${this.apiURL}/todo?id=${editedToDo.id}&desc="${editedToDo.desc}"&completed=${editedToDo.completed}`, { "method": "PUT" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)
  }

  // Removes ToDo
  removeToDo = id => {
    fetch(`${this.apiURL}/todo/${id}`, { "method": "DELETE" })
      .then(res => res.json())
      .then((data) => {

        //Refresh ToDos
        this.getTodos();

      })
      .catch(console.log)
  }

And that's it! A full working full-stack ToDo application!

This image has an empty alt attribute; its file name is image-8.png

My next plan is to port over my client React app to React Native, so stay tuned for that!

June 29

Asteroids Pt. 2: Shooting some asteroids

Last time I implemented the player flying around, but now it’s time for letting the player shoot.

First thing I do is create a own class for Bullet objects which takes a position and rotation as an argument:

class Bullet{
  
  constructor(pos, rotation){<
    this.pos = createVector(pos.x, pos.y);
    
    this.dir = p5.Vector.fromAngle(rotation - PI / 2).normalize();
  }
  
  tick(){

  }
  
  render(){

  }
  
}

And in my "sketch.js" I'll add a array to hold my bullets, loop through them to tick and render them, and add a way to create them in the keyPressed function when pressing space, sending my players position and rotation as arguments:

var bullets = [];

function draw() {
  ...

  for(let i = 0; i < bullets.length; i++) bullets[i].render();
  
}

function tick() {
  ...

  for(let i = 0; i < bullets.length; i++) bullets[i].tick();
  
}

function keyPressed() {
  ...

  //Spacebar
  if(keyCode == 32){
    bullets.push(new Bullet(ship.pos, ship.rotation));
  }
}

By taking the position and rotation from the player, I can create the bullet right on top of the player and give it the same direction of the player. I also added the tick and render functions:

class Bullet{
  
  constructor(pos, rotation){
    this.pos = createVector(pos.x, pos.y); //Create a new position based on player position
    
    ////Creates a direction vector based off on players rotation. Offset it by Pi/2 to match the ship
    this.dir = p5.Vector.fromAngle(rotation - PI / 2).normalize();
    
    this.speed = 15;  //Bullet speed
    this.size = 3;    //Bullet size
    
    //Creates a velocity vector by multiplying dir vector with the speed
    this.vel = this.dir.mult(this.speed);  
    
  }
  
  tick(){
    this.pos.add(this.vel); //Moves the bullets pos by the velocity
  }
  
  render(){
    ellipse(this.pos.x, this.pos.y, this.size); //Renders the bullet
  }
  
}

After watching some footage of the original game, I saw that the bullets also goes from one side of the screen to the other. I added the feature by simply copy-pasting the previous made code from "ship.js":

  tick(){
    this.pos.add(this.vel); //Moves the bullets pos by the velocity
    
    //Keeps the bullet 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;
    }
    
  }

However, I need to remove the bullet after it has traveled a certain distance. To do that I created a variable to keep track of the distance traveled, and mark it for removal:

  constructor(pos, rotation){
    ...

    this.dist = 0; //Distance traveled
    this.maxDist = width; //Max distance before removal
    this.remove = false;
  }

  tick(){
    ...

    this.dist += abs(this.vel.x) + abs(this.vel.y); //Records the distance travelled
    
    if(this.dist >= this.maxDist) this.remove = true; //If traveled above the limit, mark for removal
    
   ...
    
  }

The reason I mark it for removal instead of directly removing it on the spot, is because in the tick function I'm still looping through all the bullets. Removing something from a array/list while using it, usually leads to problems with the index number.

Now, to remove the bullets all I have to do is loop through the bullet array backwards (to avoid problems with the index number when you remove an object) and remove those marked in my "sketch.js":

function tick() {
  ...

  //Bullets tick
  for (let i = 0; i < bullets.length; i++) bullets[i].tick();
  
  for (let i = bullets.length - 1; i >= 0; i--) {
    if (bullets[i].remove) bullets.splice(i, 1);
  }

}

And the player can now shoot bullets!

Animated GIF

Now we need something to shoot! For rendering and rotating the asteroids I used some of the same code I made for the player:

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

    this.size = size;

    this.radius = 15;

    this.angularSpeed = random(0.01, 0.03);
    this.rotation = random(0, 2 * PI);
    this.rotation = 0;
  }

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

  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

    let r = this.radius;
    let ox = -6, oy = -17;   //Offsets the asteroid
    
    line(-r * 1.5 + ox, r / 2 + oy, -r / 2 + ox, -r / 2 + oy);
    line(-r / 2 + ox, -r / 2 + oy, r / 2 + ox, r / 4 + oy);
    line(r / 2 + ox, r / 4 + oy, r * 2 + ox, 0 + oy);
    line(r * 2 + ox, 0 + oy, r * 1.75 + ox, r + oy);
    line(r * 1.75 + ox, r + oy, r * 2 + ox, r * 2 + oy);
    line(r * 2 + ox, r * 2 + oy, r / 2 + ox, r * 3 + oy);
    line(r / 2 + ox, r * 3 + oy, -r + ox, r * 2.5 + oy);
    line(-r + ox, r * 2.5 + oy, -r * 1.5 + ox, r / 2 + oy);

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

  }

}

I render multiple line segments to make it look like the asteroids in the game. I decided to render it using a radius variable, since I need to have different sized asteroids. The idea worked perfectly, though I noticed a problem where the asteroids x and y coordinates were not centered around where I actually draw the asteroid. That creates a big problem since I rotate the asteroid around that point. To fix that I created an "ox" and an "oy" variable to offset where I could just offset the rendered asteroid.

I create an array of asteroids and loop through them to both tick and render them, and voila!

Animated GIF

The last thing to do now is making the bullets explode the asteroids. I first need to find a way to do hit detection. I decided to just create some rectangles for the bullets and asteroids since it's the most efficient method. The hitboxes won't align properly with what's rendering the screen though.

P5.js doesn't have any build in support for hitbox detection so I need to create that myself. First thing I do is create "rectangles" for my asteroids and bullets:

Asteroids
  tick() {
    ...

    //Hitbox
    this.rectLeftSide = createVector(this.pos.x - this.radius * 1.5, this.pos.y - this.radius * 1.5);
    this.rectRightSide = createVector(this.pos.x + this.radius * 1.5, this.pos.y + this.radius * 1.5);
  }
Bullets
  tick(){
    ...

    //Hitbox
    
    //Only create right side since I already have left side as "pos"
    this.rectRightSide = createVector(this.pos.x + this.size, this.pos.y + this.size);
    
  }

Creating a vector every single tick is not really good performance wise, but for this simple little game it will be fine. I don't really create a rectangle in these objects, but rather keep track off where the top-left side and bottom right-side of a rectangle would be.

Now I need a function to check if two "rectangles" are intersecting. Funnily enough, the fastest way to check if two rectangles are intersecting is to check if they are not intersecting at all:

function intersects(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2){
  return !(r1x1 > r2x2 || r2x1 > r1x2 || r1y1 > r2y2 || r2y1 > r1y2);
}

I can now make a nested loop to check if a bullet and an asteroid is collided, and remove them both:


function tick() {
  ...
  
  //Hitbox detection
  for (let i = 0; i < bullets.length; i++){
    for (let j = 0; j < asteroids.length; j++){
      
      var b = bullets[i];
      var a = asteroids[j];
      
      if(intersects(b.pos.x, b.pos.y, b.rectRightSide.x, b.rectRightSide.y,
                    a.rectLeftSide.x, a.rectLeftSide.y, a.rectRightSide.x, a.rectRightSide.y)){
         
        a.remove = true;
        b.remove = true;
      }
      
    }
  }

}
Animated GIF

Next time I need to make the asteroid split up instead of just being removed, spawn asteroids and make them move!

Full code for "sketch.js"
var ship;
var bullets = [];
var asteroids = [];

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

  ship = new Ship(width / 2, height / 2, 12);
  
  asteroids.push(new Asteroid(width / 2, height / 4, 1));
  
}

function draw() {
  background(0);

  tick();

  ship.render();
  for (let i = 0; i < bullets.length; i++) bullets[i].render();
  for (let i = 0; i < asteroids.length; i++) asteroids[i].render();

}

function tick() {
  ship.tick();

  //Bullets tick
  for (let i = 0; i < bullets.length; i++) bullets[i].tick();
  
  for (let i = bullets.length - 1; i >= 0; i--) {
    if (bullets[i].remove) bullets.splice(i, 1);
  }
  
  //Asteroids tick
  for (let i = 0; i < asteroids.length; i++) asteroids[i].tick();
  
  for (let i = asteroids.length - 1; i >= 0; i--) {
    if (asteroids[i].remove) asteroids.splice(i, 1);
  }
  
  //Hitbox detection
  for (let i = 0; i < bullets.length; i++){
    for (let j = 0; j < asteroids.length; j++){
      
      var b = bullets[i];
      var a = asteroids[j];
      
      if(intersects(b.pos.x, b.pos.y, b.rectRightSide.x, b.rectRightSide.y,
                    a.rectLeftSide.x, a.rectLeftSide.y, a.rectRightSide.x,                                   a.rectRightSide.y)){
         
        a.remove = true;
        b.remove = true;
        
      }
      
    }
  }

}

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);
  }

  //Spacebar
  if (keyCode == 32) {
    bullets.push(new Bullet(ship.pos, ship.rotation));
  }

}

function keyReleased() {
  if (keyCode == UP_ARROW) ship.setAcceleration(0);

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

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

}

function intersects(r1x1, r1y1, r1x2, r1y2, r2x1, r2y1, r2x2, r2y2){
  return !(r1x1 > r2x2 || r2x1 > r1x2 || r1y1 > r2y2 || r2y1 > r1y2);
}

[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]
Full code for "bullet.js"
class Bullet{
  
  constructor(pos, rotation){
    this.pos = createVector(pos.x, pos.y); //Create a new position based on player position
    
    ////Creates a direction vector based off on players rotation. Offset it by Pi/2 to match the ship
    this.dir = p5.Vector.fromAngle(rotation - PI / 2).normalize();
    
    this.speed = 15;  //Bullet speed
    this.size = 3;    //Bullet size
    
    //Creates a velocity vector by multiplying dir vector with the speed
    this.vel = this.dir.mult(this.speed);  
    
    this.dist = 0; //Distance traveled
    this.maxDist = width; //Max distance before removal
    this.remove = false;
    
    this.rectRightSide = createVector(pos.x + this.size, pos.y + this.size);
    
  }
  
  tick(){
    this.pos.add(this.vel); //Moves the bullets pos by the velocity
    
    this.dist += abs(this.vel.x) + abs(this.vel.y); //Records the distance travelled
    
    if(this.dist >= this.maxDist) this.remove = true; //If traveled above the limit, mark for removal
    
    //Keeps the bullet 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;
    }
    
    //Hitbox
    
    //Only create right side since I already have left side as "pos"
    this.rectRightSide = createVector(this.pos.x + this.size, this.pos.y + this.size);
    
  }
  
  render(){
    fill(255);
    stroke(255);
    
    ellipse(this.pos.x, this.pos.y, this.size); //Renders the bullet
    
  }
  
}
[collapse]
Full code for "asteroid.js"
class Asteroid {
  constructor(x, y, size) {
    this.pos = createVector(x, y);

    this.size = size;

    this.radius = 15;

    this.angularSpeed = random(0.01, 0.03);
    this.rotation = random(0, 2 * PI);
    this.rotation = 0;
    
    this.rectLeftSide = createVector(this.pos.x - this.radius * 1.5, this.pos.y - this.radius * 1.5);
    this.rectRightSide = createVector(this.radius * 3, this.radius * 3);
    
  }

  tick() {
    this.rotation += this.angularSpeed;
    
    //Hitbox
    this.rectLeftSide = createVector(this.pos.x - this.radius * 1.5, this.pos.y - this.radius * 1.5);
    this.rectRightSide = createVector(this.pos.x + this.radius * 1.5, this.pos.y + this.radius * 1.5);
  }

  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

    let r = this.radius;
    let ox = -6, oy = -17;   //Offsets the asteroid
    
    line(-r * 1.5 + ox, r / 2 + oy, -r / 2 + ox, -r / 2 + oy);
    line(-r / 2 + ox, -r / 2 + oy, r / 2 + ox, r / 4 + oy);
    line(r / 2 + ox, r / 4 + oy, r * 2 + ox, 0 + oy);
    line(r * 2 + ox, 0 + oy, r * 1.75 + ox, r + oy);
    line(r * 1.75 + ox, r + oy, r * 2 + ox, r * 2 + oy);
    line(r * 2 + ox, r * 2 + oy, r / 2 + ox, r * 3 + oy);
    line(r / 2 + ox, r * 3 + oy, -r + ox, r * 2.5 + oy);
    line(-r + ox, r * 2.5 + oy, -r * 1.5 + ox, r / 2 + oy);
    
    pop(); //Makes so that translate and rotate functions only apply to this object
    
  }

}
[collapse]
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]