### Clone and Run Spaaace Repository Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md Instructions to clone the spaaace repository, install its dependencies, and start the game server. This setup is referenced throughout the tutorial. ```shell git clone https://github.com/lance-gg/spaaace cd spaaace npm install npm start ``` -------------------------------- ### Environment Setup with Git and NPM Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Demonstrates cloning a boilerplate repository, navigating into the directory, and installing project dependencies using npm. This is the initial step for setting up the game development environment. ```shell $ git clone https://github.com/lance-gg/tinygames.git $ cd tinygames/boilerplate $ npm install ``` -------------------------------- ### Server Entry Point Setup (JavaScript) Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md This snippet demonstrates the main entry point for a Lance game server using Node.js, Express, and Socket.IO. It initializes the game engines and starts the server. ```javascript const express = require('express'); const socketIO = require('socket.io'); const path = require('path'); const PORT = process.env.PORT || 3000; const INDEX = path.join(__dirname, './index.html'); // define routes and socket const server = express(); server.get('/', function(req, res) { res.sendFile(INDEX); }); server.use('/', express.static(path.join(__dirname, './dist/'))); let requestHandler = server.listen(PORT, () => console.log(`Listening on ${ PORT }`)); const io = socketIO(requestHandler); // Game Server import SpaaaceServerEngine from './src/server/SpaaaceServerEngine.js'; import SpaaaceGameEngine from './src/common/SpaaaceGameEngine.js'; // Game Instances const gameEngine = new SpaaaceGameEngine(); const serverEngine = new SpaaaceServerEngine(io, gameEngine, { debug: {}, updateRate: 6, timeoutInterval: 0 // no timeout }); // start the game serverEngine.start(); ``` -------------------------------- ### Build and Run Game Commands Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Provides the necessary shell commands to set up, build, and run the multiplayer game. It includes navigating to the game directory, installing dependencies, rebuilding the JavaScript bundle, and starting the game server. ```shell cd ../pong npm install npm run build npm start ``` -------------------------------- ### Client Entry Point Configuration and Start Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md Sets up the game engine and client engine with synchronization options, then initiates the client's connection and game loop. It imports necessary modules for tracing, client-side logic, game logic, and styling. ```javascript import Trace from 'lance\/lib\/Trace'; import SpaaaceClientEngine from './SpaaaceClientEngine.js'; import SpaaaceGameEngine from '../common\/SpaaaceGameEngine.js'; import '../../dist\/assets\/sass\/main.scss'; // sent to both game engine and client engine const options = { traceLevel: Trace.TRACE_NONE, delayInputCount: 8, scheduler: 'render-schedule', syncOptions: { sync: 'extrapolate', localObjBending: 0.2, remoteObjBending: 0.5 } }; // create a client engine and a game engine const gameEngine = new SpaaaceGameEngine(options); const clientEngine = new SpaaaceClientEngine(gameEngine, options); clientEngine.start(); ``` -------------------------------- ### Lance One-Page Game Examples Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Highlights the availability of one-page game examples in the associated examples repository. These examples serve as practical demonstrations of the Lance framework's capabilities. ```javascript // Conceptual example of a one-page game structure using Lance // main.js import { ServerEngine } from '@lance-gg/core'; import { Player } from '@lance-gg/core'; import { GameObject } from '@lance-gg/core'; const engine = new ServerEngine(); // Example game objects class PongBall extends GameObject { // ... ball properties and methods } class Paddle extends GameObject { // ... paddle properties and methods } // Game initialization engine.on('connection', (player) => { console.log('Player connected:', player.id); const paddle = new Paddle(player.id); engine.addObject(paddle); engine.assignPlayerToRoom(player, 'main-game'); }); // Start the game server engine.start(); // Link to examples repository: https://github.com/lance-gg/tinygames ``` -------------------------------- ### Client-Side Initialization and Rendering Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Handles client-side game setup and visual updates. `clientSideInit` initializes keyboard controls, binding 'up' and 'down' keys to emit corresponding events. `clientSideDraw` updates the positions and styles of HTML elements representing the ball and paddles based on their game object states, ensuring the game is rendered on each frame. ```javascript clientSideInit() { this.controls = new KeyboardControls(this.renderer.clientEngine); this.controls.bindKey('up', 'up', { repeat: true } ); this.controls.bindKey('down', 'down', { repeat: true } ); } clientSideDraw() { function updateEl(el, obj) { let health = obj.health>0?obj.health:15; el.style.top = obj.position.y + 10 + 'px'; el.style.left = obj.position.x + 'px'; el.style.background = `#ff${health.toString(16)}f${health.toString(16)}f`; } let paddles = this.world.queryObjects({ instanceType: Paddle }); let ball = this.world.queryObject({ instanceType: Ball }); if (!ball || paddles.length !== 2) return; updateEl(document.querySelector('.ball'), ball); updateEl(document.querySelector('.paddle1'), paddles[0]); updateEl(document.querySelector('.paddle2'), paddles[1]); } ``` -------------------------------- ### Server Trace Log Format Example Source: https://github.com/lance-gg/lance/blob/master/docs/guide_tuningdebugging.md An example of the log output generated by server traces. Each line includes a timestamp, step number, and a description of game engine events, such as input processing or object destruction, aiding in debugging. ```text [2016-12-04T22:16:15.136Z]719>game engine processing input[10052] from playerId 1 [2016-12-04T22:16:15.153Z]720>game engine processing input[10053] from playerId 1 [2016-12-04T22:16:15.153Z]720>========== sending world update 720 ========== [2016-12-04T22:16:15.170Z]721>game engine processing input[10054] from playerId 1 [2016-12-04T22:16:15.170Z]721>========== destroying object DynamicObject[2] position(273.854, 304.23, NaN) velocity(-4.529, -2.119, NaN) angle205 ========== ``` -------------------------------- ### ES6 Module Usage and Configuration Source: https://github.com/lance-gg/lance/blob/master/RELEASE_NOTES.md Details on how to use ES6 modules in Lance projects, including import syntax, webpack configuration, and Babel setup for server-side execution. This section covers the transition from CommonJS to ES6 modules. ```javascript import GameEngine from 'lance/GameEngine' // Instead of: const GameEngine = require('lance/GameEngine') ``` ```configuration webpack.config.js: Configure your project's webpack.config.js to support ES6 modules. ``` ```configuration .babelrc: Create a .babelrc file to configure Babel for server-side ES6 module execution. ``` -------------------------------- ### DynamicObject NetScheme Definition for Serialization Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md Defines the serialization scheme for a game object, extending the base class's netScheme. This mechanism explicitly lists attributes to be included during serialization, ensuring data consistency between server and clients. For example, it adds 'showThrust' of type INT32. ```javascript static get netScheme() { return Object.assign({ showThrust: { type: BaseTypes.TYPES.INT32 } }, super.netScheme); } ``` -------------------------------- ### Initialize Game Engine and Register Objects Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Sets up the game engine by creating a physics engine and registering event handlers. It also registers custom game object classes (like Paddle and Ball) with the Lance serializer for proper network synchronization and persistence. ```javascript constructor(options) { super(options); this.physicsEngine = new SimplePhysicsEngine({ gameEngine: this }); // common code this.on('postStep', this.gameLogic.bind(this)); // server-only code this.on('server__init', this.serverSideInit.bind(this)); this.on('server__playerJoined', this.serverSidePlayerJoined.bind(this)); this.on('server__playerDisconnected', this.serverSidePlayerDisconnected.bind(this)); // client-only code this.on('client__rendererReady', this.clientSideInit.bind(this)); this.on('client__draw', this.clientSideDraw.bind(this)); } registerClasses(serializer) { serializer.registerClass(Paddle); serializer.registerClass(Ball); } ``` -------------------------------- ### PhysicsEngine Initialization and Usage Source: https://github.com/lance-gg/lance/blob/master/RELEASE_NOTES.md Guidance on the correct initialization and usage of the PhysicsEngine within Lance. It clarifies that the PhysicsEngine should be instantiated within the GameEngine subclass, not in separate server or client entry points, and that its constructor now handles all initialization. ```javascript class MyGameEngine extends GameEngine { constructor(options) { super(options); // Initialize PhysicsEngine here this.physicsEngine = new PhysicsEngine(this, options); } step(isReenact) { // Call super.step with isReenact argument super.step(isReenact); // ... game logic ... } } ``` -------------------------------- ### Initialize Game Objects on Server Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md This server-only method is responsible for creating and adding the initial game entities to the game world. It instantiates two paddles and a ball, setting their initial positions and velocities. ```javascript serverSideInit() { // create the paddle objects this.addObjectToWorld(new Paddle(this, null, { position: new TwoVector(PADDING, 0) })); this.addObjectToWorld(new Paddle(this, null, { position: new TwoVector(WIDTH - PADDING, 0) })); this.addObjectToWorld(new Ball(this, null, { position: new TwoVector(WIDTH /2, HEIGHT / 2), velocity: new TwoVector(2, 2) })); } ``` -------------------------------- ### GameEngine Tracing and Debugging Service Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md Explains the tracing service provided by the GameEngine super-class for debugging. It details the available logging functions (trace, debug, info, warn, error), their importance levels, and best practices for using arrow functions to optimize garbage collection. It also covers how to enable and configure trace levels for server and client logs. ```APIDOC GameEngine Tracing Service: Provides logging functions for debugging purposes: - trace(): Lowest importance level. - debug(): Debugging information. - info(): Informational messages. - warn(): Warning messages. - error(): Error messages (highest importance). Usage with Arrow Functions: - Functions receive arrow functions as input to prevent evaluation when tracing is disabled, reducing garbage collection load. - Example: `gameEngine.trace.info(() => `this just happened: ${foobar()}`);` Trace Levels: - Configurable via a minimum trace level option. - `TRACE_INFO` means 'trace' and 'debug' entries are ignored. - Setting `traceLevel=0` enables all trace messages. Enabling Traces: - Client-side: Via URL query string parameter, e.g., `&traceLevel=0`. - Server-side: By setting the `traceLevel` option on the `GameEngine` constructor. Trace Files: - Server: `server.trace` - Client: `client.N.trace` (where N is the player's ID). ``` ```javascript gameEngine.trace.info(() => `this just happened: ${foobar()}`); ``` -------------------------------- ### Enable Client Traces via URL Parameter Source: https://github.com/lance-gg/lance/blob/master/docs/guide_tuningdebugging.md Illustrates how to enable client-side tracing by appending the 'traceLevel' parameter to the game's URL. This allows for debugging individual client instances, with trace files named 'client..trace'. ```url http://127.0.0.1:3000/?sync=reflect&traceLevel=0 ``` -------------------------------- ### ClientEngine Renderer Argument Source: https://github.com/lance-gg/lance/blob/master/RELEASE_NOTES.md Details a breaking change in the `ClientEngine` constructor, which now expects the `Renderer` class itself as the third argument, rather than an instance of the `Renderer`. ```javascript // Old constructor signature (example): // new ClientEngine(gameEngine, options, rendererInstance); // New constructor signature: // new ClientEngine(gameEngine, options, RendererClass); ``` -------------------------------- ### Add Game Engine Imports in JavaScript Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md This snippet shows how to import necessary modules from the 'lance-gg' library into a JavaScript file. It includes `GameEngine`, `BaseTypes`, `DynamicObject`, `SimplePhysicsEngine`, `TwoVector`, and `KeyboardControls`, which are essential for game development. ```javascript import { GameEngine, BaseTypes, DynamicObject, SimplePhysicsEngine, TwoVector, KeyboardControls } from 'lance-gg'; ``` -------------------------------- ### Enable Server Traces with Trace Level Source: https://github.com/lance-gg/lance/blob/master/docs/guide_tuningdebugging.md Shows how to initialize the game engine with a trace level to enable server-side logging. This detailed logging, saved to 'server.trace', helps in analyzing game state changes and input processing step-by-step. ```javascript const gameEngine = new MyGameEngine({ traceLevel: 1 }); ``` -------------------------------- ### Lance Sync Strategies Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Describes various synchronization strategies employed by Lance for maintaining consistent game state across clients and servers. This includes full synchronization for new connections and smart synchronization for only changed objects. ```APIDOC Sync Strategies: Extrapolation: - Predicts future states based on current velocity and time. - Used to smooth gameplay by reducing perceived latency. Interpolation: - Renders past states smoothly between known updates. - Compensates for variable network latency and packet arrival times. Full-sync: - Transmits the complete game state to new connections. - Ensures new clients receive an accurate snapshot of the current game world. Smart sync: - Optimizes bandwidth by syncing only objects that have changed. - Reduces network traffic by avoiding redundant data transmission. ``` -------------------------------- ### Lance Game Loop and Rendering Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Explains the game loop tuning mechanism, which synchronizes the game step delta with the render draw time. This ensures a consistent and smooth visual experience by aligning simulation updates with frame rendering. ```APIDOC Renderer-controller game loop: - The game step delta is tuned to the render draw time. - Ensures simulation updates are synchronized with rendering frames. - Aims to provide a consistent frame rate and smooth animation. ``` -------------------------------- ### Lance Engine Features Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Highlights core engine capabilities and integrations, including support for different physics engines, 2D and 3D rendering, and module bundling for optimized client code. ```APIDOC Engine Capabilities: 3D Support: - Full integration for 3D game development. - Demonstrates A-Frame support. 2D Engine Support: - Integration with P2 physics engine. - Includes a sample Asteroids game. Pluggable Physics Engine: - Supports external physics engines like cannon.js. Rollup-js for Native Modules: - Enables tree-shaking for smaller client code. - Facilitates TypeScript exports. ``` -------------------------------- ### GameObject Constructor and Player ID Handling Source: https://github.com/lance-gg/lance/blob/master/RELEASE_NOTES.md Explains the updated constructor for GameObject and related classes (DynamicObject, PhysicalObject) to include the gameEngine reference. It also details the relocation of the `isOwnedByPlayer` method and how `playerId` is now managed within the GameEngine. ```javascript constructor(gameEngine, options, props) { super(gameEngine, options, props); // ... object initialization ... } // Method moved from clientEngine to GameEngine // gameEngine.isOwnedByPlayer(gameObject) ``` ```javascript class DynamicObject { constructor(gameEngine, options, props) { super(gameEngine, options, props); this.position = new TwoVector(options.x, options.y); this.velocity = new TwoVector(options.velX, options.velY); } } class PhysicalObject { constructor(gameEngine, options, props) { super(gameEngine, options, props); // ... initialization ... } } ``` -------------------------------- ### Initialize Simple2DGameEngine in JavaScript Source: https://github.com/lance-gg/lance/blob/master/docs/choosing_a_physics_engine.md Demonstrates how to initialize a GameEngine with the SimplePhysicsEngine for 2D arcade-style physics. This mode is suitable for games that do not require a full physics simulation and where physical aspects are managed by game logic. Game objects must extend the DynamicObject class. ```javascript export default class Simple2DGameEngine extends GameEngine { constructor(options) { super(options); this.physicsEngine = new SimplePhysicsEngine({ gameEngine: this, collisions: { type: 'brute' } }); } } ``` -------------------------------- ### Lance ServerEngine Room Management Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Provides methods for managing game rooms within the Lance ServerEngine. These methods allow for the creation of isolated game spaces and the assignment of game objects and players to specific rooms. ```APIDOC ServerEngine: createRoom(roomName: string) - Creates a new room for game object name-spacing. - Parameters: - roomName: The unique name for the new room. - Returns: void assignObjectToRoom(object: GameObject, roomName: string) - Assigns a game object to a specified room. - Parameters: - object: The game object to assign. - roomName: The name of the room to assign the object to. - Returns: void assignPlayerToRoom(player: Player, roomName: string) - Assigns a player to a specified room. - Parameters: - player: The player to assign. - roomName: The name of the room to assign the player to. - Returns: void ``` -------------------------------- ### Configure Server Engine Update Rates Source: https://github.com/lance-gg/lance/blob/master/docs/guide_tuningdebugging.md Demonstrates how to configure the server engine with specific update and step rates. These settings control the frequency of game state updates and processing loops, which can be adjusted to help diagnose synchronization or performance issues. ```javascript const serverEngine = new MyServerEngine(io, gameEngine, { updateRate: 6, stepRate: 60, }); ``` -------------------------------- ### Implement Core Game Logic for Pong Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Contains the primary game logic, executed after each physics step. It checks for collisions between the ball and paddles or walls, updates ball velocity upon collision, and handles scoring by decrementing player health when the ball passes a boundary. ```javascript gameLogic() { let paddles = this.world.queryObjects({ instanceType: Paddle }); let ball = this.world.queryObject({ instanceType: Ball }); if (!ball || paddles.length !== 2) return; // CHECK LEFT EDGE: if (ball.position.x <= PADDING + PADDLE_WIDTH && ball.position.y >= paddles[0].y && ball.position.y <= paddles[0].position.y + PADDLE_HEIGHT && ball.velocity.x < 0) { // ball moving left hit player 1 paddle ball.velocity.x *= -1; ball.position.x = PADDING + PADDLE_WIDTH + 1; } else if (ball.position.x <= 0) { // ball hit left wall ball.velocity.x *= -1; ball.position.x = 0; console.log(`player 2 scored`); paddles[0].health--; } // CHECK RIGHT EDGE: if (ball.position.x >= WIDTH - PADDING - PADDLE_WIDTH && ball.position.y >= paddles[1].position.y && ball.position.y <= paddles[1].position.y + PADDLE_HEIGHT && ball.velocity.x > 0) { // ball moving right hits player 2 paddle ball.velocity.x *= -1; ball.position.x = WIDTH - PADDING - PADDLE_WIDTH - 1; } else if (ball.position.x >= WIDTH ) { // ball hit right wall ball.velocity.x *= -1; ball.position.x = WIDTH - 1; console.log(`player 1 scored`); paddles[1].health--; } // ball hits top or bottom edge if (ball.position.y <= 0) { ball.position.y = 1; ball.velocity.y *= -1; } else if (ball.position.y >= HEIGHT) { ball.position.y = HEIGHT - 1; ball.velocity.y *= -1; } } ``` -------------------------------- ### Lance Keyboard Controls Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_roadmap.md Introduces the KeyboardControls class, designed for handling player input via keyboard. This class likely provides methods for mapping keys to game actions and managing input states. ```APIDOC KeyboardControls: - Class for managing keyboard input. - Likely includes methods for detecting key presses, releases, and combinations. - Used to translate raw keyboard events into game actions. ``` -------------------------------- ### Process Player Input for Paddle Movement Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Handles user input on the server to control game objects. It receives input data and a player ID, finds the associated paddle, and updates its vertical position based on 'up' or 'down' commands. ```javascript processInput(inputData, playerId) { super.processInput(inputData, playerId); // get the player paddle tied to the player socket let playerPaddle = this.world.queryObject({ playerId }); if (playerPaddle) { if (inputData.input === 'up') { playerPaddle.position.y -= 5; } else if (inputData.input === 'down') { playerPaddle.position.y += 5; } } } ``` -------------------------------- ### Babel Configuration for Lance Source: https://github.com/lance-gg/lance/blob/master/docs/introduction_faq.md Instructions on how to configure Babel to transpile Lance's ES6 code. This ensures compatibility with your project's build process, typically involving webpack or a .babelrc file. ```javascript // .babelrc or webpack.config.js { // ... other configurations "include": [ "path/to/your/project", "node_modules/lance-gg/src" // Example path, adjust as needed ] // ... other configurations } ``` -------------------------------- ### Game Engine Event Handlers Source: https://github.com/lance-gg/lance/blob/master/docs/spaceships.md Details the primary event handlers provided by the GameEngine for managing game logic. These events facilitate clear separation of concerns by allowing specific logic to be tied to distinct game occurrences. Handlers receive relevant arguments such as step numbers, reenactment status, or player objects. ```APIDOC GameEngine Events: - preStep, postStep: - Emitted by the game engine just before and after step execution. - Handlers receive: step number (int), isReenactment (bool). - objectAdded, objectDestroyed: - Emitted on object addition or destruction. - Handlers receive: the object that was added or destroyed. - syncReceived: - Emitted on the client when a synchronization event (e.g., world update) is received. - Handlers receive: sync data. - playerJoined, playerDisconnected: - Emitted on player connect or disconnect events. - Handlers receive: an object describing the player, containing at least a 'playerId' attribute. ``` -------------------------------- ### HTML Structure for Game Elements Source: https://github.com/lance-gg/lance/blob/master/docs/MyFirstGame.md Defines the necessary HTML elements for the game's visual components. This includes DIV elements styled to represent the paddles and the ball within a container. These elements are targeted by client-side JavaScript for rendering updates. ```html Another awesome Lance multiplayer network game
``` -------------------------------- ### Client Engine Synchronization Options Source: https://github.com/lance-gg/lance/blob/master/docs/guide_syncextrapolation.md Configuration options for the client engine to enable and tune the extrapolation synchronization method. These options control the synchronization strategy and the behavior of object position correction. ```APIDOC ClientEngine: syncOptions: string - Sets the synchronization method. Use "extrapolate" for extrapolation. localObjBending: number - Describes the amount of bending (0.0 to 1.0) for local objects. - Controls how gradually local objects move towards their server-corrected positions. remoteObjBending: number - Describes the amount of bending (0.0 to 1.0) for remote objects. - Controls how gradually remote objects move towards their server-corrected positions. ``` -------------------------------- ### TypeScript Support Source: https://github.com/lance-gg/lance/blob/master/RELEASE_NOTES.md Indicates the addition of TypeScript support to the Lance framework, enabling developers to use TypeScript for building their games. ```typescript // Example of using Lance with TypeScript import GameEngine from '@lance-gg/lance'; class MyGame extends GameEngine { // ... game logic ... } const game = new MyGame({}); ``` -------------------------------- ### GameEngine Core Methods Source: https://github.com/lance-gg/lance/blob/master/docs/overview_architecture.md Details key methods within the GameEngine class responsible for game progression and input handling. These methods are central to the game loop on both server and client. ```APIDOC GameEngine::step() - Advances the game state from time T to T + δt. - Handles physics, user inputs, and game mechanics logic. - Called at a fixed interval by the ServerEngine. GameEngine::processInput() - Reads and processes inputs received from clients since the previous step. - Essential for synchronizing game state based on player actions. ``` -------------------------------- ### Send Player Input to Server Source: https://github.com/lance-gg/lance/blob/master/docs/guide_clientengine.md The client engine collects player inputs, such as mouse clicks and keyboard presses, and submits them to the server by calling the `ClientEngine::sendInput()` method. This action is crucial for client-server interaction in the game. ```cpp ClientEngine::sendInput() ```