... did all the things
This commit is contained in:
parent
40d4ba4574
commit
41189401ab
|
@ -3,6 +3,7 @@
|
|||
name = "gsa"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Game development library modelled after an imaginary console"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
@ -11,7 +12,6 @@ lazy_static = "1.4.0"
|
|||
glam = "0.24.0"
|
||||
ascii = "1.1.0"
|
||||
gilrs = "0.10.2"
|
||||
cpal = "0.15.2"
|
||||
winit = "0.28.6"
|
||||
softbuffer = "0.3.0"
|
||||
gif = "0.12.0"
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use glam::IVec2;
|
||||
use gsa::{run, Gsa};
|
||||
use gsa::{run, Gsa, FACE_DOWN};
|
||||
|
||||
struct Game {}
|
||||
|
||||
fn init(gsa: &mut Gsa) -> Game {
|
||||
gsa.sprites[0].tile = 0x0300;
|
||||
gsa.sprites[1].tile = 0x0200;
|
||||
gsa.maps[0].tiles[0][0] = 0x0300;
|
||||
gsa.maps[0].tiles[1][1] = 0x0300;
|
||||
gsa.maps[1].half_tile = true;
|
||||
gsa.write_string(1, IVec2::ONE, "Hello world nyaa~");
|
||||
Game {}
|
||||
|
@ -14,8 +14,9 @@ fn init(gsa: &mut Gsa) -> Game {
|
|||
|
||||
fn update(_game: &mut Game, gsa: &mut Gsa) {
|
||||
gsa.sprites[0].pos.x = (gsa.sprites[0].pos.x + 1) % 300;
|
||||
gsa.sprites[1].pos += gsa.input.dir;
|
||||
if gsa.input.pressed.face_down {
|
||||
gsa.sprites[1].pos += gsa.input_dir();
|
||||
gsa.maps[1].scroll.x += 1;
|
||||
if gsa.button_pressed(FACE_DOWN) {
|
||||
gsa.sprites[1].tile += 1;
|
||||
}
|
||||
}
|
||||
|
|
BIN
examples/gfx.gif
BIN
examples/gfx.gif
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
111
src/buttons.rs
111
src/buttons.rs
|
@ -1,47 +1,70 @@
|
|||
/// State of all GSA buttons
|
||||
#[derive(Default, Copy, Clone)]
|
||||
pub struct Buttons {
|
||||
/// UP on dpad
|
||||
pub dpad_up: bool,
|
||||
/// DOWN on dpad
|
||||
pub dpad_down: bool,
|
||||
/// LEFT on dpad
|
||||
pub dpad_left: bool,
|
||||
/// RIGHT on dpad
|
||||
pub dpad_right: bool,
|
||||
/// X on nintendo, Y on microsoft, TRIANGLE on sony
|
||||
pub face_up: bool,
|
||||
/// B on nintendo, A on microsoft, CROSS on sony
|
||||
pub face_down: bool,
|
||||
/// Y on nintendo, X on microsoft, SQUARE on sony
|
||||
pub face_left: bool,
|
||||
/// A on nintendo, B on microsoft, CIRCLE on sony
|
||||
pub face_right: bool,
|
||||
/// left shoulder button
|
||||
pub l: bool,
|
||||
/// right shoulder button
|
||||
pub r: bool,
|
||||
/// start button
|
||||
pub start: bool,
|
||||
/// select button
|
||||
pub select: bool,
|
||||
}
|
||||
use gilrs::Button;
|
||||
use winit::event::ScanCode;
|
||||
|
||||
impl Buttons {
|
||||
pub(crate) fn pressed(old: &Buttons, new: &Buttons) -> Buttons {
|
||||
Buttons {
|
||||
dpad_up: !old.dpad_up && new.dpad_up,
|
||||
dpad_down: !old.dpad_down && new.dpad_down,
|
||||
dpad_left: !old.dpad_left && new.dpad_left,
|
||||
dpad_right: !old.dpad_right && new.dpad_right,
|
||||
face_up: !old.face_up && new.face_up,
|
||||
face_down: !old.face_down && new.face_down,
|
||||
face_left: !old.face_left && new.face_left,
|
||||
face_right: !old.face_right && new.face_right,
|
||||
l: !old.l && new.l,
|
||||
r: !old.r && new.r,
|
||||
start: !old.start && new.start,
|
||||
select: !old.select && new.select,
|
||||
}
|
||||
/// UP on dpad
|
||||
pub const DPAD_UP: Buttons = 0x0001;
|
||||
/// DOWN on dpad
|
||||
pub const DPAD_DOWN: Buttons = 0x0002;
|
||||
/// LEFT on dpad
|
||||
pub const DPAD_LEFT: Buttons = 0x0004;
|
||||
/// RIGHT on dpad
|
||||
pub const DPAD_RIGHT: Buttons = 0x008;
|
||||
/// X on nintendo, Y on microsoft, TRIANGLE on sony
|
||||
pub const FACE_UP: Buttons = 0x0010;
|
||||
/// B on nintendo, A on microsoft, CROSS on sony
|
||||
pub const FACE_DOWN: Buttons = 0x0020;
|
||||
/// Y on nintendo, X on microsoft, SQUARE on sony
|
||||
pub const FACE_LEFT: Buttons = 0x0040;
|
||||
/// A on nintendo, B on microsoft, CIRCLE on sony
|
||||
pub const FACE_RIGHT: Buttons = 0x0080;
|
||||
/// left shoulder button
|
||||
pub const L: Buttons = 0x0100;
|
||||
/// right shoulder button
|
||||
pub const R: Buttons = 0x0200;
|
||||
/// start button
|
||||
pub const START: Buttons = 0x0400;
|
||||
/// select button
|
||||
pub const SELECT: Buttons = 0x0800;
|
||||
|
||||
/// Bitmask of button states
|
||||
///
|
||||
/// GSA pretends all input is a snes-like gamepad
|
||||
/// d-pad, 4 face buttons, l and r shoulder buttons, start and select
|
||||
pub type Buttons = u16;
|
||||
|
||||
pub(crate) fn button_from_scancode(scancode: ScanCode) -> Buttons {
|
||||
//todo: currently windows only? check
|
||||
match scancode {
|
||||
0x0010 => L,
|
||||
0x0011 => FACE_UP,
|
||||
0x0012 => R,
|
||||
0x001C => START,
|
||||
0x001E => FACE_LEFT,
|
||||
0x001F => FACE_DOWN,
|
||||
0x0020 => FACE_RIGHT,
|
||||
0x0039 => SELECT,
|
||||
0xE048 => DPAD_UP,
|
||||
0xE04B => DPAD_LEFT,
|
||||
0xE04D => DPAD_RIGHT,
|
||||
0xE050 => DPAD_DOWN,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn button_from_gilrs(button: Button) -> Buttons {
|
||||
match button {
|
||||
Button::South => FACE_DOWN,
|
||||
Button::East => FACE_RIGHT,
|
||||
Button::North => FACE_UP,
|
||||
Button::West => FACE_LEFT,
|
||||
Button::LeftTrigger | Button::LeftTrigger2 => L,
|
||||
Button::RightTrigger | Button::RightTrigger2 => R,
|
||||
Button::Select => SELECT,
|
||||
Button::Start => START,
|
||||
Button::DPadUp => DPAD_UP,
|
||||
Button::DPadDown => DPAD_DOWN,
|
||||
Button::DPadLeft => DPAD_LEFT,
|
||||
Button::DPadRight => DPAD_RIGHT,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
|
35
src/gsa.rs
35
src/gsa.rs
|
@ -1,8 +1,9 @@
|
|||
use crate::input::Input;
|
||||
use crate::rgb::Rgb;
|
||||
use crate::sprite::Sprite;
|
||||
use crate::tilemap::Tilemap;
|
||||
use crate::{MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE};
|
||||
use crate::{
|
||||
Buttons, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT, DPAD_UP, MAX_SPRITES, MAX_TILEMAPS, TILEMAP_MAX_SIZE,
|
||||
};
|
||||
use ascii::{AsciiChar, AsciiStr};
|
||||
use glam::IVec2;
|
||||
|
||||
|
@ -22,8 +23,9 @@ pub struct Gsa {
|
|||
/// Chosen as half-size tile index, extends 16x16 half tiles in x and y
|
||||
pub font: u16,
|
||||
|
||||
/// Current input state
|
||||
pub input: Input,
|
||||
pub(crate) pressed: Buttons,
|
||||
pub(crate) released: Buttons,
|
||||
pub(crate) down: Buttons,
|
||||
}
|
||||
|
||||
impl Gsa {
|
||||
|
@ -41,6 +43,31 @@ impl Gsa {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if any of given buttons currently held down
|
||||
pub fn button_down(&self, button: Buttons) -> bool {
|
||||
self.down & button != 0
|
||||
}
|
||||
|
||||
/// Checks if any of given buttons been pressed in the current frame
|
||||
pub fn button_pressed(&self, button: Buttons) -> bool {
|
||||
self.pressed & button != 0
|
||||
}
|
||||
|
||||
/// Checks if any of given buttons been released in the current frame
|
||||
pub fn button_released(&self, button: Buttons) -> bool {
|
||||
self.released & button != 0
|
||||
}
|
||||
|
||||
/// Directional vector (-1 to 1) from current dpad state
|
||||
pub fn input_dir(&self) -> IVec2 {
|
||||
IVec2 {
|
||||
x: if self.button_down(DPAD_LEFT) { -1 } else { 0 }
|
||||
+ if self.button_down(DPAD_RIGHT) { 1 } else { 0 },
|
||||
y: if self.button_down(DPAD_UP) { -1 } else { 0 }
|
||||
+ if self.button_down(DPAD_DOWN) { 1 } else { 0 },
|
||||
}
|
||||
}
|
||||
|
||||
/// Write given string on given map, at given position, with font [Gsa::font]
|
||||
pub fn write_string(&mut self, map: usize, pos: IVec2, str: &str) {
|
||||
let str = AsciiStr::from_ascii(str).unwrap();
|
||||
|
|
|
@ -1,23 +1,158 @@
|
|||
use crate::{Gsa, MAX_SPRITES, TILE_SIZE};
|
||||
use crate::{
|
||||
Gsa, Rgb, Tilemap, HALF_TILE_SIZE, MAX_TILEMAPS, SCREEN_HEIGHT, SCREEN_WIDTH, TILEMAP_MAX_SIZE,
|
||||
TILESET_SIZE, TILE_SIZE,
|
||||
};
|
||||
use glam::IVec2;
|
||||
use softbuffer::Buffer;
|
||||
|
||||
pub(crate) fn render_to_window(target: &mut Buffer, gsa: &Gsa, window_size: IVec2) {
|
||||
for (y, row) in target.chunks_exact_mut(window_size.x as usize).enumerate() {
|
||||
let y = y as i32;
|
||||
for x in 0..window_size.x {
|
||||
let mut p = 0;
|
||||
for sprite in &gsa.sprites {
|
||||
if sprite.tile > 0
|
||||
&& x >= sprite.pos.x
|
||||
&& y >= sprite.pos.y
|
||||
&& x < sprite.pos.x + TILE_SIZE as i32
|
||||
&& y < sprite.pos.y + TILE_SIZE as i32
|
||||
{
|
||||
p = 1;
|
||||
}
|
||||
fn draw_tile(target: &mut [u8], pos: IVec2, tile: u16, tileset: &[u8], half: bool) {
|
||||
let tilesize = if half { HALF_TILE_SIZE } else { TILE_SIZE };
|
||||
let tx = tile as usize % 0x100 * tilesize;
|
||||
let ty = tile as usize / 0x100 * tilesize;
|
||||
let startx = pos.x.max(0).min(SCREEN_WIDTH as i32);
|
||||
let starty = pos.y.max(0).min(SCREEN_HEIGHT as i32);
|
||||
let endx = (pos.x + tilesize as i32).max(0).min(SCREEN_WIDTH as i32);
|
||||
let endy = (pos.y + tilesize as i32).max(0).min(SCREEN_HEIGHT as i32);
|
||||
for y in (starty - pos.y)..(endy - pos.y) {
|
||||
for x in (startx - pos.x)..(endx - pos.x) {
|
||||
let p = tileset[(x as usize + tx) + (y as usize + ty) * (TILESET_SIZE * TILE_SIZE)];
|
||||
if p > 0 {
|
||||
target[(x as usize + pos.x as usize)
|
||||
+ (y as usize + pos.y as usize) * SCREEN_WIDTH] = p;
|
||||
}
|
||||
row[x as usize] = gsa.palette[p].to_u32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_map(target: &mut [u8], map: &Tilemap, tileset: &[u8]) {
|
||||
let tcmult = if map.half_tile { 2 } else { 1 };
|
||||
let tilesize = if map.half_tile {
|
||||
HALF_TILE_SIZE
|
||||
} else {
|
||||
TILE_SIZE
|
||||
} as i32;
|
||||
let mut startx = map.scroll.x / tilesize;
|
||||
let mut starty = map.scroll.y / tilesize;
|
||||
let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
||||
let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
||||
startx = 0.max(startx);
|
||||
starty = 0.max(starty);
|
||||
for x in startx..endx {
|
||||
for y in starty..endy {
|
||||
let tile = map.tiles[x as usize][y as usize];
|
||||
if tile > 0 {
|
||||
draw_tile(
|
||||
target,
|
||||
IVec2 {
|
||||
x: x * tilesize - map.scroll.x,
|
||||
y: y * tilesize - map.scroll.y,
|
||||
},
|
||||
tile,
|
||||
tileset,
|
||||
map.half_tile,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_to_screen(target: &mut [u8], gsa: &Gsa, tileset: &[u8]) {
|
||||
for i in 0..MAX_TILEMAPS {
|
||||
render_map(target, &gsa.maps[i], tileset);
|
||||
for sprite in &gsa.sprites {
|
||||
if sprite.tile > 0 && sprite.priority == i as u8 {
|
||||
draw_tile(target, sprite.pos, sprite.tile, tileset, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render_to_window(
|
||||
target: &mut Buffer,
|
||||
src: &mut [u8],
|
||||
palette: &[Rgb; 256],
|
||||
window_size: IVec2,
|
||||
scale: usize,
|
||||
off_x: usize,
|
||||
off_y: usize,
|
||||
) {
|
||||
let start = window_size.x as usize * off_y;
|
||||
let end = start + window_size.x as usize * SCREEN_HEIGHT * scale;
|
||||
for (y, row) in target[start..end]
|
||||
.chunks_exact_mut(window_size.x as usize * scale)
|
||||
.enumerate()
|
||||
{
|
||||
let y = y as i32;
|
||||
for x in 0..SCREEN_WIDTH {
|
||||
let p = src[x + y as usize * SCREEN_WIDTH];
|
||||
|
||||
for scaley in 0..scale {
|
||||
for scalex in 0..scale {
|
||||
let sx = x * scale + scalex;
|
||||
row[sx + off_x + scaley * window_size.x as usize] =
|
||||
palette[p as usize].to_u32();
|
||||
}
|
||||
}
|
||||
|
||||
//unrolled is faster benchmarked... <_<
|
||||
/*
|
||||
let ww = window_size.x as usize;
|
||||
match scale {
|
||||
1 => {
|
||||
row[x + off_x] = palette[p as usize].to_u32();
|
||||
}
|
||||
2 => {
|
||||
row[x * 2 + off_x] = palette[p as usize].to_u32();
|
||||
row[x * 2 + off_x + 1] = palette[p as usize].to_u32();
|
||||
row[x * 2 + off_x + ww] = palette[p as usize].to_u32();
|
||||
row[x * 2 + off_x + ww + 1] = palette[p as usize].to_u32();
|
||||
}
|
||||
6 => {
|
||||
row[x * 6 + off_x + ww * 0] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 0 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 0 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 0 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 0 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 0 + 5] = palette[p as usize].to_u32();
|
||||
|
||||
row[x * 6 + off_x + ww * 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 1 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 1 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 1 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 1 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 1 + 5] = palette[p as usize].to_u32();
|
||||
|
||||
row[x * 6 + off_x + ww * 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 2 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 2 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 2 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 2 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 2 + 5] = palette[p as usize].to_u32();
|
||||
|
||||
row[x * 6 + off_x + ww * 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 3 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 3 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 3 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 3 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 3 + 5] = palette[p as usize].to_u32();
|
||||
|
||||
row[x * 6 + off_x + ww * 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 4 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 4 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 4 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 4 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 4 + 5] = palette[p as usize].to_u32();
|
||||
|
||||
row[x * 6 + off_x + ww * 5] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 5 + 1] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 5 + 2] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 5 + 3] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 5 + 4] = palette[p as usize].to_u32();
|
||||
row[x * 6 + off_x + ww * 5 + 5] = palette[p as usize].to_u32();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
18
src/input.rs
18
src/input.rs
|
@ -1,18 +0,0 @@
|
|||
use crate::buttons::Buttons;
|
||||
use glam::IVec2;
|
||||
|
||||
/// Input State
|
||||
///
|
||||
/// GSA pretends all input is a snes-like gamepad
|
||||
/// d-pad, 4 face buttons, l and r shoulder buttons, start and select
|
||||
#[derive(Default)]
|
||||
pub struct Input {
|
||||
/// Buttons that are currentle held down
|
||||
pub down: Buttons,
|
||||
|
||||
/// Buttons that have been pressed in the current frame
|
||||
pub pressed: Buttons,
|
||||
|
||||
/// Directional vector (-1 to 1) from current dpad state
|
||||
pub dir: IVec2,
|
||||
}
|
22
src/lib.rs
22
src/lib.rs
|
@ -12,7 +12,14 @@
|
|||
//! - Tilemaps: 4 of size 1024x1024, scrollable
|
||||
//!
|
||||
//! ## Features not yet implemented
|
||||
//! - Sound (no samples
|
||||
//! - Tilemap effects
|
||||
//! - Rotation? Scaling?
|
||||
//! - Mosaic?
|
||||
//! - Mode7?
|
||||
//! - Sprite Effects
|
||||
//! - Rotation? Scaling?
|
||||
//! - Mosaic?
|
||||
//! - Sound (no samples)
|
||||
//! - Synth
|
||||
//! - Speech
|
||||
//! - Savegames
|
||||
|
@ -23,21 +30,18 @@
|
|||
mod buttons;
|
||||
mod gsa;
|
||||
mod gsa_render_to_screen;
|
||||
mod input;
|
||||
mod rgb;
|
||||
mod run;
|
||||
mod sprite;
|
||||
mod state;
|
||||
mod tilemap;
|
||||
mod tileset;
|
||||
|
||||
pub use crate::buttons::Buttons;
|
||||
pub use crate::gsa::Gsa;
|
||||
pub use crate::input::Input;
|
||||
pub use crate::rgb::Rgb;
|
||||
pub use crate::buttons::*;
|
||||
pub use crate::gsa::*;
|
||||
pub use crate::rgb::*;
|
||||
pub use crate::run::run;
|
||||
pub use crate::sprite::Sprite;
|
||||
pub use crate::tilemap::Tilemap;
|
||||
pub use crate::sprite::*;
|
||||
pub use crate::tilemap::*;
|
||||
|
||||
/// Amount of sprites in [Gsa::sprites]
|
||||
pub const MAX_SPRITES: usize = 0xff;
|
||||
|
|
124
src/run.rs
124
src/run.rs
|
@ -1,14 +1,15 @@
|
|||
use crate::gsa_render_to_screen::render_to_window;
|
||||
use crate::state::State;
|
||||
use crate::buttons::{button_from_gilrs, button_from_scancode};
|
||||
use crate::gsa_render_to_screen::{render_to_screen, render_to_window};
|
||||
use crate::tileset::load_tileset;
|
||||
use crate::{Gsa, Rgb, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||
use gilrs::Gilrs;
|
||||
use crate::{Buttons, Gsa, Sprite, MAX_SPRITES, SCREEN_HEIGHT, SCREEN_WIDTH};
|
||||
use gilrs::EventType;
|
||||
use glam::IVec2;
|
||||
use std::cmp::min;
|
||||
use std::num::NonZeroU32;
|
||||
use std::time::{Duration, Instant};
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::event;
|
||||
use winit::event::{ElementState, Event, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
|
@ -41,7 +42,9 @@ pub fn run<TGame: 'static>(
|
|||
maps: Default::default(),
|
||||
palette,
|
||||
font: 0x1010,
|
||||
input: Default::default(),
|
||||
pressed: 0,
|
||||
released: 0,
|
||||
down: 0,
|
||||
};
|
||||
let mut game = init_fn(&mut gsa);
|
||||
/*let state = State::<TGame> {
|
||||
|
@ -64,6 +67,23 @@ pub fn run<TGame: 'static>(
|
|||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let monitor_size = window.primary_monitor().unwrap().size();
|
||||
let scale_factor = 0.9;
|
||||
let window_scale = ((monitor_size.width as f32 * scale_factor) as u32
|
||||
/ SCREEN_WIDTH as u32)
|
||||
.min((monitor_size.height as f32 * scale_factor) as u32 / SCREEN_HEIGHT as u32);
|
||||
window.set_inner_size(PhysicalSize {
|
||||
width: window_scale * SCREEN_WIDTH as u32,
|
||||
height: window_scale * SCREEN_HEIGHT as u32,
|
||||
});
|
||||
let outer_size = window.outer_size();
|
||||
window.set_outer_position(PhysicalPosition {
|
||||
x: (monitor_size.width - outer_size.width) / 2,
|
||||
y: (monitor_size.height - outer_size.height) / 2,
|
||||
});
|
||||
}
|
||||
|
||||
let context = unsafe { softbuffer::Context::new(&window) }.unwrap();
|
||||
let mut surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap();
|
||||
|
||||
|
@ -71,34 +91,97 @@ pub fn run<TGame: 'static>(
|
|||
let mut last_second = Instant::now();
|
||||
|
||||
let mut last_frame = last_second;
|
||||
let target_frame_duration = Duration::from_secs(1).div_f64(6000.0);
|
||||
let target_frame_duration = Duration::from_secs(1).div_f64(60.0);
|
||||
|
||||
let mut scale = 1usize;
|
||||
let mut off_x = 0usize;
|
||||
let mut off_y = 0usize;
|
||||
|
||||
let mut new_buttons = Buttons::default();
|
||||
// in case button gets pressed and released within the same frame need to catch them
|
||||
// seperately from the last-now comparison to catch it
|
||||
let mut new_pressed = Buttons::default();
|
||||
let mut new_released = Buttons::default();
|
||||
let mut gilrs = gilrs::Gilrs::new().unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
let scale = &mut scale;
|
||||
let off_x = &mut off_x;
|
||||
let off_y = &mut off_y;
|
||||
let new_buttons = &mut new_buttons;
|
||||
let new_pressed = &mut new_pressed;
|
||||
let new_released = &mut new_released;
|
||||
let gilrs = &mut gilrs;
|
||||
|
||||
if Instant::now() - last_second > Duration::from_secs(1) {
|
||||
println!("fps: {}", frames_since_last_second);
|
||||
frames_since_last_second = 0;
|
||||
last_second += Duration::from_secs(1);
|
||||
}
|
||||
|
||||
let mut screen_buffer = [0u8; SCREEN_WIDTH * SCREEN_HEIGHT];
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
event::KeyboardInput {
|
||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { input, .. },
|
||||
..
|
||||
} => match input.virtual_keycode {
|
||||
Some(VirtualKeyCode::Escape) => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
} => {
|
||||
let button = button_from_scancode(input.scancode);
|
||||
match input.state {
|
||||
ElementState::Pressed => {
|
||||
*new_buttons |= button;
|
||||
*new_pressed |= button;
|
||||
}
|
||||
ElementState::Released => {
|
||||
*new_buttons &= !button;
|
||||
*new_released |= button;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Event::MainEventsCleared {} => {
|
||||
if Instant::now() - last_frame >= target_frame_duration {
|
||||
//input
|
||||
while let Some(event) = gilrs.next_event() {
|
||||
match event.event {
|
||||
EventType::ButtonPressed(button, ..) => {
|
||||
let button = button_from_gilrs(button);
|
||||
*new_buttons |= button;
|
||||
*new_pressed |= button;
|
||||
}
|
||||
EventType::ButtonReleased(button, ..) => {
|
||||
let button = button_from_gilrs(button);
|
||||
*new_buttons &= !button;
|
||||
*new_released |= button;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
gsa.down = *new_buttons;
|
||||
gsa.released = *new_released;
|
||||
gsa.pressed = *new_pressed;
|
||||
*new_released = 0;
|
||||
*new_pressed = 0;
|
||||
|
||||
//update
|
||||
update_fn(&mut game, &mut gsa);
|
||||
|
||||
//graphics
|
||||
let size = window.inner_size();
|
||||
surface
|
||||
.resize(
|
||||
|
@ -114,16 +197,21 @@ pub fn run<TGame: 'static>(
|
|||
*off_x = (size.width as usize - SCREEN_WIDTH * *scale) / 2;
|
||||
*off_y = (size.height as usize - SCREEN_HEIGHT * *scale) / 2;
|
||||
|
||||
let mut buf = surface.buffer_mut().unwrap();
|
||||
let mut window_buffer = surface.buffer_mut().unwrap();
|
||||
render_to_screen(&mut screen_buffer, &gsa, &tileset);
|
||||
render_to_window(
|
||||
&mut buf,
|
||||
&gsa,
|
||||
&mut window_buffer,
|
||||
&mut screen_buffer,
|
||||
&palette,
|
||||
IVec2 {
|
||||
x: size.width as i32,
|
||||
y: size.height as i32,
|
||||
},
|
||||
*scale,
|
||||
*off_x,
|
||||
*off_y,
|
||||
);
|
||||
buf.present().unwrap();
|
||||
window_buffer.present().unwrap();
|
||||
frames_since_last_second += 1;
|
||||
last_frame += target_frame_duration;
|
||||
}
|
||||
|
|
|
@ -8,10 +8,16 @@ pub struct Sprite {
|
|||
pub pos: IVec2,
|
||||
/// Tile index
|
||||
pub tile: u16,
|
||||
/// Priority, this sprite will be drawn above map of idx priority
|
||||
pub priority: u8,
|
||||
}
|
||||
|
||||
impl Display for Sprite {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[pos: {}, tile: {}]", self.pos, self.tile)
|
||||
write!(
|
||||
f,
|
||||
"[pos: {}, tile: {}, priority: {}]",
|
||||
self.pos, self.tile, self.priority
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
147
src/state.rs
147
src/state.rs
|
@ -1,147 +0,0 @@
|
|||
use crate::buttons::Buttons;
|
||||
use crate::{Gsa, TILEMAP_MAX_SIZE};
|
||||
use gilrs::{Button, EventType, Gilrs};
|
||||
use glam::IVec2;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct State<TGame> {
|
||||
pub(crate) gsa: Gsa,
|
||||
pub(crate) game: TGame,
|
||||
pub(crate) update_fn: fn(game: &mut TGame, gsa: &mut Gsa),
|
||||
pub(crate) tileset: Vec<u8>,
|
||||
pub(crate) first: bool,
|
||||
pub(crate) gilrs: Gilrs,
|
||||
}
|
||||
|
||||
impl<TGame> State<TGame> {
|
||||
fn update(&mut self) {
|
||||
let mut new_buttons = self.gsa.input.down;
|
||||
while let Some(event) = self.gilrs.next_event() {
|
||||
match event {
|
||||
gilrs::Event {
|
||||
event: EventType::ButtonPressed(button, _),
|
||||
..
|
||||
} => match button {
|
||||
Button::South => new_buttons.face_down = true,
|
||||
Button::East => new_buttons.face_left = true,
|
||||
Button::North => new_buttons.face_up = true,
|
||||
Button::West => new_buttons.face_right = true,
|
||||
Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = true,
|
||||
Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = true,
|
||||
Button::Select => new_buttons.select = true,
|
||||
Button::Start => new_buttons.start = true,
|
||||
Button::DPadUp => new_buttons.dpad_up = true,
|
||||
Button::DPadDown => new_buttons.dpad_down = true,
|
||||
Button::DPadLeft => new_buttons.dpad_left = true,
|
||||
Button::DPadRight => new_buttons.dpad_right = true,
|
||||
_ => {}
|
||||
},
|
||||
gilrs::Event {
|
||||
event: EventType::ButtonReleased(button, _),
|
||||
..
|
||||
} => match button {
|
||||
Button::South => new_buttons.face_down = false,
|
||||
Button::East => new_buttons.face_left = false,
|
||||
Button::North => new_buttons.face_up = false,
|
||||
Button::West => new_buttons.face_right = false,
|
||||
Button::LeftTrigger | Button::LeftTrigger2 => new_buttons.l = false,
|
||||
Button::RightTrigger | Button::RightTrigger2 => new_buttons.r = false,
|
||||
Button::Select => new_buttons.select = false,
|
||||
Button::Start => new_buttons.start = false,
|
||||
Button::DPadUp => new_buttons.dpad_up = false,
|
||||
Button::DPadDown => new_buttons.dpad_down = false,
|
||||
Button::DPadLeft => new_buttons.dpad_left = false,
|
||||
Button::DPadRight => new_buttons.dpad_right = false,
|
||||
_ => {}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
self.gsa.input.pressed = Buttons::pressed(&self.gsa.input.down, &new_buttons);
|
||||
self.gsa.input.down = new_buttons;
|
||||
self.gsa.input.dir = IVec2 {
|
||||
x: if self.gsa.input.down.dpad_left { -1 } else { 0 }
|
||||
+ if self.gsa.input.down.dpad_right { 1 } else { 0 },
|
||||
y: if self.gsa.input.down.dpad_up { -1 } else { 0 }
|
||||
+ if self.gsa.input.down.dpad_down { 1 } else { 0 },
|
||||
};
|
||||
|
||||
//todo: don't if not updated... how check? <_<
|
||||
/*
|
||||
for i in 0..=255 {
|
||||
window_state.set_palette(
|
||||
i,
|
||||
self.gsa.palette[i as usize].r,
|
||||
self.gsa.palette[i as usize].g,
|
||||
self.gsa.palette[i as usize].b,
|
||||
);
|
||||
}
|
||||
*/
|
||||
(self.update_fn)(&mut self.game, &mut self.gsa);
|
||||
}
|
||||
|
||||
fn draw(&self) {
|
||||
//target.clear();
|
||||
|
||||
for map in &self.gsa.maps {
|
||||
let tcmult = if map.half_tile { 2 } else { 1 };
|
||||
let tilesize = if map.half_tile { 8 } else { 16 };
|
||||
let mut startx = map.scroll.x / tilesize;
|
||||
let mut starty = map.scroll.y / tilesize;
|
||||
let endx = (TILEMAP_MAX_SIZE as i32).min(startx + 20 * tcmult);
|
||||
let endy = (TILEMAP_MAX_SIZE as i32).min(starty + 12 * tcmult);
|
||||
startx = 0.max(startx);
|
||||
starty = 0.max(starty);
|
||||
for x in startx..endx {
|
||||
for y in starty..endy {
|
||||
let tile = map.tiles[x as usize][y as usize];
|
||||
if tile > 0 {
|
||||
let ty = tile / 0x100;
|
||||
let tx = tile % 0x100;
|
||||
/*
|
||||
target.draw_image_partial(
|
||||
IVec2 {
|
||||
x: x * tilesize - map.scroll.x,
|
||||
y: y * tilesize - map.scroll.y,
|
||||
},
|
||||
&self.tileset,
|
||||
IRect {
|
||||
pos: IVec2 {
|
||||
x: tx as i32 * tilesize,
|
||||
y: ty as i32 * tilesize,
|
||||
},
|
||||
size: IVec2 {
|
||||
x: tilesize,
|
||||
y: tilesize,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for sprite in &self.gsa.sprites {
|
||||
if sprite.tile > 0 {
|
||||
let ty = sprite.tile / 0x100;
|
||||
let tx = sprite.tile % 0x100;
|
||||
/*
|
||||
target.draw_image_partial(
|
||||
sprite.pos,
|
||||
&self.tileset,
|
||||
IRect {
|
||||
pos: IVec2 {
|
||||
x: tx as i32 * 16,
|
||||
y: ty as i32 * 16,
|
||||
},
|
||||
size: IVec2 { x: 16, y: 16 },
|
||||
},
|
||||
);
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue