···11+# Rename this file to `.env.local` to use environment variables locally with `next dev`
22+# https://nextjs.org/docs/app/building-your-application/configuring/environment-variables
33+MY_HOST="example.com"
+44
.gitignore
···11+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
22+33+# dependencies
44+/node_modules
55+/.pnp
66+.pnp.*
77+.yarn/*
88+!.yarn/patches
99+!.yarn/plugins
1010+!.yarn/releases
1111+!.yarn/versions
1212+1313+# testing
1414+/coverage
1515+1616+# excalibur
1717+dist/
1818+1919+# next.js
2020+/.next/
2121+/out/
2222+2323+# production
2424+/build
2525+2626+# misc
2727+.DS_Store
2828+*.pem
2929+3030+# debug
3131+npm-debug.log*
3232+yarn-debug.log*
3333+yarn-error.log*
3434+.pnpm-debug.log*
3535+3636+# env files (can opt-in for committing if needed)
3737+.env*
3838+3939+# vercel
4040+.vercel
4141+4242+# typescript
4343+*.tsbuildinfo
4444+next-env.d.ts
+22
README.MD
···11+# Excalibur Next.js (TypeScript) Starter
22+33+This is a barebones [Excalibur](https://excaliburjs.com) game engine starter built using Next.js (v15) and React (v19). It's a great starting place to jumpstart building your game! This repo is a template and can be used to [create your own repository](https://github.com/excaliburjs/template-nextjs/generate) in GitHub.
44+55+Check out our [other samples](https://excaliburjs.com/samples) while you build your game or [ask us questions](https://github.com/excaliburjs/Excalibur/discussions).
66+77+## Get Started
88+99+* Using [Node.js](https://nodejs.org/en/) (18 LTS - minimum) and [npm](https://www.npmjs.com/)
1010+* Run the `npm install` to install dependencies
1111+* Run the `npm run dev` to run the development server to test out changes
1212+ * [Next.js](https://nextjs.org/) will automatically handle turning the [Typescript](https://www.typescriptlang.org/) into Javascript and hosting the dev server.
1313+1414+## Publishing
1515+1616+* Run `npm run build` to produce production bundles. Note Next.js has a variety of [options/settings](https://nextjs.org/docs/app/building-your-application/deploying) to familiarize yourself with, from server side rendering to static bundles.
1717+1818+### Notes
1919+2020+When deploying an Excalibur app in a Next.js context, given the Next.js API and strong support for [React Server Components](https://react.dev/reference/rsc/server-components), one should ensure a good familiarity with the basics of the Next.js [docs](https://nextjs.org/learn) if straying too far from this template. Remember, this a barebones example, and Excalibur depends on client side APIs to run correctly.
2121+2222+Happy coding!
···11+'use client'
22+// This is a Client Component
33+// It receives data as props, has access to state and effects, and is
44+// prerendered on the server during the initial page load.
55+66+import dynamic from "next/dynamic";
77+import { useRef } from "react"
88+import { IExcaliburGameComponentProps } from "./ExcaliburGame"
99+1010+const ExcaliburGame = dynamic<IExcaliburGameComponentProps>(() => import('./ExcaliburGame'), {
1111+ ssr: false,
1212+ // width/height copied from game config in ExcaliburGame.tsx
1313+ loading: () => <div style={{width:800,height: 600}}></div>
1414+});
1515+export default function App() {
1616+1717+ const excaliburRef = useRef(null)
1818+1919+ return (
2020+ <main>
2121+ <ExcaliburGame ref={excaliburRef}/>
2222+ </main>
2323+ )
2424+ }
+31
src/components/ExcaliburGame.tsx
···11+import { DisplayMode } from "excalibur"
22+import { MyLevel } from "@/excalibur/level"
33+44+import useExcaliburGame, { IExcaliburRef } from "@/hooks/useExcaliburGame";
55+66+type IExcaliburGameComponentProps = {ref: IExcaliburRef}
77+88+99+const ExcaliburGame = ({ref}: IExcaliburGameComponentProps)=> {
1010+ const excaliburConfig = {
1111+ width: 800, // Logical width and height in game pixels
1212+ height: 600,
1313+ suppressHiDPIScaling: true,
1414+ displayMode: DisplayMode.FitScreenAndFill, // Display mode tells excalibur how to fill the window
1515+ pixelArt: true, // pixelArt will turn on the correct settings to render pixel art without jaggies or shimmering artifacts
1616+ scenes: {
1717+ start: MyLevel
1818+ },
1919+ // physics: {
2020+ // solver: SolverStrategy.Realistic,
2121+ // substep: 5 // Sub step the physics simulation for more robust simulations
2222+ // },
2323+ // fixedUpdateTimestep: 16 // Turn on fixed update timestep when consistent physic simulation is important
2424+ }
2525+2626+ const [someState, setSomeState] = useExcaliburGame(ref, excaliburConfig)
2727+2828+ return <></>
2929+}
3030+3131+export {ExcaliburGame as default, type IExcaliburGameComponentProps}
···11+import { DefaultLoader, Engine, ExcaliburGraphicsContext, Scene, SceneActivationContext } from "excalibur";
22+import { Player } from "./player";
33+44+export class MyLevel extends Scene {
55+ override onInitialize(engine: Engine): void {
66+ // Scene.onInitialize is where we recommend you perform the composition for your game
77+ const player = new Player();
88+ this.add(player); // Actors need to be added to a scene to be drawn
99+ }
1010+1111+ override onPreLoad(loader: DefaultLoader): void {
1212+ // Add any scene specific resources to load
1313+ }
1414+1515+ override onActivate(context: SceneActivationContext<unknown>): void {
1616+ // Called when Excalibur transitions to this scene
1717+ // Only 1 scene is active at a time
1818+ }
1919+2020+ override onDeactivate(context: SceneActivationContext): void {
2121+ // Called when Excalibur transitions away from this scene
2222+ // Only 1 scene is active at a time
2323+ }
2424+2525+ override onPreUpdate(engine: Engine, elapsedMs: number): void {
2626+ // Called before anything updates in the scene
2727+ }
2828+2929+ override onPostUpdate(engine: Engine, elapsedMs: number): void {
3030+ // Called after everything updates in the scene
3131+ }
3232+3333+ override onPreDraw(ctx: ExcaliburGraphicsContext, elapsedMs: number): void {
3434+ // Called before Excalibur draws to the screen
3535+ }
3636+3737+ override onPostDraw(ctx: ExcaliburGraphicsContext, elapsedMs: number): void {
3838+ // Called after Excalibur draws to the screen
3939+ }
4040+}
+86
src/excalibur/player.ts
···11+import { Actor, Collider, CollisionContact, Engine, Side, vec } from "excalibur";
22+import { Resources } from "./resources";
33+44+// Actors are the main unit of composition you'll likely use, anything that you want to draw and move around the screen
55+// is likely built with an actor
66+77+// They contain a bunch of useful components that you might use
88+// actor.transform
99+// actor.motion
1010+// actor.graphics
1111+// actor.body
1212+// actor.collider
1313+// actor.actions
1414+// actor.pointer
1515+1616+1717+export class Player extends Actor {
1818+ constructor() {
1919+ super({
2020+ // Giving your actor a name is optional, but helps in debugging using the dev tools or debug mode
2121+ // https://github.com/excaliburjs/excalibur-extension/
2222+ // Chrome: https://chromewebstore.google.com/detail/excalibur-dev-tools/dinddaeielhddflijbbcmpefamfffekc
2323+ // Firefox: https://addons.mozilla.org/en-US/firefox/addon/excalibur-dev-tools/
2424+ name: 'Player',
2525+ pos: vec(150, 150),
2626+ width: 100,
2727+ height: 100,
2828+ // anchor: vec(0, 0), // Actors default center colliders and graphics with anchor (0.5, 0.5)
2929+ // collisionType: CollisionType.Active, // Collision Type Active means this participates in collisions read more https://excaliburjs.com/docs/collisiontypes
3030+ });
3131+3232+ }
3333+3434+ override onInitialize() {
3535+ // Generally recommended to stick logic in the "On initialize"
3636+ // This runs before the first update
3737+ // Useful when
3838+ // 1. You need things to be loaded like Images for graphics
3939+ // 2. You need excalibur to be initialized & started
4040+ // 3. Deferring logic to run time instead of constructor time
4141+ // 4. Lazy instantiation
4242+ this.graphics.add(Resources.Sword.toSprite());
4343+4444+ // Actions are useful for scripting common behavior, for example patrolling enemies
4545+ this.actions.delay(2000);
4646+ this.actions.repeatForever(ctx => {
4747+ ctx.moveBy({offset: vec(100, 0), duration: 1000});
4848+ ctx.moveBy({offset: vec(0, 100), duration: 1000});
4949+ ctx.moveBy({offset: vec(-100, 0), duration: 1000});
5050+ ctx.moveBy({offset: vec(0, -100), duration: 1000});
5151+ });
5252+5353+ // Sometimes you want to click on an actor!
5454+ this.on('pointerdown', evt => {
5555+ // Pointer events tunnel in z order from the screen down, you can cancel them!
5656+ // if (true) {
5757+ // evt.cancel();
5858+ // }
5959+ console.log('You clicked the actor @', evt.worldPos.toString());
6060+ });
6161+ }
6262+6363+ override onPreUpdate(engine: Engine, elapsedMs: number): void {
6464+ // Put any update logic here runs every frame before Actor builtins
6565+ }
6666+6767+ override onPostUpdate(engine: Engine, elapsedMs: number): void {
6868+ // Put any update logic here runs every frame after Actor builtins
6969+ }
7070+7171+ override onPreCollisionResolve(self: Collider, other: Collider, side: Side, contact: CollisionContact): void {
7272+ // Called before a collision is resolved, if you want to opt out of this specific collision call contact.cancel()
7373+ }
7474+7575+ override onPostCollisionResolve(self: Collider, other: Collider, side: Side, contact: CollisionContact): void {
7676+ // Called every time a collision is resolved and overlap is solved
7777+ }
7878+7979+ override onCollisionStart(self: Collider, other: Collider, side: Side, contact: CollisionContact): void {
8080+ // Called when a pair of objects are in contact
8181+ }
8282+8383+ override onCollisionEnd(self: Collider, other: Collider, side: Side, lastContact: CollisionContact): void {
8484+ // Called when a pair of objects separates
8585+ }
8686+}
+14
src/excalibur/resources.ts
···11+import { ImageSource, Loader } from "excalibur";
22+33+// It is convenient to put your resources in one place
44+export const Resources = {
55+ Sword: new ImageSource("/images/sword.png") // public/ directory serves the root images
66+} as const; // the 'as const' is a neat typescript trick to get strong typing on your resources.
77+// So when you type Resources.Sword -> ImageSource
88+99+// We build a loader and add all of our resources to the boot loader
1010+// You can build your own loader by extending DefaultLoader
1111+export const loader = new Loader(Object.values(Resources));
1212+// for (const res of Object.values(Resources)) {
1313+// loader.addResource(res);
1414+// }