Read me and comments

Changed files
+157 -153
src
+3 -1
.gitignore
··· 1 1 /target 2 - .idea 2 + .idea 3 + 4 + .DS_Store
+11
README.md
··· 1 + # HonkBird? 2 + 3 + Idk i'm still working on the name. A gba game written in rust 4 + with [agb-rs](https://agbrs.dev/). Heavily wip, just need it in a git repo so I can hack on it on another computer. 5 + 6 + If you're really wanting to run this, check the [agb book out](https://agbrs.dev/book/), first couple chapters will get 7 + you going 8 + 9 + Special thanks 10 + 11 + - The goose is from [here](https://duckhive.itch.io/goose). Just scaled it down to 16x16 and animated it in aseprite
+132
src/game.rs
··· 1 + use crate::{Fixed, goose_sprites}; 2 + use agb::display::object::Object; 3 + use agb::display::{GraphicsFrame, HEIGHT, Priority, WIDTH}; 4 + use agb::fixnum::{Num, Vector2D, num, vec2}; 5 + use agb::input::Button; 6 + 7 + pub enum GameState { 8 + Start, 9 + Playing, 10 + GameOver, 11 + } 12 + 13 + pub struct GameLoop { 14 + pub score: u16, 15 + pub game_state: GameState, 16 + } 17 + 18 + impl GameLoop { 19 + pub fn new() -> Self { 20 + Self { 21 + score: 0, 22 + game_state: GameState::Start, 23 + } 24 + } 25 + 26 + pub fn run(&mut self, gba: &mut agb::Gba) { 27 + let mut gfx = gba.graphics.get(); 28 + 29 + let mut button_controller = agb::input::ButtonController::new(); 30 + let mut goose = Goose::new(); 31 + 32 + loop { 33 + button_controller.update(); 34 + let mut frame = gfx.frame(); 35 + 36 + let just_pressed = button_controller.is_just_pressed(Button::A); 37 + goose.move_goose(just_pressed); 38 + goose.show(&mut frame); 39 + frame.commit(); 40 + } 41 + } 42 + } 43 + 44 + /// I really don't have a better name for these 45 + #[derive(Copy, Clone)] 46 + pub enum GooseState { 47 + Zero = 0, 48 + One = 1, 49 + Two = 2, 50 + Three = 3, 51 + } 52 + 53 + pub struct Goose { 54 + pos: Vector2D<Fixed>, 55 + velocity: Vector2D<Fixed>, 56 + current_state: GooseState, 57 + //Should increase every game play loop and reset at 64 58 + tick_counter: u8, 59 + } 60 + 61 + impl Goose { 62 + pub fn new() -> Self { 63 + Self { 64 + pos: vec2(num!(5), num!(5)), 65 + velocity: vec2(num!(0.20), num!(0)), 66 + current_state: GooseState::Zero, 67 + tick_counter: 0, 68 + } 69 + } 70 + 71 + pub fn show(&mut self, frame: &mut GraphicsFrame) { 72 + let _ = Object::new(goose_sprites::GOOSE.sprite(self.current_state as usize)) 73 + .set_pos(self.pos.round()) 74 + .set_priority(Priority::P1) 75 + .show(frame); 76 + } 77 + 78 + fn animate(&mut self) { 79 + // Advance animation every 8 ticks 80 + self.tick_counter = self.tick_counter.wrapping_add(1); 81 + if self.tick_counter % 8 == 0 { 82 + self.current_state = match self.current_state { 83 + GooseState::Zero => GooseState::One, 84 + GooseState::One => GooseState::Two, 85 + GooseState::Two => GooseState::Three, 86 + GooseState::Three => GooseState::Zero, 87 + } 88 + } 89 + //Just a catch to not let that number get too big 90 + if self.tick_counter >= 64 { 91 + self.tick_counter = 0; 92 + } 93 + } 94 + 95 + fn reset(&mut self) { 96 + self.pos = vec2(num!(5), num!(5)); 97 + self.velocity = vec2(num!(0.20), num!(0)); 98 + } 99 + 100 + pub fn move_goose(&mut self, button_pressed: bool) { 101 + //If the button is pressed, go up a bit 102 + if button_pressed { 103 + self.velocity.y = num!(-1); 104 + } else { 105 + self.velocity.y += num!(0.02); 106 + } 107 + 108 + self.pos += self.velocity; 109 + 110 + //Right now just cycling through may change animations around 111 + self.animate(); 112 + 113 + //Keep in frame right now. Will later do the scrolling map thing 114 + if self.pos.y < num!(0) { 115 + self.pos.y = num!(0); 116 + self.velocity.y = num!(0); 117 + } 118 + if self.pos.y >= num!(HEIGHT) { 119 + //TODO call game end here later 120 + self.reset(); 121 + } 122 + 123 + // Keep X within screen horizontally (optional: clamp) 124 + if self.pos.x < num!(0) { 125 + self.pos.x = num!(0); 126 + } 127 + let max_x = num!(WIDTH - 1); 128 + if self.pos.x > max_x { 129 + self.pos.x = max_x; 130 + } 131 + } 132 + }
+11 -152
src/main.rs
··· 11 11 #![cfg_attr(test, reexport_test_harness_main = "test_main")] 12 12 #![cfg_attr(test, test_runner(agb::test_runner::test_runner))] 13 13 14 + mod game; 15 + 14 16 // By default no_std crates don't get alloc, so you won't be able to use things like Vec 15 17 // until you declare the extern crate. `agb` provides an allocator so it will all work 16 18 extern crate alloc; 17 19 18 - use agb::display::{GraphicsFrame, Priority, Rgb15}; 20 + use crate::game::{GameLoop, GameState}; 19 21 use agb::display::font::{AlignmentKind, Font, Layout, RegularBackgroundTextRenderer}; 20 - use agb::display::object::Object; 21 22 use agb::display::tiled::{RegularBackground, RegularBackgroundSize, TileFormat, VRAM_MANAGER}; 22 - use agb::fixnum::{num, vec2, FixedNum, Num, Vector2D}; 23 + use agb::display::{Priority, Rgb15}; 24 + use agb::fixnum::Num; 25 + use agb::input::Button; 23 26 use agb::{include_aseprite, include_font}; 24 - use agb::input::Button; 25 - 26 - const SCREEN_W: i32 = 240; 27 - const SCREEN_H: i32 = 160; 28 27 29 28 include_aseprite!( 30 29 mod goose_sprites, 31 30 "gfx/flap_animated.aseprite" 32 31 ); 33 32 34 - static FONT: Font = include_font!("fnt/Born2bSportyV2.ttf", 20); 33 + static FONT: Font = include_font!("fnt/Born2bSportyV2.ttf", 24); 35 34 36 35 type Fixed = Num<i32, 8>; 37 - 38 36 39 37 #[agb::entry] 40 38 fn main(mut gba: agb::Gba) -> ! { ··· 47 45 TileFormat::FourBpp, 48 46 ); 49 47 50 - 51 48 let mut text_layout = Layout::new( 52 - "Hello, this is some text that I want to display!", 49 + "HonkBird\nPress Start to play", 53 50 &FONT, 54 51 AlignmentKind::Left, 55 52 32, 56 53 200, 57 54 ); 58 55 59 - let mut text_renderer = RegularBackgroundTextRenderer::new((4, 0)); 56 + let mut text_renderer = RegularBackgroundTextRenderer::new((4, 50)); 60 57 let mut button_controller = agb::input::ButtonController::new(); 61 - 62 58 loop { 63 59 match game.game_state { 64 60 GameState::Start => { ··· 67 63 if let Some(letter) = text_layout.next() { 68 64 text_renderer.show(&mut bg, &letter); 69 65 } 70 - if button_controller.is_just_pressed(Button::A) { 66 + if button_controller.is_just_pressed(Button::START) { 71 67 game.game_state = GameState::Playing; 72 68 } 73 - // Scope the graphics borrow to this arm only 74 69 let mut gfx = gba.graphics.get(); 75 70 let mut frame = gfx.frame(); 76 71 ··· 79 74 frame.commit(); 80 75 } 81 76 GameState::Playing => { 82 - game.run(&mut gba); 77 + game.run(&mut gba); 83 78 } 84 79 GameState::GameOver => {} 85 80 } 86 - 87 81 } 88 82 } 89 - 90 - 91 - pub enum GameState { 92 - Start, 93 - Playing, 94 - GameOver, 95 - } 96 - 97 - struct GameLoop { 98 - still_going: bool, 99 - score: u16, 100 - game_state: GameState 101 - } 102 - 103 - impl GameLoop { 104 - pub fn new() -> Self { 105 - Self { 106 - still_going: true, 107 - score: 0, 108 - game_state: GameState::Start, 109 - } 110 - } 111 - 112 - pub fn run(&mut self, gba: &mut agb::Gba) { 113 - let mut gfx = gba.graphics.get(); 114 - 115 - let mut button_controller = agb::input::ButtonController::new(); 116 - let mut goose = Goose::new(); 117 - 118 - loop { 119 - button_controller.update(); 120 - let mut frame = gfx.frame(); 121 - 122 - let just_pressed = button_controller.is_just_pressed(Button::A); 123 - goose.move_goose(just_pressed); 124 - goose.show(&mut frame); 125 - frame.commit(); 126 - } 127 - } 128 - } 129 - 130 - 131 - 132 - 133 - /// I really don't have a better name for these 134 - #[derive(Copy, Clone)] 135 - pub enum GooseState { 136 - Zero = 0, 137 - One = 1, 138 - Two = 2, 139 - Three = 3, 140 - } 141 - 142 - 143 - struct Goose { 144 - pos: Vector2D<Fixed>, 145 - velocity: Vector2D<Fixed>, 146 - current_state: GooseState, 147 - frame_counter: u16, 148 - } 149 - 150 - impl Goose { 151 - pub fn new() -> Self { 152 - Self { 153 - pos: vec2(num!(5), num!(5)), 154 - velocity: vec2(num!(0.20), num!(0)), 155 - current_state: GooseState::Zero, 156 - frame_counter: 0, 157 - } 158 - } 159 - 160 - pub fn show(&mut self, frame: &mut GraphicsFrame) { 161 - let _ = Object::new(goose_sprites::GOOSE.sprite(self.current_state as usize)) 162 - .set_pos(self.pos.round()) 163 - .set_priority(Priority::P1) 164 - .show(frame); 165 - 166 - } 167 - 168 - fn animate(&mut self) { 169 - // Advance animation every 8 frames 170 - self.frame_counter = self.frame_counter.wrapping_add(1); 171 - if self.frame_counter % 8 == 0 { 172 - self.current_state = match self.current_state { 173 - GooseState::Zero => GooseState::One, 174 - GooseState::One => GooseState::Two, 175 - GooseState::Two => GooseState::Three, 176 - GooseState::Three => GooseState::Zero, 177 - } 178 - } 179 - } 180 - 181 - fn reset(&mut self) { 182 - self.pos = vec2(num!(5), num!(5)); 183 - self.velocity = vec2(num!(0), num!(0)); 184 - } 185 - 186 - pub fn move_goose(&mut self, button_pressed: bool) { 187 - // Flap impulse 188 - if button_pressed { 189 - // Give an upward kick similar to Flappy Bird 190 - self.velocity.y = num!(-1); 191 - }else{ 192 - // Gravity pulls down constantly 193 - self.velocity.y += num!(0.02); 194 - } 195 - 196 - 197 - // Apply velocity to position 198 - self.pos += self.velocity; 199 - 200 - // Animate sprite 201 - self.animate(); 202 - 203 - // Screen bounds checking (top-left is 0,0; bottom-right is 240,160) 204 - // Loop/reset when going out of vertical bounds 205 - if self.pos.y < num!(0) { 206 - self.pos.y = num!(0); 207 - self.velocity.y = num!(0); 208 - } 209 - if self.pos.y >= Num::<i32, 8>::from_raw((SCREEN_H as i32) << 8) { 210 - // Off the bottom of the screen — reset to start like a flappy-bird loop 211 - self.reset(); 212 - } 213 - 214 - // Keep X within screen horizontally (optional: clamp) 215 - if self.pos.x < num!(0) { 216 - self.pos.x = num!(0); 217 - } 218 - let max_x = Num::<i32, 8>::from_raw((SCREEN_W as i32 - 1) << 8); 219 - if self.pos.x > max_x { 220 - self.pos.x = max_x; 221 - } 222 - } 223 - }