6 个版本
0.0.7 | 2022年11月1日 |
---|---|
0.0.5 | 2022年9月1日 |
0.0.4 | 2022年8月8日 |
0.0.2 | 2022年7月21日 |
#755 在 游戏开发
5.5MB
2K SLoC
HeavylI Engine
HeavylI Engine
是一个基于 HeavylI
图形库的游戏引擎(包含图形、ECS 和脚本支持)。
用法
应该使用与 heavyli
(目前版本 0.0.6)包配合使用的此包以获得最佳效果。此引擎包括 ECS 支持(检查 heavyli_engine::ecs
)、本地脚本支持(检查 heavyli_engine::ecs::native_script
)、Lua 脚本支持(检查 heavyli_engine::lua_script
),以及使用 Renderer2D 和 Sprite2D 的基本精灵处理(检查 heavyli::render
)。
代码示例
首先,检出 heavyli
仓库中的资源文件夹(resources folder in the heavyli
repository),以便加载此示例所需的图像。
在这个例子中,我们将创建一个小马里奥游戏(这里没有物理效果)。
首先,在 res/test.lua
(请参阅仓库中的资源文件夹)中添加这个 Lua 脚本示例。
function start()
math.randomseed(os.time())
-- Load new textures and save their IDs:
mario_texture = add_texture("res/mario-stand.png")
block_texture = add_texture("res/basic-block.png")
-- Add new sprites:
renderer:add_sprite(0, 0.0, 0.0, 0.5, 0.5, mario_texture)
renderer:add_sprite(2, 1.0, 1.0, 0.5, 0.5, block_texture)
renderer:add_sprite(3, 0.5, 1.0, 0.5, 0.5, block_texture)
renderer:add_sprite(4, 1.0, 0.5, 0.5, 0.5, block_texture)
renderer:add_sprite(5, 0.5, 0.5, 0.5, 0.5, block_texture)
end
counter = 6
pos_x = 0
pos_y = 0
speed = 1
mario_texture = 0
block_texture = 0
function update()
speed = delta_time
-- User input:
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)
-- Randbom block generation:
if key_pressed("a") then
renderer:add_sprite(counter, counter % 12 * 0.5, math.random() % 30, 0.5, 0.5, block_texture)
counter = counter + 1
print(counter)
end
end
此外,您应该拥有这两个着色器文件: 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;
}
着色器/基本顶点.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;
}
使用此脚本,您将有一个小马里奥游戏运行。
现在,对于 Rust 代码
// Dependencies:
extern crate glfw;
extern crate heavyli_engine;
extern crate nalgebra_glm as glm;
// Modules:
use heavyli_engine::{
ecs::{
scene::{SceneCore, SceneState, Update},
scene_manager::SceneManager,
},
render::{
shader,
window::Window,
camera::Camera,
renderer_2d::{generate_sprite_2d_buffers, generate_sprite_2d_vertices, Renderer2D},
utils::{init_glfw, configure_window, window_end_frame, window_start_frame},
},
};
use glfw::Glfw;
use std::sync::{Arc, Mutex};
// Screen Size:
const SCR_WIDTH: u32 = 800;
const SCR_HEIGHT: u32 = 600;
// Main Code:
fn main() {
// Initialize OpenGL with GLFW and create a new Window:
let mut glfw = init_glfw();
let mut window = Window::new(&mut glfw, "Sandbox", SCR_WIDTH, SCR_HEIGHT);
// Configure the new window:
configure_window(&mut window);
// Create a new Scene Manager:
let mut scene_manager = SceneManager::new(Arc::new(Mutex::new(GameGlobals::new(
&mut glfw,
&mut window,
))));
// Create a new scene:
let scene1 = scene_manager.new_scene(Box::new(Scene1::new()));
// Initial scene state:
scene_manager.start_scene(scene1);
// Run scene until it closes:
while SceneState::End != scene_manager.get_scene_state(scene1).unwrap() {
scene_manager.update_scene(scene1, 120.0);
}
}
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);
}
// Create your globals for the Game:
pub struct GameGlobals<'a> {
pub glfw: &'a mut Glfw,
pub window: &'a mut Window,
}
impl<'a> GameGlobals<'a> {
pub fn new(glfw: &'a mut Glfw, window: &'a mut Window) -> Self {
Self {
glfw: glfw, // OpenGL - GLFW
window: window, // Game's window
}
}
}
// Scene Loop Implementation:
pub struct Scene1 {
delta_count: f32,
renderer: Option<Renderer2D>, // Adding Renderer2D to render sprites.
}
impl Scene1 {
fn new() -> Self {
Self {
delta_count: 0.0,
renderer: None,
}
}
}
impl<'a> Update<GameGlobals<'a>> for Scene1 {
fn start(&mut self, core: &mut SceneCore<GameGlobals>) {
// Initialize renderer here:
let mut vertices = generate_sprite_2d_vertices();
let shader_id = shader::compile("shaders/basic_vertex.glsl", "shaders/basic_fragment.glsl");
self.renderer = Some(Renderer2D::new(
core.registry.clone(),
generate_sprite_2d_buffers(&mut vertices),
vertices,
shader_id,
));
// Add camera to the scene:
core.registry.lock().unwrap().add_component(
0,
Camera::new(glm::vec3(0.0, 0.0, -5.0), glm::vec2(0.0, 90.0)),
);
// Create a new script handler:
let script_id = core.lua_script_manager.create_script_handler();
// Load the test script:
if let Err(err) = core
.lua_script_manager
.script_load(script_id, "res/test.lua")
{
println!("Error: {}", err);
}
if let Err(err) = core
.lua_script_manager
.load_renderer(script_id, self.renderer.as_ref().unwrap().clone())
{
println!("Error: {}", err);
}
}
fn update(&mut self, core: &mut SceneCore<GameGlobals>) {
window_start_frame(core.globals.lock().unwrap().window);
// Get camera view for world-location calculations:
let cam_view = core
.registry
.lock()
.unwrap()
.get_component::<Camera>(0)
.unwrap()
.borrow_mut()
.lock()
.unwrap()
.get_view();
// Render all sprites:
self.renderer
.as_ref()
.unwrap()
.render(glm::vec2(SCR_WIDTH as f32, SCR_HEIGHT as f32), &cam_view);
// Change the FPS count in title when 1 min passed:
self.delta_count += core.delta_time;
if self.delta_count >= 1.0 {
set_window_title(core.globals.lock().unwrap().window, core.delta_time);
self.delta_count = 0.0;
}
// End scene when window is closed:
if !core.globals.lock().unwrap().window.is_open() {
core.state = SceneState::End;
}
// IMPORTANT: remove all sprites' data at the end of the program:
if SceneState::End == core.state {
self.renderer.as_mut().unwrap().delete_all_sprites();
}
// Poll IO Events:
core.globals.lock().unwrap().glfw.poll_events();
// End window frame:
window_end_frame(core.globals.lock().unwrap().window);
}
}
功能
- ECS(实体组件系统)支持
- 本地脚本支持(NativeScript 特性)
- 外部语言脚本 - Lua 支持(LuaScript 结构体)
- 场景支持(场景管理器,游戏全局变量)
- 声音支持(声音播放器)
要求
- cmake
- make
- rust
- cargo
Windows 用户 - 使用 MSYS & MinGW 编译
确保已安装 MSYS
工具。然后按照以下步骤操作
- 使用以下命令更新 MSYS
pacman -Syuu
- 安装工具链:a) 64 位
pacman -S mingw-w64-x86_64-toolchain
b) 32 位
pacman -S mingw-w64-i686-toolchain
如需更多信息,请查看此链接。
-
将满足您计算机位架构的二进制文件放入
path/to/msys/mingw-your-version/lib
依赖项
~11–41MB
~633K SLoC