Perro is an experimental, open-source game engine written in Rust, designed as a modern alternative to engines like Unreal, Godot, and Unity.
It focuses on performance, flexibility, and ease of use with a unique scripting system that transpiles to Rust for native performance:
- ๐ถ Pup DSL โ a beginner-friendly, lightweight scripting language that compiles to Rust for native performance. Write your gameplay logic in Pup and get the speed of Rust.
- ๐จ FUR (Flexible UI Rules) โ a declarative UI system with layouts, panels, and boxing for easy UI design.
- ๐ฆ Type-Safe Transpilation โ full type checking and casting during code generation. All scripts transpile to Rust under the hood.
- โก Optimized Release Builds โ scripts and assets statically link into your final binary.
- ๐ Decoupled Signal System โ global, name-based signals that completely decouple emitters from listeners. Use
on SIGNALNAME() {}shorthand for automatic connections.
Clone the repository and build from source:
git clone https://github.com/PerroEngine/Perro.git
cd perro
cargo run -p perro_dev -- --path /path/to/project-- --path PATH when running perro_dev. The engine is primarily designed for compiling scripts and building projects, not for active development. For active game development, use the compiled release binary (see Performance section below).
Using the CLI:
# Create a new project (defaults to workspace/projects/ProjectName)
cargo run -p perro_core -- new MyGame
# Or specify a custom path
cargo run -p perro_core -- new MyGame /path/to/projectThis creates a new project structure with:
project.toml- Project configurationres/- Resources folder (scenes, scripts, assets)res/main.scn- Default scene with a "World" Node2D and Camera.perro/- Build artifacts (generated by compiler).gitignore- Ignores generated files
Quick Dev Command (Build + Run):
# Build scripts and run project in one command
cargo run -p perro_core -- --path /path/to/project --devThis automatically:
- Transpiles scripts (Pup โ Rust)
- Compiles scripts into a DLL
- Runs the project
Manual Workflow (if you prefer more control):
1. Build Scripts Only:
# Compile scripts only (for testing changes)
cargo run -p perro_core -- --path /path/to/project --scripts2. Run Project:
# Run project in dev mode (injects compiled scripts dynamically)
# โ ๏ธ You must specify -- --path PATH when running perro_dev
cargo run -p perro_dev -- --path /path/to/project
OR
cargo run -p perro_core -- --path /path/to/project --runNote: The perro_dev runtime requires the --path argument. The engine (perro_core) is mainly for compiling scripts, not for active development. For better performance during development, see the Performance section below.
Iteration Cycle:
- Make changes to scripts in
res/* - Re-run
--devor just--scripts+perro_dev - Fast iteration cycle (~1โ3s recompile time)
When building games from source, the runtime (perro_dev) runs in debug mode by default, which means it's unoptimized. This is fine for development, but if you need better performance:
For better performance, run in release mode:
# Run perro_dev in release mode for better performance
cargo run --release -p perro_dev -- --path /path/to/projectIf you're making engine changes:
- Rebuild the runtime in release mode when you make engine changes:
cargo build --release -p perro_dev
- Then run the release binary:
./target/release/perro_dev.exe --path /path/to/project
If you're NOT making engine changes:
- Precompile the runtime in release mode once:
cargo build --release -p perro_dev
- Then use the release binary for all your game development:
./target/release/PerroDevRuntime.exe --path /path/to/project
- If no path is provided, the release binary must be in the same directory as a project folder with a valid
project.tomlin it.
Why? The source mode workflow (cargo run) is optimized for engine development, not heavy game operations. The engine (perro_core) is primarily designed for compiling scripts and building projects, not for active development. For better performance during game development, use --release flag or a pre-built release binary.
Build Final Release:
# Build complete release (compiles everything statically and strips out console logs)
cargo run -p perro_core -- --path /path/to/project --projectBuild Verbose Release (with console window):
# Build release with verbose output and visible console
cargo run -p perro_core -- --path /path/to/project --project --verboseThis:
- Transpiles all scripts โ Rust
- Compiles scripts + project into a single binary
- Embeds assets and scripts statically
- Produces an optimized, distributable executable
- Verbose mode: Removes Windows subsystem flag so console is visible and makes console logs visible (useful for debugging)
Result: A single executable with no external dependencies or DLLs.
- Create a new project using the CLI (see above)
- Write scripts in Pup in
res/folder - Design scenes by editing
res/*.scnJSON files - Design UI with FUR files in
res/ - Follow the development workflow above to test and iterate
- Build release when ready to distribute
Pup is Perro's built-in scripting language โ simple, readable, and compiles to Rust.
Scripts are defined with the @script directive followed by a name and the node type they extend:
@script Player extends Sprite2D
var speed = 7.5
fn init() {
Console.print("Player is ready!")
set_speed(2.1)
}
fn set_speed(new_speed: float) {
speed = new_speed
}
fn update() {
var delta = Time.get_delta()
self.transform.position.x += speed * delta
}
Perro uses a global, decoupled signal system. Signals are identified by name strings, and any script can listen for any signal without needing a reference to the emitter. This completely decouples signalers from listeners.
The easiest way to handle signals is using the on keyword shorthand. This automatically creates a function and connects it to the signal at script initilization.
@script GameManager extends Node
on start_Pressed() {
Console.print("Start button was pressed!")
// Start the game...
}
on pause_Pressed() {
Console.print("Game paused")
}
The on syntax automatically:
- Connects the code inside to the signal matching the same name.
You can also manually connect signals using Signal.connect(), using the signal name, and function.
@script Player extends Sprite2D
var enemy: Node2D
var bob: Sprite2D
fn init() {
// Connect to a signal on self (function name as string)
Signal.connect("player_Died", on_player_died)
// Connect to a signal on another node (using any node reference)
Signal.connect("enemy_Defeated", enemy.on_enemy_defeated)
Signal.connect("bob_Pressed", bob.on_bob_pressed)
}
fn on_player_died() {
Console.print("Player died!")
}
Here's a complete example showing how signals work across different scripts:
FUR UI File (res/ui.fur):
[UI]
[Button id=start]
Start Game
[/Button]
[/UI]
Game Manager Script (res/game_manager.pup):
@script GameManager extends Node
fn init() {
Console.print("Game manager ready, listening for start button...")
}
// Listen for the start button signal (emitted automatically by the button)
on start_Pressed() {
Console.print("Starting the game!")
// Initialize game state, load level, etc.
}
Key Points:
- The button in FUR automatically emits
start_Pressedwhen clicked (based on itsid) - The game manager doesn't need a reference to the button
- The game manager doesn't even need to be in the same scene
- Any script anywhere can listen for
start_Pressedby name if connected - The signal system is completely global and decoupled
This decoupling means you can:
- Have UI buttons that emit signals without any scripts attached
- Have game logic scripts that listen for signals without knowing where they come from
- Easily add new listeners or emitters without modifying existing code
- Test signals independently of their sources
FUR is Perro's declarative UI system for building layouts and UI panels.
[UI]
[Panel bg=sea-5 padding=4]
[Text font-weight=bold text-color=white text-size=xl]
Hello Perro!
[/Text]
[/Panel]
[/UI]
Current Features:
- Layouts and child layouts
- Panels and boxing
- Styling and padding
See perro_editor/res/fur for real examples of FUR in use.
- Scripts are transpiled to Rust, compiled into a DLL
- Engine loads the DLL at runtime
- Load files from disk
- Make changes โ recompile (~1โ3s) โ restart to see updates
- All scripts transpile โ Rust
- Statically linked into final binary
- Result:
- Single executable (no DLLs, no source included)
- Optimized machine code from LLVM
- Scenes, FUR files, images, etc. are all statically embedded
- Your source scripts are protected
This repository contains the Perro engine source code. To build and work on the engine itself:
- Rust 1.92.0 or later (GNU toolchain required - this is what ships with the editor binary for compilation)
- Cargo
On Linux, you may need to install system dependencies before building. If you encounter errors about missing libraries (such as libdbus-1-dev or libudev-dev), run:
# Install all dependencies (recommended)
./install-deps.sh
# Or install essential packages individually
sudo apt install -y libdbus-1-dev libudev-dev pkg-configThe install-deps.sh script will install all required dependencies for your distribution (Ubuntu/Debian, Fedora/RHEL, or Arch Linux). If you encounter GPG errors with apt update, the script will continue anyway and attempt to install the packages.
Common build errors:
libdbus-sysbuild failure โ Installlibdbus-1-devandpkg-confighidapibuild failure (Unable to find libudev) โ Installlibudev-dev- Missing graphics libraries โ The install script includes all necessary graphics dependencies
Perro requires the GNU toolchain. Here's how to install and set it up:
# Install the GNU toolchain (1.92.0 or later)
rustup toolchain install stable-x86_64-pc-windows-gnu
# Set GNU toolchain as default
rustup default stable-x86_64-pc-windows-gnu
# Verify you're using GNU toolchain
rustc --version
# Should show: rustc 1.92.0 (or later) ... (x86_64-pc-windows-gnu)
# Or verify with rustup
rustup show
# Should show: default toolchain: stable-x86_64-pc-windows-gnuIf you already have Rust installed with MSVC:
# Install GNU toolchain for 1.92.0
rustup toolchain install 1.92.0-x86_64-pc-windows-gnu
# Set it as default
rustup default 1.92.0-x86_64-pc-windows-gnu
# Verify
rustc --versionUpdating an existing GNU toolchain:
# Update to latest stable GNU toolchain
rustup update stable-x86_64-pc-windows-gnu
# Or update your default (if already set to GNU)
rustup update stableperro/
โโโ perro_core/ # Core engine (structs, scene, render graph)
โโโ perro_dev/ # Dev wrapper binary (loads DLLs, runs projects with --path)
โโโ perro_editor/ # Editor game project
โ โโโ .perro/
โ โ โโโ project/ # Editor project crate (final exported binary in --project mode)
โ โ โโโ scripts/ # Editor scripts crate (contains transpiled rust + builds DLL)
โ โโโ res/ # Resources (FUR files, scenes, assets, scripts)
โโโ projects/ # Example game projects
Open the Editor in Dev Mode:
# โ ๏ธ You must specify -- --path PATH when running perro_dev
cargo run -p perro_dev -- --path /path/to/project
# For better performance during development:
cargo run --release -p perro_dev -- --path /path/to/projectBuild the Core Alone:
cargo build -p perro_coreAll projects share a build cache (the main workspace target/ in source mode), so the core only compiles once.
The editors are pinned to specific versions of the toolchain, (eg. 1.0 => 1.92.0), toolchains will NOT always be updated each engine update, as to not clog the end user's system with multiple toolchains they don't need. (1.0 and 1.1 could support the same toolchain, even if users update it only is installed once)
Current Requirements:
- Rust 1.92.0 or later (required for wgpu 28.0.0)
- Default toolchain version: 1.92.0
Project Compatibility:
- Old projects use their original editor version by default
- The Project Manager auto-updates to the latest version
- You can manually upgrade a project to a newer editor version if desired
- Older editor versions remain available for projects that haven't upgraded
- โ Pup scripting system (Pup -> Rust pipeline)
- โ Type checking and casting during Rust codegen
- โ DLL loading & dynamic script loading
- โ Static linking of scripts and assets during release
- โ FUR layouts, panels, child layouts, and boxing
- โ Global decoupled signal system with 500ns dispatch
- ๐ Pup DSL expansion (control flow, standard library)
- ๐ FUR runtime editing & editor viewer
- ๐ Scene editor
- ๐ Asset pipeline
- ๐งช C# & TypeScript Support โ Experimental transpiler support for C# and TypeScript via Tree Sitter. These languages would be great additions once the transpiler stabilizes, but are currently experimental and not all AST bindings and behavior are implemented yet.
Contributions are welcome! You can work on:
- Engine โ
perro_core(rendering, scene, runtime) - Editor โ Edit the source code and UI of the editor at
perro_editor/res - Scripting โ Pup DSL expansion, transpiler improvements, other language support as needed
- Tooling โ build system, asset pipeline
See CONTRIBUTING.md for guidelines.
Your support helps us continue developing Perro and making it accessible to everyone. With enough funding, donations can enable full-time development on Perro, dramatically accelerating progress and bringing features to production faster.
Donations help fund:
- ๐ผ Full-Time Development โ Your contributions can enable dedicated, full-time work on Perro, allowing for faster feature development, more consistent updates, and quicker bug fixes
- ๐ Accelerated Development โ More time for core features like the scene editor, asset pipeline, and expanded Pup DSL capabilities
- ๐ ๏ธ Better Tooling & Infrastructure โ Improved development tools, CI/CD pipelines, documentation, and testing infrastructure
- ๐ Enhanced Documentation โ Comprehensive tutorials, example projects, and detailed guides
- ๐ Bug Fixes & Stability โ More time fixing bugs, improving performance, and ensuring Perro runs smoothly across platforms
- ๐ Community Growth โ Supporting forums, community events, and developer outreach
- โก Performance & Optimization โ Investing in profiling tools and optimization work
Ways to Support:
Every contribution, no matter the size, makes a difference! Thank you for supporting open-source game development! ๐ฎ
Perro is licensed under the Apache 2.0 License. See LICENSE for details.
Every developer needs a loyal partner, just like a dog โ and that's what Perro means in Spanish.