heavyli_engine 0.0.2

A game engine based on the 'HeavylI' graphics library.
Documentation

HeavylI Engine

HeavylI Engine is a game engine (with graphics, ECS, and scripting support) based on the HeavylI graphics library.

Usage:

This crate should be used with the heavyli (currently version 0.0.6) crate to get the best results from the engine. This engine includes ECS support (check heavyli_engine::ecs), native script support (check heavyli_engine::ecs::native_script), Lua scripting support (check heavyli_engine::lua_script), and basic sprite handling using Renderer2D and Sprite2D (check heavyli::render).

Code Example:

First, checkout the resources folder in the heavyli repository in order to load the images needed for this example.

In this example we'll create a little mario game (no physics here though).

To start, add this Lua script example at res/test.lua (see the resources folder in the repo):

function start()
    renderer:add_sprite(0, 0.0, 0.0, 0.5, 0.5, "res/mario-stand.png")
    renderer:add_sprite(2, 1.0, 1.0, 0.5, 0.5, 'res/basic-block.png')
    renderer:add_sprite(3, 0.5, 1.0, 0.5, 0.5, 'res/basic-block.png')
    renderer:add_sprite(4, 1.0, 0.5, 0.5, 0.5, 'res/basic-block.png')
    renderer:add_sprite(5, 0.5, 0.5, 0.5, 0.5, 'res/basic-block.png')
end

counter = 6
pos_x = 0
pos_y = 0
speed = 1

function update()
    speed = delta_time
    if key_pressed("up") then
        pos_y = pos_y + speed
    elseif key_pressed("down") then
        pos_y = pos_y - speed
    end

    if key_pressed("left") then
        pos_x = pos_x + speed
    elseif key_pressed("right") then
        pos_x = pos_x - speed
    end

    renderer:set_sprite_position(0, pos_x, pos_y)
    renderer:set_camera_position(0, pos_x, pos_y)
    
    if key_pressed("a") then
        renderer:add_sprite(counter, counter % 12 * 0.5, 1.5, 0.5, 0.5, 'res/basic-block.png')

        counter = counter + 1
        print(counter)
    end
end

Also, you should have these two shader files: shader/basic_fragment.glsl:

#version 330 core

out vec4 FragColor;

in vec3 ourColor;
in vec2 texCoord;

uniform sampler2D texture1;

void main()
{
    vec4 col = texture(texture1, texCoord) * vec4(ourColor, 1.0f);

    if (0 == col.r && 0 == col.g && 0 == col.b)
    {
        discard;
    }
    
    FragColor = col;
}

shader/basic_vertex.glsl:

#version 330 core

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec3 ourColor;
out vec2 texCoord;

uniform mat4 translation;

void main()
{
    gl_Position = translation * vec4(aPos, 1.0);
    ourColor = aColor;
    texCoord = aTexCoord;
}

With this script you'll have a little mario game running.

Now, for the rust code:

// External Crates needed:
extern crate glfw;
extern crate heavyli;
extern crate heavyli_engine;
extern crate nalgebra_glm as glm;

// HeavylI Modules:
use crate::heavyli::{
    opengl_modules::init_glfw,
    rendering::shape::render,
    rendering::window::{ClearColor, Window},
};

// HeavylI Engine Modules:
use crate::heavyli_engine::{
    ecs::{native_script::native_script_manager::NativeScriptManager, registry::Registry},
    input,
    lua_script::lua_script::LuaScript,
    render::camera::Camera,
};

use glfw::Glfw;
use std::sync::{Arc, Mutex};
use std::time::Instant;

// Screen Size:
const SCR_WIDTH: u32 = 800;
const SCR_HEIGHT: u32 = 600;

// This example uses 'Scene1' struct made here.
fn main() {
    // Initialize GLFW handler and the main Window:
    let mut glfw = init_glfw();
    let mut window = Window::new(&glfw, "Sandbox", SCR_WIDTH, SCR_HEIGHT);

    // Delta Time for better object movement:
    let mut delta_time: f32 = 0.0;
    let mut delta_count = 0.0;

    // Initialize scene:
    let mut scene = Scene1::new();
    scene.init(&mut window);

    while window.is_open() {
        // Run until the window is closed.
        let start_time = Instant::now(); // Delta Time Calculation.

        // Update Scene:
        scene.update(&delta_time, &mut glfw, &mut window);

        // Limit the FPS to 120 for better performance:
        render::limit_fps(start_time, 120.0);

        // Delta Time Calculation.
        delta_time = start_time.elapsed().as_nanos() as f32 / 1000000000.0;

        delta_count += delta_time;

        if delta_count >= 1.0 {
            set_window_title(&mut window, delta_time);

            delta_count = 0.0;
        }
    }

    scene.end();
}

// Sets the title with the FPS counter.
fn set_window_title(window: &mut Window, delta_time: f32) {
    let mut title = "Sandbox | FPS: ".to_string();

    title.push_str(
        (1.0 / if 0.0 != delta_time && delta_time > 0.000001 {
            delta_time
        } else {
            f32::MIN_POSITIVE
        })
        .to_string()
        .as_str(),
    );

    window.set_title(&title);
}

struct Scene1 {
    registry: Arc<Mutex<Registry>>,
    ns_manager: NativeScriptManager, // To call all native scripts.
    script: LuaScript,               // For a single lua script.
}

impl Scene1 {
    fn new() -> Self {
        let registry = Arc::new(Mutex::new(Registry::new()));

        Self {
            registry: registry.clone(),
            ns_manager: NativeScriptManager::new(registry.clone()),
            script: LuaScript::new(registry.clone()),
        }
    }

    fn add_components(&mut self) {
        let mut registry = self.registry.lock().unwrap();

        registry.add_component(
            0,
            Camera::new(glm::vec3(0.0, 0.0, -5.0), glm::vec2(0.0, 90.0)),
        );
    }

    fn init(&mut self, window: &mut Window) {
        // Configurations to Window:
        window.make_current();
        window.set_key_polling(true);
        window.set_framebuffer_size_polling(true);
        window.load_function_pointers();

        self.add_components();

        // Load the example game lua script:
        if let Err(err) = self.script.load("res/test.lua") {
            println!("Error: {}", err);
        }

        // Execute 'start' function from lua script:
        self.script.call_function("start");

        // Start NativeScript(s):
        self.ns_manager.start_scripts();
    }

    fn update(&mut self, delta_time: &f32, glfw: &mut Glfw, window: &mut Window) {
        // Must be put at the start of the frame:
        window.process_events();

        // Clear screen with color:
        unsafe {
            Window::clear(ClearColor {
                red: 0.3,
                green: 0.5,
                blue: 1.0,
                alpha: 1.0,
            });
        }

        // Get the view matrix of the camera to calculate object's location:
        let cam_view = self
            .registry
            .lock()
            .unwrap()
            .get_component::<Camera>(0)
            .unwrap()
            .borrow_mut()
            .lock()
            .unwrap()
            .get_view();

        // Render all sprite
        // (this feature will be changed later on for better rendering scheme):
        self.script
            .render(glm::vec2(SCR_WIDTH as f32, SCR_HEIGHT as f32), &cam_view);

        // Update all NativeScript(s):
        self.ns_manager.update_scripts(delta_time);

        self.script.set_delta_time(delta_time); // Add Delta Time to use in the lua script.
        self.script.call_function("update"); // Call Update function from the lua script.

        // Must be put at the end of the frame:
        window.swap_buffers();
        glfw.poll_events();
        for (_, event) in glfw::flush_messages(&window.events()) {
            input::handle_glfw_window_event(event);
        }
    }

    fn end(&mut self) {
        self.ns_manager.end_scripts();
    }
}

Features:

  • ECS (Entity Component System) Support
  • Native Scripts support (NativeScript trait)
  • External Language Scripting - Lua Support (LuaScript struct)