Skip to main content

使用纯TypeScript的游戏开发

Feishiko
Author
Feishiko
Programming Avali

因为突然想尝试一下以HTML的Canvas为基础做一下游戏开发!所以就开始做了并且写下这篇文章记录一下

一开始为了能在Canvas上画出图形,直接在HTML里面写了JavaScript,后面为了模块化开发转成单文件。不过最开始还是写JavaScript的,因为要在画布上画东西需要Get到Canvas,所以需要document.getElementByID,但是由于JavaScript是弱类型,document.getElementByID不但不会隐式返回一个类型,而且没法标注类型,导致接下来写getContext("2d")的时候IDE/代码编辑器不会提示。因为以上原因所以毅然换了TypeScript。

TypeScript遇到的第一个问题是直接写let canvas:HTMLCanvasElement = document.getElementByID会报错,因为getElementByID依赖不会返回一个HTMLCanvasElement类型,所以需要手动as HTMLCanvasElement

然后就是tsc编译完js整个ts文件会红,这个写个tsc init生成一个tsconfig文件就能解决了。

之后的大部分时间在研究分文件写之后为什么会一直在js文件里会出现exports和require的代码,这是js所不支持的写法。解决方法就是把tsconfig中的module改成es6,target也改成es6。有可能会因为tsc的缓存没有清除所以一时不会发生改动。可以清除以下tsc的缓存文件或者使用tsc --module es6

注意import的时候路径后面要加一个.js,就算这是ts文件,如

import { Render } from "./render.js";

不然会在编译到js后找不到文件: 已拦截加载自“http://127.0.0.1:5500/public/ui”的模块,它使用了不允许的 MIME 类型(“text/html”)。

上面说使用了不允许的MIME类型,实际上是因为找不到文件而返回了404ERROR,而404ERROR是HTML不是JavaScript文件所以导致返回了使用了不允许的MINE类型。

<!-- index.html -->
<body>
    <script type="module">
        import * as main from './public/main.js';
    </script>
    </script>
    <canvas width="800" height="600" id="canvas"></canvas>
</body>

以上是HTML部分的部分代码,用这种方式去加载main.js文件,因为加入了export和import的代码已经模块化了,再在里面写function并不在全局作用域内。

// main.ts
import {UI} from "./ui.js";
import { Render } from "./render.js";

export class Game {
    private x: number;
    private render: Render;
    private ui:UI;

    constructor() {
        this.x = 10;
        this.render = new Render();
        this.ui = new UI();
        setInterval(() => this.Update(), 1000 / 60);
    }

    Draw():void {
        if (this.render.ctx) {
            this.render.ctx.clearRect(0, 0, 800, 600);
            this.render.ctx.font = "20px serif";
            this.render.ctx.fillText("random text", 10 + this.x, 10)
            this.ui.Draw();
        }
    }

    private Update():void {
        this.x += 1;
        this.Draw();
    }
}

new Game()

很显而易见的为了模块化我们给每个文件起了一个class,与此同时在顶上和构造函数里面进行初始化。

构造函数中的最后一行

setInterval(() => this.Update(), 1000 / 60);

是为了设定主循环,60每帧,中间用箭头函数是因为箭头函数不会创建自己的this,而是从外部作用域捕获this的值。因此,可以使用箭头函数来解决这个问题。

// render.ts
export class Render {
    public canvas: HTMLCanvasElement;
    public ctx: CanvasRenderingContext2D;

    constructor() {
        this.canvas = document.getElementById("canvas") as HTMLCanvasElement
        this.ctx = this.canvas.getContext("2d") as CanvasRenderingContext2D;
    }
}

render.ts中单独设置了画布的渲染,要用到draw的部分调用render.ts。

我想这样一个基础的游戏框架就搭建好了。


comments powered by Disqus