1
- //! Provides an [`Index`] system parameter, allowing a user to lookup an [`Entity`]
2
- //! based on the value of one of its [`Components`][`Component`].
1
+ use std:: { hash:: Hash , marker:: PhantomData } ;
2
+
3
+ use bevy_app:: { App , Plugin , Update } ;
3
4
4
- use crate as bevy_ecs;
5
5
use bevy_ecs:: {
6
6
component:: { Component , Tick } ,
7
7
prelude:: { Changed , Entity , Query , Ref , RemovedComponents , ResMut } ,
8
8
query:: ReadOnlyWorldQuery ,
9
- system:: { SystemChangeTick , SystemParam } ,
9
+ system:: { Resource , SystemChangeTick , SystemParam } ,
10
10
} ;
11
11
12
- use bevy_ecs_macros:: Resource ;
13
-
14
12
use bevy_utils:: { default, EntityHashMap , EntityHashSet , HashMap } ;
15
13
16
- use std:: { hash:: Hash , marker:: PhantomData } ;
17
-
18
- /// Describes how to transform a [`Component`] `Input` into an `Index` suitable for an [`Index`].
14
+ /// Describes how to transform an `Input` into an `Index` suitable for an [`Index`].
19
15
pub trait Indexer {
20
- /// The input [`Component`] to index against.
21
- type Input : Component ;
16
+ /// The input to index against.
17
+ type Input ;
22
18
23
- /// A type suitable for indexing the [`Component`] `Input`
24
- type Index : Hash + Eq + Clone + Sync + Send + ' static ;
19
+ /// A type suitable for indexing the `Input`
20
+ type Index ;
25
21
26
22
/// Generate an `Index` from the provided `Input`
27
23
fn index ( input : & Self :: Input ) -> Self :: Index ;
28
24
}
29
25
30
- /// A basic [`Indexer`] which directly uses the [`Component`] `T`'s value.
26
+ /// A basic [`Indexer`] which directly uses `T`'s value.
31
27
pub struct SimpleIndexer < T > ( PhantomData < T > ) ;
32
28
33
29
impl < T > Indexer for SimpleIndexer < T >
34
30
where
35
- T : Component + Hash + Eq + Clone ,
31
+ T : Clone ,
36
32
{
37
33
type Input = T ;
38
34
45
41
46
42
/// Stored data required for an [`Index`].
47
43
#[ derive( Resource ) ]
48
- pub struct IndexBacking < T , F = ( ) , I = SimpleIndexer < T > >
44
+ struct IndexBacking < T , F = ( ) , I = SimpleIndexer < T > >
49
45
where
50
46
I : Indexer ,
51
47
{
@@ -75,10 +71,16 @@ where
75
71
impl < T , F , I > IndexBacking < T , F , I >
76
72
where
77
73
I : Indexer < Input = T > ,
74
+ I :: Index : Hash + Clone + Eq ,
78
75
{
79
76
fn update ( & mut self , entity : Entity , value : Option < & T > ) -> Option < I :: Index > {
80
77
let value = value. map ( |value| I :: index ( value) ) ;
81
78
79
+ if self . reverse . get ( & entity) == value. as_ref ( ) {
80
+ // Return early since the value is already up-to-date
81
+ return None ;
82
+ }
83
+
82
84
let old = if let Some ( ref value) = value {
83
85
self . reverse . insert ( entity, value. clone ( ) )
84
86
} else {
@@ -102,13 +104,33 @@ where
102
104
old
103
105
}
104
106
107
+ fn insert ( & mut self , entity : Entity , value : & T ) -> Option < I :: Index > {
108
+ self . update ( entity, Some ( value) )
109
+ }
110
+
111
+ fn remove_by_entity ( & mut self , entity : Entity ) -> Option < I :: Index > {
112
+ self . update ( entity, None )
113
+ }
114
+
105
115
fn get ( & self , value : & T ) -> impl Iterator < Item = Entity > + ' _ {
116
+ self . get_by_index ( & I :: index ( value) )
117
+ }
118
+
119
+ fn get_by_index ( & self , index : & I :: Index ) -> impl Iterator < Item = Entity > + ' _ {
106
120
self . forward
107
- . get ( & I :: index ( value ) )
121
+ . get ( index)
108
122
. unwrap_or ( & self . empty )
109
123
. iter ( )
110
124
. copied ( )
111
125
}
126
+
127
+ fn iter (
128
+ & mut self ,
129
+ ) -> impl Iterator < Item = ( & I :: Index , impl Iterator < Item = Entity > + ' _ ) > + ' _ {
130
+ self . forward
131
+ . iter ( )
132
+ . map ( |( index, entities) | ( index, entities. iter ( ) . copied ( ) ) )
133
+ }
112
134
}
113
135
114
136
/// Allows for lookup of an [`Entity`] based on the [`Component`] `T`'s value.
@@ -121,64 +143,92 @@ where
121
143
T : Component ,
122
144
I : Indexer + ' static ,
123
145
F : ReadOnlyWorldQuery + ' static ,
146
+ I :: Index : Send + Sync + ' static ,
124
147
{
125
148
changed : Query < ' w , ' s , ( Entity , Ref < ' static , T > ) , ( Changed < T > , F ) > ,
126
149
removed : RemovedComponents < ' w , ' s , T > ,
127
150
index : ResMut < ' w , IndexBacking < T , F , I > > ,
128
- this_run : SystemChangeTick ,
151
+ change_tick : SystemChangeTick ,
129
152
}
130
153
131
154
impl < ' w , ' s , T , F , I > Index < ' w , ' s , T , F , I >
132
155
where
133
156
T : Component ,
134
157
I : Indexer < Input = T > + ' static ,
135
158
F : ReadOnlyWorldQuery + ' static ,
159
+ I :: Index : Hash + Clone + Eq + Send + Sync + ' static ,
136
160
{
137
161
fn update_index_internal ( & mut self ) {
138
- let this_run = self . this_run . this_run ( ) ;
162
+ let this_run = self . change_tick . this_run ( ) ;
139
163
140
164
// Remove old entires
141
165
for entity in self . removed . read ( ) {
142
- self . index . update ( entity, None ) ;
166
+ self . index . remove_by_entity ( entity) ;
143
167
}
144
168
145
169
// Update new and existing entries
146
170
for ( entity, component) in self . changed . iter ( ) {
147
- self . index . update ( entity, Some ( component. as_ref ( ) ) ) ;
171
+ self . index . insert ( entity, component. as_ref ( ) ) ;
148
172
}
149
173
150
174
self . index . last_this_run = Some ( this_run) ;
151
175
}
152
176
153
177
/// System to keep [`Index`] coarsely updated every frame
154
- pub fn update_index ( mut index : Index < T , F , I > ) {
178
+ fn update_index ( mut index : Index < T , F , I > ) {
155
179
index. update_index_internal ( ) ;
156
180
}
157
181
158
182
fn ensure_updated ( & mut self ) {
159
- let this_run = self . this_run . this_run ( ) ;
183
+ let this_run = self . change_tick . this_run ( ) ;
160
184
161
185
if self . index . last_this_run != Some ( this_run) {
162
186
self . update_index_internal ( ) ;
163
187
}
164
188
}
165
189
166
- /// Get
190
+ /// Get all [entities](`Entity`) with a [`Component`] of `value`.
167
191
pub fn get ( & mut self , value : & T ) -> impl Iterator < Item = Entity > + ' _ {
168
192
self . ensure_updated ( ) ;
169
193
170
194
self . index . get ( value)
171
195
}
172
196
197
+ /// Get all [entities](`Entity`) with an `index`.
198
+ pub fn get_by_index ( & mut self , index : & I :: Index ) -> impl Iterator < Item = Entity > + ' _ {
199
+ self . ensure_updated ( ) ;
200
+
201
+ self . index . get_by_index ( index)
202
+ }
203
+
173
204
/// Iterate over [entities](`Entity`) grouped by their [Index](`Indexer::Index`)
174
205
pub fn iter (
175
206
& mut self ,
176
207
) -> impl Iterator < Item = ( & I :: Index , impl Iterator < Item = Entity > + ' _ ) > + ' _ {
177
208
self . ensure_updated ( ) ;
178
209
179
- self . index
180
- . forward
181
- . iter ( )
182
- . map ( |( index, entities) | ( index, entities. iter ( ) . copied ( ) ) )
210
+ self . index . iter ( )
211
+ }
212
+ }
213
+
214
+ /// Starts indexing the [`Component`] `T`. This provides access to the [`Index`] system parameter.
215
+ pub struct IndexPlugin < T , F = ( ) , I = SimpleIndexer < T > > ( PhantomData < fn ( T , F , I ) > ) ;
216
+
217
+ impl < T , F , I > Default for IndexPlugin < T , F , I > {
218
+ fn default ( ) -> Self {
219
+ Self ( PhantomData )
220
+ }
221
+ }
222
+
223
+ impl < T , I , F > Plugin for IndexPlugin < T , F , I >
224
+ where
225
+ T : Component ,
226
+ I : Indexer < Input = T > + ' static ,
227
+ F : ReadOnlyWorldQuery + ' static ,
228
+ I :: Index : Hash + Clone + Eq + Send + Sync + ' static ,
229
+ {
230
+ fn build ( & self , app : & mut App ) {
231
+ app. init_resource :: < IndexBacking < T , F , I > > ( )
232
+ . add_systems ( Update , Index :: < T , F , I > :: update_index) ;
183
233
}
184
234
}
0 commit comments