# 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](https://gitlab.com/ovid.odedbe/heavyli/-/tree/main/heavyli/res) 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):
```lua
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`:
```c
#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`:
```c
#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:
```rust
// 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)