I'm building a rogue-like, I've already gotten a data loader working and part of the ECS working (building from scratch). The data is stored in .yml files and is used to describe things in the game (in this instance mobs) and what features those things have, for example:
--- orc: feature_packs: - physical - basic_identifiers_mob features: - component: char initial_value: T goblin: feature_packs: - physical - basic_identifiers_mob features: - component: char initial_value: t As you can see, there are two mobs described, a goblin and an orc, they both possess two feature packs (groups of features) and also posses a char feature that's used to describe what they look like to the player.
The initial_value field can be a string, an integer, a floating point, a bool, a range, etc depending on what the component requires, this will indicate the value or possible values the component may have when the component is generated during entity building/creation.
The problem is that I don't know how to, when iterating over the features, select the struct based on the component's name, for example, select the Char struct for the "char" feature.
To better describe what I mean, I've written an example in a language I better understand, Ruby:
data_manager = function_that_loads_data('folder_path') Entity_Manager.build(:mob, :orc, data_manager) class Entity_Manager class << self attr_accessor :entities, :components end def self.build(entity_type, template_name, data_manager) template = data_manager[entity_type][template_name] entity_id = generate_unique_id entities[entity_id] = Entity.new(entity_id, components: template.components.keys) template.components.each do |component| components[component.name][entity_id] = Components.get(component.name).new(component.initial_value) # <= This part, how do I do the equivalent in rust, a function that will return or allow me to get or create a struct based on the value of a string variable end end end Now serde is the only thing I know that seems to be able to read text data and transform it into data, so to that end
How can I use serde (or a more appropriate non-serde using solution) to take the names of the feature and retrieve the correct struct, all implementing a type?
Incidentally, the one solution I'm trying not to use is a giant match statement.
The repo of my work as it stands is here
- Data manager - Loads and manages data loaded into the game
- Entity manager - Manages entities and there components (doesn't support bit keys atm)
- Entity Builder - Where Entity's will be built using data from the data manager (this is where I'm currently stuck)
- Components - a list of simple components
What I'm trying to avoid is doing somthing like this:
pub fn get(comp_name: &String) -> impl Component { match comp_name.as_ref() { "kind" => Kind, "location" => Location, "name" => Name, "position" => Position, "char" => Char, } } because it's not really maintainable, though a macro would help a lot, I'm not very good at those atm and it doesn't even work, rust keeps thinking I'm trying to initialize the types when I just want to return one of several possible types that all will implement Component
EDIT: Becuase it looks like I'm not clear enough:
- I'm not trying to load gameplay objects into the game, I'm loading templates
- I'm using those templates to then generate the entities that will be exist during gameplay
- I can already load the data I want into the game in the following structure:
pub enum InitialValue { Char(char), String(String), Int(i32), Float(f32), Bool(bool), Range(Range<i32>), Point((i32,i32)) } impl InitialValue { pub fn unwrap_char(&self) -> &char { match &self { InitialValue::Char(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_string(&self) -> &String { match &self { InitialValue::String(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_int(&self) -> &i32 { match &self { InitialValue::Int(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_float(&self) -> &f32 { match &self { InitialValue::Float(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_bool(&self) -> &bool { match &self { InitialValue::Bool(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_range(&self) -> &Range<i32> { match &self { InitialValue::Range(val) => val, _ => panic!("Stored value does not match unwrap type") } } pub fn unwrap_point(&self) -> &(i32, i32) { match &self { InitialValue::Point(val) => val, _ => panic!("Stored value does not match unwrap type") } } } #[derive(Debug, Deserialize)] pub struct Component { #[serde(rename="component")] name: String, #[serde(default)] initial_value: Option<InitialValue>, } #[derive(Debug, Deserialize)] pub struct Template { pub feature_packs: Vec<String>, pub features: Vec<Component>, } How do I transform the templates into instances of entities?
Specifcally, How do I for a given
Component.namefind the component and then initialize it? OR is my aproach wrong and there's a better way.- And if I am doing it wrong, How do other games load data in and then use it to generate in game entities?
#[derive(Serialize, Deserialize)]to get things working. What exactly is your problem?