add amplitude

This commit is contained in:
Corwin 2023-04-25 20:32:28 +01:00
parent 3ec9bd2610
commit e78a6624ff
No known key found for this signature in database
12 changed files with 1131 additions and 0 deletions

View file

@ -0,0 +1,14 @@
[unstable]
build-std = ["core", "alloc"]
build-std-features = ["compiler-builtins-mem"]
[build]
target = "thumbv4t-none-eabi"
[target.thumbv4t-none-eabi]
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
runner = "mgba-qt"
[target.armv4t-none-eabi]
rustflags = ["-Clink-arg=-Tgba.ld", "-Ctarget-cpu=arm7tdmi"]
runner = "mgba-qt"

407
examples/amplitude/Cargo.lock generated Normal file
View file

@ -0,0 +1,407 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler32"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "agb"
version = "0.14.0"
dependencies = [
"agb_fixnum",
"agb_hashmap",
"agb_image_converter",
"agb_macros",
"agb_sound_converter",
"bare-metal",
"bitflags 2.1.0",
"modular-bitfield",
"rustc-hash",
]
[[package]]
name = "agb_fixnum"
version = "0.14.0"
dependencies = [
"agb_macros",
]
[[package]]
name = "agb_hashmap"
version = "0.14.0"
dependencies = [
"rustc-hash",
]
[[package]]
name = "agb_image_converter"
version = "0.14.0"
dependencies = [
"asefile",
"fontdue",
"image",
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]]
name = "agb_macros"
version = "0.14.0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]]
name = "agb_sound_converter"
version = "0.14.0"
dependencies = [
"hound",
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]]
name = "amplitude"
version = "0.1.0"
dependencies = [
"agb",
]
[[package]]
name = "asefile"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a71de7aecd2d0a76ec90fde2c443d12667c737d92de76bd187f101eca37891"
dependencies = [
"bitflags 1.3.2",
"byteorder",
"flate2",
"image",
"log",
"nohash",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bare-metal"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
[[package]]
name = "bytemuck"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "deflate"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
dependencies = [
"adler32",
"byteorder",
]
[[package]]
name = "flate2"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide 0.6.2",
]
[[package]]
name = "fontdue"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0793f5137567643cf65ea42043a538804ff0fbf288649e2141442b602d81f9bc"
dependencies = [
"hashbrown",
"ttf-parser",
]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]]
name = "hound"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d13cdbd5dbb29f9c88095bbdc2590c9cba0d0a1269b983fef6b2cdd7e9f4db1"
[[package]]
name = "image"
version = "0.23.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"num-iter",
"num-rational",
"num-traits",
"png",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "modular-bitfield"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74"
dependencies = [
"modular-bitfield-impl",
"static_assertions",
]
[[package]]
name = "modular-bitfield-impl"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "nohash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f889fb66f7acdf83442c35775764b51fed3c606ab9cee51500dbde2cf528ca"
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "png"
version = "0.16.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"deflate",
"miniz_oxide 0.3.7",
]
[[package]]
name = "proc-macro2"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "ttf-parser"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd"
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

View file

@ -0,0 +1,20 @@
[package]
name = "amplitude"
version = "0.1.0"
authors = [""]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
agb = { version = "0.14.0", path = "../../agb" }
[profile.dev]
opt-level = 2
debug = true
[profile.release]
panic = "abort"
lto = true
debug = true
codegen-units = 1

View file

@ -0,0 +1,72 @@
# AGBRS template
## A basic template example for agb projects
This makes getting started with a new project for the Game Boy Advance in rust really simple, by providing
all the boiler plate files for you.
## Building
### Prerequisites
You will need the following installed in order to build and run this project:
* A recent version of `rustup`. See the [rust website](https://www.rust-lang.org/tools/install) for instructions for your operating system
* `arm-none-eabi-binutils` for assembling and linking
* Windows: [GNU Arm Embedded Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads).
Make sure you select "Add path to environment variable" during the install
* Debian and derivatives (e.g. Ubuntu, raspberry pi OS, linux mint): `sudo apt install binutils-arm-none-eabi`
* Arch linux and derivatives: `sudo pacman -S arm-none-eabi-binutils`
You will also want to install an emulator. The best support in agb is with [mgba](https://mgba.io), with
`println!` support via `agb::println!` but any emulator should work. You'll get the best experience if
`mgba-qt` is in your `PATH`.
If you want to run your game on real hardware, you will also need to install `agb-gbafix` which you can do after installing
rust with the following: `cargo install agb-gbafix`. This is not required if you are only running your game in an emulator.
### Running in an emulator
Once you have the prerequisites installed, you should be able to build using
```sh
cargo build
```
or in release mode (recommended for the final version to ship to players)
```sh
cargo build --release
```
The resulting file will be in `target/thumbv4t-none-eabi/debug/<your game>` or `target/thumbv4t-none-eabi/release/<your game>` depending on
whether you did a release or debug build.
If you have `mgba-qt` in your path, you will be able to run your game with
```sh
cargo run
```
or in release mode
```sh
cargo run --release
```
## Starting development
You can find the documentation for agb [here](https://docs.rs/agb/latest/agb/).
You may also want to change the package name and version in `Cargo.toml` before you start.
## Shipping a .gba file for real hardware
To make a game run on real hardware, you will need to convert the built file into a file suitable for
running on the real thing.
First build the binary in release mode using the instructions above, then do the following:
```sh
agb-gbafix target/thumbv4t-none-eabi/release/<your game> -o <your game>.gba
```

117
examples/amplitude/gba.ld Normal file
View file

@ -0,0 +1,117 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(__start)
EXTERN(__RUST_INTERRUPT_HANDLER)
EXTERN(__agbabi_memset)
EXTERN(__agbabi_memcpy)
MEMORY {
ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K
iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K
rom (rx) : ORIGIN = 0x08000000, LENGTH = 32M
}
__text_start = ORIGIN(rom);
INPUT (agb.a)
SECTIONS {
. = __text_start;
.text : {
KEEP(*(.crt0));
*(.crt0 .crt0*);
*(.text .text*);
. = ALIGN(4);
} > rom
__text_end = .;
.rodata : {
*(.rodata .rodata.*);
. = ALIGN(4);
} > rom
__iwram_rom_start = .;
.iwram : {
__iwram_data_start = ABSOLUTE(.);
*(.iwram .iwram.*);
. = ALIGN(4);
*(.text_iwram .text_iwram.*);
. = ALIGN(4);
__iwram_data_end = ABSOLUTE(.);
} > iwram AT>rom
. = __iwram_rom_start + (__iwram_data_end - __iwram_data_start);
__ewram_rom_start = .;
.ewram : {
__ewram_data_start = ABSOLUTE(.);
*(.ewram .ewram.*);
. = ALIGN(4);
*(.data .data.*);
. = ALIGN(4);
__ewram_data_end = ABSOLUTE(.);
} > ewram AT>rom
.bss : {
*(.bss .bss.*);
. = ALIGN(4);
__iwram_end = ABSOLUTE(.);
} > iwram
__iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start;
__iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2;
__ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start;
__ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2;
.shstrtab : {
*(.shstrtab)
}
/* debugging sections */
/* Stabs */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.debug_ranges 0 : { *(.debug_ranges) }
/* discard anything not already mentioned */
/DISCARD/ : { *(*) }
}

View file

@ -0,0 +1,115 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(__start)
EXTERN(__RUST_INTERRUPT_HANDLER)
EXTERN(__agbabi_memset)
EXTERN(__agbabi_memcpy)
MEMORY {
ewram (w!x) : ORIGIN = 0x02000000, LENGTH = 256K
iwram (w!x) : ORIGIN = 0x03000000, LENGTH = 32K
}
__text_start = ORIGIN(ewram);
INPUT (agb.a)
SECTIONS {
. = __text_start;
.text : {
KEEP(*(.crt0));
*(.crt0 .crt0*);
*(.text .text*);
. = ALIGN(4);
} > rom
__text_end = .;
.rodata : {
*(.rodata .rodata.*);
. = ALIGN(4);
} > ewram
__iwram_rom_start = .;
.iwram : {
__iwram_data_start = ABSOLUTE(.);
*(.iwram .iwram.*);
. = ALIGN(4);
*(.text_iwram .text_iwram.*);
. = ALIGN(4);
__iwram_data_end = ABSOLUTE(.);
} > iwram AT>ewram
. = __iwram_rom_start + (__iwram_data_end - __iwram_data_start);
__ewram_rom_start = .;
.ewram : {
__ewram_data_start = ABSOLUTE(.);
*(.ewram .ewram.*);
. = ALIGN(4);
*(.data .data.*);
. = ALIGN(4);
__ewram_data_end = ABSOLUTE(.);
} > ewram AT>ewram
.bss : {
*(.bss .bss.*);
. = ALIGN(4);
__iwram_end = ABSOLUTE(.);
} > iwram
__iwram_rom_length_bytes = __iwram_data_end - __iwram_data_start;
__iwram_rom_length_halfwords = (__iwram_rom_length_bytes + 1) / 2;
__ewram_rom_length_bytes = __ewram_data_end - __ewram_data_start;
__ewram_rom_length_halfwords = (__ewram_rom_length_bytes + 1) / 2;
.shstrtab : {
*(.shstrtab)
}
/* debugging sections */
/* Stabs */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
.debug_ranges 0 : { *(.debug_ranges) }
/* discard anything not already mentioned */
/DISCARD/ : { *(*) }
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"
components = ["rust-src", "clippy", "rustfmt"]

View file

@ -0,0 +1,373 @@
#![no_std]
#![no_main]
#![cfg_attr(test, feature(custom_test_frameworks))]
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
extern crate alloc;
use agb::{
display::{
self,
affine::{self, AffineMatrix},
object::{
AffineMatrixInstance, AffineMode, Graphics, OamIterator, ObjectUnmanaged, Sprite,
SpriteLoader, SpriteVram, Tag, TagMap,
},
palette16::Palette16,
},
fixnum::{num, Num, Vector2D},
include_aseprite,
input::{Button, ButtonController},
rng,
};
use alloc::{boxed::Box, collections::VecDeque, vec::Vec};
type Number = Num<i32, 8>;
struct Saw {
object: ObjectUnmanaged,
position: Vector2D<Number>,
angle: Number,
rotation_speed: Number,
}
enum Colour {
Red,
Blue,
}
struct Circle {
colour: Colour,
position: Vector2D<Number>,
}
#[derive(Clone)]
struct SpriteCache {
saw: SpriteVram,
blue: SpriteVram,
red: SpriteVram,
numbers: Box<[SpriteVram]>,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum DrawDirection {
Left,
Right,
}
fn draw_number(
mut number: u32,
position: Vector2D<i32>,
oam: &mut OamIterator,
direction: DrawDirection,
sprite_cache: &SpriteCache,
) {
let mut digits = Vec::new();
if number == 0 {
digits.push(0);
}
while number != 0 {
digits.push(number % 10);
number /= 10;
}
let mut current_position = if direction == DrawDirection::Right {
position + (4 * (digits.len() - 1) as i32, 0).into()
} else {
position
};
for digit in digits {
let mut obj = ObjectUnmanaged::new(sprite_cache.numbers[digit as usize].clone());
obj.show().set_position(current_position);
if let Some(slot) = oam.next() {
slot.set(&obj);
}
current_position -= (4, 0).into();
}
}
impl SpriteCache {
fn new(loader: &mut SpriteLoader) -> Self {
const SPRITES: &Graphics = include_aseprite!(
"gfx/circles.aseprite",
"gfx/saw.aseprite",
"gfx/numbers.aseprite"
);
const NUMBERS: &Tag = SPRITES.tags().get("numbers");
const BLUE_CIRCLE: &Sprite = SPRITES.tags().get("Blue").sprite(0);
const RED_CIRCLE: &Sprite = SPRITES.tags().get("Red").sprite(0);
const SAW: &Sprite = SPRITES.tags().get("Saw").sprite(0);
Self {
saw: loader.get_vram_sprite(SAW),
blue: loader.get_vram_sprite(BLUE_CIRCLE),
red: loader.get_vram_sprite(RED_CIRCLE),
numbers: (0..10)
.map(|x| NUMBERS.sprite(x))
.map(|x| loader.get_vram_sprite(x))
.collect::<Vec<_>>()
.into_boxed_slice(),
}
}
}
struct Game {
settings: FinalisedSettings,
circles: VecDeque<Circle>,
saws: VecDeque<Saw>,
head_position: Vector2D<Number>,
phase_time: Number,
input: ButtonController,
energy: Number,
frame_since_last_saw: i32,
alive_frames: u32,
}
enum GameState {
Continue,
Loss(u32),
}
impl Game {
fn from_settings(settings: Settings) -> Self {
let finalised = settings.to_finalised_settings();
let mut circles = VecDeque::with_capacity(finalised.number_of_circles);
for idx in 0..finalised.number_of_circles {
circles.push_back(Circle {
colour: Colour::Red,
position: Vector2D::new(
finalised.speed * idx as i32 - 4,
settings.head_start_position.y,
),
})
}
Game {
energy: 100.into(),
input: agb::input::ButtonController::new(),
settings: finalised,
circles,
saws: VecDeque::new(),
head_position: settings.head_start_position,
phase_time: 0.into(),
frame_since_last_saw: 0,
alive_frames: 0,
}
}
fn frame(&mut self, sprite_cache: &SpriteCache) -> GameState {
self.input.update();
let (height, colour) = if self.input.is_pressed(Button::A) {
(self.settings.wave_height_ability, Colour::Blue)
} else {
(self.settings.wave_height_normal, Colour::Red)
};
let next_phase_time = self.phase_time + self.settings.phase_speed;
let this_frame_y_delta = next_phase_time.cos() - self.phase_time.cos();
self.phase_time = next_phase_time % num!(1.);
let this_frame_y_delta = this_frame_y_delta * height;
self.head_position.y += this_frame_y_delta;
// update circles
for circle in self.circles.iter_mut() {
circle.position.x -= self.settings.speed;
}
self.circles.pop_front();
// generate circle
let circle = Circle {
colour,
position: self.head_position,
};
self.circles.push_back(circle);
// update saws + check for death
let mut saw_has_hit_head = false;
let mut number_of_saws_to_pop = 0;
for (idx, saw) in self.saws.iter_mut().enumerate() {
saw.position.x -= self.settings.speed;
if saw.position.x < (-32).into() {
number_of_saws_to_pop = idx + 1;
}
saw.angle += saw.rotation_speed;
let angle_affine_matrix = AffineMatrix::from_rotation(saw.angle);
saw.object.set_affine_matrix(AffineMatrixInstance::new(
angle_affine_matrix.to_object_wrapping(),
));
saw.object.show_affine(AffineMode::Affine);
saw.object
.set_position(saw.position.floor() - (16, 16).into());
if (saw.position - self.head_position).magnitude_squared()
< ((16 + 4) * (16 + 4)).into()
{
saw_has_hit_head = true;
}
}
// destroy saws
for _ in 0..number_of_saws_to_pop {
self.saws.pop_front();
}
// create saw
self.frame_since_last_saw -= 1;
if self.frame_since_last_saw <= 0 {
self.frame_since_last_saw = self.settings.frames_between_saws;
let mut rotation_direction = rng::gen().signum();
if rotation_direction == 0 {
rotation_direction = 1;
}
let rotation_magnitude =
Number::from_raw(rng::gen().abs() % (1 << 8)) % num!(0.02) + num!(0.005);
let rotation_speed = rotation_magnitude * rotation_direction;
let saw = Saw {
object: ObjectUnmanaged::new(sprite_cache.saw.clone()),
position: (300, rng::gen().rem_euclid(display::HEIGHT)).into(),
angle: 0.into(),
rotation_speed,
};
self.saws.push_back(saw);
}
self.alive_frames += 1;
let out_of_bounds_death = self.head_position.y.floor() < -4
|| (self.head_position.y + 1).floor() > display::HEIGHT + 4;
if saw_has_hit_head || out_of_bounds_death {
GameState::Loss(self.alive_frames)
} else {
GameState::Continue
}
}
fn render(&self, oam: &mut OamIterator, sprite_cache: &SpriteCache) {
for saw in self.saws.iter() {
if let Some(slot) = oam.next() {
slot.set(&saw.object);
}
}
for circle in self.circles.iter() {
if let Some(slot) = oam.next() {
let mut object = ObjectUnmanaged::new(match circle.colour {
Colour::Red => sprite_cache.red.clone(),
Colour::Blue => sprite_cache.blue.clone(),
});
object
.show()
.set_position(circle.position.floor() - (4, 4).into());
slot.set(&object);
}
}
}
}
struct Settings {
phase_speed: Number,
frames_between_saws: i32,
speed: Number,
head_start_position: Vector2D<Number>,
wave_height_normal: Number,
wave_height_ability: Number,
}
impl Settings {
fn to_finalised_settings(&self) -> FinalisedSettings {
FinalisedSettings {
number_of_circles: ((self.head_start_position.x + 4) / self.speed + 1)
.floor()
.try_into()
.expect("number should be positive"),
speed: self.speed,
phase_speed: self.phase_speed,
frames_between_saws: self.frames_between_saws,
wave_height_ability: self.wave_height_ability,
wave_height_normal: self.wave_height_normal,
}
}
}
struct FinalisedSettings {
wave_height_normal: Number,
wave_height_ability: Number,
phase_speed: Number,
frames_between_saws: i32,
speed: Number,
number_of_circles: usize,
}
pub fn main(mut gba: agb::Gba) -> ! {
let (mut unmanaged, mut sprites) = gba.display.object.get_unmanaged();
let sprite_cache = SpriteCache::new(&mut sprites);
let (_background, mut vram) = gba.display.video.tiled0();
vram.set_background_palettes(&[Palette16::new([u16::MAX; 16])]);
let vblank = agb::interrupt::VBlank::get();
let mut max_score = 0;
loop {
let mut game = Game::from_settings(Settings {
phase_speed: num!(0.02),
frames_between_saws: 60,
speed: num!(1.),
head_start_position: (40, 100).into(),
wave_height_normal: 20.into(),
wave_height_ability: 5.into(),
});
loop {
let state = game.frame(&sprite_cache);
if game.alive_frames > max_score {
max_score = game.alive_frames;
}
vblank.wait_for_vblank();
let oam_frame = &mut unmanaged.iter();
draw_number(
max_score,
(display::WIDTH - 4, 1).into(),
oam_frame,
DrawDirection::Left,
&sprite_cache,
);
draw_number(
game.alive_frames,
(1, 1).into(),
oam_frame,
DrawDirection::Right,
&sprite_cache,
);
game.render(oam_frame, &sprite_cache);
if let GameState::Loss(score) = state {
for _ in 0..30 {
vblank.wait_for_vblank();
}
break;
}
}
}
}

View file

@ -0,0 +1,10 @@
#![no_std]
#![no_main]
#![cfg_attr(test, feature(custom_test_frameworks))]
#![cfg_attr(test, reexport_test_harness_main = "test_main")]
#![cfg_attr(test, test_runner(agb::test_runner::test_runner))]
#[agb::entry]
fn main(mut gba: agb::Gba) -> ! {
amplitude::main(gba)
}