Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Architecture

SGE was designed to be very flexible and easy to use, even for someone unfamiliar with Rust, for this reason it does not impose any set structure on how you must organize your code.

The most basic SGE project is a single file with this structure:

use sge::*;

#[main("Title for the window")]
async fn main() {
    // do initialization here

    loop {
        // frame loop here
        
        if should_quit() {
            break;
        }
        
        next_frame().await;
    }
    
    // do cleanup here
}

For more complex apps it may make more sense to store state in a struct, created at the start of the main function, and then have a frame loop comprised of just state.update().

The main function can optionally return a result (anyhow is included, use anyhow::Result<()>), which will be unwrapped.

We will talk more about why async is needed, what the #[main] macro is for, and how to initialize the engine with custom parameters later.

Drawing Shapes

There are two main spaces that shapes can be drawn to.

  1. The screen: coordinates are relative to the top left of the screen, and one unit always corresponds to one pixel. Positive y is down. Positive x is right.
  2. The world: coordinates are relative to the position and scale of the Camera2D. This means that you can move the camera around to change what is shown on screen, and one world unit does not always correspond to one pixel. By default, the world coordinate (0, 0) is in the center of the screen.

There are built in functions for drawing different shapes to each of these spaces. There are functions provided for the following shapes:

  • Circles
    • Ellipses
    • Arcs
    • Segments
    • Rings
    • Radial gradients
  • Rectangles and squares
    • With rounded corners
  • N-sided polygons
  • Custom shapes from a series of points
  • Lines
    • With caps
    • Dotted
    • Zig zags
    • Arrows
    • Paths
  • Curves
    • Quadratic bezier
    • Cubic bezier
  • Single pixels/pixel lines
  • Arbritrary triangles/quads
  • Other more niche shapes like hearts, moons, and n-sided stars.

All shapes can be additionally drawn with an outline.

For example to draw a square, in world space, with an outline, you would use this code:

#![allow(unused)]
fn main() {
// pub fn draw_square_with_outline_world(
//     top_left: Vec2,
//     size: f32,
//     color: Color,
//     thickness: f32,
//     outline_color: Color,
// )

draw_square_outline_world(vec2(20.0, 30.0), 40.0, Color::RED_500, 2.0, Color::RED_300);
}

See: /examples/simple.rs

Color type

SGE comes with a featureful Color type. You can create a Color using Color::from_rgb(r: f32, g: f32, b: f32), where rgb range from 0.0 to 1.0, or Color::from_rgb_u8(r: u8, g: u8, b: u8), where rgb range from 0 to 255. Both of these functions have corresponding from_rgba variants that allow you to also specify an opacity/alpha value.

In addition, the Color type has associated constants for every CSS color, and Tailwind color.

Check the reference documentation for the color type for more detail.

Drawing text

There are 3 types of text drawing functions:

  1. draw_text_*: for basic text drawing.
  2. draw_multiline_text_*: for properly drawing text with newlines in it.
  3. draw_wrapped_text_*: for drawing text with a max width, that wraps when it gets to the edge.

There are also equivalent functions for measuring the area text will take up when drawn.

See: /examples/text.rs

Advanced Shapes

Shapes that cannot be represented by vertices (i.e.: have smooth edges, like circles), are drawn using signed distance functions, this means that they are drawn exactly, no matter how much you zoom in.

You can access the signed distance function object directly to specify additional effects like a drop shadow, corner radius, or a patterned fill.

For example:

#![allow(unused)]
fn main() {
let object = Sdf::pentagram(c, 50.0)
    .with_fill(Color::RED_500, Color::RED_400, 0.0, 5.0, SdfFill::Grid)
    .with_corner_radius(5.0)
    .with_stroke(2.0, Color::RED_200, SdfStroke::Outside)
    .with_shadow(vec2(10.0, 10.0), 10.0, Color::NEUTRAL_900);

draw_sdf(object);
}

See: /exampes/sdf.rs