Skip to content

Latest commit

ย 

History

1,400 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Perro Engine

Perro Logo

Perro is an experimental, open-source game engine written in Rust. With a focus on performance and simplicity without sacrificing either.

Philosophy

  • Simple To Learn: start with scenes, nodes, and Rust scripts without large registration steps or boilerplate.
  • Flexible To Use: reduces borrow-checker friction with direct APIs and scoped closures for mutable access. Separating state from script behavior prevents runtime borrow failures.
  • Fast In Release: nodes and scripts are laid out for efficient node and state access, all resources are statically baked in release for efficient and quick retrieval

Design Goals

  • Full Game-Making Scope: 2D, 3D, and UI all matter. Perro aims to support both 2D and 3D performantly, with high frame rates and a workflow that stays simple.
  • Simple Start: get first scene and script running quickly, with minimal setup and no script-registration boilerplate.
  • Compiler-Managed Workflow: let Perro sync scripts, generate glue code, and prepare supported assets so project setup stays small.
  • Split Model: scripts are just Rust files (lifecycle + methods); they store #[State] structs which each instance gets a copy of.
  • Safe Mutation: access through NodeID closures and engine-managed storage avoids borrow-contention edge cases in normal gameplay code (no "try_get_mut" fails).
  • Fast Access: flat ID lookups keep common node/script operations efficient, with room to cache IDs for hot paths.
  • Quick Iteration: project scripts build and reload in usually less than 1 second after initial compilation.

Game Engineering Workflow

Project scripts use a small, predictable shape. Put per-instance data in a #[State] struct, lifecycle callbacks in lifecycle!, and methods in methods!:

#[derive(Default, Variant)]
struct MotionState {
    speed: f32,
}

#[State]
struct GameState {
    score: i32,
    motion: MotionState,
}

lifecycle!({
    fn on_update(&self, ctx: &mut ScriptContext<'_, API>) {
        self.internal_method(ctx);
    }
});

methods!({
    // GameState methods

    fn internal_method(&self, ctx: &mut ScriptContext<'_, API>) {
        // private, lifecycle-local logic
    }

    pub fn externally_callable_method(&self, ctx: &mut ScriptContext<'_, API>) {
        // externally callable entry point
    }
});

The state type may use any clear name. Group related values in nested structs when that keeps one script readable. Keep same-script helpers private with fn; mark methods pub fn only when other scripts, signals, or generated dispatch need to call them.

Good: keep durable per-node fields in #[State]; group related fields in nested structs. Good: call private methods from lifecycle callbacks; keep free helpers pure. Bad: pass ScriptContext or ScriptAPI into free functions; use lifecycle or methods instead. Bad: default gameplay state to Mutex, RefCell, or thread_local!.

Author scene topology, child nodes, script attachments, refs, and defaults in composable .scn files. Do not construct authored scene trees in gameplay code. Use runtime scene APIs to load or instantiate authored .scn assets when runtime composition needs them. Engine internals, tests, and editor/tooling may build nodes when their job requires it.

Treat res/**/*.rs and .scn files as source. Treat .perro/ generated glue as output: inspect it for diagnosis, never edit it as source.

For more details, see the full documentation: perroengine.com/docs.

Local reference:

Dev Checks

  • cargo check --workspace --all-targets
  • cargo test --workspace
  • cargo clippy --workspace --all-targets -- -D warnings -F clippy::all

Major Features

  • Behavior Scripts + Per-Node State: a script is function entry points (lifecycle hooks + methods), not a mutable behavior object. When a node binds that script, runtime uses that nodeโ€™s ctx.id to run behavior and resolve that nodeโ€™s own #[State] via with_state!/with_state_mut!.
  • Object-Centric Scene Model: parent/child relationships, concrete node types, and traditional game-object structure stay front and center.
  • Compiler-Backed Asset Flow: dev stays flexible with plain files, while build/export bakes supported assets into fast static lookup paths and packs the rest.
  • Powerful UI System: UI is built as a real engine system with relative sizing, clamping, and layouts designed to scale from simple menus to larger game interfaces.
  • Flat ID-Based Runtime Access: node and script data are addressed by NodeID, enabling constant-time lookups for common operations and efficient cross-system interaction.
  • Predictable Failure Modes: most runtime misses come from real-world state changes (deleted node, missing tag/name match, unbound script), not from borrow contention between unrelated systems (no try_get_mut runtime errors).
  • Powerful Query Layer: if you prefer query-style access, filter by type, base type, tag, name, and subtree to gather NodeIDs, then operate directly through script/node APIs. See Query System.

Contributions

Perro is, of course, open source, and contributions are always appreciated: issue reports, new features, system optimizations, and other improvements. Everyone is welcome to join the project.

Support Perro

Donations help fund full-time development, faster features, and better tooling. If you want to support the project:


License

Perro is licensed under the Apache 2.0 License. See LICENSE for details.


About

๐Ÿ• Perro โ€” A game engine written in Rust attemping to bridge the gap between simplicity, safety, and performance

Topics

Resources

Stars

53 stars

Watchers

4 watching

Forks

Sponsor this project

Used by

Contributors

Languages