Working init version
This commit is contained in:
parent
0527e0ccaa
commit
5d1354d92e
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
|
|
@ -16,4 +16,12 @@ opt-level = 1
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bevy = "0.11"
|
bevy_mod_scripting = { git = "https://github.com/makspll/bevy_mod_scripting", features = [
|
||||||
|
"lua54", "lua",
|
||||||
|
"lua_script_api",
|
||||||
|
], rev ="c497b432b53e7d" }
|
||||||
|
bevy_mod_scripting_core = { git = "https://github.com/makspll/bevy_mod_scripting", rev ="c497b432b53e7d" }
|
||||||
|
bevy_mod_scripting_lua = { git = "https://github.com/makspll/bevy_mod_scripting", rev ="c497b432b53e7d" }
|
||||||
|
bevy_script_api = { git = "https://github.com/makspll/bevy_mod_scripting", rev ="c497b432b53e7d", features= ["lua"] }
|
||||||
|
bevy = { version = "0.13.1", features = ["multi-threaded"] }
|
||||||
|
rand = "0.8.5"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
function on_event(id)
|
||||||
|
print(string.format("on_event, script_id: %d, Handling:", script_id))
|
||||||
|
print(string.format("\t-> id: %d", id))
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
math.randomseed(os.time())
|
||||||
|
|
||||||
|
function init()
|
||||||
|
local LifeState = world:get_type_by_name("LifeState")
|
||||||
|
local life_state = world:get_component(entity, LifeState)
|
||||||
|
local cells = life_state.cells
|
||||||
|
|
||||||
|
-- set some cells alive
|
||||||
|
for _ = 1, 10000 do
|
||||||
|
local index = math.random(#cells)
|
||||||
|
cells[index] = 255
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_update()
|
||||||
|
local LifeState = world:get_type_by_name("LifeState")
|
||||||
|
local Settings = world:get_type_by_name("Settings")
|
||||||
|
|
||||||
|
local life_state = world:get_component(entity, LifeState)
|
||||||
|
-- note currently this is a copy of the cells, as of now the macro does not automatically support Vec<T> proxies by reference
|
||||||
|
local cells = life_state.cells
|
||||||
|
|
||||||
|
-- note that here we do not make use of LuaProxyable and just go off pure reflection
|
||||||
|
local settings = world:get_resource(Settings)
|
||||||
|
local dimensions = settings.physical_grid_dimensions
|
||||||
|
|
||||||
|
|
||||||
|
-- primitives are passed by value to lua, keep a hold of old state but turn 255's into 1's
|
||||||
|
local prev_state = {}
|
||||||
|
for k, v in pairs(cells) do
|
||||||
|
prev_state[k] = (not (v == 0)) and 1 or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, (dimensions[1] * dimensions[2]) do
|
||||||
|
local north = prev_state[i - dimensions[1]] or 1
|
||||||
|
local south = prev_state[i + dimensions[1]] or 1
|
||||||
|
local east = prev_state[i + 1] or 1
|
||||||
|
local west = prev_state[i - 1] or 1
|
||||||
|
local northeast = prev_state[i - dimensions[1] + 1] or 1
|
||||||
|
local southeast = prev_state[i + dimensions[1] + 1] or 1
|
||||||
|
local northwest = prev_state[i - dimensions[1] - 1] or 1
|
||||||
|
local southwest = prev_state[i + dimensions[1] - 1] or 1
|
||||||
|
|
||||||
|
local neighbours = north + south + east + west
|
||||||
|
+ northeast + southeast + northwest + southwest
|
||||||
|
|
||||||
|
-- was dead and got 3 neighbours now
|
||||||
|
if prev_state[i] == 0 and neighbours == 3 then
|
||||||
|
cells[i] = 255
|
||||||
|
-- was alive and should die now
|
||||||
|
elseif prev_state[i] == 1 and ((neighbours < 2) or (neighbours > 3)) then
|
||||||
|
cells[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- propagate the updates
|
||||||
|
life_state.cells = cells
|
||||||
|
end
|
||||||
225
src/main.rs
225
src/main.rs
|
|
@ -1,22 +1,219 @@
|
||||||
// Bevy code commonly triggers these lints and they may be important signals
|
#![allow(deprecated)]
|
||||||
// about code quality. They are sometimes hard to avoid though, and the CI
|
use std::sync::Mutex;
|
||||||
// workflow treats them as errors, so this allows them throughout the project.
|
|
||||||
// Feel free to delete this line.
|
|
||||||
#![allow(clippy::too_many_arguments, clippy::type_complexity)]
|
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::{
|
||||||
|
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||||
|
prelude::*,
|
||||||
|
reflect::Reflect,
|
||||||
|
render::{
|
||||||
|
render_asset::RenderAssetUsages,
|
||||||
|
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||||
|
texture::ImageSampler,
|
||||||
|
},
|
||||||
|
window::{PrimaryWindow, WindowResized},
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
use bevy_mod_scripting::prelude::*;
|
||||||
App::new()
|
|
||||||
.add_plugins(DefaultPlugins)
|
#[derive(Debug, Default, Clone, Reflect, Component, LuaProxy)]
|
||||||
.add_systems(Startup, setup)
|
#[reflect(Component, LuaProxyable)]
|
||||||
.run();
|
pub struct LifeState {
|
||||||
|
pub cells: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
#[derive(Default)]
|
||||||
|
pub struct LifeAPI;
|
||||||
|
|
||||||
|
impl APIProvider for LifeAPI {
|
||||||
|
type APITarget = Mutex<Lua>;
|
||||||
|
type ScriptContext = Mutex<Lua>;
|
||||||
|
type DocTarget = LuaDocFragment;
|
||||||
|
|
||||||
|
fn attach_api(&mut self, _: &mut Self::APITarget) -> Result<(), ScriptError> {
|
||||||
|
// we don't actually provide anything global
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_with_app(&self, app: &mut App) {
|
||||||
|
// this will register the `LuaProxyable` typedata since we derived it
|
||||||
|
// this will resolve retrievals of this component to our custom lua object
|
||||||
|
app.register_type::<LifeState>();
|
||||||
|
app.register_type::<Settings>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Reflect, Resource)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
pub struct Settings {
|
||||||
|
physical_grid_dimensions: (u32, u32),
|
||||||
|
display_grid_dimensions: (u32, u32),
|
||||||
|
border_thickness: u32,
|
||||||
|
live_color: u8,
|
||||||
|
dead_color: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
border_thickness: 1,
|
||||||
|
live_color: 255u8,
|
||||||
|
dead_color: 0u8,
|
||||||
|
physical_grid_dimensions: (88, 50),
|
||||||
|
display_grid_dimensions: (0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut assets: ResMut<Assets<Image>>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
settings: Res<Settings>,
|
||||||
|
) {
|
||||||
|
let mut image = Image::new_fill(
|
||||||
|
Extent3d {
|
||||||
|
width: settings.physical_grid_dimensions.0,
|
||||||
|
height: settings.physical_grid_dimensions.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
TextureDimension::D2,
|
||||||
|
&[0u8],
|
||||||
|
TextureFormat::R8Unorm,
|
||||||
|
RenderAssetUsages::RENDER_WORLD | RenderAssetUsages::MAIN_WORLD,
|
||||||
|
);
|
||||||
|
|
||||||
|
image.sampler = ImageSampler::nearest();
|
||||||
|
|
||||||
|
let script_path = bevy_mod_scripting_lua::lua_path!("game_of_life");
|
||||||
|
|
||||||
commands.spawn(Camera2dBundle::default());
|
commands.spawn(Camera2dBundle::default());
|
||||||
commands.spawn(SpriteBundle {
|
commands
|
||||||
texture: asset_server.load("ducky.png"),
|
.spawn(SpriteBundle {
|
||||||
|
texture: assets.add(image),
|
||||||
|
sprite: Sprite {
|
||||||
|
custom_size: Some(Vec2::new(
|
||||||
|
settings.display_grid_dimensions.0 as f32,
|
||||||
|
settings.display_grid_dimensions.1 as f32,
|
||||||
|
)),
|
||||||
|
color: Color::TOMATO,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.insert(LifeState {
|
||||||
|
cells: vec![
|
||||||
|
0u8;
|
||||||
|
(settings.physical_grid_dimensions.0 * settings.physical_grid_dimensions.1)
|
||||||
|
as usize
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.insert(ScriptCollection::<LuaFile> {
|
||||||
|
scripts: vec![Script::new(
|
||||||
|
script_path.to_owned(),
|
||||||
|
asset_server.load(script_path),
|
||||||
|
)],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sync_window_size(
|
||||||
|
mut resize_event: EventReader<WindowResized>,
|
||||||
|
mut settings: ResMut<Settings>,
|
||||||
|
mut query: Query<&mut Sprite, With<LifeState>>,
|
||||||
|
primary_windows: Query<&Window, With<PrimaryWindow>>,
|
||||||
|
) {
|
||||||
|
if let Some(e) = resize_event
|
||||||
|
.read()
|
||||||
|
.filter(|e| primary_windows.get(e.window).is_ok())
|
||||||
|
.last()
|
||||||
|
{
|
||||||
|
let primary_window = primary_windows.get(e.window).unwrap();
|
||||||
|
settings.display_grid_dimensions = (
|
||||||
|
primary_window.physical_width(),
|
||||||
|
primary_window.physical_height(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// resize all game's of life, retain aspect ratio and fit the entire game in the window
|
||||||
|
for mut sprite in query.iter_mut() {
|
||||||
|
let scale = if settings.physical_grid_dimensions.0 > settings.physical_grid_dimensions.1
|
||||||
|
{
|
||||||
|
// horizontal is longer
|
||||||
|
settings.display_grid_dimensions.1 as f32
|
||||||
|
/ settings.physical_grid_dimensions.1 as f32
|
||||||
|
} else {
|
||||||
|
// vertical is longer
|
||||||
|
settings.display_grid_dimensions.0 as f32
|
||||||
|
/ settings.physical_grid_dimensions.0 as f32
|
||||||
|
};
|
||||||
|
|
||||||
|
sprite.custom_size = Some(Vec2::new(
|
||||||
|
(settings.physical_grid_dimensions.0 as f32) * scale,
|
||||||
|
(settings.physical_grid_dimensions.1 as f32) * scale,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs after LifeState components are updated, updates their rendered representation
|
||||||
|
pub fn update_rendered_state(
|
||||||
|
mut assets: ResMut<Assets<Image>>,
|
||||||
|
query: Query<(&LifeState, &Handle<Image>)>,
|
||||||
|
) {
|
||||||
|
for (new_state, old_rendered_state) in query.iter() {
|
||||||
|
let old_rendered_state = assets
|
||||||
|
.get_mut(old_rendered_state)
|
||||||
|
.expect("World is not setup correctly");
|
||||||
|
|
||||||
|
old_rendered_state.data = new_state.cells.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends events allowing scripts to drive update logic
|
||||||
|
pub fn send_on_update(mut events: PriorityEventWriter<LuaEvent<()>>) {
|
||||||
|
events.send(
|
||||||
|
LuaEvent {
|
||||||
|
hook_name: "on_update".to_owned(),
|
||||||
|
args: (),
|
||||||
|
recipients: Recipients::All,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends initialization event
|
||||||
|
pub fn send_init(mut events: PriorityEventWriter<LuaEvent<()>>) {
|
||||||
|
events.send(
|
||||||
|
LuaEvent {
|
||||||
|
hook_name: "init".to_owned(),
|
||||||
|
args: (),
|
||||||
|
recipients: Recipients::All,
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const UPDATE_FREQUENCY: f32 = 1.0 / 60.0;
|
||||||
|
|
||||||
|
fn main() -> std::io::Result<()> {
|
||||||
|
let mut app = App::new();
|
||||||
|
|
||||||
|
app.add_plugins(DefaultPlugins)
|
||||||
|
.insert_resource(Time::<Fixed>::from_seconds(UPDATE_FREQUENCY.into()))
|
||||||
|
.add_plugins(LogDiagnosticsPlugin::default())
|
||||||
|
.add_plugins(FrameTimeDiagnosticsPlugin)
|
||||||
|
.add_plugins(ScriptingPlugin)
|
||||||
|
.init_resource::<Settings>()
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Startup, send_init)
|
||||||
|
.add_systems(Update, sync_window_size)
|
||||||
|
.add_systems(FixedUpdate, update_rendered_state.after(sync_window_size))
|
||||||
|
.add_systems(FixedUpdate, send_on_update.after(update_rendered_state))
|
||||||
|
.add_systems(FixedUpdate, script_event_handler::<LuaScriptHost<()>, 0, 1>)
|
||||||
|
.add_script_host::<LuaScriptHost<()>>(PostUpdate)
|
||||||
|
.add_api_provider::<LuaScriptHost<()>>(Box::new(LuaCoreBevyAPIProvider))
|
||||||
|
.add_api_provider::<LuaScriptHost<()>>(Box::new(LifeAPI));
|
||||||
|
|
||||||
|
app.run();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue