From 8bd46de2a681f7bfbd93778b952b06f270feab94 Mon Sep 17 00:00:00 2001 From: EvanTheProgrammer <86786575+EvanTheProgrammer@users.noreply.github.com> Date: Thu, 16 Nov 2023 15:05:32 -0500 Subject: [PATCH] Add an example for a basic pong game with mode 3 graphics and drawing on vblank. (#189) * Create mode3_pong_example_game.rs * Update mode3_pong_example_game.rs * Update mode3_pong_example_game.rs --- examples/mode3_pong_example_game.rs | 159 ++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 examples/mode3_pong_example_game.rs diff --git a/examples/mode3_pong_example_game.rs b/examples/mode3_pong_example_game.rs new file mode 100644 index 00000000..67ec9cb2 --- /dev/null +++ b/examples/mode3_pong_example_game.rs @@ -0,0 +1,159 @@ +/* +* Made by Evan Goemer +* Discord: @evangoemer +*/ + +#![no_std] +#![no_main] + +use gba::{prelude::*, mem_fns::__aeabi_memset}; + +const SCREEN_WIDTH: u16 = 240; +const SCREEN_HEIGHT: u16 = 160; + +const PADDLE_WIDTH: u16 = 4; +const PADDLE_HEIGHT: u16 = 20; +const BALL_SIZE: u16 = 2; + +struct Paddle { + x: u16, + y: u16, +} + +struct Ball { + x: u16, + y: u16, + dx: i16, + dy: i16, +} + +impl Paddle { + fn new(x: u16, y: u16) -> Self { + Self { + x, + y, + } + } + + fn update(&mut self) { + let keys = KEYINPUT.read(); + if keys.up() && self.y > 1 { + self.y -= 1; + } + + if keys.down() && self.y + PADDLE_HEIGHT + 1 < SCREEN_HEIGHT { + self.y += 1; + } + } +} + +impl Ball { + fn new(x: u16, y: u16) -> Self { + Self { x, y, dx: 1, dy: 1 } + } + + fn update(&mut self, paddle1: &Paddle, paddle2: &Paddle) { + if self.y <= 0 || self.y + BALL_SIZE >= SCREEN_HEIGHT { + self.dy = -self.dy; + } + + if self.x + BALL_SIZE >= paddle1.x + && self.x <= paddle1.x + PADDLE_WIDTH + && self.y + BALL_SIZE >= paddle1.y + && self.y <= paddle1.y + PADDLE_HEIGHT + { + self.dx = -self.dx; + self.dy = -self.dy; + } + + if self.x + BALL_SIZE >= paddle2.x + && self.x <= paddle2.x + PADDLE_WIDTH + && self.y + BALL_SIZE >= paddle2.y + && self.y <= paddle2.y + PADDLE_HEIGHT + { + self.dx = -self.dx; + self.dy = -self.dy; + } + + + if self.x + BALL_SIZE <= 1 + BALL_SIZE { + self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; + self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; + self.dx = 1; + self.dy = 1; + } + + if self.x >= SCREEN_WIDTH - BALL_SIZE - 1 { + self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2; + self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2; + self.dx = -1; + self.dy = 1; + } + self.x = (self.x as i16 + self.dx) as u16; + self.y = (self.y as i16 + self.dy) as u16; + } +} + +static SPRITE_POSITIONS: [GbaCell; 6] = [ + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), + GbaCell::new(0), +]; + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[no_mangle] +fn main() -> ! { + DISPCNT.write( + DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true), + ); + + RUST_IRQ_HANDLER.write(Some(draw_sprites)); + DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true)); + IE.write(IrqBits::VBLANK); + IME.write(true); + + let mut left_paddle = Paddle::new(10, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2); + let mut right_paddle = Paddle::new(SCREEN_WIDTH as u16 - 10 - PADDLE_WIDTH, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2); + let mut ball = Ball::new(SCREEN_WIDTH as u16 / 2, SCREEN_HEIGHT as u16 / 2); + + loop { + left_paddle.update(); + right_paddle.update(); + ball.update(&left_paddle, &right_paddle); + + SPRITE_POSITIONS[0].write(left_paddle.x); + SPRITE_POSITIONS[1].write(left_paddle.y); + SPRITE_POSITIONS[2].write(right_paddle.x); + SPRITE_POSITIONS[3].write(right_paddle.y); + SPRITE_POSITIONS[4].write(ball.x); + SPRITE_POSITIONS[5].write(ball.y); + + VBlankIntrWait(); + } +} + +extern "C" fn draw_sprites(_bits: IrqBits) { + unsafe { + let p = VIDEO3_VRAM.as_usize() as *mut u8; + __aeabi_memset(p, 240*160*2, 0) + } + + draw_rect(SPRITE_POSITIONS[0].read(), SPRITE_POSITIONS[1].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::WHITE); + draw_rect(SPRITE_POSITIONS[2].read(), SPRITE_POSITIONS[3].read(), PADDLE_WIDTH, PADDLE_HEIGHT, Color::WHITE); + draw_rect(SPRITE_POSITIONS[4].read(), SPRITE_POSITIONS[5].read(), BALL_SIZE, BALL_SIZE, Color::WHITE); +} + +fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) { + for i in 0..width { + for j in 0..height { + VIDEO3_VRAM.index((x + i) as usize, (y + j) as usize).write(color); + } + } +}