1
0
Fork 0

Add README, more examples and audio imports

This commit is contained in:
Pabloader 2024-07-09 12:38:03 +00:00
parent a8bfe73321
commit 6405aedaa7
10 changed files with 177 additions and 7 deletions

107
README.md
View File

@ -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
```

32
src/build/audioPlugin.ts Normal file
View File

@ -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;

View File

@ -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 (publish) {
const result = await $`scp "${filePath}" "${publish}${game}.html"`;
if (result.exitCode === 0) {
console.log(`Build successful: https://games.pafnooty.ru/${game}.html`);
console.log(`Build successful: ${process.env.PUBLISH_URL}${game}.html`);
}
}

View File

@ -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(),

12
src/common/types.d.ts vendored
View File

@ -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;
}

View File

@ -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.

View File

@ -0,0 +1,3 @@
export function awoo(a: i32, b: i32): void {
console.log((a * b).toString());
}

View File

@ -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
);
}

View File

@ -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/**/*"]
}