Compare commits
3 Commits
a1c6e8d371
...
3c447c764f
Author | SHA1 | Date |
---|---|---|
dani | 3c447c764f | |
dani | 8372690bc0 | |
dani | d2155a718b |
|
@ -0,0 +1,6 @@
|
||||||
|
# 0.2.0 -
|
||||||
|
- added README.md
|
||||||
|
- renamed tilemaps to backgrounds to make space for... tilemaps
|
||||||
|
|
||||||
|
# 0.1.0 - 2023-07-21
|
||||||
|
initial release
|
|
@ -13,7 +13,6 @@ keywords = ["gamedev"]
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.4.0"
|
|
||||||
glam = "0.24.0"
|
glam = "0.24.0"
|
||||||
ascii = "1.1.0"
|
ascii = "1.1.0"
|
||||||
gilrs = "0.10.2"
|
gilrs = "0.10.2"
|
||||||
|
|
|
@ -6,8 +6,8 @@ struct Game {}
|
||||||
fn init(gsa: &mut Gsa) -> Game {
|
fn init(gsa: &mut Gsa) -> Game {
|
||||||
gsa.sprites[0].tile = 0x0300;
|
gsa.sprites[0].tile = 0x0300;
|
||||||
gsa.sprites[1].tile = 0x0200;
|
gsa.sprites[1].tile = 0x0200;
|
||||||
gsa.maps[0].tiles[1][1] = 0x0300;
|
gsa.bgs[0].tiles[1][1] = 0x0300;
|
||||||
gsa.maps[1].half_tile = true;
|
gsa.bgs[1].half_tile = true;
|
||||||
gsa.write_string(1, IVec2::ONE, "Hello world nyaa~");
|
gsa.write_string(1, IVec2::ONE, "Hello world nyaa~");
|
||||||
Game {}
|
Game {}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ fn init(gsa: &mut Gsa) -> Game {
|
||||||
fn update(_game: &mut Game, gsa: &mut Gsa) {
|
fn update(_game: &mut Game, gsa: &mut Gsa) {
|
||||||
gsa.sprites[0].pos.x = (gsa.sprites[0].pos.x + 1) % 300;
|
gsa.sprites[0].pos.x = (gsa.sprites[0].pos.x + 1) % 300;
|
||||||
gsa.sprites[1].pos += gsa.input_dir();
|
gsa.sprites[1].pos += gsa.input_dir();
|
||||||
gsa.maps[1].scroll.x += 1;
|
gsa.bgs[1].scroll.x += 1;
|
||||||
if gsa.button_pressed(FACE_DOWN) {
|
if gsa.button_pressed(FACE_DOWN) {
|
||||||
gsa.sprites[1].tile += 1;
|
gsa.sprites[1].tile += 1;
|
||||||
}
|
}
|
||||||
|
|
BIN
examples/gfx.gif
BIN
examples/gfx.gif
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 27 KiB |
|
@ -1,9 +1,9 @@
|
||||||
use crate::TILEMAP_MAX_SIZE;
|
use crate::BACKGROUND_MAX_SIZE;
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
|
|
||||||
/// Tilemap which will be rendered on screen
|
/// Tilemap which will be rendered on screen
|
||||||
pub struct Tilemap {
|
pub struct Background {
|
||||||
/// Tiles in idx, accessible via \[x\]\[y\], x and y 0..[TILEMAP_MAX_SIZE]
|
/// Tiles in idx, accessible via \[x\]\[y\], x and y 0..[BACKGROUND_MAX_SIZE]
|
||||||
pub tiles: Vec<Vec<u16>>,
|
pub tiles: Vec<Vec<u16>>,
|
||||||
/// Camera scroll (negative draw offset) for rendering
|
/// Camera scroll (negative draw offset) for rendering
|
||||||
pub scroll: IVec2,
|
pub scroll: IVec2,
|
||||||
|
@ -11,10 +11,10 @@ pub struct Tilemap {
|
||||||
pub half_tile: bool,
|
pub half_tile: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Tilemap {
|
impl Default for Background {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let row = vec![0u16; TILEMAP_MAX_SIZE];
|
let row = vec![0u16; BACKGROUND_MAX_SIZE];
|
||||||
let tiles = vec![row; TILEMAP_MAX_SIZE];
|
let tiles = vec![row; BACKGROUND_MAX_SIZE];
|
||||||
Self {
|
Self {
|
||||||
tiles,
|
tiles,
|
||||||
scroll: IVec2::ZERO,
|
scroll: IVec2::ZERO,
|
15
src/gsa.rs
15
src/gsa.rs
|
@ -1,8 +1,9 @@
|
||||||
|
use crate::background::Background;
|
||||||
use crate::rgb::Rgb;
|
use crate::rgb::Rgb;
|
||||||
use crate::sprite::Sprite;
|
use crate::sprite::Sprite;
|
||||||
use crate::tilemap::Tilemap;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Buttons, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE,
|
Buttons, BACKGROUND_MAX_SIZE, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, MAX_BACKGROUNDS,
|
||||||
|
MAX_SPRITES,
|
||||||
};
|
};
|
||||||
use ascii::{AsciiChar, AsciiStr};
|
use ascii::{AsciiChar, AsciiStr};
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
|
@ -16,7 +17,7 @@ pub struct Gsa {
|
||||||
pub palette: [Rgb; 256],
|
pub palette: [Rgb; 256],
|
||||||
|
|
||||||
/// Tilemap layers available
|
/// Tilemap layers available
|
||||||
pub maps: [Tilemap; MAX_TILEMAPS],
|
pub bgs: [Background; MAX_BACKGROUNDS],
|
||||||
|
|
||||||
/// Currently selected font
|
/// Currently selected font
|
||||||
///
|
///
|
||||||
|
@ -36,9 +37,9 @@ impl Gsa {
|
||||||
|
|
||||||
/// Sets all tiles of map to val
|
/// Sets all tiles of map to val
|
||||||
pub fn fill_map(&mut self, map: usize, val: u16) {
|
pub fn fill_map(&mut self, map: usize, val: u16) {
|
||||||
for x in 0..TILEMAP_MAX_SIZE {
|
for x in 0..BACKGROUND_MAX_SIZE {
|
||||||
for y in 0..TILEMAP_MAX_SIZE {
|
for y in 0..BACKGROUND_MAX_SIZE {
|
||||||
self.maps[map].tiles[x][y] = val;
|
self.bgs[map].tiles[x][y] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ impl Gsa {
|
||||||
pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) {
|
pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) {
|
||||||
let str = AsciiStr::from_ascii(str).unwrap();
|
let str = AsciiStr::from_ascii(str).unwrap();
|
||||||
for (i, ch) in str.into_iter().enumerate() {
|
for (i, ch) in str.into_iter().enumerate() {
|
||||||
self.maps[map].tiles[pos.x as usize + i][pos.y as usize] = self.get_char_tile(*ch);
|
self.bgs[map].tiles[pos.x as usize + i][pos.y as usize] = self.get_char_tile(*ch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Gsa, Rgb, Tilemap, HALF_TILE_SIZE, MAX_TILEMAPS, SCREEN_HEIGHT, SCREEN_WIDTH, TILEMAP_MAX_SIZE,
|
Background, Gsa, Rgb, BACKGROUND_MAX_SIZE, EMPTY_TILE, HALF_TILE_SIZE, MAX_BACKGROUNDS,
|
||||||
TILESET_SIZE, TILE_SIZE,
|
SCREEN_HEIGHT, SCREEN_WIDTH, TILESET_SIZE, TILE_SIZE, TRANSPARENT,
|
||||||
};
|
};
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use softbuffer::Buffer;
|
use softbuffer::Buffer;
|
||||||
|
@ -16,7 +16,7 @@ fn draw_tile(target: &mut [u8], pos: IVec2, tile: u16, tileset: &[u8], half: boo
|
||||||
for y in (starty - pos.y)..(endy - pos.y) {
|
for y in (starty - pos.y)..(endy - pos.y) {
|
||||||
for x in (startx - pos.x)..(endx - pos.x) {
|
for x in (startx - pos.x)..(endx - pos.x) {
|
||||||
let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)];
|
let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)];
|
||||||
if p > 0 {
|
if p != TRANSPARENT {
|
||||||
target[(x as usize + pos.x as usize)
|
target[(x as usize + pos.x as usize)
|
||||||
+ (y as usize + pos.y as usize) * SCREEN_WIDTH] = p;
|
+ (y as usize + pos.y as usize) * SCREEN_WIDTH] = p;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ fn draw_tile(target: &mut [u8], pos: IVec2, tile: u16, tileset: &[u8], half: boo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_map(target: &mut [u8], map: &Tilemap, tileset: &[u8]) {
|
fn render_map(target: &mut [u8], map: &Background, tileset: &[u8]) {
|
||||||
let tcmult = if map.half_tile { 2 } else { 1 };
|
let tcmult = if map.half_tile { 2 } else { 1 };
|
||||||
let tilesize = if map.half_tile {
|
let tilesize = if map.half_tile {
|
||||||
HALF_TILE_SIZE
|
HALF_TILE_SIZE
|
||||||
|
@ -33,14 +33,14 @@ fn render_map(target: &mut [u8], map: &Tilemap, tileset: &[u8]) {
|
||||||
} as i32;
|
} as i32;
|
||||||
let mut startx = map.scroll.x / tilesize;
|
let mut startx = map.scroll.x / tilesize;
|
||||||
let mut starty = map.scroll.y / tilesize;
|
let mut starty = map.scroll.y / tilesize;
|
||||||
let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
let endx = (BACKGROUND_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
||||||
let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
let endy = (BACKGROUND_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
||||||
startx = 0.max(startx);
|
startx = 0.max(startx);
|
||||||
starty = 0.max(starty);
|
starty = 0.max(starty);
|
||||||
for x in startx..endx {
|
for x in startx..endx {
|
||||||
for y in starty..endy {
|
for y in starty..endy {
|
||||||
let tile = map.tiles[x as usize][y as usize];
|
let tile = map.tiles[x as usize][y as usize];
|
||||||
if tile > 0 {
|
if tile > EMPTY_TILE {
|
||||||
draw_tile(
|
draw_tile(
|
||||||
target,
|
target,
|
||||||
IVec2 {
|
IVec2 {
|
||||||
|
@ -57,8 +57,8 @@ fn render_map(target: &mut [u8], map: &Tilemap, tileset: &[u8]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8]) {
|
pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8]) {
|
||||||
for i in 0..MAX_TILEMAPS {
|
for i in 0..MAX_BACKGROUNDS {
|
||||||
render_map(target, &gsa.maps[i], tileset);
|
render_map(target, &gsa.bgs[i], tileset);
|
||||||
for sprite in &gsa.sprites {
|
for sprite in &gsa.sprites {
|
||||||
if sprite.tile > 0 && sprite.priority == i as u8 {
|
if sprite.tile > 0 && sprite.priority == i as u8 {
|
||||||
draw_tile(target, sprite.pos, sprite.tile, tileset, false);
|
draw_tile(target, sprite.pos, sprite.tile, tileset, false);
|
||||||
|
|
29
src/lib.rs
29
src/lib.rs
|
@ -8,11 +8,12 @@
|
||||||
//! - Colors: 256 (indexed out of a possible 24-bit)
|
//! - Colors: 256 (indexed out of a possible 24-bit)
|
||||||
//! - Tilesize: 16x16 (or 8x8 for half-tiles)
|
//! - Tilesize: 16x16 (or 8x8 for half-tiles)
|
||||||
//! - Tileset: 65536 tiles, indexed via 0xYYXX
|
//! - Tileset: 65536 tiles, indexed via 0xYYXX
|
||||||
|
//! - rectangle(not id range) of 0x7000 to 0x7F7F semi-reserved
|
||||||
//! - Sprites: 256 of size 16x16 (pondering allowing larger sprites)
|
//! - Sprites: 256 of size 16x16 (pondering allowing larger sprites)
|
||||||
//! - Tilemaps: 4 of size 1024x1024, scrollable
|
//! - Backgrounds: 4 of size 1024x1024, scrollable
|
||||||
//!
|
//!
|
||||||
//! ## Features not yet implemented
|
//! ## Features not yet implemented
|
||||||
//! - Tilemap effects
|
//! - Background effects
|
||||||
//! - Rotation? Scaling?
|
//! - Rotation? Scaling?
|
||||||
//! - Mosaic?
|
//! - Mosaic?
|
||||||
//! - Mode7?
|
//! - Mode7?
|
||||||
|
@ -27,21 +28,21 @@
|
||||||
//! - Gamepad text keyboard input
|
//! - Gamepad text keyboard input
|
||||||
//! - Menus
|
//! - Menus
|
||||||
|
|
||||||
|
mod background;
|
||||||
mod buttons;
|
mod buttons;
|
||||||
mod gsa;
|
mod gsa;
|
||||||
mod gsa_render_to_screen;
|
mod gsa_render_to_screen;
|
||||||
mod rgb;
|
mod rgb;
|
||||||
mod run;
|
mod run;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod tilemap;
|
|
||||||
mod tileset;
|
mod tileset;
|
||||||
|
|
||||||
|
pub use crate::background::*;
|
||||||
pub use crate::buttons::*;
|
pub use crate::buttons::*;
|
||||||
pub use crate::gsa::*;
|
pub use crate::gsa::*;
|
||||||
pub use crate::rgb::*;
|
pub use crate::rgb::*;
|
||||||
pub use crate::run::run;
|
pub use crate::run::run;
|
||||||
pub use crate::sprite::*;
|
pub use crate::sprite::*;
|
||||||
pub use crate::tilemap::*;
|
|
||||||
|
|
||||||
/// Amount of sprites in [Gsa::sprites]
|
/// Amount of sprites in [Gsa::sprites]
|
||||||
pub const MAX_SPRITES: usize = 0xff;
|
pub const MAX_SPRITES: usize = 0xff;
|
||||||
|
@ -52,8 +53,17 @@ pub const SCREEN_WIDTH: usize = 304;
|
||||||
/// Screen Height in pixels
|
/// Screen Height in pixels
|
||||||
pub const SCREEN_HEIGHT: usize = 176;
|
pub const SCREEN_HEIGHT: usize = 176;
|
||||||
|
|
||||||
/// X and y dimensions of maps in [Gsa::maps]
|
/// X and y dimensions of maps in [Gsa::bgs]
|
||||||
pub const TILEMAP_MAX_SIZE: usize = 1024;
|
pub const BACKGROUND_MAX_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
/// Tile considered empty (never drawn even if has contents)
|
||||||
|
pub const EMPTY_TILE: u16 = 0xffff
|
||||||
|
|
||||||
|
/// Tile id of bold default font
|
||||||
|
pub const FONT_BOLD: u16 = 0xf000;
|
||||||
|
|
||||||
|
/// Tile id of thin default font
|
||||||
|
pub const FONT_THIN: u16 = 0xe000;
|
||||||
|
|
||||||
/// Width and height (in tiles) of tileset
|
/// Width and height (in tiles) of tileset
|
||||||
pub const TILESET_SIZE: usize = 0x100;
|
pub const TILESET_SIZE: usize = 0x100;
|
||||||
|
@ -61,8 +71,11 @@ pub const TILESET_SIZE: usize = 0x100;
|
||||||
/// Width and height of a tile
|
/// Width and height of a tile
|
||||||
pub const TILE_SIZE: usize = 16;
|
pub const TILE_SIZE: usize = 16;
|
||||||
|
|
||||||
|
/// Palette index which is treated as transparent
|
||||||
|
pub const TRANSPARENT: u8 = 0xff;
|
||||||
|
|
||||||
/// Width and height of a tile in half-tile mode
|
/// Width and height of a tile in half-tile mode
|
||||||
pub const HALF_TILE_SIZE: usize = 8;
|
pub const HALF_TILE_SIZE: usize = 8;
|
||||||
|
|
||||||
/// Amount of tile maps in [Gsa::maps]
|
/// Amount of tile maps in [Gsa::bgs]
|
||||||
pub const MAX_TILEMAPS: usize = 4;
|
pub const MAX_BACKGROUNDS: usize = 4;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::buttons::{button_from_gilrs, button_from_scancode};
|
use crate::buttons::{button_from_gilrs, button_from_scancode};
|
||||||
use crate::gsa_render_to_screen::{render_to_screen, render_to_window};
|
use crate::gsa_render_to_screen::{render_to_screen, render_to_window};
|
||||||
use crate::tileset::load_tileset;
|
use crate::tileset::load_tileset;
|
||||||
use crate::{Buttons, Gsa, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
|
use crate::{Buttons, Gsa, Sprite, FONT_BOLD, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||||
use gilrs::EventType;
|
use gilrs::EventType;
|
||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
@ -39,9 +39,9 @@ pub fn run<TGame: 'static>(
|
||||||
|
|
||||||
let mut gsa = Gsa {
|
let mut gsa = Gsa {
|
||||||
sprites: [Sprite::default(); MAX_SPRITES],
|
sprites: [Sprite::default(); MAX_SPRITES],
|
||||||
maps: Default::default(),
|
bgs: Default::default(),
|
||||||
palette,
|
palette,
|
||||||
font: 0x1010,
|
font: FONT_BOLD,
|
||||||
pressed: 0,
|
pressed: 0,
|
||||||
released: 0,
|
released: 0,
|
||||||
down: 0,
|
down: 0,
|
||||||
|
|
Loading…
Reference in New Issue