added ability to generate script skeleton for template generator

This commit is contained in:
Dmitry
2022-07-22 22:05:04 +03:00
parent dd0f5921b3
commit 163374e718
3 changed files with 190 additions and 53 deletions
+3 -1
View File
@@ -14,4 +14,6 @@ readme = "README.md"
rust-version = "1.56"
[dependencies]
clap = { version = "3", features = ["derive"] }
clap = { version = "3", features = ["derive"] }
uuid = { version = "1", features = ["v4"] }
convert_case = "0.5.0"
+37 -21
View File
@@ -1,34 +1,50 @@
# Fyrox Engine Project Template Generator
# Fyrox Engine Template Generator
This tiny utility handles project generation for Fyrox Game Engine. It creates a workspace with three projects:
This tiny utility handles project and script generation for Fyrox Game Engine.
## Installation
Install it via `cargo install`:
```shell
cargo install fyrox-template
```
## Generating New Project
`fyrox-template init [--name <name> --style <style>]`
- `name` - a name of new project (default is `my_game`)
- `style` - defines a default scene type, either `2d` or `3d` (default is `3d`)
It creates a workspace with three projects:
- Game - your game project (library)
- Editor - the editor with your game attached as a plugin
- Executor - the "runner" for your game.
It also populates each project with boilerplate code. The main purpose of the project is to reduce amount of time
that is needed to set up a new project.
## Usage
Install it via `cargo` first:
```shell
cargo install fyrox-template
```
Navigate to a folder and call:
```shell
fyrox-template --name <project_name>
```
that is needed to set up a new project.
It will create a new folder with `<project_name>` and it will contain three projects, runnable only two of them:
- `cargo run --package editor --release` - to run your game inside the editor.
- `cargo run --package executor --release` - to run your game as a standalone project. It will also produce final
binary of your game, that can be shipped to a store.
- `cargo run --package executor --release` - to run your game as a standalone project. It will also produce final
binary of your game, that can be shipped to a store.
## Tips
### Tips
There is nothing special in generated project, so you can tweak them as you wish.
There is nothing special in generated project, so you can tweak them as you wish.
## Adding New Scripts
`fyrox-template script [--name <name>]`
- `name` - a name of your script (default is `MyScript`)
The tool is also capable to generate script skeleton for you, filling it with all required boilerplate. Generated scripts
will be added to `game/src` folder, so you should run the tool from the root folder of your game (where the root Cargo.toml
is located).
Do not forget to add the script to your module tree at required position, you probably will need some small tweaks
to generated content, it can be easily automated by modern IDEs.
+150 -31
View File
@@ -1,22 +1,39 @@
//! Fyrox Project Template Generator.
use clap::Parser;
use std::fs::create_dir_all;
use clap::{Parser, Subcommand};
use convert_case::{Case, Casing};
use std::{
fs::{remove_dir_all, File},
fs::{create_dir_all, remove_dir_all, File},
io::Write,
path::Path,
process::Command,
};
use uuid::Uuid;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(short, long, default_value = "my_game")]
name: String,
#[clap(subcommand)]
command: Commands,
}
#[clap(short, long, default_value = "3d")]
style: String,
#[derive(Subcommand, Debug)]
enum Commands {
/// Initializes a new game project of given name and style.
#[clap(arg_required_else_help = true)]
Init {
#[clap(short, long, default_value = "my_game")]
name: String,
#[clap(short, long, default_value = "3d")]
style: String,
},
/// Adds a script with given name. The name will be capitalized.
#[clap(arg_required_else_help = true)]
Script {
#[clap(short, long, default_value = "MyScript")]
name: String,
},
}
fn write_file<P: AsRef<Path>, S: AsRef<str>>(path: P, content: S) {
@@ -29,7 +46,7 @@ fn write_file_binary<P: AsRef<Path>>(path: P, content: &[u8]) {
file.write_all(content).unwrap();
}
fn init_game(base_path: &Path, args: &Args) {
fn init_game(base_path: &Path, name: &str) {
Command::new("cargo")
.args(&["init", "--lib", "--vcs", "none"])
.arg(base_path.join("game"))
@@ -48,7 +65,7 @@ edition = "2021"
[dependencies]
fyrox = "0.27""#,
args.name,
name,
),
);
@@ -152,7 +169,7 @@ impl Plugin for Game {
);
}
fn init_executor(base_path: &Path, args: &Args) {
fn init_executor(base_path: &Path, name: &str) {
Command::new("cargo")
.args(&["init", "--bin", "--vcs", "none"])
.arg(base_path.join("executor"))
@@ -172,7 +189,7 @@ edition = "2021"
[dependencies]
fyrox = "0.27"
{} = {{ path = "../game" }}"#,
args.name,
name,
),
);
@@ -189,12 +206,12 @@ fn main() {{
executor.add_plugin_constructor(GameConstructor);
executor.run()
}}"#,
args.name
name
),
);
}
fn init_editor(base_path: &Path, args: &Args) {
fn init_editor(base_path: &Path, name: &str) {
Command::new("cargo")
.args(&["init", "--bin", "--vcs", "none"])
.arg(base_path.join("editor"))
@@ -215,7 +232,7 @@ edition = "2021"
fyrox = "0.27"
fyroxed_base = "0.14"
{} = {{ path = "../game" }}"#,
args.name,
name,
),
);
@@ -240,7 +257,7 @@ fn main() {{
editor.run(event_loop)
}}
"#,
args.name
name
),
);
}
@@ -278,26 +295,128 @@ fn init_data(base_path: &Path, style: &str) {
}
}
fn main() {
let args = Args::parse();
if args.name.contains('-') {
panic!("The project name cannot contain `-`.")
fn init_script(raw_name: &str) {
let base_path = Path::new("game/src/");
if !base_path.exists() {
panic!("game/src directory does not exists!")
}
let base_path = Path::new(&args.name);
let script_file_stem = raw_name.to_case(Case::Snake);
let script_name = raw_name.to_case(Case::UpperCamel);
let file_name = base_path.join(script_file_stem + ".rs");
init_workspace(base_path);
init_data(base_path, &args.style);
init_game(base_path, &args);
init_editor(base_path, &args);
init_executor(base_path, &args);
if file_name.exists() {
panic!("Script {} already exists!", script_name);
}
println!("Project {} was generated successfully!", args.name);
let script_uuid = Uuid::new_v4().to_string();
write_file(
file_name,
format!(
r#"
use crate::GameConstructor;
use fyrox::{{
core::{{inspect::prelude::*, uuid::{{Uuid, uuid}}, visitor::prelude::*}},
engine::resource_manager::ResourceManager,
event::Event,
gui::inspector::PropertyChanged,
handle_object_property_changed, impl_component_provider,
scene::{{graph::map::NodeHandleMap, node::TypeUuidProvider}},
script::{{ScriptContext, ScriptDeinitContext, ScriptTrait}},
}};
#[derive(Visit, Inspect, Default, Debug, Clone)]
struct {name} {{
// Add fields here.
}}
impl_component_provider!({name});
impl TypeUuidProvider for {name} {{
fn type_uuid() -> Uuid {{
uuid!("{id}")
}}
}}
impl ScriptTrait for {name} {{
fn on_property_changed(&mut self, args: &PropertyChanged) -> bool {{
// Use `handle_object_property_changed!(self, args, Self::FOO => foo)` here to handle
// changing of object properties.
false
}}
fn on_init(&mut self, context: ScriptContext) {{
// Put initialization logic here.
}}
fn on_deinit(&mut self, context: ScriptDeinitContext) {{
// Put de-initialization logic here.
}}
fn on_os_event(&mut self, event: &Event<()>, context: ScriptContext) {{
// Respond to OS events here.
}}
fn on_update(&mut self, context: ScriptContext) {{
// Put object logic here.
}}
fn remap_handles(&mut self, old_new_mapping: &NodeHandleMap) {{
// Remap handles to other scene nodes here.
}}
fn restore_resources(&mut self, resource_manager: ResourceManager) {{
// Restore resource handles here.
}}
fn id(&self) -> Uuid {{
Self::type_uuid()
}}
fn plugin_uuid(&self) -> Uuid {{
GameConstructor::type_uuid()
}}
}}
"#,
name = script_name,
id = script_uuid
),
);
println!(
"Navigate to {} directory and use one of the following commands:",
args.name
"Script {} was added successfully! Do not forget to add it to your module tree!",
script_name
);
println!("\tRun the Editor: cargo run --package editor --release");
println!("\tRun the Executor: cargo run --package executor --release");
}
fn main() {
let args: Args = Args::parse();
match args.command {
Commands::Init { name, style } => {
if name.contains('-') {
panic!("The project name cannot contain `-`.")
}
let base_path = Path::new(&name);
init_workspace(base_path);
init_data(base_path, &style);
init_game(base_path, &name);
init_editor(base_path, &name);
init_executor(base_path, &name);
println!("Project {} was generated successfully!", name);
println!(
"Navigate to {} directory and use one of the following commands:",
name
);
println!("\tRun the Editor: cargo run --package editor --release");
println!("\tRun the Executor: cargo run --package executor --release");
}
Commands::Script { name } => {
init_script(&name);
}
}
}