Merge pull request #209 from corwinkuiper/z-ordered-sprites

Z ordered sprites
This commit is contained in:
Corwin 2022-04-07 22:46:26 +01:00 committed by GitHub
commit f7ef25f3f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 392 additions and 150 deletions

View file

@ -20,7 +20,7 @@ enum State {
}
struct Character<'a> {
object: Object<'a, 'a>,
object: Object<'a>,
position: Vector2D,
velocity: Vector2D,
}
@ -88,7 +88,8 @@ fn main(mut gba: agb::Gba) -> ! {
.object
.set_y((chicken.position.y >> 8).try_into().unwrap());
chicken.object.show();
chicken.object.commit();
object.commit();
let acceleration = 1 << 4;
let gravity = 1 << 4;
@ -133,8 +134,7 @@ fn main(mut gba: agb::Gba) -> ! {
restrict_to_screen(&mut chicken);
update_chicken_object(&mut chicken, &object, state, frame_count);
// Commit the chicken to vram
chicken.object.commit();
object.commit();
}
}

View file

@ -6,7 +6,6 @@ extern crate alloc;
use agb::display::object::{Graphics, ObjectController, Sprite, TagMap};
use alloc::vec::Vec;
const GRAPHICS: &Graphics = agb::include_aseprite!(
"../examples/the-purple-night/gfx/objects.aseprite",
"../examples/the-purple-night/gfx/boss.aseprite"
@ -49,7 +48,7 @@ fn all_sprites(gfx: &ObjectController) {
for (i, obj) in objs.iter_mut().enumerate() {
let this_image = (image + i * SPRITES.len() / objs_len) % SPRITES.len();
obj.set_sprite(gfx.sprite(&SPRITES[this_image]));
obj.commit();
gfx.commit();
}
}
}
@ -91,8 +90,8 @@ fn all_tags(gfx: &ObjectController) {
image += 1;
for (obj, tag) in objs.iter_mut() {
obj.set_sprite(gfx.sprite(tag.animation_sprite(image)));
obj.commit();
}
gfx.commit();
}
}
}
@ -101,10 +100,6 @@ fn all_tags(gfx: &ObjectController) {
fn main(mut gba: agb::Gba) -> ! {
let gfx = gba.display.object.get();
let mut timers = gba.timers.timers();
let _a = agb::interrupt::profiler(&mut timers.timer0, 5000);
loop {
all_tags(&gfx);
all_sprites(&gfx);

View file

@ -1,7 +1,10 @@
use alloc::vec::Vec;
use core::alloc::Layout;
use core::cell::RefCell;
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use core::slice;
use modular_bitfield::prelude::{B10, B2, B3, B4, B5, B8, B9};
@ -16,9 +19,82 @@ use crate::agb_alloc::bump_allocator::StartEnd;
use crate::dma;
use crate::fixnum::Vector2D;
use crate::hash_map::HashMap;
use crate::interrupt::free;
use bare_metal::Mutex;
use core::cell::RefCell;
use attributes::*;
static mut OBJECT_CONTROLLER: MaybeUninit<ObjectControllerStatic> = MaybeUninit::uninit();
unsafe fn init_object_controller() {
OBJECT_CONTROLLER.write(ObjectControllerStatic::new());
}
unsafe fn uninit_object_controller() {
OBJECT_CONTROLLER.assume_init_drop()
}
struct ObjectControllerRef {}
impl Deref for ObjectControllerRef {
type Target = ObjectControllerStatic;
fn deref(&self) -> &'static ObjectControllerStatic {
unsafe { OBJECT_CONTROLLER.assume_init_ref() }
}
}
impl DerefMut for ObjectControllerRef {
fn deref_mut(&mut self) -> &'static mut ObjectControllerStatic {
unsafe { OBJECT_CONTROLLER.assume_init_mut() }
}
}
#[cfg(debug_assertions)]
static OBJECT_REFS_CURRENT: Mutex<RefCell<i32>> = Mutex::new(RefCell::new(0));
impl ObjectControllerRef {
fn new() -> Self {
#[cfg(debug_assertions)]
{
let a = free(|c| {
let mut b = OBJECT_REFS_CURRENT.borrow(*c).borrow_mut();
let a = *b;
*b += 1;
a
});
assert_eq!(a, 0);
}
Self {}
}
unsafe fn very_unsafe_borrow(&self) -> &'static mut ObjectControllerStatic {
OBJECT_CONTROLLER.assume_init_mut()
}
}
#[cfg(debug_assertions)]
impl Drop for ObjectControllerRef {
fn drop(&mut self) {
free(|c| {
let mut b = OBJECT_REFS_CURRENT.borrow(*c).borrow_mut();
*b -= 1;
})
}
}
unsafe fn get_object_controller(_r: &ObjectControllerReference) -> ObjectControllerRef {
ObjectControllerRef::new()
}
/// Include this type if you call `get_object_controller` in impl block. This
/// helps you use the right lifetimes and doesn't impl Sync (using from two
/// "threads" without syncronisation is not safe), but sending to another
/// "thread" is safe.
type ObjectControllerReference<'a> = PhantomData<&'a UnsafeCell<()>>;
static SPRITE_ALLOCATOR: BlockAllocator = unsafe {
BlockAllocator::new(StartEnd {
start: || TILE_SPRITE,
@ -258,7 +334,7 @@ pub struct SpriteBorrow<'a> {
id: SpriteId,
sprite_location: u16,
palette_location: u16,
controller: &'a RefCell<SpriteControllerInner>,
phantom: ObjectControllerReference<'a>,
}
#[derive(Clone, Copy)]
@ -288,6 +364,7 @@ impl Storage {
}
}
#[derive(PartialEq, Eq)]
struct Attributes {
a0: ObjectAttribute0,
a1s: ObjectAttribute1Standard,
@ -304,13 +381,35 @@ impl Attributes {
a2: ObjectAttribute2::new(),
}
}
fn commit(&self, location: usize) {
let mode = self.a0.object_mode();
let attrs: [[u8; 2]; 3] = match mode {
ObjectMode::Normal => [
self.a0.into_bytes(),
self.a1s.into_bytes(),
self.a2.into_bytes(),
],
_ => [
self.a0.into_bytes(),
self.a1a.into_bytes(),
self.a2.into_bytes(),
],
};
unsafe {
let attrs: [u16; 3] = core::mem::transmute(attrs);
let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(location * 4);
ptr.add(0).write_volatile(attrs[0]);
ptr.add(1).write_volatile(attrs[1]);
ptr.add(2).write_volatile(attrs[2]);
};
}
}
pub struct Object<'a, 'b> {
sprite: SpriteBorrow<'a>,
previous_sprite: SpriteBorrow<'a>,
loan: Loan<'b>,
attrs: Attributes,
pub struct Object<'a> {
loan: Loan<'a>,
}
struct SpriteControllerInner {
@ -318,28 +417,113 @@ struct SpriteControllerInner {
sprite: HashMap<SpriteId, Storage>,
}
pub struct SpriteController {
inner: RefCell<SpriteControllerInner>,
}
struct Loan<'a> {
index: u8,
free_list: &'a RefCell<Vec<u8>>,
phantom: ObjectControllerReference<'a>,
}
impl Drop for Loan<'_> {
fn drop(&mut self) {
let mut list = self.free_list.borrow_mut();
list.push(self.index);
let mut s = unsafe { get_object_controller(&self.phantom) };
unsafe {
s.shadow_oam[self.index as usize]
.as_mut()
.unwrap_unchecked()
.destroy = true
};
}
}
struct ObjectInner {
attrs: Attributes,
sprite: SpriteBorrow<'static>,
previous_sprite: SpriteBorrow<'static>,
destroy: bool,
z: i32,
}
struct ObjectControllerStatic {
free_affine_matricies: Vec<u8>,
free_object: Vec<u8>,
shadow_oam: Vec<Option<ObjectInner>>,
z_order: Vec<u8>,
sprite_controller: SpriteControllerInner,
}
impl ObjectControllerStatic {
unsafe fn new() -> Self {
Self {
shadow_oam: (0..128).map(|_| None).collect(),
z_order: (0..128).collect(),
free_object: (0..128).collect(),
free_affine_matricies: (0..32).collect(),
sprite_controller: SpriteControllerInner::new(),
}
}
fn update_z_ordering(&mut self) {
let shadow_oam = &self.shadow_oam;
self.z_order.sort_by_key(|&a| {
shadow_oam[a as usize]
.as_ref()
.map(|s| s.z)
.unwrap_or(i32::MAX)
});
}
}
pub struct ObjectController {
free_objects: RefCell<Vec<u8>>,
sprite_controller: SpriteController,
phantom: ObjectControllerReference<'static>,
}
impl Drop for ObjectController {
fn drop(&mut self) {
unsafe {
uninit_object_controller();
}
}
}
const HIDDEN_VALUE: u16 = 0b10 << 8;
impl ObjectController {
pub fn commit(&self) {
let mut s = unsafe { get_object_controller(&self.phantom) };
let s = &mut *s;
for (i, &z) in s.z_order.iter().enumerate() {
if let Some(o) = &mut s.shadow_oam[z as usize] {
if o.destroy {
s.free_object.push(z);
unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
.add((i as usize) * 4)
.write_volatile(HIDDEN_VALUE)
}
let a = unsafe { s.shadow_oam[z as usize].take().unwrap_unchecked() };
a.previous_sprite.drop(&mut s.sprite_controller);
a.sprite.drop(&mut s.sprite_controller);
} else {
o.attrs.commit(i);
let mut a = o.sprite.clone(&mut s.sprite_controller);
core::mem::swap(&mut o.previous_sprite, &mut a);
a.drop(&mut s.sprite_controller);
}
} else {
unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
.add(i * 4)
.write_volatile(HIDDEN_VALUE)
}
}
}
}
pub(crate) fn new() -> Self {
DISPLAY_CONTROL.set_bits(1, 1, 0x6);
DISPLAY_CONTROL.set_bits(1, 1, 0xC);
@ -349,26 +533,22 @@ impl ObjectController {
unsafe {
(OBJECT_ATTRIBUTE_MEMORY as *mut u16)
.add(i * 4)
.write_volatile(0b10 << 8)
.write_volatile(HIDDEN_VALUE)
}
}
unsafe { init_object_controller() };
Self {
free_objects: RefCell::new((0..128).collect()),
sprite_controller: SpriteController::new(),
phantom: PhantomData,
}
}
pub fn object<'a, 'b>(&'a self, sprite: SpriteBorrow<'b>) -> Object<'b, 'a> {
pub fn object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Object<'a> {
self.try_get_object(sprite).expect("No object available")
}
pub fn try_get_object<'a, 'b>(&'a self, sprite: SpriteBorrow<'b>) -> Option<Object<'b, 'a>> {
let mut inner = self.free_objects.borrow_mut();
let loan = Loan {
index: inner.pop()?,
free_list: &self.free_objects,
};
pub fn try_get_object<'a>(&'a self, sprite: SpriteBorrow<'a>) -> Option<Object<'a>> {
let mut s = unsafe { get_object_controller(&self.phantom) };
let mut attrs = Attributes::new();
@ -379,113 +559,134 @@ impl ObjectController {
attrs.a1a.set_size(shape_size.1);
attrs.a1s.set_size(shape_size.1);
Some(Object {
previous_sprite: sprite.clone(),
sprite,
loan,
let index = s.free_object.pop()?;
let new_sprite: SpriteBorrow<'static> = unsafe { core::mem::transmute(sprite) };
s.shadow_oam[index as usize] = Some(ObjectInner {
attrs,
})
z: 0,
previous_sprite: new_sprite.clone(&mut s.sprite_controller),
destroy: false,
sprite: new_sprite,
});
let loan = Loan {
index: index as u8,
phantom: PhantomData,
};
s.update_z_ordering();
Some(Object { loan })
}
pub fn sprite(&self, sprite: &'static Sprite) -> SpriteBorrow {
self.sprite_controller
.try_get_sprite(sprite)
self.try_get_sprite(sprite)
.expect("No slot for sprite available")
}
pub fn try_get_sprite(&self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
self.sprite_controller.try_get_sprite(sprite)
let s = unsafe { get_object_controller(&self.phantom) };
unsafe {
s.very_unsafe_borrow()
.sprite_controller
.try_get_sprite(sprite)
}
}
}
impl Drop for Object<'_, '_> {
fn drop(&mut self) {
self.attrs.a0.set_object_mode(ObjectMode::Disabled);
self.commit();
impl<'a> Object<'a> {
#[inline(always)]
unsafe fn object_inner(&mut self) -> &mut ObjectInner {
let s = get_object_controller(&self.loan.phantom);
s.very_unsafe_borrow().shadow_oam[self.loan.index as usize]
.as_mut()
.unwrap_unchecked()
}
}
impl<'a, 'b> Object<'a, 'b> {
pub fn set_sprite(&'_ mut self, sprite: SpriteBorrow<'a>) {
self.attrs.a2.set_tile_index(sprite.sprite_location);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a2.set_tile_index(sprite.sprite_location);
let shape_size = sprite.id.sprite().size.shape_size();
self.attrs.a2.set_palete_bank(sprite.palette_location as u8);
self.attrs.a0.set_shape(shape_size.0);
self.attrs.a1a.set_size(shape_size.1);
self.attrs.a1s.set_size(shape_size.1);
self.sprite = sprite;
object_inner
.attrs
.a2
.set_palete_bank(sprite.palette_location as u8);
object_inner.attrs.a0.set_shape(shape_size.0);
object_inner.attrs.a1a.set_size(shape_size.1);
object_inner.attrs.a1s.set_size(shape_size.1);
object_inner.sprite = unsafe { core::mem::transmute(sprite) };
}
pub fn show(&mut self) -> &mut Self {
self.attrs.a0.set_object_mode(ObjectMode::Normal);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a0.set_object_mode(ObjectMode::Normal);
self
}
pub fn set_hflip(&mut self, flip: bool) -> &mut Self {
self.attrs.a1s.set_horizontal_flip(flip);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a1s.set_horizontal_flip(flip);
self
}
pub fn set_vflip(&mut self, flip: bool) -> &mut Self {
self.attrs.a1s.set_vertical_flip(flip);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a1s.set_vertical_flip(flip);
self
}
pub fn set_x(&mut self, x: u16) -> &mut Self {
self.attrs.a1a.set_x(x.rem_euclid(1 << 9) as u16);
self.attrs.a1s.set_x(x.rem_euclid(1 << 9) as u16);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a1a.set_x(x.rem_euclid(1 << 9) as u16);
object_inner.attrs.a1s.set_x(x.rem_euclid(1 << 9) as u16);
self
}
pub fn set_priority(&mut self, priority: Priority) -> &mut Self {
self.attrs.a2.set_priority(priority);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a2.set_priority(priority);
self
}
pub fn hide(&mut self) -> &mut Self {
self.attrs.a0.set_object_mode(ObjectMode::Disabled);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a0.set_object_mode(ObjectMode::Disabled);
self
}
pub fn set_y(&mut self, y: u16) -> &mut Self {
self.attrs.a0.set_y(y as u8);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a0.set_y(y as u8);
self
}
pub fn set_z(&mut self, z: i32) -> &mut Self {
let object_inner = unsafe { self.object_inner() };
object_inner.z = z;
unsafe {
get_object_controller(&self.loan.phantom).update_z_ordering();
}
self
}
pub fn set_position(&mut self, position: Vector2D<i32>) -> &mut Self {
self.attrs.a0.set_y(position.y as u8);
self.attrs.a1a.set_x(position.x.rem_euclid(1 << 9) as u16);
self.attrs.a1s.set_x(position.x.rem_euclid(1 << 9) as u16);
let object_inner = unsafe { self.object_inner() };
object_inner.attrs.a0.set_y(position.y as u8);
object_inner
.attrs
.a1a
.set_x(position.x.rem_euclid(1 << 9) as u16);
object_inner
.attrs
.a1s
.set_x(position.x.rem_euclid(1 << 9) as u16);
self
}
pub fn commit(&mut self) {
let mode = self.attrs.a0.object_mode();
let attrs: [[u8; 2]; 3] = match mode {
ObjectMode::Normal => [
self.attrs.a0.into_bytes(),
self.attrs.a1s.into_bytes(),
self.attrs.a2.into_bytes(),
],
_ => [
self.attrs.a0.into_bytes(),
self.attrs.a1a.into_bytes(),
self.attrs.a2.into_bytes(),
],
};
unsafe {
let attrs: [u16; 3] = core::mem::transmute(attrs);
let ptr = (OBJECT_ATTRIBUTE_MEMORY as *mut u16).add(self.loan.index as usize * 4);
ptr.add(0).write_volatile(attrs[0]);
ptr.add(1).write_volatile(attrs[1]);
ptr.add(2).write_volatile(attrs[2]);
};
self.previous_sprite = self.sprite.clone();
}
}
/// The Sprite Id is a thin wrapper around the pointer to the sprite in
@ -535,30 +736,25 @@ impl Sprite {
}
}
impl SpriteController {
fn new() -> Self {
Self {
inner: RefCell::new(SpriteControllerInner::new()),
}
}
fn try_get_sprite(&self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
let mut inner = self.inner.borrow_mut();
impl SpriteControllerInner {
fn try_get_sprite(&mut self, sprite: &'static Sprite) -> Option<SpriteBorrow> {
let id = sprite.id();
if let Some(storage) = inner.sprite.get_mut(&id) {
if let Some(storage) = self.sprite.get_mut(&id) {
storage.count += 1;
let location = storage.location;
let palette_location = inner.palette(sprite.palette).unwrap();
let palette_location = self.palette(sprite.palette).unwrap();
Some(SpriteBorrow {
id,
palette_location,
sprite_location: location,
controller: &self.inner,
phantom: PhantomData,
})
} else {
// layout is non zero sized, so this is safe to call
let dest = unsafe { SPRITE_ALLOCATOR.alloc(sprite.layout())? };
let palette_location = inner.palette(sprite.palette);
let palette_location = self.palette(sprite.palette);
let palette_location = match palette_location {
Some(a) => a,
None => {
@ -576,13 +772,13 @@ impl SpriteController {
}
let storage = Storage::from_sprite_ptr(dest);
inner.sprite.insert(id, storage);
self.sprite.insert(id, storage);
Some(SpriteBorrow {
id,
controller: &self.inner,
palette_location,
sprite_location: storage.location,
phantom: PhantomData,
})
}
}
@ -649,22 +845,33 @@ impl SpriteControllerInner {
impl<'a> Drop for SpriteBorrow<'a> {
fn drop(&mut self) {
let mut inner = self.controller.borrow_mut();
inner.return_sprite(self.id.sprite())
let mut s = unsafe { get_object_controller(&self.phantom) };
s.sprite_controller.return_sprite(self.id.sprite())
}
}
impl<'a> SpriteBorrow<'a> {
fn drop(self, s: &mut SpriteControllerInner) {
s.return_sprite(self.id.sprite());
core::mem::forget(self);
}
fn clone(&self, s: &mut SpriteControllerInner) -> Self {
s.sprite.entry(self.id).and_modify(|a| a.count += 1);
let _ = s.palette(self.id.sprite().palette).unwrap();
Self {
id: self.id,
sprite_location: self.sprite_location,
palette_location: self.palette_location,
phantom: PhantomData,
}
}
}
impl<'a> Clone for SpriteBorrow<'a> {
fn clone(&self) -> Self {
let mut inner = self.controller.borrow_mut();
inner.sprite.entry(self.id).and_modify(|a| a.count += 1);
let _ = inner.palette(self.id.sprite().palette).unwrap();
Self {
id: self.id,
sprite_location: self.sprite_location,
palette_location: self.palette_location,
controller: self.controller,
}
let mut s = unsafe { get_object_controller(&self.phantom) };
self.clone(&mut s.sprite_controller)
}
}
@ -694,7 +901,7 @@ enum ColourMode {
mod attributes {
use super::*;
#[bitfield]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) struct ObjectAttribute0 {
pub y: B8,
pub object_mode: ObjectMode,
@ -705,7 +912,7 @@ mod attributes {
}
#[bitfield]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) struct ObjectAttribute1Standard {
pub x: B9,
#[skip]
@ -716,7 +923,7 @@ mod attributes {
}
#[bitfield]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) struct ObjectAttribute1Affine {
pub x: B9,
pub affine_index: B5,
@ -724,10 +931,53 @@ mod attributes {
}
#[bitfield]
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) struct ObjectAttribute2 {
pub tile_index: B10,
pub priority: Priority,
pub palete_bank: B4,
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::size_of;
#[test_case]
fn size_of_ObjectControllerReference(_: &mut crate::Gba) {
assert_eq!(size_of::<ObjectControllerReference>(), 0);
}
#[test_case]
fn object_usage(gba: &mut crate::Gba) {
const GRAPHICS: &Graphics = include_aseprite!(
"../examples/the-purple-night/gfx/objects.aseprite",
"../examples/the-purple-night/gfx/boss.aseprite"
);
const BOSS: &Tag = GRAPHICS.tags().get("Boss");
const EMU: &Tag = GRAPHICS.tags().get("emu - idle");
let object = gba.display.object.get();
let mut objects: Vec<_> = alloc::vec![
object.object(object.sprite(BOSS.sprite(0))),
object.object(object.sprite(EMU.sprite(0))),
]
.into_iter()
.map(Some)
.collect();
object.commit();
let x = objects[0].as_mut().unwrap();
x.set_hflip(true);
x.set_vflip(true);
x.set_position((1, 1).into());
x.set_z(100);
x.set_sprite(object.sprite(BOSS.sprite(2)));
object.commit();
}
}

View file

@ -48,6 +48,6 @@ fn main(mut gba: Gba) -> ! {
ball.set_x(ball_x as u16).set_y(ball_y as u16);
agb::display::busy_wait_for_vblank();
ball.commit();
object.commit();
}
}

View file

@ -111,7 +111,7 @@ const HAT_SPIN_3: &Tag = TAG_MAP.get("HatSpin3");
type FixedNumberType = FixedNum<10>;
pub struct Entity<'a> {
sprite: Object<'a, 'a>,
sprite: Object<'a>,
position: Vector2D<FixedNumberType>,
velocity: Vector2D<FixedNumberType>,
collision_mask: Vector2D<u16>,
@ -258,7 +258,6 @@ impl<'a> Entity<'a> {
} else {
self.sprite.show();
}
self.sprite.commit();
}
}
@ -356,8 +355,7 @@ impl<'a> Player<'a> {
wizard.sprite.show();
hat.sprite.show();
wizard.sprite.commit();
hat.sprite.commit();
hat.sprite.set_z(-1);
wizard.position = start_position;
hat.position = start_position - (0, 10).into();
@ -498,22 +496,6 @@ impl<'a> Player<'a> {
_ => HAT_SPIN_3,
};
match self.facing {
agb::input::Tri::Negative => {
self.wizard.sprite.set_hflip(true);
self.hat
.sprite
.set_sprite(controller.sprite(hat_base_tile.sprite(5)));
}
agb::input::Tri::Positive => {
self.wizard.sprite.set_hflip(false);
self.hat
.sprite
.set_sprite(controller.sprite(hat_base_tile.sprite(0)));
}
_ => {}
}
let hat_resting_position = match self.wizard_frame {
1 | 2 => (0, 9).into(),
5 => (0, 10).into(),
@ -571,6 +553,21 @@ impl<'a> Player<'a> {
self.hat_slow_counter = 0;
self.hat_left_range = false;
self.hat.position = self.wizard.position - hat_resting_position;
match self.facing {
agb::input::Tri::Negative => {
self.wizard.sprite.set_hflip(true);
self.hat
.sprite
.set_sprite(controller.sprite(hat_base_tile.sprite(5)));
}
agb::input::Tri::Positive => {
self.wizard.sprite.set_hflip(false);
self.hat
.sprite
.set_sprite(controller.sprite(hat_base_tile.sprite(0)));
}
_ => {}
}
}
HatState::WizardTowards => {
self.hat.sprite.set_sprite(
@ -907,6 +904,8 @@ fn main(mut agb: agb::Gba) -> ! {
mixer.after_vblank();
}
object.commit();
level.show_backgrounds();
world_display.hide();
@ -925,6 +924,7 @@ fn main(mut agb: agb::Gba) -> ! {
mixer.frame();
vblank.wait_for_vblank();
mixer.after_vblank();
object.commit();
}
break;
}
@ -938,6 +938,7 @@ fn main(mut agb: agb::Gba) -> ! {
mixer.frame();
vblank.wait_for_vblank();
mixer.after_vblank();
object.commit();
}
level.hide_backgrounds();

View file

@ -152,7 +152,7 @@ impl<'a> Level<'a> {
}
struct Entity<'a> {
sprite: Object<'a, 'a>,
sprite: Object<'a>,
position: Vector2D<Number>,
velocity: Vector2D<Number>,
collision_mask: Rect<u16>,
@ -285,7 +285,6 @@ impl<'a> Entity<'a> {
self.sprite.show();
}
}
self.sprite.commit();
}
fn commit_with_size(&mut self, offset: Vector2D<Number>, size: Vector2D<i32>) {
@ -304,7 +303,6 @@ impl<'a> Entity<'a> {
self.sprite.show();
}
}
self.sprite.commit();
}
}
@ -540,7 +538,6 @@ impl<'a> Player<'a> {
entity.sprite.set_sprite(s);
entity.sprite.show();
entity.position = (144, 0).into();
entity.sprite.commit();
Player {
entity,
@ -1405,8 +1402,6 @@ impl<'a> Enemy<'a> {
entity.sprite.set_sprite(sprite);
entity.sprite.show();
entity.sprite.commit();
Self { entity, enemy_data }
}
@ -2285,6 +2280,7 @@ fn game_with_level(gba: &mut agb::Gba) {
sfx.frame();
vblank.wait_for_vblank();
sfx.after_vblank();
object.commit();
match game.advance_frame(&object, &mut vram, &mut sfx) {
GameStatus::Continue => {}
GameStatus::Lost => {