#ECS solution for a card game

5 messages · Page 1 of 1 (latest)

calm hearth
#

Hello,

I'm developing a card game where I want to retain the location of each card (in deck, in player's hand, ...).

My first solution works but it is not very evolutive, lacks genericity and is quite verbose. That is creating an enum and a component for each enum value :

enum CardLocationEnum {
    Deck,
    SouthPlayerHand,
    SouthPlayerTake,
    NorthPlayerHand,
    NorthPlayerTake,
}

#[derive(Component, Debug)]
struct InDeck;

#[derive(Component, Debug)]
struct InSouthPlayerHand;

#[derive(Component, Debug)]
struct InSouthPlayerTake;

#[derive(Component, Debug)]
struct InNorthPlayerHand;

#[derive(Component, Debug)]
struct InNorthPlayerTake;

... where often, I have to write such dirty pattern matching :

match target_location {
    CardLocationEnum::SouthPlayerHand => { commands.entity(c).insert(InSouthPlayerHand); () },
    CardLocationEnum::NorthPlayerHand => { commands.entity(c).insert(InNorthPlayerHand); () },
    _ => (),
}

or for systems and queries :

fn center_cards_north_player(
    card_query: Query<(Entity, &mut Transform), With<InNorthPlayerHand()>>,
) {}

fn center_cards_south_player(
    card_query: Query<(Entity, &mut Transform), With<InSouthPlayerHand()>>,
) {}

How can I improve my code ? Thank you for your help !

little iris
#

Hi, first of all, you can create a code block by wrapping it in trible backquotes (like this ```), and you can even enable syntax highlighting by adding rust right after the first set of backquotes:
```rust
fn main() {}
```
becomes:

fn main() {}
#

As for your question, I think a solution would be to have the enum be the component, and do manual filtering of the queries based on the value of the enum depending on what you want

#

it would technically be slower than using With<> but unless you have 10 000 cards I don't think it's gonna matter much

dim fox
#

My first idea was to have the locations store the cards instead of having the cards store their locations. You could have the locations (Hand, Deck Take) be a HashSet<Entity> where the Entity is a card entity. Then if you wanted to look up all cards in a player's hand you could

#[derive(Resource)]
pub struct PlayersBySeatPosition(pub HashMap<SeatPosition, Entity>)

get_player_cards(
    player_seat_position: &SeatPosition,
    players: &Query<(CardHand, Deck, CardTake)>,
    players_by_seat_position: &Res<PlayersBySeatPosition>
) {
    let player_entity = players_by_seat_position.get(player_seat_position).expect("");
    let (hand, deck, take) = players.get(player_entity).expect("");
}

Although maybe this won't work for your purposes since it doesn't let you take a card and look up a card's location, it is more intuitive to me to store cards in a location like in real life, than to store locations on a card. You could also make a resource for the reverse lookup use case.

pub enum CardLocation{
    Hand,
    Deck,
    Take
}

#[derive(Resource)]
// key is the card entity, value is a tuple of player entity and card location
pub struct card_locations(pub HashMap<Entity, (Entity, CardLocation)>)

You could update that every time a card moves and use it to keep track of where each card is.