To get started, first install Excalibur through npm (TypeScript typings are best supported in npm):
npm install excalibur
In a TypeScript project, you can reference Excalibur with the ES6 import style syntax:
// Excalibur is loaded into the ex global namespace
import * as ex from 'excalibur'
or
// Excalibur is loaded into the ex global namespace
import { Actor, Engine } from 'excalibur'
We have a base recommended tsconfig.json that the TypeScript compiler uses as configuration. In this example we assume all the source is in a ./src/
directory.
{
"include": ["src/**/*"],
"compilerOptions": {
/* Basic Options */
"target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "es2015" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
/* Specify library files to be included in the compilation. */
"lib": [
"dom",
"es5",
"es2015.Proxy",
"es2015.promise",
"es2015.collection",
"es2015.iterable",
"es2015"
],
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
/* Module Resolution Options */
"moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
"baseUrl": "./src" /* Base directory to resolve non-absolute module names. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
In a module loader system, such as Webpack or Parcel, it will automatically bundle Excalibur. See the webpack example repo or parcel repo
To support tree-shaking, you should use named imports:
import { Actor } from 'excalibur'
Parcel is by far the easiest way to get a bundler up and running with excalibur. We recommend looking at the template.
Using npm to install parcel, excalibur, and typescript
npm install parcel-bundler excalibur typescript
Configure you tsconfig.json
tsc --init
Build your game script
// ./src/index.ts
const game = new Engine({
width: 600,
height: 400,
})
game.start()
Include the typescript file in your html
<html>
<head></head>
<body>
<script src="./src/index.ts" />
</body>
</html>
Build and run with parcel!
parcel index.html --no-autoinstall
Webpack is the battleship solution, and if you need a lot of control over your build process, this might be the way to go.
Deno is a runtime for JavaScript and TypeScript and much more than a bundler, but we put this section here since you'll be doing a lot of bundling :).
For Excalibur to work in a Deno environment, use a Content Delivery Network for making Node.js packages compatible with Deno: two popular ones are esm.sh and skypack.dev.
To keep it simple, we use esm.sh
in the following examples, but feel free to make your own choice - at the time of writing the procedure for esm.sh
and skypack.dev
is nearly identical (for a difference, see the section Types and IntelliSense).
Sometimes, you just want to start hacking. You can do so by using Deno to get a bundled version of Excalibur and directly applying the module to your HTML.
Either esm.sh or skypack.dev should generate a bundle that works:
deno bundle https://esm.sh/excalibur excalibur.bundle.js
Using as a native module works in Chrome without any extras:
<!-- index.html -->
<script type="module">
import ex from './excalibur.bundle.js'
const game = new ex.Engine()
game.start()
</script>
The drawback to this method is that it doesn't give you type annotations in your IDE, like the following method.
More likely, you will want to import Excalibur into a JavaScript or TypeScript file.
Since it goes very well with Deno, we use TypeScript in the following examples. We will have to set up our TypeScript compiler and pick our import syntax before we can make our bundle.
A custom tsconfig.json
has to be used with strict
turned off and a few DOM libraries added:
// tsconfig.json
{
"compilerOptions": {
"strict": false,
"lib": ["dom", "dom.iterable", "dom.asynciterable", "Deno.ns"]
}
}
Then Excalibur can be imported. This can be done in two ways:
Recommended: Named Import
// index.ts
import { Engine } from 'https://esm.sh/excalibur@0.26.0-alpha.264'
const game = new Engine()
game.start()
**Alternative: Namespace Import **
// index.ts
import * as ex from 'https://esm.sh/excalibur@0.26.0-alpha.264'
const game = new ex.Engine()
game.start()
There are two main reasons:
We find named imports more expressive and explicit than the wildcard *
syntax. It's easier for you and your collaborators to see what parts of the module are actually being used if they are all listed in the import statement.
They can help with tree shaking, as explained in this relatively beginner-friendly developers.google tutorial.
Both approaches work. you might have your reasons for using namespace imports. We encourage you to do your own research!
Set up your tsconfig.json
and your index.ts
as described in [the section above]((#importing-into-a-javascript-or-typescript-file). Then Deno should successfully bundle:
deno bundle index.ts game.bundle.js --config tsconfig.json
And you can apply it in your index.html
with a regular script tag:
<script src="game.bundle.js"></script>
To enable type annotations and IntelliSense in a Deno environment, you might need to install a special extension in your IDE, like this one for VSCode: Deno extension by denoland.
Make sure to read the instructions of the extension. you might also need to install the Deno CLI and later initialize the extension in your IDE workspace.
If you're using skypack.dev, you might also need to add the ?dts
query parameter to the package name. your import should read something like this:
import { Engine } from "https://cdn.skypack.dev/excalibur?dts
Or, if you want to use a specific version:
import { Engine } from "https://cdn.skypack.dev/excalibur@0.26.0-alpha.264?dts