From 6417570a5158df15217668b7a379501162740115 Mon Sep 17 00:00:00 2001 From: Corwin Date: Fri, 21 Apr 2023 17:14:51 +0100 Subject: [PATCH] add docs --- agb/src/display/object/managed.rs | 51 ++++++++++++--- agb/src/display/object/unmanaged/object.rs | 72 ++++++++++++++++++++-- 2 files changed, 110 insertions(+), 13 deletions(-) diff --git a/agb/src/display/object/managed.rs b/agb/src/display/object/managed.rs index 0194cfa4..f950a3f5 100644 --- a/agb/src/display/object/managed.rs +++ b/agb/src/display/object/managed.rs @@ -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, @@ -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) -> &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) }; diff --git a/agb/src/display/object/unmanaged/object.rs b/agb/src/display/object/unmanaged/object.rs index fb1c42de..fc9fd4ac 100644 --- a/agb/src/display/object/unmanaged/object.rs +++ b/agb/src/display/object/unmanaged/object.rs @@ -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, previous_frame_sprites: Vec, } +/// 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, @@ -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, @@ -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) -> &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);