From 432c7f12f0ff0c66f7880187267c42fcafc5d9cc Mon Sep 17 00:00:00 2001 From: Corwin Kuiper Date: Fri, 12 Mar 2021 13:43:09 +0000 Subject: [PATCH] chicken object added --- examples/chicken.rs | 83 ++++++++++++++++++++++++- src/display/object.rs | 138 +++++++++++++++++++++++++++++++++++++++--- src/display/tiled0.rs | 6 +- 3 files changed, 215 insertions(+), 12 deletions(-) diff --git a/examples/chicken.rs b/examples/chicken.rs index 4d454357..cb735ef2 100644 --- a/examples/chicken.rs +++ b/examples/chicken.rs @@ -2,19 +2,33 @@ #![feature(start)] extern crate gba; +use core::convert::{Into, TryInto}; +use gba::{ + display::{object::ObjectStandard, tiled0, HEIGHT, WIDTH}, + number::Num, +}; -use gba::display::tiled0; +struct Character { + object: ObjectStandard, + position: Vector2D, + velocity: Vector2D, +} struct Vector2D { x: i32, y: i32, } +fn frame_ranger(count: u32, start: u32, end: u32, delay: u32) -> u32 { + ((count / delay) % (end + 1 - start)) + start +} + #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { let mut gba = gba::Gba::new(); let mut gfx = gba.display.video.tiled0(); let vblank = gba.display.vblank.get(); + let mut input = gba::input::ButtonController::new(); gfx.set_sprite_palette(&CHICKEN_PALETTE); gfx.set_sprite_tilemap(&CHICKEN_TILES); @@ -33,8 +47,75 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize { gfx.background_0.set_screen_base_block(1); gfx.copy_to_map(1, &MAP_MAP); + let mut object = gfx.object; + + object.enable(); + unsafe { object.clear_objects() }; + let mut chicken = Character { + object: object.get_object(0), + position: Vector2D { + x: 20 << 8, + y: 20 << 8, + }, + velocity: Vector2D { x: 0, y: 0 }, + }; + + chicken.object.set_tile_id(0); + chicken + .object + .set_x((chicken.position.x >> 8).try_into().unwrap()); + chicken + .object + .set_y((chicken.position.y >> 8).try_into().unwrap()); + chicken.object.commit(); + + let acceleration = 1 << 8; + let mut frame_count = 0; + loop { vblank.wait_for_VBlank(); + frame_count += 1; + + input.update(); + + // Horizontal movement + chicken.velocity.x += (input.x_tri() as i32) * acceleration; + chicken.velocity.x = 10 * chicken.velocity.x / 16; + + // Update position based on velocity + chicken.position.x += chicken.velocity.x; + chicken.position.y += chicken.velocity.y; + + // Ensure the chicken remains within bounds + chicken.position.x = chicken.position.x.clamp(0, (WIDTH - 8) << 8); + chicken.position.y = chicken.position.y.clamp(0, (HEIGHT - 8) << 8); + + // Update direction the chicken is facing + if chicken.velocity.x > 1 { + chicken.object.set_hflip(false); + } else if chicken.velocity.x < -1 { + chicken.object.set_hflip(true); + } + + // Update the frame of the chicken + if (chicken.velocity.x != 0) { + chicken + .object + .set_tile_id(frame_ranger(frame_count, 1, 3, 10)); + } else { + chicken.object.set_tile_id(0); + } + + // Update the position of the chicken + chicken + .object + .set_x((chicken.position.x >> 8).try_into().unwrap()); + chicken + .object + .set_y((chicken.position.y >> 8).try_into().unwrap()); + + // Commit the chicken to vram + chicken.object.commit(); } } diff --git a/src/display/object.rs b/src/display/object.rs index 69ca4011..c831a48d 100644 --- a/src/display/object.rs +++ b/src/display/object.rs @@ -1,12 +1,134 @@ -#[non_exhaustive] -pub struct Object {} +use super::DISPLAY_CONTROL; -impl Object { - pub(crate) fn new() -> Self { - Object {} +const OBJECT_MEMORY_STANDARD: *mut [ObjectAttributeStandard; 128] = 0x0700_0000 as *mut [_; 128]; + +#[non_exhaustive] +pub struct ObjectControl {} + +#[non_exhaustive] +pub struct ObjectStandard { + attributes: ObjectAttributeStandard, + id: usize, +} + +pub enum Mode { + Normal = 0, + Affline = 1, + Hidden = 2, + AfflineDouble = 3, +} + +impl ObjectStandard { + pub fn commit(&self) { + unsafe { + (&mut (*OBJECT_MEMORY_STANDARD)[self.id] as *mut ObjectAttributeStandard) + .write_volatile(self.attributes) + } } - pub fn enable(&mut self) {} - - pub fn disable(&mut self) {} + pub fn set_x(&mut self, x: u8) { + self.attributes.set_x(x) + } + pub fn set_y(&mut self, y: u8) { + self.attributes.set_y(y) + } + pub fn set_tile_id(&mut self, id: u32) { + self.attributes.set_tile_id(id) + } + pub fn set_hflip(&mut self, hflip: bool) { + self.attributes.set_hflip(hflip) + } +} + +#[repr(packed)] +#[derive(Clone, Copy)] +pub struct ObjectAttributeStandard { + low: u32, + high: u32, +} + +impl ObjectAttributeStandard { + pub fn set_hflip(&mut self, hflip: bool) { + let mask = (1 << 0xC) << 16; + let attr = self.low; + let attr = attr & !mask; + if hflip { + self.low = attr | mask + } else { + self.low = attr + } + } + + pub fn set_x(&mut self, x: u8) { + let mask = ((1 << 8) - 1) << 16; + let attr1 = self.low; + let attr_without_x = attr1 & !mask; + let attr_with_new_x = attr_without_x | ((x as u32) << 16); + self.low = attr_with_new_x; + } + + pub fn set_y(&mut self, y: u8) { + let mask = (1 << 8) - 1; + let attr0 = self.low; + let attr_without_y = attr0 & !mask; + let attr_with_new_y = attr_without_y | y as u32; + self.low = attr_with_new_y; + } + + pub fn set_tile_id(&mut self, id: u32) { + let mask = (1 << 9) - 1; + assert!(id <= mask, "tile id is greater than 9 bits"); + let attr = self.high; + let attr = attr & !mask; + let attr = attr | id; + self.high = attr; + } + + pub fn set_mode(&mut self, mode: Mode) { + let mask = 0b11 << 0x8; + self.low = (self.low & !mask) | ((mode as u32) << 0x8); + } +} + +impl ObjectAttributeStandard { + fn new() -> Self { + ObjectAttributeStandard { low: 0, high: 0 } + } +} + +impl ObjectControl { + pub(crate) fn new() -> Self { + ObjectControl {} + } + + /// # Safety + /// Temporary + pub unsafe fn clear_objects(&mut self) { + let mut o = ObjectAttributeStandard::new(); + o.set_mode(Mode::Hidden); + for index in 0..(*OBJECT_MEMORY_STANDARD).len() { + (&mut (*OBJECT_MEMORY_STANDARD)[index] as *mut ObjectAttributeStandard) + .write_volatile(o); + } + } + + pub fn enable(&mut self) { + let disp = DISPLAY_CONTROL.get(); + let disp = disp | (1 << 0x0C); + DISPLAY_CONTROL.set(disp); + } + + pub fn disable(&mut self) { + let disp = DISPLAY_CONTROL.get(); + let disp = disp & !(1 << 0x0C); + DISPLAY_CONTROL.set(disp); + } + + pub fn get_object(&self, id: usize) -> ObjectStandard { + assert!(id < 128, "object id must be less than 128"); + ObjectStandard { + attributes: ObjectAttributeStandard::new(), + id, + } + } } diff --git a/src/display/tiled0.rs b/src/display/tiled0.rs index 3ba0e070..100deaa0 100644 --- a/src/display/tiled0.rs +++ b/src/display/tiled0.rs @@ -3,7 +3,7 @@ use core::convert::TryInto; use crate::memory_mapped::MemoryMapped1DArray; use super::{ - object::Object, set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, + object::ObjectControl, set_graphics_mode, set_graphics_settings, DisplayMode, GraphicsSettings, DISPLAY_CONTROL, }; @@ -102,7 +102,7 @@ pub struct Tiled0 { pub background_1: Background, pub background_2: Background, pub background_3: Background, - pub object: Object, + pub object: ObjectControl, } impl Tiled0 { @@ -114,7 +114,7 @@ impl Tiled0 { background_1: Background { layer: 1 }, background_2: Background { layer: 2 }, background_3: Background { layer: 3 }, - object: Object::new(), + object: ObjectControl::new(), } }