Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

#### Added

- **utilities:** `createGame` takes an optional second `options` argument whose `renderContext` field is forwarded to `createRenderContext`, so a game set up with `createGame` can cap its render resolution, e.g. `createGame('game', { renderContext: { maxPixelRatio: 1.5 } })`

#### Fixed

- **rendering:** Gaussian blur and bloom are now sized in CSS pixels instead of render target (device) pixels, so the same `passes`, `threshold` and `intensity` look the same at every display pixel ratio. Since 0.25.6, a high-DPI display made the blur and the bloom halo spread less far on screen, and made bloom much brighter close to small bright sprites. Looks at a pixel ratio of 1 are unchanged. On a high-DPI display the Gaussian blur now runs at CSS-pixel resolution, which also makes it cheaper there
Expand Down
11 changes: 11 additions & 0 deletions documentation-site/docs/docs/ecs/game.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ const { game, world, time, renderContext, resizeSync } = createGame('game');
game.run();
```

`createGame` takes an optional second argument. Its `renderContext` field is
forwarded to [`createRenderContext`](/Forge/docs/api/functions/createRenderContext),
so you can, for example, cap the render resolution on high-DPI displays (see
[High-DPI displays](../rendering/world-units-and-cameras.md#high-dpi-displays)):

```ts
const { game, renderContext } = createGame('game', {
renderContext: { maxPixelRatio: 1.5 },
});
```

Manual setup (when you need fine-grained control):

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ can cap it with `maxPixelRatio`:
const renderContext = createRenderContext(canvas, { maxPixelRatio: 2 });
```

If you set up with `createGame`, pass the same options through its
`renderContext` option:

```ts
const { renderContext } = createGame('game-container', {
renderContext: { maxPixelRatio: 1.5 },
});
```

Pass `maxPixelRatio: 1` to always render at CSS resolution, the engine's
behavior before it supported high-DPI displays.

Expand Down
20 changes: 20 additions & 0 deletions src/utilities/create-game.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ describe('createGame', () => {
expect(createRenderContext).toHaveBeenCalled();
});

it('creates the render context with the canvas and no options by default', () => {
const canvas = document.createElement('canvas');
vi.mocked(createCanvas).mockReturnValueOnce(canvas);

createGame('game-container');

expect(createRenderContext).toHaveBeenLastCalledWith(canvas, {});
});

it('forwards renderContext options to createRenderContext', () => {
const canvas = document.createElement('canvas');
vi.mocked(createCanvas).mockReturnValueOnce(canvas);

createGame('game-container', { renderContext: { maxPixelRatio: 1.5 } });

expect(createRenderContext).toHaveBeenLastCalledWith(canvas, {
maxPixelRatio: 1.5,
});
});

it('returns a resize sync', () => {
const { resizeSync } = createGame('game-container');
expect(typeof resizeSync.stop).toBe('function');
Expand Down
31 changes: 29 additions & 2 deletions src/utilities/create-game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,52 @@ import {
createCanvas,
createRenderContext,
RenderContext,
RenderContextOptions,
} from '../rendering/index.js';
import {
ContainerResizeSync,
createContainerResizeSync,
} from './create-container-resize-sync.js';
import { Game } from './game.js';

/**
* Options for `createGame`.
*/
export interface CreateGameOptions {
/**
* Options forwarded to `createRenderContext` for the game's render context,
* e.g. `{ maxPixelRatio: 1.5 }` to cap the render resolution on high-DPI
* displays.
*/
renderContext?: RenderContextOptions;
}

const defaultCreateGameOptions = {
renderContext: {},
};

/**
* Creates a new game instance with the specified container ID.
* @param containerId - The ID of the container element where the game will be rendered.
* @param options - Options for the game, such as the `RenderContextOptions` to create its render context with.
* @returns An object containing the game instance, ECS world, render context, time, and the resize sync keeping the render context's canvas sized to the container.
* @throws An error if no DOM element with `containerId` exists.
*/
export function createGame(containerId: string): {
export function createGame(
containerId: string,
options: CreateGameOptions = {},
): {
game: Game;
world: EcsWorld;
renderContext: RenderContext;
time: Time;
resizeSync: ContainerResizeSync;
} {
const { renderContext: renderContextOptions } = {
...defaultCreateGameOptions,
...options,
};

const time = new Time();
const world = new EcsWorld();
const container = document.getElementById(containerId);
Expand All @@ -33,7 +60,7 @@ export function createGame(containerId: string): {

const canvas = createCanvas(container);

const renderContext = createRenderContext(canvas);
const renderContext = createRenderContext(canvas, renderContextOptions);

const resizeSync = createContainerResizeSync(container, [renderContext]);

Expand Down
Loading