### Initialize Rune Game Logic (JavaScript) Source: https://developers.rune.ai/docs/quick-start Sets up the core game logic for a Rune game, including player count, setup function, and available actions. The `setup` function initializes the game state, and `actions` define how the game state can be modified. ```javascript function setup() { const game = { cells: new Array(9).fill(null), // 3x3 cell grid // ... rest of the game state } return game } function claimCell(cellIndex, { game, playerId }) { game.cells[cellIndex] = playerId // ... rest of the logic, like checking for win condition } Rune.initLogic({ minPlayers: 2, maxPlayers: 2, setup, actions: { claimCell, }, }) ``` -------------------------------- ### Setup Initial Game State (JavaScript) Source: https://developers.rune.ai/blog/tags/game-development Sets up the initial game state when the game starts. It creates the game map, initializes controls, and adds a character for each player. ```javascript setup: (allPlayerIds) => { const state: GameState = { map: createGameMap(), controls: {}, characters: [], } // create a character for each player for (const id of allPlayerIds) { addCharacter(id, state) } return state }, ``` -------------------------------- ### Initialize Game Logic with Rune SDK Source: https://developers.rune.ai/blog/multiplayer-platformer-fun Initializes the game logic using `Rune.initLogic`. The `setup` function is responsible for creating the initial `GameState`, including player entities based on provided player IDs. This setup ensures all clients start with a consistent game state. ```typescript Rune.initLogic({ setup: (allPlayerIds) => { const initialState: GameState = { // for each of the players Rune says are in the game // create a new player entity. We'll initialize their // location to place them in the world players: allPlayerIds.map((p, index) => { return { x: 20 + (index + 1) * 32, y: 260, playerId: p, type: "PLAYER", sprite: PLAYER_TYPES[index % PLAYER_TYPES.length], animation: Animation.IDLE, controls: { left: false, right: false, jump: false, }, flipped: false, vx: 0, vy: 0, } }), } return initialState }, ``` -------------------------------- ### Upload Game to Rune App Source: https://developers.rune.ai/docs/quick-start Uploads the current game project to the Rune application for testing and playing. This command is typically run from the project's root directory. ```bash npm run upload ``` -------------------------------- ### Initialize Rune SDK and Physics World Setup Source: https://developers.rune.ai/blog/phaser Initializes the Rune SDK with player count and sets up the initial game state. This includes creating a propel-js physics world, adding platforms and player bodies, and initializing player controls. ```typescript Rune.initLogic({ minPlayers: 1, maxPlayers: 4, setup: (allPlayerIds) => { const initialState: GameState = { world: physics.createWorld({ x: 0, y: 800 }), controls: {}, } // phasers setup world but in propel-js physics physics.addBody( initialState.world, physics.createRectangle( initialState.world, { x: 0 * PHYSICS_WIDTH, y: 0.2 * PHYSICS_HEIGHT }, 0.5 * PHYSICS_WIDTH, 0.05 * PHYSICS_HEIGHT, 0, 1, 1 ) ) physics.addBody( initialState.world, physics.createRectangle( initialState.world, { x: 0.75 * PHYSICS_WIDTH, y: 0.4 * PHYSICS_HEIGHT }, 0.5 * PHYSICS_WIDTH, 0.05 * PHYSICS_HEIGHT, 0, 1, 1 ) ) physics.addBody( initialState.world, physics.createRectangle( initialState.world, { x: 0.5 * PHYSICS_WIDTH, y: 0.6 * PHYSICS_HEIGHT }, 0.5 * PHYSICS_WIDTH, 0.05 * PHYSICS_HEIGHT, 0, 1, 1 ) ) physics.addBody( initialState.world, physics.createRectangle( initialState.world, { x: 0.5 * PHYSICS_WIDTH, y: 0.9 * PHYSICS_HEIGHT }, 1 * PHYSICS_WIDTH, 0.3 * PHYSICS_HEIGHT, 0, 1, 1 ) ) // create a player body for each player in the game for (const playerId of allPlayerIds) { const rect = physics.createRectangleShape( initialState.world, { x: 0.5 * PHYSICS_WIDTH, y: 0.5 * PHYSICS_HEIGHT }, 0.1 * PHYSICS_WIDTH, 0.1 * PHYSICS_HEIGHT ) const footSensor = physics.createRectangleShape( initialState.world, { x: 0.5 * PHYSICS_WIDTH, y: 0.55 * PHYSICS_HEIGHT }, 0.05 * PHYSICS_WIDTH, 0.005 * PHYSICS_HEIGHT, 0, true ) const player = physics.createRigidBody( initialState.world, { x: 0.5 * PHYSICS_WIDTH, y: 0.5 * PHYSICS_HEIGHT }, 1, 0, 0, [rect, footSensor] ) as physics.DynamicRigidBody player.fixedRotation = true player.data = { player: true, playerId } physics.addBody(initialState.world, player) initialState.controls[playerId] = { left: false, right: false, up: false, } } // create a few stars to play with for (let i = 0; i < 5; i++) { const rect = physics.createCircleShape( initialState.world, { x: i * 0.2 * PHYSICS_WIDTH, y: 0.15 * PHYSICS_HEIGHT }, 0.04 * PHYSICS_WIDTH ) const star = physics.createRigidBody( initialState.world, { x: i * 0.2 * PHYSICS_WIDTH, y: 0.15 * PHYSICS_HEIGHT }, 10, 1, 1, [rect], { star: true } ) as physics.DynamicRigidBody physics.addBody(initialState.world, star) } return initialState } ``` -------------------------------- ### Initialize Game Logic with Rune SDK (JavaScript) Source: https://developers.rune.ai/blog/tags/game-development/page/2 Sets up the initial state of the game using `Rune.initLogic`. This function defines how the game world is constructed at the start, including player positions and static entities like trees, based on provided player IDs. ```javascript Rune.initLogic({ setup: (allPlayerIds) => { const initialState: GameState = { entities: [], players: allPlayerIds.map((p, index) => { return { x: (index + 1) * 64, y: (index + 1) * 64, playerId: p, type: "PLAYER", sprite: index % 4, animation: Animation.IDLE, controls: { left: false, right: false, up: false, down: false, }, flipped: false, vx: 0, vy: 0, }; }), }; for (const tree of trees) { initialState.entities.push({ type: "TREE", x: tree[0], y: tree[1], sprite: 4, }); } return initialState; }, ``` -------------------------------- ### Logic-side Game State Definition and Initialization Source: https://developers.rune.ai/blog Defines the `GameState` interface and the `setup` function for initializing the game state. The `GameState` includes the game map, player controls, and characters. The `setup` function creates the initial game map and adds a character for each player. ```typescript // the game state we store for the running game export interface GameState { // the gamp map for collisions etc map: GameMap // the controls reported for each player controls: Record // the characters in the game world characters: Character[] } setup: (allPlayerIds) => { const state: GameState = { map: createGameMap(), controls: {}, characters: [], } // create a character for each player for (const id of allPlayerIds) { addCharacter(id, state) } return state }, ``` -------------------------------- ### Initialize Rune Client Rendering (JavaScript) Source: https://developers.rune.ai/docs/quick-start Initializes the client-side logic for a Rune game, handling game state changes and user input. The `onChange` function is called whenever the game state is updated, and it's responsible for updating the game's visuals. ```javascript function onChange({ game, players, yourPlayerId, action }) { const { cells, lastMovePlayerId } = game // ... update your game visuals according to latest received game state. Also play sound effects, update styles, etc. } Rune.initClient({ onChange }) const button = // ... get the cell button.addEventListener("click", () => Rune.actions.claimCell(cellIndex)) ``` -------------------------------- ### Getting AI Responses Source: https://developers.rune.ai/blog This section describes how to receive and process the AI's response using the `promptResponse` callback within `Rune.initLogic`. ```APIDOC ## GET Rune.ai.promptResponse ### Description Handles the response received from an AI prompt request. The response text is provided to the game logic, allowing for updates to the game state. ### Method Callback (within `Rune.initLogic`) ### Endpoint N/A (Client-side SDK callback) ### Parameters #### Request Body (Callback arguments) - **response** (Object) - Required - An object containing the AI's response. - **response** (String) - Required - The text content of the AI's response. ### Request Example (See `Rune.initLogic` configuration below) ### Response #### Success Response (Callback execution) - **response** (String) - Description of the AI's textual reply. #### Response Example ```javascript Rune.initLogic({ // ... other configurations ai: { promptResponse: ({ response }) => { console.log("AI Response:", response); // Example: Update UI element with the response // game.ui.displayMessage(response); }, }, }); ``` ``` -------------------------------- ### Get Game Time with Rune.gameTime() Source: https://developers.rune.ai/docs/api-reference Retrieves the elapsed time in milliseconds since the game started. This function is useful for time-based game mechanics. ```javascript const elapsedTime = Rune.gameTime(); ``` -------------------------------- ### Include Rune SDK and Game Files in HTML Source: https://developers.rune.ai/docs/how-it-works/existing-game This HTML snippet demonstrates how to include the Rune SDK and the game's logic and client files in the `index.html`. It ensures the SDK is loaded before other scripts, enabling multiplayer functionality for the game. This is part of the manual setup approach. ```html ``` -------------------------------- ### Get World Time with Rune.worldTime() Source: https://developers.rune.ai/docs/api-reference Returns the current time in milliseconds since the epoch with second precision. This can be used for synchronization or time-based events independent of game start. ```javascript const worldTime = Rune.worldTime(); ``` -------------------------------- ### Track Game Time with Rune.gameTime() Source: https://developers.rune.ai/docs/advanced/real-time-games Use Rune.gameTime() to get the milliseconds passed since the game started. This is useful for time-based scoring or tracking player actions within the game. It has a default precision of one second. ```javascript // logic.js function allPlayersDone(game) { // ... } function setNewQuestionAndAnswer(game) { // ... } Rune.initLogic({ setup: (allPlayerIds) => { return { scores: Object.fromEntries(allPlayerIds.map((id) => [id, 0])), roundStartAt: 0, question: "A group of otters is called what?", correctAnswer: "A raft" } }, actions: { guess: ({ answer }, { game, playerId }) => { if (answer === game.correctAnswer) { // Increment score based on time const timeTaken = Rune.gameTime() - roundStartAt scores[playerId] += max(30 - timeTaken, 0) } // Start a new round once everyone has answered if (allPlayersDone(game)) { roundStartAt = Rune.gameTime() setNewQuestionAndAnswer(game) } }, }, }) ``` -------------------------------- ### Initialize Phaser Game and Load Assets Source: https://developers.rune.ai/blog/phaser Sets up the Phaser game instance, configures the renderer, and preloads essential game assets like images and spritesheets. This is a standard Phaser initialization process. ```typescript export default class TutorialGame extends Phaser.Scene { preload() { // preload our assets with phaser this.load.image("sky", "assets/sky.png") this.load.image("ground", "assets/platform.png") this.load.image("star", "assets/star.png") this.load.image("bomb", "assets/bomb.png") this.load.spritesheet("dude", "assets/dude.png", { frameWidth: 32, frameHeight: 48, }) } } const config = { type: Phaser.AUTO, width: window.innerWidth, height: window.innerHeight, scene: TutorialGame, scale: { mode: Phaser.Scale.ScaleModes.FIT, }, } new Phaser.Game(config) ``` -------------------------------- ### Create Rune Game Project using Rune CLI Source: https://developers.rune.ai/docs/how-it-works/existing-game This command initializes a new Rune game project using the Rune CLI. It sets up the necessary boilerplate and configuration for a multiplayer game, allowing developers to quickly start building or porting their game logic and client-side rendering. ```bash npx rune@latest create ``` -------------------------------- ### Install Rune CLI Source: https://developers.rune.ai/docs/publishing/cli Installs the Rune CLI globally using npm. Requires Node.js version 14.17 or higher. ```bash npm install -g rune ``` -------------------------------- ### Initialize Game Logic with Player Join/Leave Callbacks Source: https://developers.rune.ai/docs/advanced/joining-leaving Demonstrates how to initialize game logic in Rune AI, specifying minimum and maximum players, game setup, and custom callbacks for playerJoined and playerLeft events to manage game state dynamically. ```javascript Rune.initLogic({ minPlayers: 1, maxPlayers: 4, setup: (allPlayerIds) => { const scores = {} for (playerId in allPlayerIds) { scores[playerId] = 0 } return { scores } }, actions: {}, events: { playerJoined: (playerId, { game }) => { game.scores[playerId] = 0 } } }) ``` -------------------------------- ### Get Map Height at Coordinates (TypeScript) Source: https://developers.rune.ai/blog Calculates the height of the game map at a given (x, z) coordinate. It floors the input coordinates to get the integer grid position and returns the height from the `GameMap` array, defaulting to 0 if the position is undefined. ```typescript export function getHeightAt(map: GameMap, x: number, z: number) { x = Math.floor(x) z = Math.floor(z) return map[x + z * GAME_MAP_WIDTH] ?? 0 } ``` -------------------------------- ### Initialize Rune Client for Game State Updates Source: https://developers.rune.ai/blog/tags/networking This code initializes the Rune client on the rendering side, allowing it to receive game state updates. The `onChange` callback is crucial for updating the local game state and player ID, which are then used for rendering. ```javascript // Start the Rune SDK on the client rendering side. // This tells the Rune app that we're ready for players // to see the game. It's also the hook // that lets the Rune SDK update us on // changes to game state Rune.initClient({ // notification from Rune that there is a new game state onChange: ({ game, yourPlayerId }) => { // record the ID of our local player so we can // center the camera on that player. myPlayerId = yourPlayerId // record the current game state for rendering in // our core loop gameState = game }, }) ``` -------------------------------- ### Initialize Game State with Rune SDK (JavaScript) Source: https://developers.rune.ai/blog/tags/tech-demo Demonstrates how to initialize the game state using `Rune.initLogic`. This function sets up the initial `GameState`, including player positions and entities like trees, based on provided player IDs. It ensures all clients start from a consistent state. ```javascript Rune.initLogic({ setup: (allPlayerIds) => { const initialState: GameState = { entities: [], players: allPlayerIds.map((p, index) => { return { x: (index + 1) * 64, y: (index + 1) * 64, playerId: p, type: "PLAYER", sprite: index % 4, animation: Animation.IDLE, controls: { left: false, right: false, up: false, down: false, }, flipped: false, vx: 0, vy: 0, } }), } for (const tree of trees) { initialState.entities.push({ type: "TREE", x: tree[0], y: tree[1], sprite: 4, }) } return initialState }, }) ``` -------------------------------- ### Initialize Client with Rune.initClient() Source: https://developers.rune.ai/docs/api-reference Initializes the Rune client, setting up the game state updates and rendering logic. The `onChange` callback is crucial for updating the UI based on game state changes. ```javascript Rune.initClient({ onChange: ({ game, previousGame, futureGame, yourPlayerId, players, allPlayerIds, action, event, rollbacks, }) => { render(game) }, }) ```