mirror of
https://github.com/italicsjenga/agb.git
synced 2025-01-10 00:51:34 +11:00
Add a no game (#427)
This adds a "no game" to replace the template. * Inspired by how Love2D has a default game that says "No Game". * This screen: https://youtube.com/clip/Ugkx6atqwerxyyUSiVrFhmAh7pK2xNgjHxI9 - [x] Changelog updated
This commit is contained in:
commit
84f22c0b30
|
@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Changed the default template game
|
||||||
|
|
||||||
## [0.15.0] - 2023/04/25
|
## [0.15.0] - 2023/04/25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -239,9 +239,36 @@ impl ToTokens for ByteString<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn include_colours_inner(input: TokenStream) -> TokenStream {
|
||||||
|
let input_filename = parse_macro_input!(input as LitStr);
|
||||||
|
let input_filename = input_filename.value();
|
||||||
|
|
||||||
|
let root = std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
|
||||||
|
let input_filename = Path::new(&root).join(input_filename);
|
||||||
|
|
||||||
|
let image = Image::load_from_file(Path::new(&input_filename));
|
||||||
|
|
||||||
|
let mut palette_data = Vec::with_capacity(image.width * image.height);
|
||||||
|
for y in 0..image.height {
|
||||||
|
for x in 0..image.width {
|
||||||
|
palette_data.push(image.colour(x, y).to_rgb15())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = input_filename.to_string_lossy();
|
||||||
|
|
||||||
|
TokenStream::from(quote! {
|
||||||
|
{
|
||||||
|
const _: &[u8] = include_bytes!(#filename);
|
||||||
|
[#(#palette_data),*]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
|
pub fn include_aseprite_inner(input: TokenStream) -> TokenStream {
|
||||||
let parser = Punctuated::<LitStr, syn::Token![,]>::parse_separated_nonempty;
|
let parser = Punctuated::<LitStr, syn::Token![,]>::parse_terminated;
|
||||||
let parsed = match parser.parse(input) {
|
let parsed = match parser.parse(input) {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
Err(e) => return e.to_compile_error().into(),
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
@ -432,8 +459,8 @@ fn palette_tile_data(
|
||||||
|
|
||||||
let mut tile_data = Vec::new();
|
let mut tile_data = Vec::new();
|
||||||
|
|
||||||
for image in images {
|
for (image_idx, image) in images.iter().enumerate() {
|
||||||
add_image_to_tile_data(&mut tile_data, image, optimiser, 0)
|
add_image_to_tile_data(&mut tile_data, image, optimiser, image_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
let tile_data = collapse_to_4bpp(&tile_data);
|
let tile_data = collapse_to_4bpp(&tile_data);
|
||||||
|
|
|
@ -138,17 +138,7 @@ impl Palette16Optimiser {
|
||||||
while !unsatisfied_palettes.is_empty() {
|
while !unsatisfied_palettes.is_empty() {
|
||||||
let palette = self.find_maximal_palette_for(&unsatisfied_palettes);
|
let palette = self.find_maximal_palette_for(&unsatisfied_palettes);
|
||||||
|
|
||||||
for test_palette in unsatisfied_palettes.clone() {
|
unsatisfied_palettes.retain(|test_palette| !test_palette.is_satisfied_by(&palette));
|
||||||
if test_palette.is_satisfied_by(&palette) {
|
|
||||||
unsatisfied_palettes.remove(&test_palette);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, overall_palette) in self.palettes.iter().enumerate() {
|
|
||||||
if overall_palette.is_satisfied_by(&palette) {
|
|
||||||
assignments[i] = optimised_palettes.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
optimised_palettes.push(palette);
|
optimised_palettes.push(palette);
|
||||||
|
|
||||||
|
@ -157,6 +147,13 @@ impl Palette16Optimiser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i, overall_palette) in self.palettes.iter().enumerate() {
|
||||||
|
assignments[i] = optimised_palettes
|
||||||
|
.iter()
|
||||||
|
.position(|palette| overall_palette.is_satisfied_by(palette))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
Palette16OptimisationResults {
|
Palette16OptimisationResults {
|
||||||
optimised_palettes,
|
optimised_palettes,
|
||||||
assignments,
|
assignments,
|
||||||
|
|
7
agb/examples/no_game.rs
Normal file
7
agb/examples/no_game.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
#[agb::entry]
|
||||||
|
fn main(gba: agb::Gba) -> ! {
|
||||||
|
agb::no_game(gba);
|
||||||
|
}
|
BIN
agb/gfx/pastel.png
Normal file
BIN
agb/gfx/pastel.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 355 B |
|
@ -94,6 +94,7 @@ macro_rules! align_bytes {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! include_aseprite {
|
macro_rules! include_aseprite {
|
||||||
($($aseprite_path: expr),*) => {{
|
($($aseprite_path: expr),*) => {{
|
||||||
|
#[allow(unused_imports)]
|
||||||
use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics};
|
use $crate::display::object::{Size, Sprite, Tag, TagMap, Graphics};
|
||||||
use $crate::display::palette16::Palette16;
|
use $crate::display::palette16::Palette16;
|
||||||
use $crate::align_bytes;
|
use $crate::align_bytes;
|
||||||
|
@ -367,4 +368,11 @@ impl Size {
|
||||||
Size::S32x64 => (32, 64),
|
Size::S32x64 => (32, 64),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Returns the width and height of the size in pixels.
|
||||||
|
pub const fn to_tiles_width_height(self) -> (usize, usize) {
|
||||||
|
let wh = self.to_width_height();
|
||||||
|
(wh.0 / 8, wh.1 / 8)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use core::ptr::NonNull;
|
use core::{alloc::Allocator, ptr::NonNull};
|
||||||
|
|
||||||
use alloc::rc::{Rc, Weak};
|
use alloc::{
|
||||||
|
alloc::Global,
|
||||||
|
boxed::Box,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd},
|
agb_alloc::{block_allocator::BlockAllocator, bump_allocator::StartEnd},
|
||||||
|
@ -285,34 +290,61 @@ impl Default for SpriteLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sprite data that can be used to create sprites in vram.
|
/// Sprite data that can be used to create sprites in vram.
|
||||||
pub struct DynamicSprite<'a> {
|
pub struct DynamicSprite<A: Allocator = Global> {
|
||||||
data: &'a [u8],
|
data: Box<[u8], A>,
|
||||||
size: Size,
|
size: Size,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicSprite<'_> {
|
impl DynamicSprite {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Creates a new dynamic sprite from underlying bytes. Note that despite
|
/// Creates a new dynamic sprite.
|
||||||
/// being an array of u8, this must be aligned to at least a 2 byte
|
pub fn new(size: Size) -> Self {
|
||||||
/// boundary.
|
Self::new_in(size, Global)
|
||||||
pub fn new(data: &[u8], size: Size) -> DynamicSprite {
|
|
||||||
let ptr = &data[0] as *const _ as usize;
|
|
||||||
if ptr % 2 != 0 {
|
|
||||||
panic!("data is not aligned to a 2 byte boundary");
|
|
||||||
}
|
}
|
||||||
if data.len() != size.number_of_tiles() * BYTES_PER_TILE_4BPP {
|
|
||||||
panic!(
|
|
||||||
"data is not of expected length, got {} expected {}",
|
|
||||||
data.len(),
|
|
||||||
size.number_of_tiles() * BYTES_PER_TILE_4BPP
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<A: Allocator> DynamicSprite<A> {
|
||||||
|
#[must_use]
|
||||||
|
/// Creates a new dynamic sprite of a given size in a given allocator.
|
||||||
|
pub fn new_in(size: Size, allocator: A) -> Self {
|
||||||
|
let num_bytes = size.number_of_tiles() * BYTES_PER_TILE_4BPP;
|
||||||
|
let mut data = Vec::with_capacity_in(num_bytes, allocator);
|
||||||
|
|
||||||
|
data.resize(num_bytes, 0);
|
||||||
|
|
||||||
|
let data = data.into_boxed_slice();
|
||||||
|
|
||||||
DynamicSprite { data, size }
|
DynamicSprite { data, size }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the pixel of a sprite to a given paletted pixel. Panics if the
|
||||||
|
/// coordinate is out of range of the sprite or if the paletted pixel is
|
||||||
|
/// greater than 4 bits.
|
||||||
|
pub fn set_pixel(&mut self, x: usize, y: usize, paletted_pixel: usize) {
|
||||||
|
assert!(paletted_pixel < 0x10);
|
||||||
|
|
||||||
|
let (sprite_pixel_x, sprite_pixel_y) = self.size.to_width_height();
|
||||||
|
assert!(x < sprite_pixel_x, "x too big for sprite size");
|
||||||
|
assert!(y < sprite_pixel_y, "y too big for sprite size");
|
||||||
|
|
||||||
|
let (sprite_tile_x, _) = self.size.to_tiles_width_height();
|
||||||
|
|
||||||
|
let (adjust_tile_x, adjust_tile_y) = (x / 8, y / 8);
|
||||||
|
|
||||||
|
let tile_number_to_modify = adjust_tile_x + adjust_tile_y * sprite_tile_x;
|
||||||
|
|
||||||
|
let byte_to_modify_in_tile = x / 2 + y * 4;
|
||||||
|
let byte_to_modify = tile_number_to_modify * BYTES_PER_TILE_4BPP + byte_to_modify_in_tile;
|
||||||
|
let mut byte = self.data[byte_to_modify];
|
||||||
|
let parity = (x & 0b1) * 4;
|
||||||
|
|
||||||
|
byte = (byte & !(0b1111 << parity)) | ((paletted_pixel as u8) << parity);
|
||||||
|
self.data[byte_to_modify] = byte;
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to copy the sprite to vram to be used to set object sprites.
|
/// Tries to copy the sprite to vram to be used to set object sprites.
|
||||||
pub fn try_vram(&self, palette: PaletteVram) -> Result<SpriteVram, LoaderError> {
|
pub fn try_vram(&self, palette: PaletteVram) -> Result<SpriteVram, LoaderError> {
|
||||||
SpriteVram::new(self.data, self.size, palette)
|
SpriteVram::new(&self.data, self.size, palette)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|
|
@ -27,3 +27,12 @@ impl Palette16 {
|
||||||
Layout::new::<Self>()
|
Layout::new::<Self>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! include_palette {
|
||||||
|
($palette:literal) => {
|
||||||
|
$crate::include_colours_inner!($palette)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use include_palette;
|
||||||
|
|
|
@ -104,6 +104,9 @@ pub use agb_image_converter::include_aseprite_inner;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use agb_image_converter::include_font as include_font_inner;
|
pub use agb_image_converter::include_font as include_font_inner;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use agb_image_converter::include_colours_inner;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! include_font {
|
macro_rules! include_font {
|
||||||
($font_path: literal, $font_size: literal) => {{
|
($font_path: literal, $font_size: literal) => {{
|
||||||
|
@ -165,6 +168,11 @@ pub mod syscall;
|
||||||
/// Interactions with the internal timers
|
/// Interactions with the internal timers
|
||||||
pub mod timer;
|
pub mod timer;
|
||||||
|
|
||||||
|
mod no_game;
|
||||||
|
|
||||||
|
/// Default game
|
||||||
|
pub use no_game::no_game;
|
||||||
|
|
||||||
pub(crate) mod arena;
|
pub(crate) mod arena;
|
||||||
|
|
||||||
pub use {agb_alloc::ExternalAllocator, agb_alloc::InternalAllocator};
|
pub use {agb_alloc::ExternalAllocator, agb_alloc::InternalAllocator};
|
||||||
|
|
213
agb/src/no_game.rs
Normal file
213
agb/src/no_game.rs
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
//! The no game screen is what is displayed if there isn't a game made yet.
|
||||||
|
|
||||||
|
use agb_fixnum::{num, Num, Vector2D};
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use alloc::{boxed::Box, vec};
|
||||||
|
|
||||||
|
use crate::display::object::{DynamicSprite, PaletteVram, Size, SpriteVram};
|
||||||
|
use crate::display::palette16::Palette16;
|
||||||
|
use crate::{
|
||||||
|
display::{object::ObjectUnmanaged, HEIGHT, WIDTH},
|
||||||
|
include_palette,
|
||||||
|
interrupt::VBlank,
|
||||||
|
};
|
||||||
|
|
||||||
|
const PALETTE: &[u16] = &include_palette!("gfx/pastel.png");
|
||||||
|
|
||||||
|
fn letters() -> Vec<Vec<Vector2D<Num<i32, 8>>>> {
|
||||||
|
vec![
|
||||||
|
// N
|
||||||
|
vec![
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(1.)).into(),
|
||||||
|
(num!(2.), num!(2.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(3.), num!(1.)).into(),
|
||||||
|
(num!(3.), num!(2.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
],
|
||||||
|
// O
|
||||||
|
vec![
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(1.), num!(3.)).into(),
|
||||||
|
(num!(2.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(2.)).into(),
|
||||||
|
(num!(3.), num!(1.)).into(),
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(2.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(0.)).into(),
|
||||||
|
],
|
||||||
|
// G
|
||||||
|
vec![
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(2.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(1.), num!(3.)).into(),
|
||||||
|
(num!(2.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(2.25)).into(),
|
||||||
|
(num!(3.), num!(1.5)).into(),
|
||||||
|
(num!(2.), num!(1.5)).into(),
|
||||||
|
],
|
||||||
|
// A
|
||||||
|
vec![
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(2.)).into(),
|
||||||
|
(num!(3.), num!(1.)).into(),
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(2.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(1.5)).into(),
|
||||||
|
(num!(2.), num!(1.5)).into(),
|
||||||
|
],
|
||||||
|
// M
|
||||||
|
vec![
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(2.)).into(),
|
||||||
|
(num!(3.), num!(1.)).into(),
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(1.5), num!(1.5)).into(),
|
||||||
|
(num!(0.75), num!(0.75)).into(),
|
||||||
|
(num!(2.25), num!(0.75)).into(),
|
||||||
|
],
|
||||||
|
// E
|
||||||
|
vec![
|
||||||
|
(num!(0.), num!(0.)).into(),
|
||||||
|
(num!(0.), num!(1.)).into(),
|
||||||
|
(num!(0.), num!(2.)).into(),
|
||||||
|
(num!(0.), num!(3.)).into(),
|
||||||
|
(num!(1.), num!(3.)).into(),
|
||||||
|
(num!(2.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(3.)).into(),
|
||||||
|
(num!(3.), num!(0.)).into(),
|
||||||
|
(num!(2.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(0.)).into(),
|
||||||
|
(num!(1.), num!(1.5)).into(),
|
||||||
|
(num!(2.), num!(1.5)).into(),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_sprites() -> Box<[SpriteVram]> {
|
||||||
|
let mut sprites = Vec::new();
|
||||||
|
|
||||||
|
// generate palettes
|
||||||
|
|
||||||
|
let palettes: Vec<PaletteVram> = PALETTE
|
||||||
|
.chunks(15)
|
||||||
|
.map(|x| {
|
||||||
|
core::iter::once(0)
|
||||||
|
.chain(x.iter().copied())
|
||||||
|
.chain(core::iter::repeat(0))
|
||||||
|
.take(16)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.map(|palette| {
|
||||||
|
let palette = Palette16::new(palette.try_into().unwrap());
|
||||||
|
PaletteVram::new(&palette).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// generate sprites
|
||||||
|
let mut sprite = DynamicSprite::new(Size::S8x8);
|
||||||
|
for (palette, colour) in (0..PALETTE.len()).map(|x| (x / 15, x % 15)) {
|
||||||
|
for y in 0..8 {
|
||||||
|
for x in 0..8 {
|
||||||
|
sprite.set_pixel(x, y, colour + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sprites.push(sprite.to_vram(palettes[palette].clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
sprites.into_boxed_slice()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn no_game(mut gba: crate::Gba) -> ! {
|
||||||
|
let (mut oam, _) = gba.display.object.get_unmanaged();
|
||||||
|
|
||||||
|
let squares = generate_sprites();
|
||||||
|
|
||||||
|
let mut letter_positons = Vec::new();
|
||||||
|
|
||||||
|
let square_positions = {
|
||||||
|
let mut s = letters();
|
||||||
|
for letter in s.iter_mut() {
|
||||||
|
letter.sort_by_key(|a| a.magnitude_squared());
|
||||||
|
}
|
||||||
|
s
|
||||||
|
};
|
||||||
|
for (letter_idx, letter_parts) in square_positions.iter().enumerate() {
|
||||||
|
for part in letter_parts.iter() {
|
||||||
|
let position = part
|
||||||
|
.hadamard((8, 10).into())
|
||||||
|
.hadamard((num!(3.) / 2, num!(3.) / 2).into());
|
||||||
|
|
||||||
|
let letter_pos = Vector2D::new(
|
||||||
|
60 * (1 + letter_idx as i32 - ((letter_idx >= 2) as i32 * 3)),
|
||||||
|
70 * ((letter_idx >= 2) as i32),
|
||||||
|
);
|
||||||
|
|
||||||
|
letter_positons.push(position + letter_pos.change_base());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let bottom_right = letter_positons
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.max_by_key(|x| x.manhattan_distance())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let difference = (Vector2D::new(WIDTH - 8, HEIGHT - 8).change_base() - bottom_right) / 2;
|
||||||
|
|
||||||
|
for pos in letter_positons.iter_mut() {
|
||||||
|
*pos += difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut time: Num<i32, 8> = num!(0.);
|
||||||
|
let time_delta: Num<i32, 8> = num!(0.025);
|
||||||
|
|
||||||
|
let vblank = VBlank::get();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
time += time_delta;
|
||||||
|
time %= 1;
|
||||||
|
let letters: Vec<ObjectUnmanaged> = letter_positons
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, position)| {
|
||||||
|
let time = time + Num::<i32, 8>::new(idx as i32) / 128;
|
||||||
|
(idx, *position + Vector2D::new(time.sin(), time.cos()) * 10)
|
||||||
|
})
|
||||||
|
.map(|(idx, pos)| {
|
||||||
|
let mut obj = ObjectUnmanaged::new(squares[idx % squares.len()].clone());
|
||||||
|
obj.show().set_position(pos.floor());
|
||||||
|
obj
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
vblank.wait_for_vblank();
|
||||||
|
for (obj, slot) in letters.iter().zip(oam.iter()) {
|
||||||
|
slot.set(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,22 +14,10 @@
|
||||||
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
|
||||||
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
|
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
|
||||||
|
|
||||||
use agb::{display, syscall};
|
|
||||||
|
|
||||||
// The main function must take 1 arguments and never return. The agb::entry decorator
|
// The main function must take 1 arguments and never return. The agb::entry decorator
|
||||||
// ensures that everything is in order. `agb` will call this after setting up the stack
|
// ensures that everything is in order. `agb` will call this after setting up the stack
|
||||||
// and interrupt handlers correctly. It will also handle creating the `Gba` struct for you.
|
// and interrupt handlers correctly. It will also handle creating the `Gba` struct for you.
|
||||||
#[agb::entry]
|
#[agb::entry]
|
||||||
fn main(mut gba: agb::Gba) -> ! {
|
fn main(mut gba: agb::Gba) -> ! {
|
||||||
let mut bitmap = gba.display.video.bitmap3();
|
agb::no_game(gba);
|
||||||
|
|
||||||
for x in 0..display::WIDTH {
|
|
||||||
let y = syscall::sqrt(x << 6);
|
|
||||||
let y = (display::HEIGHT - y).clamp(0, display::HEIGHT - 1);
|
|
||||||
bitmap.draw_point(x, y, 0x001F);
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
syscall::halt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue