<canvas id="game" />
body {
  margin: 0;
}

#game {
  background-color: #222;
  width: 100%;
  height: 100%;
}
// Use this constant to make the world larger or smaller.
const worldSize = 50;

// These are the keycodes for the arrow keys on the keyboard.
const upKey = 38;
const downKey = 40;
const leftKey = 37;
const rightKey = 39;

// Create an array to hold the currently pressed key codes
let keys = [];

// Define the points of the ship
const shipPoints = [
  [1, 0],
  [-0.714, 0.571],
  [-0.429, 0.286],
  [-0.429, -0.286],
  [-0.714, -0.571]
];

// Initialize ship position as center of world
const shipPosition = [worldSize / 2, worldSize / 2];


const update = () => {
    if (keys.includes(upKey)) {
      shipPosition[1] -= 0.1
    }
    if (keys.includes(downKey)) {
      shipPosition[1] += 0.1
    }
    if (keys.includes(leftKey)) {
      shipPosition[0] -= 0.1
    }
    if (keys.includes(rightKey)) {
      shipPosition[0] += 0.1
    }
};

const render = (context, mapWorldToScreenSpace) => {
  /**
   * The render method is where you want to draw the scene for
   * the current frame. It is immediatley called after the update
   * method.
   *
   * The first argument contains the native canvas context. You may
   * wish to see the docs here:
   *   https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
   *
   * The second argument is a method that takes an array with
   * of length 2 representing a points X and Y coordinates and
   * returns a new array of length 2 representing X and Y mapped
   * from world space to view space. This is helpful to ensure your
   * project displays correctly on different size screens.
   *
   * mapWorldToScreenSpace([x, y])
   *   returns a new array [x, y] mapped into screen space
   */
  
  // Tell the context we want to begin a path
  context.beginPath()

  shipPoints
    // Position the ship
    .map(([x, y]) => [x + shipPosition[0], y + shipPosition[1]])
    // Map points to screen space
    .map(mapWorldToScreenSpace)
    // Draw the points
    .forEach(([x, y], index) => {
    // If this is the first point, move the pen to that location
    if (index === 0) {
      context.moveTo(x, y)
      return
    }
    // Else draw a line from the previous location to the current
    context.lineTo(x, y)
  })

  // Join the path back to the first point
  context.closePath()

  // Set the context stroke style to white
  context.strokeStyle = 'white'

  // Draw the line
  context.stroke()
};

// Reference to the canvas instance
const canvas = document.getElementById("game");
const context = canvas.getContext("2d");

// Function to map world to screen space
function mapWorldToScreenSpace([x, y]) {
  return [
    (x / worldSize) *
      Math.min(canvas.width, canvas.height) +
      (
        canvas.width > canvas.height
          ? (canvas.width - canvas.height) / 2
          : 0
      ),
    (y / worldSize) *
      Math.min(canvas.width, canvas.height) +
      (
        canvas.height > canvas.width
          ? (canvas.height - canvas.width) / 2
          : 0
      )
  ];
}

function clipScreen() {
  context.fillStyle = "#222";
  if (canvas.width > canvas.height) {
    context.clearRect(
      0,
      0,
      (canvas.width - canvas.height) / 2,
      canvas.height
    );
    context.clearRect(
      canvas.width - ((canvas.width - canvas.height) / 2),
      0,
      (canvas.width - canvas.height) / 2,
      canvas.height
    );
  } else {
    context.clearRect(
      0,
      0,
      canvas.width,
      (canvas.height - canvas.width) / 2
    );
    context.clearRect(
      0,
      canvas.height - ((canvas.height - canvas.width) / 2),
      canvas.width,
      (canvas.height - canvas.width) / 2
    );
  }
}

function loop() {  
  // Clear the canvas
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = "black";
  context.fillRect(0, 0, canvas.width, canvas.height);
  
  // Call the update method
  update();
  
  // Call the render method
  render(context, mapWorldToScreenSpace);
  
  // Clip off content out of bounds
  clipScreen();
  
  // Request the next frame
  window.requestAnimationFrame(loop);
}

// This function is fired every time the window is resized, this lets
// us ensure that the canvas width and height properties are accurate
const resizeCanvas = () => {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
};

// Input event handlers
const handleKeyDown = e => {
  e.preventDefault();
  if (!keys.includes(e.keyCode)) {
    keys.push(e.keyCode);
  }
};

const handleKeyUp = e => {
  e.preventDefault();
  keys = keys.filter(key => key !== e.keyCode);
};

// The resize canvas method is invoked as soon as possible to prevent
// a flicker on load
resizeCanvas();

// Kick off the game loop
window.requestAnimationFrame(loop);

// Bind the event handlers
window.addEventListener("resize", resizeCanvas);
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.