1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![allow(deprecated)] // TODO: Remove deprecated code
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use bevy_ecs::relationship::Relationship;
use moonshine_kind::prelude::*;
use moonshine_save::load::Unload;
/// Common elements for the view system.
pub mod prelude {
pub use super::{RegisterViewable, View, Viewable, ViewableKind};
}
#[cfg(test)]
mod tests;
/// Trait used to register a [`ViewableKind`] with an [`App`].
pub trait RegisterViewable {
/// Adds a given [`Kind`] as viewable.
fn register_viewable<T: ViewableKind>(&mut self) -> &mut Self;
}
impl RegisterViewable for App {
fn register_viewable<T: ViewableKind>(&mut self) -> &mut Self {
self.add_systems(PreUpdate, trigger_build_view::<T>);
self
}
}
/// A trait used to define a [`Kind`] as viewable.
pub trait ViewableKind: Kind {
/// Returns the default view [`Bundle`] for this [`Kind`].
///
/// # Usage
/// By default, this returns an [`Unload`] component to ensure all views are despawned when the game is loaded.
///
/// The output bundle is inserted into the [`View`] entity when it is spawned before [`Viewable<T>`] is inserted.
/// This is useful for initializing the view entity before anything else can react to it.
fn view_bundle() -> impl Bundle {
Unload
}
}
/// A [`Component`] which represents a view of an [`Entity`] of the given [`ViewableKind`].
///
/// A "view entity" is analogous to the View in the Model-View-Controller (MVC) pattern.
#[derive(Component)]
#[component(on_insert = <Self as Relationship>::on_insert)]
#[component(on_replace = <Self as Relationship>::on_replace)]
pub struct View<T: ViewableKind> {
viewable: Instance<T>,
}
impl<T: ViewableKind> View<T> {
/// Returns the associated viewable entity.
pub fn viewable(&self) -> Instance<T> {
self.viewable
}
}
impl<T: ViewableKind> Relationship for View<T> {
type RelationshipTarget = Viewable<T>;
fn get(&self) -> Entity {
self.viewable.entity()
}
fn from(entity: Entity) -> Self {
Self {
viewable: unsafe { Instance::from_entity_unchecked(entity) },
}
}
fn set_risky(&mut self, entity: Entity) {
unsafe {
*self.viewable.as_entity_mut() = entity;
}
}
}
/// A [`Component`] which represents an [`Entity`] associated with a [`View`].
///
/// A "viewable entity" is analogous to the Model in the Model-View-Controller (MVC) pattern.
#[derive(Component, Debug)]
#[component(on_replace = <Self as RelationshipTarget>::on_replace)]
#[component(on_despawn = <Self as RelationshipTarget>::on_despawn)]
pub struct Viewable<T: ViewableKind> {
view: Instance<View<T>>,
}
impl<T: ViewableKind> Viewable<T> {
/// Returns the [`View`] [`Instance`] associated with this [`Viewable`].
pub fn view(&self) -> Instance<View<T>> {
self.view
}
}
impl<T: ViewableKind> RelationshipTarget for Viewable<T> {
const LINKED_SPAWN: bool = true;
type Relationship = View<T>;
type Collection = Instance<View<T>>;
fn collection(&self) -> &Self::Collection {
&self.view
}
fn collection_mut_risky(&mut self) -> &mut Self::Collection {
&mut self.view
}
fn from_collection_risky(collection: Self::Collection) -> Self {
Self { view: collection }
}
}
fn trigger_build_view<T: ViewableKind>(
query: Query<Instance<T>, Without<Viewable<T>>>,
mut commands: Commands,
) {
for viewable in query.iter() {
commands.spawn((T::view_bundle(), View { viewable }));
}
}