encompass-cs-docs/content/pong/polish/title.md

3.8 KiB

title date weight
Title 2019-06-09T16:51:13-07:00 20

It would be nice to have a title screen. Let's make that happen.

I would like us to have a concept of game state. The title menu is a pretty distinct thing from the game itself so it feels nicer to have it be self contained instead of managing extra state in the game world.

Let's create a new class in game/state.ts:

export abstract class State {
    public abstract load(): void;
    public abstract update(dt: number): void;
    public abstract draw(): void;
}

Remember, abstract means that the class cannot be used directly, but describes features that exist in inherited classes. So we know that anything we make that inherits from State must have a load(), update(dt), and draw() method.

Let's create a new folder, game/states, and put game.ts in there. Let's also make it inherit from State:

export class Game extends State {

Let's make a new State called Title. It doesn't need to do much - just display the game title and a prompt for the player to start the game.

import { State } from "game/state";

export class Title extends State {
    private title_font: Font;
    private title_text: Text;

    private play_font: Font;
    private play_text: Text;

    public load() {
        this.title_font = love.graphics.newFont("game/assets/fonts/Squared Display.ttf", 128);
        this.title_text = love.graphics.newText(this.title_font, "Encompass Pong");

        this.play_font = love.graphics.newFont("game/assets/fonts/Squared Display.ttf", 32);
        this.play_text = love.graphics.newText(this.play_font, "Press Space");
    }

    public update() {}

    public draw() {
        love.graphics.draw(
            this.title_text,
            640,
            240,
            0,
            1,
            1,
            this.title_text.getWidth() * 0.5,
            this.title_text.getHeight() * 0.5,
        );

        love.graphics.draw(
            this.play_text,
            640,
            480,
            0,
            1,
            1,
            this.play_text.getWidth() * 0.5,
            this.play_text.getHeight() * 0.5,
        );
    }
}

Now in main.ts we can put code to handle our states.

let menu: Title;
let game: Game;
let current_state: State;

love.load = () => {
    ...

    menu = new Menu();
    menu.load();

    game = new Game();
    game.load();

    current_state = menu;
};

love.update = (dt) => {
    current_state.update(dt);
    if (current_state === menu) {
        if (love.keyboard.isDown("space")) {
            current_state = game;
        }
    }
};

love.draw = () => {
    current_state.draw();

    ...
}

The final result of main.ts should look like this.

declare global {let PROF_CAPTURE: boolean; }
PROF_CAPTURE = false; // set this to true to enable profiling

import * as jprof from "encompass-jprof";
import { State } from "game/state";
import { Game } from "game/states/game";
import { Title } from "game/states/title";

let menu: Title;
let game: Game;
let current_state: State;

love.load = () => {
    love.window.setMode(1280, 720, {vsync: false, msaa: 2});
    love.math.setRandomSeed(os.time());
    love.mouse.setVisible(false);

    menu = new Title();
    menu.load();

    game = new Game();
    game.load();

    current_state = menu;
};

love.update = (dt) => {
    current_state.update(dt);
    if (current_state === menu) {
        if (love.keyboard.isDown("space")) {
            current_state = game;
        }
    }
};

love.draw = () => {
    current_state.draw();

    love.graphics.setBlendMode("alpha");
    love.graphics.setColor(1, 1, 1, 1);
    love.graphics.print("Current FPS: " + tostring(love.timer.getFPS()), 10, 10);
};

love.quit = () => {
    jprof.write("prof.mpack");
    return false;
};

Let's try it!

pong title

Nice!