Hi! This is like 50% me asking for help and 50% me just
ing a bit.
I am creating a game which is similar to SimAnt. It has two main perspectives for viewing ants: a side-view perspective of an ant nest and a tilted, top-down perspective of ants on the surface.
I am trying to figure out how to best represent the concept of orientation.
I built the side perspective first and am now trying to retrofit my logic to support the top-down perspective. The way I implemented orientation for the side perspective is:
#[derive(Component, Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Reflect, Default)]
#[reflect(Component)]
pub struct AntOrientation {
facing: Facing,
angle: Angle,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Reflect, Default)]
pub enum Facing {
#[default]
Left,
Right,
}
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Reflect, Default)]
pub enum Angle {
#[default]
Zero,
Ninety = 90,
OneHundredEighty = 180,
TwoHundredSeventy = 270,
}
All permutations of facing and angle are valid for the side perspective. Ants can walk on the ground, the walls, or even upside down. They can also face the left or right. This means there are 8 different ways to rotate/flip a single sprite. My first screenshot shows a few examples.
Now, the top-down perspective is a little different. There aren't 8 permutations - just 4. An ant could be facing up/down/left/right. An ant facing left/right is identical to an ant facing/left with an Angle of Zero in the side-view perspective, but the parallels end there.
An ant facing Up on the top-down perspective is sort of like an ant turned Ninety degrees, but with neither Left nor Right facing.
So, this begs the question, how to best go about supporting this? I feel there are a few options:
- Continue using
AntOrientation. IntroduceFacing::UpandFacing::Downwhich are only applicable to the top-down perspective and acknowledge that onlyAngle::Zerois applicable to the top-down perspective. - Introduce distinct components for each perspective:
AntSideOrientationAntTopOrientation. This would allow me to more clearly represent valid states, but invalidate a lot of helper functions.
I feel like I am naturally inclined to take the second approach, but it makes life a lot harder and also feels a little weird because both perspectives are a 2D tilemap.
For example, I have an "ant hunger" system which encourages ants to eat by checking the location ahead of them for food. This requires knowing orientation. I don't really want to create separate hunger systems for each zone just to support distinct orientations. The system works functionally equivalent if an ant is facing "up" or if its angled "90 degrees, facing left/right" - in both scenarios one tile up on the y-axis is the target position.
Similarly, I have a custom command, spawn_ant, which expects AntOrientation. I would need to change this to accepting one of AntSideOrientation or AntTopOrientation which starts to feel like a code smell.
fn spawn_ant<Z: Zone>(
&mut self,
position: Position,
color: AntColor,
orientation: AntOrientation,
inventory: AntInventory,
role: AntRole,
name: AntName,
initiative: Initiative,
zone: Z,
);
So yeah, I guess I am a bit torn. Cramming both concepts into my existing AntOrientation feels easier to get started with, but more difficult to maintain and reason about. It feels more "ECS-y" to swap components in/out when not relevant, but difficult to reuse generic systems when I'm making such granular distinctions.