# Facial Unification
**Facial Unification** is a non-HTTP module for Roblox which converts Dynamic Faces to their classic 2D variants. It works without any kind of a HTTP request. This module was written by me from scratch. I have compiled the face and head IDs for every face which is currently dynamic.

[DevForum page](https://devforum.roblox.com/t/4312162)

# Where to download?
Check the [recent releases](https://github.com/Dawgra/Facial-Unification/releases)!
All you have to do is insert this as a Roblox model.
![Right Click in the Explorer > Insert > Import Roblox Model](https://i.imgur.com/BHQFt4O.png)

# Guide?
* **Main.luau** - The main part of the Facial Unification module. You can name it as you like.
* **Content** - Modules that should be located inside of Main.luau instance.
    * You can remove "Attribution" when you credit me somewhere in-game, in the description or DevForum Bulletin Board post.

Starting from version 1.4, you will get notifications in the console about Updates!


<source srcset="/gh-assets/LogoStamp.svg"></source>

  Easy, themable avatar catalog for Roblox

## Demo 📽

https://github.com/user-attachments/assets/4f278391-142b-46b9-bed0-8018e638d70c

[Or try it out in-game! 🎮](https://www.roblox.com/games/95426183703947)

## Simplicity 😌

Avalog prioritizes your experience, and doesn't bombard you with branding, sign-up, or clunk.

## Customization 🖌

Comprehensive theming properties allow you to match your game's mood and style with ease.

## Monetization 💸

Earn 40% on all items sold in your game, and list your own UGC items.

## [Documentation 📄](https://loneka.com/avalog/)


### Simplicity

Avalog prioritizes your experience, and doesn't bombard you with branding, sign-up, or clunk.

### Customization 🖌

Comprehensive theming properties allow you to match your game's mood and style with ease.

### Monetization 💸

Earn 40% on all items sold in your game, and list your own UGC items.


# Nexus-Admin
Nexus Admin is admin system built on top of [Cmdr](https://github.com/evaera/Cmdr)
to simplify load and add additional functionality, including
a built in UI. Included is an API for registering custom commands
for integration into games that require more than what Cmdr offers.

## Should I Just Use Cmdr?
Nexus Admins adds additional functionality that may be useful through
some of it's modules, like fast flags and authorization. If none of the
additional features or commands are needed and the more involved setup
is acceptable for a lighter weight system, Cmdr should be considered.

## Documentation
See the [docs](docs/) directory for documentation.

### Frequently Asked Questions
Before creating a GitHub Issue or using private communication methods
for support, please view the [frequently asked questions](docs/frequently-asked-questions.md).
Historically, all support requests could have been resolved by viewing
the questions.

## License
Nexus Admin is available under the terms of the MIT
Liscence. See [LICENSE](LICENSE) for details.


# fluid

## Overview

- Declarative UI library aimed to support modern UI
  - New solver focus
  - No defaults (Causes friction with stylesheets)
- Focus on correctness, simplicity, & stability
- Mixed-table approach to properties & children. `fluid.create("Frame")({ Name = "hi", child() })`
- Go fast and try things out, we can slow down when we approach 1.0

## Interesting features

This section details the interesting bits where Fluid has an advantage.

- Support for async reactive nodes. `fluid.async`
- Push-pull reactive graph with deferred mode
- `interval` utility to poll values. `fluid.interval` will run at the specified hz
- Exported `UsedAs` type
- `_G.__DEV__` for development mode, which will enhance error messages & print useful information at the cost of performance.

## Prior Art

- **Heavily** based off [Vide](https://github.com/centau/vide/), which is based off [solidjs](https://github.com/solidjs/solid). This project started as a series of W.I.P. pull requests to Vide, which ended up in full (opinionated) rewrites and significant breaking changes.
  - Vide was used as reference for implementing the reactive graph functions (derive, source, )
- Utilizes Alice's [hybrid push-pull reactive graph](https://gist.github.com/alicesaidhi/32f5bd225932a5d0239d0798c3d2e292) (based off Vide's reactive graph)
- Animation, time & color utilities inspired by [Fusion](https://github.com/dphfox/Fusion).


# Installation

WARNING

Fluid is extremely early in development. There are dragons.

Fluid is available on Wally.

toml

    fluid = "ffrostfall/fluid@<release>"

# API overview

  * fluid.create("Frame")({ property = value })
  * fluid.source()
  * fluid.async()
  * fluid.derive()
  * fluid.cleanup()
  * fluid.root()
  * fluid.untrack()
  * fluid.effect()
  * fluid.interval()
  * fluid.read()
  * fluid.for_values()
  * fluid.action()


# Upside Engine
Upside Engine It's a 2d framework that lets you create amazing games with ease. You can use it to make platformers, puzzles, shooters, and more. Upside Engine has many features to help you design your game, such as physics, animations, sounds and more. Upside Engine is the ultimate tool for 2d game development in roblox.

[Get Started](https://notreux.github.io/UpsideEngine/tutorials/get-started/Installation.html) [Documentation](https://notreux.github.io/UpsideEngine/documentation/Welcome.html)

# Roadmap
✅ Collision Masks
✅ Parallel lighting system
✅ Angular Velocity
✅ Parallax Objects
⬜ Beams
✅ Spotlights
✅ Update outdated tests

### WARNING
Some Upside Engine features may not work if you don't have [EditableImages](https://create.roblox.com/docs/es-es/reference/engine/classes/EditableImage) enabled in your game

# Changelog v3.3.0

## Summary

Physics improvements focused on correctness and per-object control. Adds gravity scaling, mass-based collision response, and fixes raycast blacklist filtering.

### Added

- `GravityScale` property on `PhysicalObject`. Multiplier for gravity per object (default `1`). Set to `0` to disable gravity (useful for projectiles, floating objects).

### Changed

- Collision response now distributes correction proportionally to mass. Heavier objects move less on impact, lighter objects move more. Falls back to 50/50 when both masses are zero.

### Fixed

- Raycast blacklist mode now correctly accepts arrays (`{ objectA, objectB }`) in addition to dictionaries (`{ [id] = true }`), consistent with whitelist mode.

# Changelog v3.2.0

## Summary

Major physics overhaul focused on performance and reliability. Introduces angular velocity, optimizes the collision pipeline, and fixes several physics behavioral issues.

**Key Features:**
- Angular velocity system with collision-generated spin and configurable properties
- Adaptive spatial hash grid for broad-phase collision optimization
- Fixed-timestep physics loop with accumulator pattern
- Improved collision response and jump reliability

### Added

- Angular physics properties: `AngularVelocity`, `AngularFriction`, `AngularScale`, `AngularStiffness`, `Torque`.
- `ApplyTorque()` method on `PhysicalObject`.
- Spatial hash grid for O(n) broad-phase collision detection (activates at 30+ objects).
- Continuous collision detection via velocity subdivision.
- Spring-damper stabilization system that snaps objects to stable angles when grounded.

### Changed

- Physics loop now runs at a fixed 250Hz timestep with accumulator cap to prevent death spiral on lag spikes.
- Camera and parallax trackers moved to fixed-rate loop for smoother visuals.
- Collision corrections use min/max per direction to prevent wall-squeeze flying.
- Velocity corrections clamped to never reverse direction.
- Jump uses direct velocity impulse instead of `ApplyForce` for reliability.
- GJK/EPA collision modules now use `--!native` and `--!optimize 2` for native compilation.
- Pre-allocated table buffers to reduce GC pressure.

### Fixed

- Objects flying when squeezed between walls or jumping against walls.
- Jump pausing at apex due to incorrect `IsGrounded` persistence.
- Performance regression from spatial grid table allocation (resolved with table reuse).
- Character visual judder caused by camera tracking at different rate than physics.


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/Welcome.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/Welcome.md "View source of this page")

# Welcome to the documentation!¶

Cite

If a class was inherited from another class, the properties of the class from which it was inherited will not show its properties on the current page, you will have to go to its respective page to read more about its information.

Here are a few pages that are essential to read if this is your first time using the upside engine framework:

  * [Upside Engine](https://notreux.github.io/UpsideEngine/documentation/UpsideEngine.html)
  * [SceneManager](https://notreux.github.io/UpsideEngine/documentation/autogen/SceneManager.html)
  * [CrossPlatformService](https://notreux.github.io/UpsideEngine/documentation/autogen/CrossPlatformService.html)
  * [Scene](https://notreux.github.io/UpsideEngine/documentation/autogen/Scene.html)
  * [Sprite](https://notreux.github.io/UpsideEngine/documentation/autogen/Sprite.html)

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/get-started/Installation.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/get-started/Installation.md "View source of this page")

# Installation¶

Working-with-roblox-studio

This tutorial is oriented to external code editors, so there are probably some terms that you do not understand if you only use roblox studio, so here are some clarifications:

  * When the tutorial talks about `whatever.client.luau` or `whatever.server.luau`:
    * in the .client case is talking about a _local script_ inside StarterPlayerScripts.
    * in the .server case is talking about a _script_ inside ServerScriptService.
  * When the tutorial talks about any script like for example `whatever.client.luau`, the script's name would be everything before the first dot. In this case, it would be "whatever".
  * When the tutorial doesn't specify if a script is a local script or a server script in the script name (example: `whatever.luau`), the tutorial is talking about a module script.
  * When we talk about `src/client` we are are talking about `StarterPlayerScripts`.

Follow this steps to "install" the package in roblox studio

  * Create a folder in ReplicatedStorage and then name it as "packages"
  * Place the upside engine module in the packages folder (click on the download button below to get the module)
  * Once you finished the steps before, continue the tutorial in the "An important step" section.

[Download Upside Engine](https://github.com/notreux/UpsideEngine/releases/latest/download/UpsideEngine.rbxm)

## Project Setup¶

For this tutorial, we will need the following:

  * Initialize Git
  * Start a new project with Rojo

## Downloading Upside Engine¶

To install Upside Engine, you can use `github submodules` or `wally`

### Github submodules¶

To install upside engine using github submodules run this command:

    git submodule add https://github.com/notreux/UpsideEngine packages/UpsideEngine

### Wally¶

To install upside engine with wally add this line to your `wally.toml`:

    UpsideEngine = "notreux/upsideengine@3.0.0"

## Recommended Rojo Template (optional)¶

Info

       "name": "My first 2D Game",
        "tree": {
            "$className": "DataModel",
            "ReplicatedStorage": {
                "$className": "ReplicatedStorage",
                "packages": {
                    "$className": "Folder",
                    "$path": "packages"
            },

            "StarterPlayer": {
                "$className": "StarterPlayer",
                "StarterPlayerScripts": {
                    "$className": "StarterPlayerScripts",
                    "client":{
                        "$path": "src/client"
            },

            "ServerScriptService": {
                "$className": "ServerScriptService",
                "server": {
                    "$path":"src/server"

## Recommended LSP (optional)¶

For the best experience, we recommend to use the [LuauLSP](https://marketplace.visualstudio.com/items?itemName=JohnnyMorganz.luau-lsp) extension for Visual Studio Code. Once you have installed LuauLSP then go to extension settings and search “Definition Files”

Typescript-types

You can also use TypeScript, just make sure you have [roblox-ts](https://roblox-ts.com/) installed

### Github submodules¶

If you are using github submodules, click on "Add Item" and then enter this path `packages/UpsideEngine/src/init.d.luau`.

### Wally¶

Warning

In the paths below change `VERSION` for the version you are using, for example, if you use the version 3.0.0 change the path to `Packages/_Index/notreux_upsideengine@3.0.0/upsideengine/src/init.d.luau`

If you are using wally, click on "Add Item" and then enter this path `Packages/_Index/notreux_upsideengine@VERSION/upsideengine/src/init.d.luau`.

## An important step¶

To ensure that the Upside Engine works correctly, it is important to initialize the engine on the server, even if you are not using any server-side functionality. This is because some services, such as `NetworkingService`, depend on the server side.

We will create a new script `initializer.server.luau` in `ServerScriptService` with the following content:

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local packages = replicatedStorage.packages

    local upsideEngine = require(packages.UpsideEngine)
    print("Upside Engine version: " .. upsideEngine.Version)

##### Congratulations you finished the installation of the upside engine framework 🎉🎉¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/games.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/games.md "View source of this page")

# Games¶

Here you can explore a list of games created with the `Upside Engine Framework`, if you would like to add your game here send us a request in [our discord ](https://discord.com/invite/pE3svUvmnu). Before send us a request about your game make sure your game is public, otherwise it will be rejected.

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/get-started/FirstGame.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/get-started/FirstGame.md "View source of this page")

# Your first game

In this guide, we'll walk you through the process of creating a basic game scenario using Upside Engine, a framework for Roblox 2d game development.

Tip

In this guide we are going to use Offset, scale should never be used for a 2D game, as it is problematic on some devices, it is recommended that when building your game you use 1920x1080 resolution in the device emulator as it will adapt to most devices.

## Step 1: Setting up the scene¶

The first thing we need to do is to create a scenario for our game. To do this, we'll use the `Scene` object, which is a useful tool for creating scenarios quickly and easily.

Warning

If you are using roblox studio, the scripts with "init" as name works different, means that all the scripts in the same directory are gonna be children of that script, so to explain it easier, just create a local script named "Client" in StarterPlayerScripts and place any `module script` as a children of it.

example:

In your `init.client.luau` script, add the following code:

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local tweenService = game:GetService("TweenService")
    local players = game:GetService("Players")

    local packages = replicatedStorage.packages
    local playerGui = players.LocalPlayer:WaitForChild("PlayerGui")

    local upsideEngine = require(packages.UpsideEngine)
    local screen = Instance.new("ScreenGui")
    screen.Name = "MyGame"
    screen.IgnoreGuiInset = true
    screen.Parent = playerGui

    local scene = upsideEngine.new("Scene")
    scene.Instance.Parent = screen
    scene:SetName("MyFirstScene") -- We set the scene name
    scene:Enable() -- We mark as enabled the scene

This code creates a new Scene object and adds it to the PlayerGui object in your game.

## Step 2: Creating the floor¶

Now that we have our scene set up, we can create a floor for our game. To do this, we'll use the PhysicalObject object.

    local leftFloor = upsideEngine.new("PhysicalObject")
    leftFloor.Anchored = true
    leftFloor:SetScene(scene)

    local lfInstance = leftFloor.Instance
    lfInstance.Image = "rbxassetid://12980969571" -- We set the floor texture
    lfInstance.Size = UDim2.fromOffset(600, 160) -- We set the size to 600x160 pixels
    lfInstance.Position = UDim2.fromOffset(300, 1000) -- We set the position to 300x1000 pixels

    local rightFloor = upsideEngine.new("PhysicalObject") -- We create the floor and pass the scene as the parent object
    rightFloor.Anchored = true
    rightFloor:SetScene(scene)

    local rfInstance = rightFloor.Instance
    rfInstance.Image = "rbxassetid://12980969571" -- We set the floor texture
    rfInstance.Size = UDim2.fromOffset(600, 160) -- We set the size to 600x160 pixels
    rfInstance.Position = UDim2.fromOffset(1620, 1000) -- We set the position to 1620x1000 pixels

This code creates a new PhysicalObject object and adds it to our scene. We then set the floor's texture and size.

## Step 3: Creating a background¶

Next, let's add a background to our game. This time we'll use another PhysicalObject object, but we'll set it up a little differently.

    local background = Instance.new("Frame")
    background.BackgroundTransparency = 0 -- We set the background transparency
    background.BackgroundColor3 = Color3.fromRGB(27, 62, 82)
    background.Size = UDim2.fromScale(1, 1) -- We set the size to the target screen size
    background.Position = UDim2.fromOffset(0.5, 0.5) -- We set the position to the center
    background.ZIndex = -1
    background.Parent = scene.Instance.Parent

This code creates a new PhysicalObject object and adds it to our scene. We then set the background's texture and size to the full screen size.

## Step 4: Adding decoration¶

Next, let's add a the decoration to our game. This time we'll use another PhysicalObject object, but we'll set it up a little differently.

    local decoration = upsideEngine.new("PhysicalObject") -- We create the decoration and pass the scene as the parent object
    decoration.TrackCollisions = false
    decoration:SetScene(scene)

    local decInstance = decoration.Instance
    decInstance.Image = "rbxassetid://12993235175" -- We set the decoration texture
    decInstance.Size = UDim2.fromOffset(1920, 1080) -- We set the size to the target screen size
    decInstance.Position = UDim2.fromOffset(960, 540) -- We set the position to the center
    decInstance.ZIndex = 0

This code creates a new PhysicalObject object and adds it to our scene. We then set the background's texture and size to the full screen size.

## Step 5: Creating platforms¶

Finally, let's create some interactive platforms for our game, we will create a platform that falls once it detects a collision.

    -- Create platform object and set properties
    local function createPlatform(x, y)
        local position = UDim2.fromOffset(x, y)
        local platform = upsideEngine.new("PhysicalObject")
        platform:SetScene(scene)
        platform.Mass = 0
        platform.Anchored = false

        local platInstance = platform.Instance
        platInstance.Image = "rbxassetid://12979703349"
        platInstance.Size = UDim2.fromOffset(250, 80)
        platInstance.Position = position
        platInstance.ZIndex = 2

        -- Create Tween to animate platform to its original position on collision
        local info = TweenInfo.new(1)
        local goal = { Position = position }

        local toOrigin = tweenService:Create(platform.Instance, info, goal)
        local falling = false
        toOrigin.Completed:Connect(function()
            falling = false
        end)

        -- Listen to the "Collision" event
        platform:On("Collision", function(object) -- Create a function to detect when the plaform collides
            if not object:IsA("Character") or falling then
                return
            end

            task.wait(1)
            falling = true
            platform.Mass = 200

            task.wait(5)
            platform.Mass = 0
            platform.Force = Vector2.zero

            toOrigin:Play()
        end)
    end

    createPlatform(800, 900)
    createPlatform(1120, 900)

    for _, scr in script:GetChildren() do
        require(scr) --Initializate the secondary scripts
    end

##### Congratulations! You've now created your first scenario using Upside Engine 🎉🎉¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/TextTagService.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/TextTagService.md "View source of this page")

# [Extended from BaseClass](https://notreux.github.io/UpsideEngine/documentation/autogen/BaseClass.md) TextTagService¶

# Properties¶

## [table](https://notreux.github.io/UpsideEngine/documentation/autogen/table.md) TextTags¶

# Methods¶

There is no methods for this class

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/get-started/LicensePage.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/get-started/LicensePage.md "View source of this page")

# License¶

The Upside Engine Framework has an Apache 2.0 License, so you can use this engine for anything you want, it will always be mandatory to give credits except if it is for a videogame `(but it would be very appreciated)`, for any other kind of projects it will be mandatory to give credits, you can read more about this license [here ](https://github.com/TheHackerPuppy/UpsideEngine/blob/main/LICENSE).

If you still have any doubt on how can you use the upside engine framework in your projects ask us in our [discord ](https://discord.com/invite/pE3svUvmnu).

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/plugin-guide/MCPGuide.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/plugin-guide/MCPGuide.md "View source of this page")

# Using MCP

### Using the Upside Engine MCP Plugin¶

In this page, you will learn how to use the MCP (Model Context Protocol) for the Upside Engine plugin. If you are not interested in integrating the Upside Engine plugin with AI, you can skip this page.

To follow along, make sure you have **Claude Desktop** or **Cursor** installed, as they are required to connect the plugin with a Large Language Model (LLM).

### Setup¶

Once you have Claude Desktop or Cursor installed, follow the installation guide for the [Upside Engine MCP](https://github.com/notreux/UpsideEngineMCP). After completing the setup, open Claude Desktop or Cursor. You will find two new tools available:

  * `get_tilemap`
  * `place_tiles`

These tools allow the LLM to create scenarios using the plugin. Currently, only these two tools exist, but more may be added in the future.

### Important Notes¶

For the LLM to function correctly, **every tile you want it to use must have a name**. If a tile is unnamed, the LLM will not be able to see it.

In the image below, the first tile is named **"LeftWall"**. Be sure to give your tiles very descriptive names. For example, instead of simply **"LeftWall"**, a more informative name like **"LeftTopCornerWall"** provides much clearer context for the LLM. Since tile names are the only reference the LLM has for placement, descriptive naming is key.

## Example¶

The model that we recommend is `Claude 4.1 opus`, because it usually give better results than other models.

### Prompt¶

> Using Upside Engine, create a basic scenario for a 2D top-down game about a dungeon

Output

As you can see, the result is not perfect, but it can be very useful to speed up the scenario building process.

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/style-guide/ProjectStructure.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/style-guide/ProjectStructure.md "View source of this page")

# Upside Engine Project Structure¶

In this guide, we'll explain the recommended project structure for a basic game scenario using Upside Engine. The structure is designed to keep your code modular, maintainable, and scalable.

This is an example how the project structure of a game should look:

    UpsideEngineGame/
    ├── client/
    │   ├── main.client.luau
    │   ├── ui/**
    │   └── modules/
    │       ├── globals/
    │       │   ├── sword.luau
    │       │   └── player.luau
    │       ├── city/
    │       │   ├── car.luau
    │       │   ├── building.luau
    │       │   ├── shop.luau
    │       │   └── etc.
    │       └── forest/
    │           ├── item.luau
    │           ├── tree.luau
    │           ├── enemy.luau
    │           └── etc.
    ├── server/
    │   ├── main.server.luau
    │   └── modules/
    │       ├── networking.luau
    │       ├── gameLogic.luau
    │       ├── dataStore.luau
    │       └── etc.
    ├── shared/
    │   ├── util/
    │   │   ├── setup.luau
    │   │   └── etc.
    │   └── etc.
    └── packages/
        └── UpsideEngine

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/shader-guide/BasicEffect.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/shader-guide/BasicEffect.md "View source of this page")

# Basic effect

On this page, you will explore the fundamentals of how a shader works and discover techniques for creating a variety of interesting effects. By understanding the underlying principles, you'll be able to enhance your projects with visually stunning elements.

### How it works¶

When we create a function for our shader, we are creating a function that is executed for each pixel of an image. This function allows us to manipulate various parameters, to achieve the desired visual effects, such as color and position.

Upside Engine provides a typed table containing all the necessary variables. This approach simplifies the function interface by eliminating the need for numerous parameters, as the table's structure ensures that all required variables are clearly defined and accessible.

### Shader variables¶

  * red  Represents the red intensity of the pixel, with a value ranging from 0 to 255.
  * green  Represents the green intensity of the pixel, with a value ranging from 0 to 255.
  * blue  Represents the blue intensity of the pixel, with a value ranging from 0 to 255.
  * opacity  Represents the opacity of the pixel, with a value ranging from 0 to 255.
  * x  Represents the position of the pixel on the X-axis.
  * y  Represents the position of the pixel on the Y-axis.

### Inverting the color of our image¶

If we want to invert the colors of our image, we just need to return to our `Shader.luau` script and write the following code:

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local packages = replicatedStorage:WaitForChild("packages")
    local upsideEngine = require(packages:WaitForChild("UpsideEngine"))

    @native
    local function shadingFunction(params: upsideEngine.ShadingParams)
        params.red = 255 - params.red
        params.green = 255 - params.green
        params.blue = 255 - params.blue
    end

    return shadingFunction

Since all values range from 0 to 255, subtracting each color value from 255 inverts the pixel's color for that channel.

### Water Shader¶

Let's take it a step further and create an interesting effect, such as a water effect. We can achieve this using the following code:

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local packages = replicatedStorage:WaitForChild("packages")
    local upsideEngine = require(packages:WaitForChild("UpsideEngine"))

    @native
    local function shadingFunction(params: upsideEngine.ShadingParams)
        local clock = os.clock()
        local speed = 10

        local amplitude = 0.1
        local waveSize = 1

        local timeFactor = clock * speed
        local offset = params.x * amplitude + params.y * amplitude

        params.x += math.sin(timeFactor + offset) * waveSize
        params.y += math.cos(timeFactor + offset) * waveSize
    end

    return shadingFunction

`clock` continuously increases, causing our shader to move constantly. The `x` and `y` variables help the shader move diagonally, adding more realism to the effect. If we only used `x`, the shader would move horizontally, and similarly, if we only used `y`, it would move vertically.

`math.sin` and `math.cos` are functions that return values ranging from -1 to 1. These functions help create the wave patterns that give the water effect its characteristic movement.

As you can see at the bottom of the image, there is a blue gradient that seems unrelated to our image. But why does this happen? When we modify the position of a pixel in our shader, gaps can appear in the image. Upside Engine automatically fills these gaps in a way that is not noticeable, which results in this effect.

Fortunately, there is a property that allows us to fix this issue: `Precision`. The values for this property should be between 0 and 1. Adjust the value as needed to ensure your shader appears as expected. For example, changing `Precision` to `0.75` resolves the problem.

##### Good job, you finished the first steps to create a basic effect with a Shader!¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/TextTag.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/TextTag.md "View source of this page")

# [Extended from BaseClass](https://notreux.github.io/UpsideEngine/documentation/autogen/BaseClass.md) TextTag¶

Class representing a text tag for UpsideEngine.

# Properties¶

## [string](https://notreux.github.io/UpsideEngine/documentation/autogen/string.md) TagName¶

The tag name that defines a rendering style.

## [string](https://notreux.github.io/UpsideEngine/documentation/autogen/string.md) DisplayMode¶

Display mode, either "letter" (character by character) or "word" (word by word).

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) TypingSoundVolume¶

Volume of the typing sound.

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) WordsPerSecond¶

Typing speed expressed in words per second.

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) IdleTime¶

Idle time.

# Methods¶

There is no methods for this class

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/Light.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/Light.md "View source of this page")

# [Extended from StaticObject](https://notreux.github.io/UpsideEngine/documentation/autogen/StaticObject.html) Light¶

This class is used to illuminate areas in the darkness

# Properties¶

## [string](https://notreux.github.io/UpsideEngine/documentation/autogen/string.md) Shape¶

Is how the light should be shown, there are two modes "PointLight" and "SpotLight"

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Rotation¶

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Angle¶

## [Color3](https://notreux.github.io/UpsideEngine/documentation/autogen/Color3.md) Color¶

The color you want the light to have

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Range¶

Is the range of the light

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Brightness¶

Is the brightness of the light

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) Inverted¶

Warning

Only works on Pointlights

Inverts the light source

# Methods¶

There is no methods for this class

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/proximity-prompt/Introduction.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/proximity-prompt/Introduction.md "View source of this page")

# Introduction

### ProximityPrompt2D¶

The `ProximityPrompt2D` is a UI component in the Upside Engine that allows players to interact with in-game objects when they're nearby, using a customizable 2D interface.

### How it works¶

To create a `ProximityPrompt2D`, you instantiate it using the Upside Engine:

    local ProximityPrompt = UpsideEngine.new("ProximityPrompt2D")

Once created, you must define its visual behavior and connection to your scene:

    ProximityPrompt:SetScene(Scene)
    ProximityPrompt.Instance.Position = UDim2.new(0, 0, 0, -250)
    ProximityPrompt.Range = 500

To attach the prompt to a specific object (so it follows its position), use:

    ProximityPrompt:Attach(car)

To detach it:

    ProximityPrompt:Detach()

### Key Properties¶

  * **Range : `number`** Maximum distance at which the prompt becomes visible (default: `100`).

  * **ActionName : `string`** Identifier used to bind the prompt to a specific input key or control scheme. Defaults to `"Interact"`.

To configure which key triggers it, use:

    CrossPlatformService:SetDeviceKey("Keyboard", "T", "Interact")

  * **Enabled : `boolean`** Enables or disables the prompt entirely.

  * **ShowIfClosest : `boolean`** When true (default), the prompt only shows if it's the closest one to the player.

  * **FadeDuration : `number`** Time (in seconds) for the prompt to fade in or out when entering or leaving range.

  * **LabelPositions : `UDim2[]`** An array of `UDim2` values defining the position of the text label for each frame of the idle spritesheet animation. The label adjusts its position depending on the current frame.

  * **IsClosest : `boolean`** _(informative)_ Read-only value set by the system to indicate whether this is the closest prompt.

  * **InRange : `boolean`** _(informative)_ Read-only value set by the system to indicate if the player is within range.

### Customizing the Visuals¶

You can override the default idle animation by changing the spritesheet:

    ProximityPrompt:SetSpriteSheet("idle", url, Vector2.new(framesX, framesY))

Make sure the number of elements in `LabelPositions` matches the number of frames defined in the spritesheet.

### Example Usage¶

    local ProximityPrompt = UpsideEngine.new("ProximityPrompt2D")
    ProximityPrompt.Instance.Position = UDim2.new(0, 0, 0, -250)
    ProximityPrompt:SetScene(Scene)
    ProximityPrompt.Range = 500
    ProximityPrompt.ActionName = "OpenShop"

    CrossPlatformService:SetDeviceKey("Keyboard", "F", "OpenShop")

This configuration creates a prompt that becomes visible when the player is within 500 pixels and will trigger when the "F" key is pressed.

### Use Cases¶

  * Opening a door when the player gets close
  * Talking to an NPC
  * Interacting with a vendor, terminal, or sign

### Final Notes¶

  * You should not modify `IsClosest` or `InRange`; they are managed internally.
  * Always call `SetScene` to ensure the prompt is correctly added to the game scene.

##### With ProximityPrompt2D, bringing interactive UI to life in 2D games has never been easier!¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/plugin-guide/PluginScripts.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/plugin-guide/PluginScripts.md "View source of this page")

# Plugin Scripts

Now you are ready to use the plugin, but you can't run the game only with the plugin, so you will need to import upside engine and also some code.

## Import upside engine¶

Follow this steps to import upside engine

  * Create a folder in ReplicatedStorage and then name it as "packages"
  * Click on the button below to download the latest Upside Engine rbxm file
  * Place the upside engine module in the packages folder

[Download Upside Engine](https://github.com/notreux/UpsideEngine/releases/latest/download/UpsideEngine.rbxm)

## Essential code¶

Place this in StarterPlayerScripts as a local script to make your player move

    ------------- SETTINGS -------------
    local sceneName = "MyScene" -- Change "MyScene" for your scene name
    local characterName = "MyCharacter" -- Change "MyCharacter" for your character name
    local isPlatformer = true -- Change this to false if your game is not a platformer
    ------------------------------------
    local replicatedStorage = game:GetService("ReplicatedStorage")
    local playerGui = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")

    local packages = replicatedStorage:WaitForChild("packages")
    local upsideEngine = require(packages:WaitForChild("UpsideEngine"))

    local sceneManager = upsideEngine.GetService("SceneManager")
    local crossPlatformService = upsideEngine.GetService("CrossPlatformService")
    local pluginSupportService = upsideEngine.GetService("PluginSupportService")
    pluginSupportService:LoadPluginContent()

    local scene = sceneManager:FindByName(sceneName)
    local character = scene.Objects:FindByName(characterName)

    crossPlatformService.SideView = isPlatformer
    crossPlatformService:SetPlayerCharacter(character)
    scene.Camera:SetSubject(character)
    scene:Enable()

    local screenGui = Instance.new("ScreenGui")
    screenGui.IgnoreGuiInset = true
    screenGui.ResetOnSpawn = false
    screenGui.Parent = playerGui
    screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Global
    scene.Instance.Parent = screenGui

##### Congratulations! you are true master of upside engine 🎉🎉!!¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/lighting-guide/Spotlight.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/lighting-guide/Spotlight.md "View source of this page")

# Spotlight¶

Spotlights create a focused beam of light. In this section, we create a spotlight and add interactivity by making it follow the mouse cursor and rotate over time.

    local spotlight = UpsideEngine.new("Light")
    spotlight:SetScene(scene)
    spotlight.Shape = "spotlight"               -- Set the light's shape to "spotlight" to simulate
                                                -- a directional beam.
    spotlight.Range = 500
    spotlight.Angle = 100                       -- Set the angle (in degrees) of the spotlight's beam
                                                -- smaller angle results in a narrower, more focused beam.
    spotlight.Color = Color3.fromRGB(0, 0, 255)

    RunService.Heartbeat:Connect(function(dt)
        spotlight.Instance.Position = UDim2.fromOffset(mouse.X, mouse.Y)
        spotlight.Rotation = spotlight.Rotation + 100 * dt -- Rotate the spotlight continuously
    end)

With these three sections, You have learned how the UpsideEngine lighting system works. Experiment with the properties and behavior of the lights to best suit your game's esthetic and interactive needs.

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/SoundEnvironment.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/SoundEnvironment.md "View source of this page")

# [Extended from Environment](https://notreux.github.io/UpsideEngine/documentation/autogen/Environment.html) SoundEnvironment¶

# Properties¶

# Methods¶

There is no methods for this class

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/datatypes/Raycast2DParams.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/datatypes/Raycast2DParams.md "View source of this page")

# Raycast2DParams¶

The parameters for a raycast operation

# Properties¶

        FilterType -> "Whitelist", -- Whitelist/Blacklist
        From -> Vector2.new(),
        To -> Vector2.new(),
        List -> { ... } -- Dictionary<objectID, object>

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/replication-guide/AuthoritySystem.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/replication-guide/AuthoritySystem.md "View source of this page")

# Authority System¶

The Authority System is one of the most important features introduced in Upside Engine v3.1.0. It determines who has control over objects and prevents conflicts in multiplayer environments.

## What is authority?¶

Authority determines which entity (client or server) has the right to modify an object and send updates. Think of it as "ownership" or "control rights" over an object.

## Authority types¶

There are two types of authority:

Authority | Description | Can modify object | Updates are sent
---|---|---|---
`"Server"` | Server controls the object | Only server | Server → Clients
`"Client"` | Client controls the object | Specific client + server | Client → Server → Other clients

## How authority works¶

### Default behavior¶

When the server accepts the first replication request, **the server automatically takes authority**:

Server Code - Default Behavior

    -- Server code
    networkingService:On("ReplicationRequest", function(request)
        local object = request:Accept()
        -- At this point, server has authority
        -- ReplicationRequest won't fire again for this object!
    end)

Important

Without client authority, `ReplicationRequest` fires **only once** (when the object is created). The client cannot send more replication updates.

### Granting client authority¶

To allow clients to continue sending replication updates, you must explicitly grant authority:

Server Code - Grant Authority

    -- Server code
    networkingService:On("ReplicationRequest", function(request)
        local object = request:Accept()
        authorityService:SetAuthority(object, "Client")
        -- Now ReplicationRequest will fire on every property change
        -- You must continue to accept each request!
    end)

With Client Authority

With client authority, `ReplicationRequest` fires **on every property change**. You must call `Accept()` each time to apply the changes.

## Practical examples¶

### Example 1: Player characters (Client authority)¶

Players should control their own characters:

Server Code - Player Characters

    -- Server code
    networkingService:On("ReplicationRequest", function(request)
        if request.Content.ClassName == "Character" then
            local character = request:Accept()
            -- Give control back to the client
            authorityService:SetAuthority(character, "Client")
        end
    end)

Result

  * Players can move freely
  * Movement is synchronized to all clients
  * Smooth multiplayer experience

### Example 2: Server-controlled NPCs (Server authority)¶

NPCs should be controlled by the server:

Server Code - NPCs

    -- Server code
    local npc = UpsideEngine.new("Character")
    npc:SetScene(scene)
    -- Configure NPC...

    -- Server objects replicate automatically to all clients
    -- No need to call ReplicateOnChange() - it's automatic!
    -- Server keeps authority (default)
    -- Server can move NPC, clients just see updates

Result

  * Server controls NPC behavior
  * Clients can't manipulate NPCs
  * Prevents cheating
  * Automatic replication to all clients

### Example 3: Temporary server control¶

Sometimes you need to temporarily take control from a client:

Server Code - Temporary Control

    -- Server code
    local function applyKnockback(character, force)
        -- Take temporary control
        authorityService:SetAuthority(character, "Server")

        -- Apply physics force
        character:ApplyForce(force)

        -- Let physics settle
        task.wait(0.5)

        -- Give control back to client
        authorityService:SetAuthority(character, "Client")
    end

Use cases

  * Knockback effects
  * Server-validated teleportation
  * Forced movement (wind, conveyor belts)
  * Stuns or crowd control effects

## Authority and the replication flow¶

Let's see the complete flow with authority:

### For client-created objects:¶

    1. CLIENT creates object and calls ReplicateOnChange()
       │
       ├──> ReplicationRequest sent to server (first time)
       │
    2. SERVER receives ReplicationRequest event
       │
       ├──> request:Accept() called
       │
       ├──> Object created on server
       │
       ├──> ⚠ Server automatically has authority
       │
       ├──> authorityService:SetAuthority(object, "Client")
       │
       ├──> ✅ Client now has authority
       │
    3. CLIENT modifies a property
       │
       ├──> ReplicationRequest sent to server (with changes)
       │
    4. SERVER receives ReplicationRequest event again
       │
       ├──> request:Accept() called
       │
       ├──> Changes applied
       │
       ├──> Server distributes changes to all other clients
       │
    5. REPEAT steps 3-4 for every property change
       │
       ├──> Each change triggers new ReplicationRequest
       │
       ├──> Server must Accept() each one

### For server-created objects:¶

    1. SERVER creates object
       │
       ├──> Object automatically replicates to all clients
       │
       ├──> Server has authority (default)
       │
    2. SERVER modifies a property
       │
       ├──> Changes automatically replicate to all clients
       │
       ├──> No ReplicationRequest events

## Checking authority¶

You can check who has authority over an object:

Check Authority

    local authority = authorityService:GetAuthority(object)
    -- Returns "Server" or "Client"

    if authority == "Server" then
        print("Server controls this object")
    elseif authority == "Client" then
        print("Client controls this object")
    end

## Common mistakes¶

### ❌ Mistake 1: Forgetting to set authority¶

Server Code - WRONG!

    -- Server code - WRONG!
    networkingService:On("ReplicationRequest", function(request)
        request:Accept()
        -- No authority set - client can't update!
    end)

Problem

Players can't move, or only they see their own movement locally.

### ❌ Mistake 2: Setting authority on the wrong side¶

Client Code - WRONG!

    -- Client code - WRONG!
    authorityService:SetAuthority(character, "Client")
    -- This does nothing! Authority can only be set on the server

Problem

Authority management is server-only. Clients can't set authority.

### ❌ Mistake 3: Constantly switching authority¶

Server Code - WRONG!

    -- Server code - WRONG!
    while true do
        authorityService:SetAuthority(object, "Server")
        task.wait(0.1)
        authorityService:SetAuthority(object, "Client")
        task.wait(0.1)
    end

Problem

Creates synchronization issues and lag. Set authority once and only change it when necessary.

## Best practices¶

Best Practices

  1. **Set authority once**: Set it in the `ReplicationRequest` handler and leave it

  2. **Client authority for player objects**: Players should control their own characters

  3. **Server authority for game objects**: NPCs, items, and world objects should be server-controlled

  4. **Temporary server control is okay**: It's fine to temporarily take control for specific events

  5. **Don't overthink it**: For most games, simply giving clients authority over their characters is enough

## Authority and security¶

The authority system is designed to prevent unauthorized modifications:

Security Benefits

  * Clients can only send updates for objects they have authority over
  * The server validates that the client has authority before accepting updates
  * Other clients cannot modify objects they don't have authority over

This means:

  * Players can't modify other players' characters
  * Players can't modify server-controlled objects
  * Server has final say on authority assignments

## Summary¶

Scenario | Authority | Who sets it | When to set it | ReplicationRequest fires | Replication type
---|---|---|---|---|---
Player character | Client | Server | After first Accept() | On every property change | Client → Server → Clients
Server NPC | Server | Default | No action needed | No | Server → Clients (automatic)
Temporary control | Server ↔ Client | Server | As needed | Depends on current authority | Varies
World objects | Server | Default | No action needed | No | Server → Clients (automatic)

Remember

  * Server objects replicate automatically - no `ReplicateOnChange()` needed
  * Client objects need `ReplicateOnChange()` to send updates to server
  * Authority determines how often `ReplicationRequest` fires (for client objects only)
  * Without client authority: Fires **once** (object creation)
  * With client authority: Fires **on every property change**
  * You must call `Accept()` on **every** request to apply changes
  * Set authority on the server, after accepting the first request

**Next:** [Practical Example](https://notreux.github.io/UpsideEngine/tutorials/replication-guide/PracticalExample.html)

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/Environment.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/Environment.md "View source of this page")

# [Extended from BaseObject](https://notreux.github.io/UpsideEngine/documentation/autogen/BaseObject.html) Environment¶

This class is used to storage objects and interact with them in an easier way

# Properties¶

## [table](https://notreux.github.io/UpsideEngine/documentation/autogen/table.md) Content¶

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Count¶

The amount of objects in this environment

# Methods¶

## [any](https://notreux.github.io/UpsideEngine/documentation/autogen/any.link) Get(`index: string`)¶

Gets the object with the specified index

## [void](https://notreux.github.io/UpsideEngine/documentation/autogen/any.link) SetOne(`value: any, index: string`)¶

Adds an object with the specified index, if no parameter is specified it will use the next number of the count property as index

## [void](https://notreux.github.io/UpsideEngine/documentation/autogen/any.link) AddOne(`value: any, index: string`)¶

Adds an object with the specified index, if no parameter is specified it will use the next number of the count property as index

## [void](https://create.roblox.com/docs/scripting/luau/nil) RemoveOne(`index: string`)¶

Removes the object with the specified index

## [boolean](https://create.roblox.com/docs/scripting/luau/booleans) HasOne(`index: string`)¶

Checks if the environment has a value with the specified index

## [(boolean, Dictionary)](https://create.roblox.com/docs/scripting/luau/booleans) Has(`objects: Array<string>`)¶

Checks if the environment contains every specified index and returns a boolean and a dictionary with boolean values, example:

    local hasAll, dictionary = treeEnv:Has({
        "Tree1",
        "Tree2",
        "Tree4"
    })

    print(hasAll, dictionary) -- output: false, { Tree1 = true, Tree2 = true, Tree4 = false }

## [void](https://create.roblox.com/docs/scripting/luau/nil) Add(`objects: Dictionary<string, any>`)¶

Adds objects with the specified index

    treeEnv:Add({
        "Tree1" = tree.new(),
        "Tree2" = tree.new(),
        "Tree3" = tree.new()
    })

    print(treeEnv:Get("Tree2")) -- output: Tree2

## [void](https://create.roblox.com/docs/scripting/luau/nil) Remove(`objects: Array<string>`)¶

Removes the objects with the specified index, example:

    treeEnv:Remove({ "Tree1", "Tree2", "Tree3" })
    print(treeEnv:Get("Tree2")) -- output: nil

## [void](https://create.roblox.com/docs/scripting/luau/nil) Update(`objects: Dictionary<string, any>`)¶

Updates the objects with the specified index, example:

    treeEnv:Update({
        Tree1 = treeEnv:Get("Tree2"),
        Tree2 = treeEnv:Get("Tree1"),
    })

## [any](https://notreux.github.io/UpsideEngine/documentation/autogen/any.link) FindByName(`name: string`)¶

Finds an object by his name

## [void](https://create.roblox.com/docs/scripting/luau/nil) SetProperty(`property: string, value: any`)¶

Sets the specified property in every object in the environment

## [void](https://create.roblox.com/docs/scripting/luau/nil) Run(`method: string, ...any`)¶

Executes the specified methods in every object in the environment with the specified parameters

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/plugin-guide/CollisionEditor.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/plugin-guide/CollisionEditor.md "View source of this page")

# Using collision editor

On this page, you will learn an essential tool to make better collisions for your game objects.

## Introduction to collision mask¶

Click on the Collision Editor button, and a new window named "Collision Editor" will appear. In this interface, you can easily create a collision mask.

## A basic usage¶

Select an object, and to start from scratch Let's remove the whole collision mask, for this we will right click 4 times on every red point.

Now we can draw our collision mask, Let's draw a mask around the object, for this we will use left click.

##### Congratulations! you are ready create collision masks, let's move on to the next page 🎉🎉¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/Character.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/Character.md "View source of this page")

# [Extended from Sprite](https://notreux.github.io/UpsideEngine/documentation/autogen/Sprite.html) Character¶

This class is used for the player character and for npcs

# Properties¶

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) Anchored¶

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Health¶

The amount of health of the character

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) MaxHealth¶

The maximum amount of health of the character

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) WalkSpeed¶

The walk speed of the character

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) JumpPower¶

The jump power of the character

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) IsJumping¶

Set to true when the character is jumping

# Methods¶

## [void](https://create.roblox.com/docs/scripting/luau/nil) UpdateHealth(`Health: number`)¶

Updates the amount of health of the character

## [void](https://create.roblox.com/docs/scripting/luau/nil) Jump(`jumpPower: number?`)¶

The character jumps with the provided jump power, if none is provided it will use the JumpPower property as value

## [void](https://create.roblox.com/docs/scripting/luau/nil) MoveTo(`target: Vector2`)¶

The character walks directly to the provided position

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/ProximityPrompt2D.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/ProximityPrompt2D.md "View source of this page")

# [Extended from Sprite](https://notreux.github.io/UpsideEngine/documentation/autogen/Sprite.html) ProximityPrompt2D¶

The proximity prompt is a UI element that will show a prompt when the player is close to it, this is used to interact with objects in the game, it will show a label and a hitbox that will be used to detect when the player is close to it

# Properties¶

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) Range¶

The range of the prompt, this will be used to detect when the prompt should be shown

## [string](https://notreux.github.io/UpsideEngine/documentation/autogen/string.md) ActionName¶

The action name of the prompt, this will be used to detect when the prompt is triggered, if you change the default action name you should change or add a new action in the CrossPlatformService, 'Interact' by default

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) Enabled¶

If the prompt is enabled or not, if it is not enabled the prompt will not be shown

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) IsClosest¶

True if the proximity prompt is the closest one to the player

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) InRange¶

True if the proximity prompt is visible on the player's screen. Useful for detecting when the prompt is within interaction range

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) ShowIfClosest¶

Determines whether this proximity prompt should only be shown when it is the closest one to the player. If set to true, the prompt will only appear if it is the nearest among all prompts within interaction range. If set to false, the prompt will be shown as long as it is within range, regardless of other nearby prompts. The property is enabled by default

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) FadeDuration¶

The duration of the fade in and out of the prompt, this will be used to make the prompt appear and disappear smoothly

## [Instance](https://notreux.github.io/UpsideEngine/documentation/autogen/Instance.md) Label¶

The text label of the prompt, this will be shown when the prompt is active

## [Instance](https://notreux.github.io/UpsideEngine/documentation/autogen/Instance.md) HitboxButton¶

The button that will be used to trigger the proximity prompt on mobile devices

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) SecondsPerFrame¶

## [string](https://notreux.github.io/UpsideEngine/documentation/autogen/string.md) Name¶

## [table](https://notreux.github.io/UpsideEngine/documentation/autogen/table.md) LabelPositions¶

A table containing the positions(as UDim2) of the label, this will be used to adjust the text to the proximity prompt sprites, position 0 will be the position on the first frame, position 1 will be the position on the second frame, etc.

# Methods¶

There is no methods for this class

# Events¶

Name | Description
Triggered | Fired when the proximity prompt is triggered. This occurs when the player begins interacting with the prompt.

TriggerEnded | Fired when the proximity prompt interaction ends. This occurs when the player releases the interaction.

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/replication-guide/ServerSetup.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/replication-guide/ServerSetup.md "View source of this page")

# Server Setup¶

The server plays a crucial role in replication - it validates requests, manages authority, and distributes updates to all clients. Let's set up a proper server for your multiplayer game.

## Understanding server replication¶

Before we dive into the code, it's important to understand two types of replication:

Replication Types

  1. **Client-to-Server replication**: When clients create objects (like player characters) and want to share them with other players, they use `ReplicateOnChange()`. The server receives `ReplicationRequest` events and must accept them.

  2. **Server-to-Clients replication**: When the server creates objects (like NPCs, world objects, etc.), they automatically replicate to all clients. No `ReplicateOnChange()` needed - it's automatic!

This guide focuses on handling client-to-server replication requests.

## Basic server structure¶

Here's a minimal server setup:

Basic Server Script

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Players = game:GetService("Players")

    local UpsideEngine = require(ReplicatedStorage.Packages.UpsideEngine)
    local networkingService = UpsideEngine.GetService("NetworkingService")
    local authorityService = UpsideEngine.GetService("AuthorityService")

    -- Handle replication requests from clients
    networkingService:On("ReplicationRequest", function(request)
        -- Accept the request and create the object
        local object = request:Accept()

        -- CRITICAL: Grant authority to the client
        -- Without this, the client won't be able to update the object!
        authorityService:SetAuthority(object, "Client")
    end)

That's It!

**This is all you need for basic multiplayer functionality!**

## Understanding the request object¶

The `request` parameter contains important information:

Request Object Properties

    networkingService:On("ReplicationRequest", function(request)
        -- Get information about the request
        local clientId = request.ClientId        -- The UserId of the player
        local content = request.Content          -- Information about the object
        local className = content.ClassName      -- Type of object ("Character", "StaticObject", etc.)
        local instance = content.Instance        -- The Roblox instance data

        -- Accept the request
        local object = request:Accept()
    end)

## Why you must set authority¶

Let me explain why setting authority is critical with a comparison:

❌ Without authority assignment✅ With authority assignment

    networkingService:On("ReplicationRequest", function(request)
        request:Accept()
        -- Server now has authority
    end)

**What happens:**

  1. Client sends first replication → `ReplicationRequest` fires → Object created
  2. Client moves → No `ReplicationRequest` sent (server has authority)
  3. `ReplicationRequest` event never fires again
  4. Other players see a frozen character
  5. Only the local player sees movement (not synchronized)

    networkingService:On("ReplicationRequest", function(request)
        local object = request:Accept()
        authorityService:SetAuthority(object, "Client")
        -- Client now has authority
    end)

**What happens:**

  1. Client sends first replication → `ReplicationRequest` fires → Object created + Authority granted
  2. Client moves → `ReplicationRequest` fires again with new position
  3. Server calls `Accept()` → Changes applied and distributed to all clients
  4. Every property change → New `ReplicationRequest` → You accept it
  5. All players see smooth synchronized movement

The key point

  * When the server calls `request:Accept()` on the first request, it automatically takes authority
  * Without client authority: `ReplicationRequest` fires **only once** (object creation)
  * With client authority: `ReplicationRequest` fires **every time the client changes a property**
  * You must call `Accept()` on each request to apply the changes

## Tracking players and their objects¶

A better approach is to keep track of each player's objects:

Enhanced Server Script with Tracking

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Players = game:GetService("Players")

    local UpsideEngine = require(ReplicatedStorage.Packages.UpsideEngine)
    local networkingService = UpsideEngine.GetService("NetworkingService")
    local authorityService = UpsideEngine.GetService("AuthorityService")

    -- Store each player's character
    local characters = {}

    networkingService:On("ReplicationRequest", function(request)
        local content = request.Content
        local player = Players:GetPlayerByUserId(request.ClientId)

        -- Check if this is a character
        if content.ClassName == "Character" then
            local character = request:Accept()

            -- Only grant authority if this is a new character for this player
            if not characters[player] then
                authorityService:SetAuthority(character, "Client")
                characters[player] = character

                print(player.Name .. "'s character replicated successfully")
            end
        else
            -- For other object types, accept and grant authority
            local object = request:Accept()
            authorityService:SetAuthority(object, "Client")
        end
    end)

    -- Clean up when players leave
    Players.PlayerRemoving:Connect(function(player)
        characters[player] = nil
        print(player.Name .. " left the game")
    end)

## Managing the scene on the server¶

You can also set up a scene on the server for server-side logic:

Server Scene Setup

    local sceneManager = UpsideEngine.GetService("SceneManager")
    local scene = sceneManager:FindByName("game") -- Find your scene

    -- Wait for the scene to be ready
    while not scene do
        scene = sceneManager:FindByName("game")
        task.wait()
    end

    -- Now you can do server-side operations
    print("Server scene is ready!")

## Complete server example¶

Here's a complete, production-ready server setup:

Complete Server Script

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Players = game:GetService("Players")

    local UpsideEngine = require(ReplicatedStorage.Packages.UpsideEngine)
    local networkingService = UpsideEngine.GetService("NetworkingService")
    local authorityService = UpsideEngine.GetService("AuthorityService")
    local sceneManager = UpsideEngine.GetService("SceneManager")

    -- State tracking
    local characters = {}
    local scene = nil

    -- Initialize server
    local function initialize()
        -- Find the game scene
        scene = sceneManager:FindByName("game")
        while not scene do
            scene = sceneManager:FindByName("game")
            task.wait()
        end

        print("Server initialized successfully")
    end

    -- Handle replication requests
    networkingService:On("ReplicationRequest", function(request)
        local content = request.Content
        local player = Players:GetPlayerByUserId(request.ClientId)

        if not player then
            print("Warning: Received request from unknown player")
            return
        end

        -- Accept the object
        local object = request:Accept()

        -- Handle different object types
        if content.ClassName == "Character" then
            -- Track the player's character
            if not characters[player] then
                authorityService:SetAuthority(object, "Client")
                characters[player] = object
                print(player.Name .. " joined the game")
            end
        else
            -- For other objects, grant authority
            authorityService:SetAuthority(object, "Client")
        end
    end)

    -- Handle player disconnections
    Players.PlayerRemoving:Connect(function(player)
        characters[player] = nil
        print(player.Name .. " left the game")
    end)

    -- Start the server
    initialize()

## Important notes¶

ReplicationRequest Behavior

  1. **ReplicationRequest behavior depends on authority**:
  2. Without client authority: Fires **once** (object creation only)
  3. With client authority: Fires **on every property change**

  4. **You must accept every request**: Call `request:Accept()` on every `ReplicationRequest` to apply the changes

  5. **Authority must be set on the first request**: Call `SetAuthority()` after accepting the first request to enable continuous replication

  6. **Server has authority by default**: After calling `request:Accept()` on the first request, the server automatically takes authority

  7. **Don't reject requests unnecessarily**: Every time you don't call `Accept()`, those changes are discarded

## What about validation?¶

You might be wondering: "Should I validate player positions or check for exploits?"

Validation Guidelines

While you _can_ add validation, it's often better to:

  * Trust the client for basic movement
  * Use the authority system to prevent unauthorized modifications
  * Implement server-side validation only for critical actions (damage, item pickup, etc.)
  * Consider player experience - aggressive validation can cause lag or false positives

If you need validation, do it for specific actions using RemoteEvents, not for every position update.

**Next:** [Authority System](https://notreux.github.io/UpsideEngine/tutorials/replication-guide/AuthoritySystem.html)

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/CrossPlatformService.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/CrossPlatformService.md "View source of this page")

# [Extended from EventEmitter](https://notreux.github.io/UpsideEngine/documentation/autogen/EventEmitter.html) CrossPlatformService¶

This class is used to make the controls functional in any device (Keyboards, Mobiles, Gamepads), this service vinculate actions to specified keys, and also provides a movement system for the player character which can be disabled with the `DefaultControllersEnabled` property, here is an example to make our player jumps in every device:

    --                                  Device,     Key,    Action
    CrossPlatformService:SetDeviceKey("Keyboard", "Space", "Up")
    CrossPlatformService:SetDeviceKey("Mobile", "JumpButton", "Up")
    CrossPlatformService:SetDeviceKey("Gamepad", "ButtonA", "Up")

But this is not limited only to movement actions, you can also assign other kind of actions, for example:

    CrossPlatformService:SetDeviceKey("Keyboard", "E", "Collect")
    CrossPlatformService:SetDeviceKey("Mobile", "JumpButton", "Collect")
    CrossPlatformService:SetDeviceKey("Gamepad", "ButtonA", "Collect")

We assigned an action for our devices but how can we detect when an action is triggered? well we can listen to three events "InputBegin", "InputChange", "InputEnd", example of use:

    -- If the movement belongs to a stick, the second parameter will give the current position of the stick
    CrossPlatformService:On("InputBegin", function(inputObject)
        local character = CrossPlatformService.Character

        if inputObject.Action == "Up" then
            character:Jump(150)
        end
    end)

# Properties¶

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) DefaultControllersEnabled¶

Defines if the default movement system is enabled

## [boolean](https://notreux.github.io/UpsideEngine/documentation/autogen/boolean.md) SideView¶

Defines if the character is going to be seen from the side or from the top

## [number](https://notreux.github.io/UpsideEngine/documentation/autogen/number.md) StickSensibility¶

This is the sensibility of the sticks in mobile and in game controllers

## [CharacterDestroyConnection](https://notreux.github.io/UpsideEngine/documentation/autogen/CharacterDestroyConnection.md) CharacterDestroyConnection¶

## [table](https://notreux.github.io/UpsideEngine/documentation/autogen/table.md) Configs¶

This table stores the default controllers

        ["Keyboard"]: {
            ["W"]: string,
            ["A"]: string,
            ["S"]: string,
            ["D"]: string,
            ["Up"]: string,
            ["Left"]: string,
            ["Down"]: string,
            ["Right"]: string,
            ["Space"]: string,
            ["E"]: string,
        },
        ["Gamepad"]: {
            ["ButtonA"]: string,
            ["ButtonX"]: string,
            ["Thumbstick1"]: {
                ["Up"]: string,
                ["Left"]: string,
                ["Down"]: string,
                ["Right"]: string,
            },
        },
        ["Mobile"]: {
            ["JumpButton"]: string,
            ["Thumbstick1"]: {
                ["Up"]: string,
                ["Left"]: string,
                ["Down"]: string,
                ["Right"]: string,
            },
        },

# Methods¶

## [string](https://create.roblox.com/docs/reference/engine/libraries/string) DetectCurrentDevice()¶

Detects the current device type based on user input capabilities

## [string?](https://create.roblox.com/docs/reference/engine/libraries/string) GetTargetActionKey(`action: string`)¶

Returns the current key for a specific action based on the current device (Mobile, Gamepad, or Keyboard)

## [void](https://create.roblox.com/docs/scripting/luau/nil) SetDeviceKey(`device: string, key: string, action: string`)¶

Assigns an action to a device key, example:

    CrossPlatformService:SetDeviceKey("Keyboard", "Space", "Up")

## [void](https://create.roblox.com/docs/scripting/luau/nil) SetDeviceConfig(`device: string, controls: Dictionary<string, string>`)¶

Sets the entire configuration of a device, example:

    CrossPlatformService:SetDeviceConfig("Keyboard", {
        W = "Up",
        A = "Left",
        S = "Down",
        D = "Right",

        Up = "Up",
        Left = "Left",
        Down = "Down",
        Right = "Right",
        Space = "Up",
    })

## [void](https://create.roblox.com/docs/scripting/luau/nil) SetPlayerCharacter(`character: Character`)¶

Sets the player character

# Events¶

Name | Description
InputBegin | Params -> [UpsideEngineInput](https://notreux.github.io/documentation/datatypes/UpsideEngineInput.html)
Fired when one of the keys/sticks in the configuration is pressed/moved

InputChange | Params -> [UpsideEngineInput](https://notreux.github.io/documentation/datatypes/UpsideEngineInput.html)
Fired when the an active input change its value, for example the position of a stick

InputEnd | Params -> [UpsideEngineInput](https://notreux.github.io/documentation/datatypes/UpsideEngineInput.html)
Fired when one of the keys/sticks in the configuration finish to be pressed/moved

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/replication-guide/HowItWorks.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/replication-guide/HowItWorks.md "View source of this page")

# How Replication Works¶

Understanding the replication flow is crucial for building robust multiplayer games. Let's break down exactly what happens when you replicate an object.

## The replication flow¶

Here's the complete flow of how replication works in Upside Engine v3.1.0:

### Client to Server replication (Client-created objects)¶

    CLIENT                          SERVER                          OTHER CLIENTS
      │                               │                                    │
      ├─ Create object                │                                    │
      ├─ ReplicateOnChange()          │                                    │
      │   (Tells server about object) │                                    │
      │                               │                                    │
      ├──── ReplicationRequest ──────>│                                    │
      │     (First time - new object) │                                    │
      │                               ├─ ReplicationRequest event fires    │
      │                               ├─ request:Accept()                  │
      │                               ├─ Object created on server          │
      │                               ├─ Server takes authority            │
      │                               │                                    │
      │                               ├──── Replicate to all clients ─────>│
      │                               │                                    ├─ Build event fires
      │                               │                                    ├─ Object created
      │                               │                                    │
      │         ⚠ PROBLEM: Client can't send more updates!                │
      │         ReplicationRequest won't fire again without authority      │
      │                               │                                    │
      │                               ├─ SetAuthority(object, "Client")    │
      │                               │   ✅ NOW client can send updates   │
      │                               │                                    │
      ├──── ReplicationRequest ──────>│                                    │
      │     (Every property change!)  ├─ ReplicationRequest event fires    │
      │                               ├─ request:Accept()                  │
      │                               ├──── Updates to all clients ───────>│
      │                               │                                    │
      ├──── ReplicationRequest ──────>│                                    │
      │     (Another change)          ├─ ReplicationRequest event fires    │
      │                               ├─ request:Accept()                  │
      │                               ├──── Updates to all clients ───────>│
      │                               │                                    │

### Server to Client replication (Server-created objects)¶

    SERVER                          ALL CLIENTS
      │                                    │
      ├─ Create object                     │
      │  (Automatically replicates)        │
      │                                    │
      ├───── Replicate to all clients ────>│
      │                                    ├─ Build event fires
      │                                    ├─ Object created
      │                                    │
      ├─ Modify object property            │
      │  (Automatically replicates)        │
      │                                    │
      ├───── Updates to all clients ──────>│
      │                                    ├─ Property updated
      │                                    │

Important

Server objects replicate to clients automatically by default - no `ReplicateOnChange()` needed!

## Key concepts¶

### 1. ReplicateOnChange (Client-side)¶

`ReplicateOnChange()` is used by **clients** to tell the server about objects they want to replicate:

Client Code

    -- Client code
    local character = UpsideEngine.new("Character")
    character:SetScene(scene)
    -- Tell server to replicate this object
    networkingService:ReplicateOnChange(character)

Server Objects

Server objects replicate automatically without calling `ReplicateOnChange()`.

### 2. ReplicationRequest (Server-side only)¶

The `ReplicationRequest` event is **only fired on the server** when a client sends replication data.

Server Code - ONLY works on the server!

    -- This ONLY works on the server!
    networkingService:On("ReplicationRequest", function(request)
        -- You must accept each request
        request:Accept()
    end)

Critical Understanding

  * **Without client authority**: `ReplicationRequest` fires **only once** (when the object is first created)
  * **With client authority**: `ReplicationRequest` fires **on every property change** that the client sends

### 3. How ReplicationRequest behavior changes with authority¶

This is the most important concept to understand:

Without client authority (default)With client authority

    networkingService:On("ReplicationRequest", function(request)
        request:Accept()
        -- Server has authority, ReplicationRequest won't fire again
    end)

  * First `ReplicationRequest` fires → Object created
  * No more `ReplicationRequest` events → Client can't update
  * Object appears frozen to other players

    networkingService:On("ReplicationRequest", function(request)
        local object = request:Accept()
        authorityService:SetAuthority(object, "Client")
        -- Client has authority, ReplicationRequest will fire on every change
    end)

  * First `ReplicationRequest` fires → Object created
  * Client modifies object → `ReplicationRequest` fires again
  * You call `request:Accept()` → Changes applied and distributed
  * Every property change → New `ReplicationRequest` → Must accept
  * Smooth continuous replication

### 4. Authority system¶

Authority determines who can send replication updates:

Authority Type | Who can send updates | ReplicationRequest behavior
---|---|---
`"Server"` | Only the server | Fires once (object creation only)
`"Client"` | Specific client + server | Fires on every property change

Critical Point

When the server accepts the first replication request of a new object, it **automatically takes authority**. You must explicitly grant authority to the client if you want them to continue sending replication updates.

## Why authority assignment is critical¶

Let's see what happens without proper authority management:

❌ BAD: Without Authority✅ GOOD: With Authority

    -- ReplicationRequest won't fire again after the first time
    networkingService:On("ReplicationRequest", function(request)
        request:Accept()
        -- Server has authority, ReplicationRequest won't fire again!
    end)

**What happens:**

  1. First `ReplicationRequest` fires → Object created
  2. Client modifies object → No `ReplicationRequest` sent
  3. `ReplicationRequest` event never fires again
  4. Player's character appears frozen to other players
  5. Player sees their own movement locally but others can't

    -- ReplicationRequest will fire on every property change
    networkingService:On("ReplicationRequest", function(request)
        local object = request:Accept()
        authorityService:SetAuthority(object, "Client")
        -- Now ReplicationRequest fires on every change!
    end)

**What happens:**

  1. First `ReplicationRequest` fires → Object created → Authority granted
  2. Client modifies object → `ReplicationRequest` fires again
  3. Server calls `Accept()` → Changes applied and distributed
  4. Every property change → New `ReplicationRequest` → You must accept it
  5. Smooth synchronized multiplayer experience

## Understanding the Accept method¶

The `request:Accept()` method is crucial - it's what actually applies the changes:

    networkingService:On("ReplicationRequest", function(request)
        -- This applies the changes from the client and distributes to other clients
        request:Accept()
    end)

Important

You must call `Accept()` on **every** `ReplicationRequest`:

  * **First request**: Creates the object
  * **Subsequent requests** (with client authority): Applies property changes
  * If you don't call `Accept()`, the changes are rejected and not applied

## Summary¶

Event/Method | Where it's used | When it's used | Purpose
---|---|---|---
`ReplicateOnChange()` | Client only | For client-created objects | Tell server to replicate object
`ReplicationRequest` | Server only | Once per new client object | Accept/reject replication
Automatic replication | Server only | For server-created objects | Objects replicate automatically
Property updates | Automatic | Continuous | Sync object changes

Remember

  * Client objects need `ReplicateOnChange()` to be sent to the server
  * Server objects replicate automatically - no `ReplicateOnChange()` needed
  * `ReplicationRequest` fires **once** per client object, not per update
  * Server gets authority by default after accepting for the first time
  * You **must** set client authority for continuous updates from client
  * Once authority is set, updates happen automatically

**Next:** [Client Setup](https://notreux.github.io/UpsideEngine/tutorials/replication-guide/ClientSetup.html)

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/EventEmitter.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/EventEmitter.md "View source of this page")

# [Extended from BaseClass](https://notreux.github.io/UpsideEngine/documentation/autogen/BaseClass.md) EventEmitter¶

The event emitter is used to manage the events of a class

# Properties¶

## [table](https://notreux.github.io/UpsideEngine/documentation/autogen/table.md) EventsStorage¶

This table store all the events of the class

# Methods¶

## [Connection](https://notreux.github.io/UpsideEngine/documentation/autogen/Connection.html) On(`name: string, callback: () -> any`)¶

## [Connection](https://notreux.github.io/UpsideEngine/documentation/autogen/Connection.html) Once(`name: string, callback: () -> any`)¶

## [void](https://create.roblox.com/docs/scripting/luau/nil) Fire(`name: string, ...any`)¶

Triggers an event with the specified arguments

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/shader-guide/FirstSteps.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/shader-guide/FirstSteps.md "View source of this page")

# First Steps

On this page, you will learn how a shader is created and how to assign it to your objects. To start Let's create a new script `guide.client.luau` in which we will place this code:

## A basic setup¶

Let's begin by setting up the essentials. We'll create a scene that will be our main workspace for building and refining our project in this guide.

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local players = game:GetService("Players")

    local packages = replicatedStorage.packages
    local playerGui = players.LocalPlayer:WaitForChild("PlayerGui")

    local upsideEngine = require(packages.UpsideEngine)
    local screen = Instance.new("ScreenGui")
    screen.Name = "Screen"
    screen.IgnoreGuiInset = true
    screen.Parent = playerGui

    local scene = upsideEngine.new("Scene")
    scene.Instance.Parent = screen
    scene:SetName("MyScene")
    scene:Enable()

## Assets¶

Roblox only allows you to modify the images uploaded by the owner of the experience, so you will have to download the assets we will work with in this guide.

## Creating our shader¶

First, we need to create a new Module Script that returns a function. Let's name it `Shader.luau`.

    return function()
        -- shader code
    end

Now, back in our main script "guide.client.luau", let's create the shader:

    local shader = upsideEngine.new("Shader")
    shader:SetSource(script.Parent.Shader) -- we set the shader source in the module script created previously

Next, let's create an object to apply our shader to:

    local water = upsideEngine.new("StaticObject")
    water:SetScene(scene)
    water:SetShader(shader)

    local instance = water.Instance
    instance.Image = "rbxassetid://waterId"
    instance.Size = UDim2.fromOffset(800, 800)

Let's add some decoration too:

    local terrain = upsideEngine.new("StaticObject")
    terrain:SetScene(scene)

    local instance = terrain.Instance
    instance.Image = "rbxassetid://terrainId"
    instance.Size = UDim2.fromOffset(800, 800)

##### Good job, you finished the first steps to create a shader!¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/BaseClass.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/BaseClass.md "View source of this page")

# BaseClass¶

All objects are based on this class

# Properties¶

## [readonly](https://notreux.github.io/UpsideEngine/documentation/BaseClass.html) [string](https://create.roblox.com/docs/reference/engine/libraries/string) _ClassName_¶

The name of the class

## [readonly](https://notreux.github.io/UpsideEngine/documentation/BaseClass.html) [string](https://create.roblox.com/docs/reference/engine/libraries/string) _Name_¶

The name of the object

## [string](https://create.roblox.com/docs/reference/engine/libraries/string) _Id_¶

A unique identifier for each object

# Methods¶

## [BaseClass](https://notreux.github.io/UpsideEngine/documentation/BaseClass.html) `new()`¶

Creates a new object

Example

    BaseClass.new()

## [boolean](https://create.roblox.com/docs/scripting/luau/nil) `IsA(className: string)`¶

IsA returns true if the Instance's class is equivalent to or a subclass of a given class name

## [void](https://create.roblox.com/docs/scripting/luau/nil) `SetName(name: string)`¶

Sets the object name

## [void](https://create.roblox.com/docs/scripting/luau/nil) `Destroy()`¶

Destroys the object

# Events¶

## Destroy¶

Parameters | DataType
Property | String

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/reactive-label/TextTags.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/reactive-label/TextTags.md "View source of this page")

# Creating a Custom TextTag

Upside Engine allows you to create custom text effects using the `TextTag` object. This makes it possible to extend the functionality of `ReactiveLabel` with your own unique animations and formatting behaviors.

In this guide, you'll learn how to define a custom tag called `wave` that animates characters in a vertical sinusoidal motion.

### Step-by-Step: Creating the `wave` TextTag¶

    local frameRate = 60
    local defaultAmplitude = 5

    local wave = upsideEngine.new("TextTag")
    wave:SetTagName("wave")

    wave:SetRenderFunction(function(label, i, args)
        local amplitude = args.intensity or defaultAmplitude
        local duration = args.duration or 1
        local steps = math.max(1, math.floor(duration * frameRate))

        for step = 0, steps do
            local phase = (2 * math.pi) * (step / steps) + (i or 0)
            local offsetY = amplitude * math.sin(phase)
            label.Position = UDim2.new(0, 0, 0, offsetY)
            task.wait(duration / steps)
        end

        label.Position = UDim2.new(0, 0, 0, 0)
    end)

#### Key Elements:¶

  * `SetTagName("wave")` defines the tag name used inside the text.
  * `SetRenderFunction(...)` registers the function that will animate each character individually.
  * `i` refers to the character index in the string.
  * `args` allows you to pass custom parameters from the tag, like `intensity` and `duration`.

### Using the Custom Tag in ReactiveLabel¶

Once you've defined your `wave` tag, you can use it directly inside the text string assigned to `ReactiveLabel.Text`:

    reactiveLabel.Text = [[
        <wave intensity=8 duration=1.2>Wavy text is cool!</wave>

    reactiveLabel:Render()

This will animate each character with a vertical wave effect using the parameters provided.

### Final Result Example¶

    local reactiveLabel = upsideEngine.new("ReactiveLabel")
    reactiveLabel.Instance.Parent = dialogBox
    reactiveLabel.Instance.Size = UDim2.fromScale(1, 1)
    reactiveLabel.Font = Enum.Font.Arcade
    reactiveLabel.StopSoundOnFinish = false

    reactiveLabel.Text = [[
        <wave intensity=6 duration=1> Hello!! </wave>
        I'm feeling good today,
        <wave><gradient startColor="#ff4500" endColor="#ff9933"> how are you? </gradient></wave>

    reactiveLabel:Render()

In this example, the `wave` tag is combined with color and gradient tags to produce a vibrant animated dialog effect.

> ✅ You can define as many custom `TextTags` as you'd like—experiment with different animations and bring your text to life!

### Tips¶

  * Keep animations lightweight for performance.
  * Combine tags creatively (e.g., `<shake><font>text</font></shake>`).
  * Always test on multiple screen resolutions for best results.

##### You're now ready to build your own animated text effects using `TextTag`!¶

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/replication-guide/Introduction.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/replication-guide/Introduction.md "View source of this page")

# Understanding Replication in Upside Engine¶

Welcome to the comprehensive guide on replication in Upside Engine! This tutorial will teach you how to create multiplayer games by understanding how objects are synchronized across clients.

## What is replication?¶

Replication is the process of synchronizing game objects and their properties across multiple clients in a multiplayer environment. When a player moves their character, jumps, or performs any action, that information needs to be:

  1. Sent to the server
  2. Validated by the server
  3. Distributed to all other connected clients

This ensures that all players see a consistent game state.

## Prerequisites¶

Before starting this tutorial, you should have:

Requirements

  * Upside Engine installed in your project. Follow the [installation guide](https://notreux.github.io/UpsideEngine/tutorials/get-started/Installation.html)
  * Basic understanding of Luau/Lua programming
  * Basic knowledge of Roblox's client-server architecture

## Tutorial structure¶

This guide is divided into several sections:

Guide Sections

  1. **How Replication Works** \- Understanding the core concepts and flow
  2. **Client Setup** \- Setting up your client-side replication
  3. **Server Setup** \- Handling replication requests on the server
  4. **Authority System** \- Understanding and managing object authority
  5. **Practical Example** \- Building a complete multiplayer game

Let's get started!

**Next:** [How Replication Works](https://notreux.github.io/UpsideEngine/tutorials/replication-guide/HowItWorks.html)

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/ai-assets/Introduction.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/ai-assets/Introduction.md "View source of this page")

# Creating Pixel Art Assets Using AI¶

For developers working with limited budgets, hiring professional pixel artists may not always be an option. Fortunately, there are some AI-powered tools that can help generate pixel art assets efficiently and affordably.

Here are a couple of suggestions that might be useful:

  * <https://www.pixellab.ai/>
  * <https://www.retrodiffusion.ai/>

Please note that we are not affiliated with any of these platforms—this is simply a recommendation based on available tools that could support your creative workflow.

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/documentation/autogen/PluginSupportService.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/documentation/autogen/PluginSupportService.md "View source of this page")

# [Extended from EventEmitter](https://notreux.github.io/UpsideEngine/documentation/autogen/EventEmitter.html) PluginSupportService¶

This class save and build the engine data

# Properties¶

# Methods¶

## [void](https://create.roblox.com/docs/scripting/luau/nil) LoadPluginContent()¶

Loads the engine data stored in the "UpsideEngineDB" attribute of replicated storage, when it ends sets the attribute as an empty table

## [void](https://create.roblox.com/docs/scripting/luau/nil) SavePluginContent(`content: {}`)¶

Saves the engine data in replicated storage as attribute with the name "UpsideEngineDB" can be useful to create plugins

# Events¶

There is no events for this class

Back to top


[ ](https://github.com/notreux/UpsideEngine/edit/docs/docs/tutorials/style-guide/ModuleStructure.md "Edit this page") [ ](https://github.com/notreux/UpsideEngine/raw/docs/docs/tutorials/style-guide/ModuleStructure.md "View source of this page")

# Module Structure¶

On this page, you will learn the recommended approach to structuring modules, which will enable us to develop in a more scalable and user-friendly manner.

## Structure¶

    -- imports
    local exports = {}
    function exports.load(scene: Scene)
        -- initialization code
    end

    function exports.start(scene: Scene)
        -- code to run once everything is ready
    end

    function exports.unload(scene: Scene)
        -- code to run before the scene is unloaded
    end

    return exports

## The Benefits of This Structure¶

This organization ensures that all objects you intend to use are properly initialized. For instance, if you call something like `scene.Objects:FindByName("Dog")` before the object has been created, it could lead to an error. By initializing everything in the `load` function and then using them in the `start` function, we guarantee that all required elements are ready as expected. Additionally, the `unload` function is executed when a scene is unloaded, making it the perfect place to handle scene transitions safely.

`client/modules/myScene/character.luau`

    local replicatedStorage = game:GetService("ReplicatedStorage")
    local runService = game:GetService("RunService")
    local packages = replicatedStorage:WaitForChild("packages")

    local upsideEngine = require(packages.UpsideEngine)
    local crossPlatformService = upsideEngine.GetService("CrossPlatformService")

    local exports = {}
    function exports.load(scene: Scene)
        local character = upsideEngine.new("Character")
        character.JumpPower = 100
        character:SetScene(scene)
        character:SetSpriteSheet("idle", "rbxassetid://12908048527", Vector2.new(12, 1))
        character:SetSpriteSheet("right", "rbxassetid://12908048527", Vector2.new(12, 1))
        character:SetSpriteSheet("jump", "rbxassetid://12908048527", Vector2.new(12, 1))
        character:SetSpriteSheet("left", "rbxassetid://12970115106", Vector2.new(12, 1))

        local plrInstance = character.Instance
        plrInstance.ZIndex = 2
        plrInstance.ImageRectSize = Vector2.new(37, 64)
        plrInstance.Size = UDim2.fromOffset(100, 100)

        crossPlatformService:SetPlayerCharacter(character)
        scene.Camera:SetSubject(character)

        character:Play("idle")
        exports.character = character
    end

    function exports.start(scene: Scene)
        local spawnPosition = UDim2.fromOffset(350, 800)
        local character = exports.character
        local instance = character.Instance

        -- Avoid infinite fall
        exports.connection = runService.Heartbeat:Connect(function()
            local yPosition = instance.Position.Y.Offset
            if yPosition >= 1000 then
                instance.Position = spawnPosition
            end
        end)
    end

    function exports.unload()
        exports.connection:Disconnect()
    end

    return exports

Back to top