This commit is contained in:
Corwin 2023-04-21 17:14:51 +01:00
parent f59e4ad322
commit 6417570a51
No known key found for this signature in database
2 changed files with 110 additions and 13 deletions

View file

@ -125,6 +125,15 @@ impl Store {
}
}
/// OAM that manages z ordering and commit all visible objects in one call. This
/// is simpler to use than the [`OamUnmanaged`], but is less performant
/// depending on how objects are stored.
///
/// Use this if:
/// * You don't want to handle z ordering.
/// * You don't want to deal with the complexity of committing all objects during vblank.
///
/// Otherwise I'd recommend using [`OamUnmanaged`].
pub struct OamManaged<'gba> {
object_store: Store,
sprite_loader: UnsafeCell<SpriteLoader>,
@ -154,6 +163,8 @@ impl OamManaged<'_> {
c(sprite_loader)
}
/// Commits all the visible objects. Call during vblank to make changes made
/// to objects visible.
pub fn commit(&self) {
// safety: commit is not reentrant
let unmanaged = unsafe { &mut *self.unmanaged.get() };
@ -172,11 +183,13 @@ impl OamManaged<'_> {
}
}
/// Creates an object from the sprite in vram.
pub fn object(&self, sprite: SpriteVram) -> Object<'_> {
self.object_store
.insert_object(ObjectUnmanaged::new(sprite))
}
/// Creates a sprite in vram from a static sprite from [`include_aseprite`][crate::include_aseprite].
pub fn get_sprite(&self, sprite: &'static Sprite) -> SpriteVram {
// safety: not reentrant
unsafe {
@ -184,11 +197,13 @@ impl OamManaged<'_> {
}
}
/// Creates a sprite in vram and uses it to make an object from a static sprite from [`include_aseprite`][crate::include_aseprite].
pub fn object_sprite(&self, sprite: &'static Sprite) -> Object<'_> {
self.object(self.get_sprite(sprite))
}
}
/// A managed object used with the [`OamManaged`] interface.
pub struct Object<'controller> {
me: ObjectKey,
store: &'controller Store,
@ -291,6 +306,13 @@ fn move_after(store: &Store, source: ObjectKey, after_this: ObjectKey) {
}
impl Object<'_> {
/// Sets the z position of an object. This is not a GBA concept. It causes
/// the order of rendering to be different, thus changing whether objects
/// are rendered above eachother.
///
/// Negative z is more towards the outside and positive z is further into
/// the screen => an object with a more *negative* z is drawn on top of an
/// object with a more *positive* z.
pub fn set_z(&mut self, z_index: i32) -> &mut Self {
let my_object = &self.store.get_object(self.me);
@ -350,11 +372,14 @@ impl Object<'_> {
}
#[must_use]
/// Checks whether the object is not marked as hidden. Note that it could be
/// off screen or completely transparent and still claimed to be visible.
pub fn is_visible(&self) -> bool {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object_shared() }.is_visible()
}
/// Display the sprite in Normal mode.
pub fn show(&mut self) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().show() };
@ -362,6 +387,7 @@ impl Object<'_> {
self
}
/// Display the sprite in Affine mode.
pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().show_affine(affine_mode) };
@ -369,6 +395,7 @@ impl Object<'_> {
self
}
/// Sets the horizontal flip, note that this only has a visible affect in Normal mode.
pub fn set_hflip(&mut self, flip: bool) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_hflip(flip) };
@ -376,6 +403,7 @@ impl Object<'_> {
self
}
/// Sets the vertical flip, note that this only has a visible affect in Normal mode.
pub fn set_vflip(&mut self, flip: bool) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_vflip(flip) };
@ -383,13 +411,7 @@ impl Object<'_> {
self
}
pub fn set_x(&mut self, x: u16) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_x(x) };
self
}
/// Sets the priority of the object relative to the backgrounds priority.
pub fn set_priority(&mut self, priority: Priority) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_priority(priority) };
@ -397,6 +419,9 @@ impl Object<'_> {
self
}
/// Changes the sprite mode to be hidden, can be changed to Normal or Affine
/// modes using [`show`][Object::show] and
/// [`show_affine`][Object::show_affine] respectively.
pub fn hide(&mut self) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().hide() };
@ -404,6 +429,15 @@ impl Object<'_> {
self
}
/// Sets the x position of the object.
pub fn set_x(&mut self, x: u16) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_x(x) };
self
}
/// Sets the y position of the object.
pub fn set_y(&mut self, y: u16) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_y(y) };
@ -411,6 +445,7 @@ impl Object<'_> {
self
}
/// Sets the position of the object.
pub fn set_position(&mut self, position: Vector2D<i32>) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_position(position) };
@ -418,6 +453,7 @@ impl Object<'_> {
self
}
/// Sets the affine matrix. This only has an affect in Affine mode.
pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_affine_matrix(affine_matrix) };
@ -425,6 +461,7 @@ impl Object<'_> {
self
}
/// Sets the current sprite for the object.
pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self {
// safety: only have one of these, doesn't modify slotmap
unsafe { self.object().set_sprite(sprite) };

View file

@ -21,12 +21,51 @@ struct OamFrameModifyables {
previous_index: usize,
}
/// This handles the unmanaged oam system which gives more control to the OAM slots.
/// This is utilised by calling the iter function and writing objects to those slots.
pub struct OamUnmanaged<'gba> {
phantom: PhantomData<&'gba ()>,
frame_data: UnsafeCell<OamFrameModifyables>,
previous_frame_sprites: Vec<SpriteVram>,
}
/// The iterator over the OAM slots. Dropping this will finalise the frame. To
/// use, iterate over and write to each slot.
///
/// For example, it could look like this:
///
/// ```no_run
/// # #![no_main]
/// # #![no_std]
/// use agb::display::object::{OamIterator, ObjectUnmanaged};
///
/// fn write_to_oam(oam_iterator: OamIterator, objects: &[ObjectUnmanaged]) {
/// for (object, slot) in objects.iter().zip(oam_iterator) {
/// slot.set(&object);
/// }
/// }
/// ```
///
/// # Pitfalls
/// You *must* use each OamSlot you obtain, this can be an issue if instead of
/// the above you write
///
/// ```no_run
/// # #![no_main]
/// # #![no_std]
/// use agb::display::object::{OamIterator, ObjectUnmanaged};
///
/// fn write_to_oam(oam_iterator: OamIterator, objects: &[ObjectUnmanaged]) {
/// for (slot, object) in oam_iterator.iter().zip(objects.iter()) {
/// slot.set(&object);
/// }
/// }
/// ```
///
/// This will panic if called because when you run out of objects the zip will
/// have already grabbed the next OamSlot before realising there are no more
/// objects.
pub struct OamIterator<'oam> {
index: usize,
frame_data: &'oam UnsafeCell<OamFrameModifyables>,
@ -34,6 +73,8 @@ pub struct OamIterator<'oam> {
/// A slot in Oam that you can write to. Note that you must call [OamSlot::set]
/// or else it is a bug and will panic when dropped.
///
/// See [`OamIterator`] for potential pitfalls.
pub struct OamSlot<'oam> {
slot: usize,
frame_data: &'oam UnsafeCell<OamFrameModifyables>,
@ -128,6 +169,7 @@ impl Drop for OamIterator<'_> {
}
impl OamUnmanaged<'_> {
/// Returns the OamSlot iterator for this frame.
pub fn iter(&mut self) -> OamIterator<'_> {
let frame_data = self.frame_data.get_mut();
frame_data.frame = frame_data.frame.wrapping_add(1);
@ -162,6 +204,8 @@ impl OamUnmanaged<'_> {
}
#[derive(Debug, Clone)]
/// An object to be used by the [`OamUnmanaged`] system. Changes made here are
/// reflected when set to an OamSlot using [`OamSlot::set`].
pub struct ObjectUnmanaged {
attributes: Attributes,
sprite: SpriteVram,
@ -170,6 +214,7 @@ pub struct ObjectUnmanaged {
impl ObjectUnmanaged {
#[must_use]
/// Creates an unmanaged object from a sprite in vram.
pub fn new(sprite: SpriteVram) -> Self {
let sprite_location = sprite.location();
let palette_location = sprite.palette_location();
@ -188,16 +233,20 @@ impl ObjectUnmanaged {
}
#[must_use]
/// Checks whether the object is not marked as hidden. Note that it could be
/// off screen or completely transparent and still claimed to be visible.
pub fn is_visible(&self) -> bool {
self.attributes.is_visible()
}
/// Display the sprite in Normal mode.
pub fn show(&mut self) -> &mut Self {
self.attributes.show();
self
}
/// Display the sprite in Affine mode.
pub fn show_affine(&mut self, affine_mode: AffineMode) -> &mut Self {
assert!(
self.affine_matrix.is_some(),
@ -209,42 +258,51 @@ impl ObjectUnmanaged {
self
}
/// Sets the horizontal flip, note that this only has a visible affect in Normal mode.
pub fn set_hflip(&mut self, flip: bool) -> &mut Self {
self.attributes.set_hflip(flip);
self
}
/// Sets the vertical flip, note that this only has a visible affect in Normal mode.
pub fn set_vflip(&mut self, flip: bool) -> &mut Self {
self.attributes.set_vflip(flip);
self
}
pub fn set_x(&mut self, x: u16) -> &mut Self {
self.attributes.set_x(x);
self
}
/// Sets the priority of the object relative to the backgrounds priority.
pub fn set_priority(&mut self, priority: Priority) -> &mut Self {
self.attributes.set_priority(priority);
self
}
/// Changes the sprite mode to be hidden, can be changed to Normal or Affine
/// modes using [`show`][ObjectUnmanaged::show] and
/// [`show_affine`][ObjectUnmanaged::show_affine] respectively.
pub fn hide(&mut self) -> &mut Self {
self.attributes.hide();
self
}
/// Sets the x position of the object.
pub fn set_x(&mut self, x: u16) -> &mut Self {
self.attributes.set_x(x);
self
}
/// Sets the y position of the object.
pub fn set_y(&mut self, y: u16) -> &mut Self {
self.attributes.set_y(y);
self
}
/// Sets the position of the object.
pub fn set_position(&mut self, position: Vector2D<i32>) -> &mut Self {
self.set_y(position.y.rem_euclid(1 << 9) as u16);
self.set_x(position.x.rem_euclid(1 << 9) as u16);
@ -252,6 +310,7 @@ impl ObjectUnmanaged {
self
}
/// Sets the affine matrix. This only has an affect in Affine mode.
pub fn set_affine_matrix(&mut self, affine_matrix: AffineMatrixInstance) -> &mut Self {
let vram = affine_matrix.vram();
self.affine_matrix = Some(vram);
@ -269,6 +328,7 @@ impl ObjectUnmanaged {
self
}
/// Sets the current sprite for the object.
pub fn set_sprite(&mut self, sprite: SpriteVram) -> &mut Self {
self.set_sprite_attributes(&sprite);