Pong using Deno and caviar as a native app using WebGPU
Tags: javascript, deno, webgpu, typescript
Published: 2022-02-14 03:36:18

Recently Deno has been taking the world by storm. Deno is a fast, secure, and open-source runtime environment created as a replacement for Node.js.  In this tutorial we will create a basic pong game using the Caviar library which uses WebGPU bindings and resembles most javascript game engines. Feel free to check out caviar [here](https://github.com/load1n9/caviar).

you can find the final version in the examples repo right [here](https://github.com/load1n9/caviar/examples/pong) aswell.

lets get started shall we?

## Project Setup
for this project all we'll need is a file hierarchy as such

```
├── pong
│   ├── src
│   │   ├── scenes
│   │   │   ├── Game.ts
│   ├── main.ts
```

lets start by creating a basic setup in `main.ts`

```typescript
import { World } from 'https://deno.land/x/caviar/mod.ts';
import { Game } from './src/scenes/Game.ts';

const pong = new World({
    title: "test",
    width: 1300,
    height: 800,
    resizable: true,
}, [Game]);
await pong.start();
```
after this we will no longer need to touch `main.ts` and we'll direct our attention to the `Game.ts` file

## Setting up our Components
in the `Game.ts` file we'll add the following code to create our first player

```typescript
import { PICO8, Scene, TextureSprite } from 'https://deno.land/x/caviar/mod.ts';

export class Game extends Scene {
    public p1: TextureSprite | undefined;
    public setup() {
      this.p1 = new TextureSprite(this, 0, 336, {
        data: [
          ".9.",
          ".9.",
          ".9.",
        ],
        pixelWidth: 32,
        pixelHeight: 32,
        palette: PICO8,
      })
      this.addChild(this.p1);
    }
    public update() {

    }

}
```
we create a new texture sprite with 3 pixels down and we use the built-in PICO8 palette.
if we run our code using `deno run -A --unstable main.ts` we should get a window looking something like ![screenshot1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6r1p0zwbke6f8a9sxpq.png)
we now create a second player and a ball the same way

```typescript
export class Game extends Scene {
    public ball: TextureSprite | undefined;
    public p1: TextureSprite | undefined;
    public p2: TextureSprite | undefined;
    public setup() {
      this.p1 = new TextureSprite(this, 0, 336, {
        data: [
          ".9.",
          ".9.",
          ".9.",
        ],
        pixelWidth: 32,
        pixelHeight: 32,
        palette: PICO8,
      });
      this.p2 = new TextureSprite(this, 1168, 336, {
        data: [
          ".A.",
          ".A.",
          ".A.",
        ],
        pixelWidth: 32,
        pixelHeight: 32,
        palette: PICO8,
      });
      this.ball = new TextureSprite(this, 568, 336, {
        data: [
          "E",
        ],
        pixelWidth: 32,
        pixelHeight: 32,
        palette: PICO8,
      });
      this.addChild(this.p1);
      this.addChild(this.p2);
      this.addChild(this.ball);
    }
    public update() {

    }

}
```
![screenshot2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m687bc8q6c4zel2gdqt8.png)

## Movement
to listen for keypresses in Caviar you need to define which keys you want to listen for, do this at the beginning of the `setup` method.  In this tutorial we'll listen for `W`,`S`,`E` and `D`.

```typescript
 ...
public setup() {
    this.setKeys(['W','S','E','D']);
    ...
```
next we'll create a `keyDown` method and check for each key and change the player's positions based on the key pressed
```typescript
...
public keyDown(key: any) {
    const p1 = this.p1 as TextureSprite;
    const p2 = this.p2 as TextureSprite;
    switch (key) {
      case "W":
        if (p1.y > 25) p1.setY(p1.y - 4);
        break;
      case "S":
        if (p1.y < 700) p1.setY(p1.y + 4);
        break;
      case "E":
        if (p2.y > 25) p2.setY(p2.y - 4);
        break;
      case "D":
        if (p2.y < 700) p2.setY(p2.y + 4);
        break;
    }
  }
```
 ![screenshot3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h7ns63plubywhx872486.png)

## Ball Movement
first lets create 2 new properties `vx` and `vy` for the balls velocity and why not also make a score property aswell

```typescript
...
public vx = 2;
public vy = 2;
public score: number[] = [0,0];
...
```
now we add the ball physics to the update function 

```typescript
...
public update() {
    const ball = this.ball as TextureSprite;
    const p1 = this.p1 as TextureSprite;
    const p2 = this.p2 as TextureSprite;
    if (ball.y > 25 || ball.y < 10) { 
      this.vy *= -1;
    }
  
    if ( 
      ball.x < p1.x + 32 + 10 &&
      ball.y > p1.y &&
      ball.y < p1.y + 96
    ) {
      this.vx *= -1.1;
      this.vy = Math.floor(Math.random() * 8) - 4; 
    }
  
    if (
      ball.x > p2.x - 10 && 
      ball.y > p2.y &&
      ball.y < p2.y + p2.height
    ) {
      this.vx *= -1.1;
      this.vy = Math.floor(Math.random() * 8) - 4; 
    }
    if (ball.y < 25 || ball.y > 800) {
      this.vy *= -1;
    }
    if (ball.x < 25) {
      //p1 side
      ball.setX(568);
      ball.setY(336);
      this.score[1] += 1;
      this.vx = 4;
      this.vy = 4;
    }
    if (ball.x > 1168) {
      //p2 side
      ball.setX(568);
      ball.setY(336);
      this.score[0] += 1;
      this.vx = -4;
      this.vy = 4;
    } 
    ball.setX(ball.x + this.vx);
    ball.setY(ball.y + this.vy);

  }
...
```
![demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8zowiywuf3ve1u2hwvzq.png)
now the game should be working.

## Conclusion
Caviar is currently only native (time of writing) but we plan to implement cross platform features in the future.  feel free to contribute [here](https://github.com/load1n9/caviar)