Add README, more examples and audio imports
This commit is contained in:
parent
a8bfe73321
commit
6405aedaa7
107
README.md
107
README.md
|
|
@ -1,14 +1,117 @@
|
|||
# TS-Games
|
||||
|
||||
To install dependencies:
|
||||
Custom framework/build system for simple single-file TypeScript games.
|
||||
|
||||
## Installing dependencies
|
||||
|
||||
- Install bun (https://bun.sh/)
|
||||
|
||||
```bash
|
||||
bun install
|
||||
```
|
||||
|
||||
To run:
|
||||
## Make a game
|
||||
|
||||
- Create your game folder in [src/games](src/games)
|
||||
- Create `src/games/<yourgame>/index.ts` with default exported function.
|
||||
|
||||
## Running:
|
||||
|
||||
```bash
|
||||
bun start
|
||||
```
|
||||
Navigate to `http://localhost:3000` to see the list of your games.
|
||||
Game rebuilds on each reload.
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
bun run build <project>
|
||||
```
|
||||
Will create `<project>.html` in `dist` folder.
|
||||
|
||||
Or to select project from list:
|
||||
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Bun ♥
|
||||
- TypeScript
|
||||
- Building into single `.html` file without any dependencies, all assets are inlined as data-urls.
|
||||
- `src/games/<yourgame>/assets/favicon.ico` is used as page icon if present.
|
||||
|
||||
- TSX supported with [Preact](https://preactjs.com/), because it's lightweight.
|
||||
|
||||
- Import images as `HTMLImageElement`
|
||||
- PNG & JPG
|
||||
```typescript
|
||||
import spritesheet from './assets/spritesheet.png';
|
||||
console.log(spritesheet); // <img src="data:..." />
|
||||
```
|
||||
|
||||
- Import audio as `HTMLAudioElement`
|
||||
- WAV, MP3 & OGG
|
||||
```typescript
|
||||
import heal from './assets/heal.ogg';
|
||||
console.log(heal); // <audio src="data:..." />
|
||||
heal.play()
|
||||
```
|
||||
|
||||
- Import css with [LigntningCSS](https://lightningcss.dev/)
|
||||
- Regular CSS
|
||||
```ts
|
||||
import "./assets/styles.css";
|
||||
```
|
||||
|
||||
- CSS modules is supported
|
||||
```tsx
|
||||
import styles from './assets/styles.module.css';
|
||||
console.log(styles.awoo); // G7sddg_awoo
|
||||
|
||||
export default <div className={styles.root}></div>;
|
||||
```
|
||||
|
||||
- Modern CSS features are transpiled
|
||||
- Nested CSS
|
||||
```css
|
||||
.root {
|
||||
display: flex;
|
||||
.row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
```
|
||||
- Vendor prefixed if needed
|
||||
|
||||
- Import fonts (see [example](src/common/assets/vga.font.css))
|
||||
```ts
|
||||
import "./assets/lcd.font.css";
|
||||
```
|
||||
|
||||
- [AssemblyScript](https://www.assemblyscript.org/) support (TypeScript-like language compiled into WebAssembly)
|
||||
- [Example](src/games/playground/awoo.wasm.ts)
|
||||
- Triggered by file name `*.wasm.ts`
|
||||
|
||||
|
||||
## Publishing
|
||||
|
||||
- Make sure you have `scp` installed (it most certainly is)
|
||||
- Make `.env` file
|
||||
```bash
|
||||
PUBLISH_LOCATION=ssh.example.com:/var/www/games/
|
||||
PUBLISH_URL=https://example.com/
|
||||
```
|
||||
|
||||
- Run build & publish
|
||||
```bash
|
||||
bun run build <project>
|
||||
```
|
||||
|
||||
Or to select project from list:
|
||||
```bash
|
||||
bun run build
|
||||
```
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
import { plugin, type BunPlugin } from "bun";
|
||||
|
||||
const audioPlugin: BunPlugin = {
|
||||
name: "Audio loader",
|
||||
async setup(build) {
|
||||
build.onLoad({ filter: /\.(wav|mp3|ogg)$/ }, async (args) => {
|
||||
const arrayBuffer = await Bun.file(args.path).arrayBuffer();
|
||||
const ext = args.path.split('.').pop();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
const src = `data:audio/${ext};base64,${buffer.toString('base64')}`;
|
||||
return {
|
||||
contents: `
|
||||
const audio = document.createElement('audio');
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
audio.oncanplay = resolve;
|
||||
audio.onerror = () => reject(audio.error);
|
||||
});
|
||||
audio.src = (${JSON.stringify(src)});
|
||||
|
||||
await promise;
|
||||
|
||||
export default audio;
|
||||
`,
|
||||
loader: 'js',
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
plugin(audioPlugin);
|
||||
|
||||
export default audioPlugin;
|
||||
|
|
@ -10,6 +10,7 @@ const outDir = path.resolve(import.meta.dir, '..', '..', 'dist');
|
|||
await fs.mkdir(outDir, { recursive: true });
|
||||
|
||||
let game = process.argv[2];
|
||||
const publish = process.env.PUBLISH_LOCATION;
|
||||
|
||||
while (!await isGame(game)) {
|
||||
const answer = await inquirer.prompt([{
|
||||
|
|
@ -28,7 +29,9 @@ if (!html) {
|
|||
const filePath = path.resolve(outDir, `${game}.html`);
|
||||
await Bun.write(filePath, html);
|
||||
|
||||
const result = await $`scp "${filePath}" "pabloid@games.pafnooty.ru:/var/www/games/${game}.html"`;
|
||||
if (result.exitCode === 0) {
|
||||
console.log(`Build successful: https://games.pafnooty.ru/${game}.html`);
|
||||
if (publish) {
|
||||
const result = await $`scp "${filePath}" "${publish}${game}.html"`;
|
||||
if (result.exitCode === 0) {
|
||||
console.log(`Build successful: ${process.env.PUBLISH_URL}${game}.html`);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import fontPlugin from './fontPlugin';
|
|||
import lightningcss from 'bun-lightningcss';
|
||||
|
||||
import { getGames } from './isGame';
|
||||
import audioPlugin from './audioPlugin';
|
||||
|
||||
export async function buildHTML(game: string, production = false, portable = false) {
|
||||
const html = await Bun.file(path.resolve(import.meta.dir, '..', 'assets', 'index.html')).text();
|
||||
|
|
@ -22,6 +23,7 @@ export async function buildHTML(game: string, production = false, portable = fal
|
|||
},
|
||||
plugins: [
|
||||
imagePlugin,
|
||||
audioPlugin,
|
||||
fontPlugin,
|
||||
wasmPlugin({ production, portable }),
|
||||
lightningcss(),
|
||||
|
|
|
|||
|
|
@ -10,3 +10,15 @@ declare module "*.jpeg" {
|
|||
const image: HTMLImageElement;
|
||||
export default image;
|
||||
}
|
||||
declare module "*.wav" {
|
||||
const audio: HTMLAudioElement;
|
||||
export default audio;
|
||||
}
|
||||
declare module "*.mp3" {
|
||||
const audio: HTMLAudioElement;
|
||||
export default audio;
|
||||
}
|
||||
declare module "*.ogg" {
|
||||
const audio: HTMLAudioElement;
|
||||
export default audio;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { BrickDisplay, type BrickDisplayImage } from "@common/display";
|
||||
import { BrickDisplay } from "@common/display";
|
||||
import { isPressed, updateKeys } from "@common/input";
|
||||
|
||||
import spritesheetImage from './assets/spritesheet.png';
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
|||
export function awoo(a: i32, b: i32): void {
|
||||
console.log((a * b).toString());
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { awoo } from "./awoo.wasm";
|
||||
import { render } from "preact";
|
||||
import attack from './assets/attack.wav';
|
||||
|
||||
export default function main() {
|
||||
awoo(42, 69);
|
||||
|
||||
render(
|
||||
<button onClick={() => attack.play()}>
|
||||
Attack
|
||||
</button>,
|
||||
document.body
|
||||
);
|
||||
}
|
||||
|
|
@ -28,5 +28,6 @@
|
|||
"@common/*": ["./src/common/*"]
|
||||
}
|
||||
},
|
||||
"include": ["./**/*.ts", "./**/*.tsx", "./node_modules/assemblyscript/std/portable/index.d.ts"],
|
||||
"include": ["./**/*.ts", "./**/*.tsx", "./**/*.js", "./**/*.jsx", "./node_modules/assemblyscript/std/portable/index.d.ts"],
|
||||
"exclude": ["./dist/**/*"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue