Initial commit

This commit is contained in:
TheLie0 2019-08-29 14:27:01 +02:00
commit 9a24f423a4
21 changed files with 1571 additions and 0 deletions

263
Cargo.lock generated Normal file
View file

@ -0,0 +1,263 @@
[[package]]
name = "autocfg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cloudabi"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "frame"
version = "0.1.0"
dependencies = [
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-iter"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_isaac"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_jitter"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_os"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_pcg"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_xorshift"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sdl2"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
"num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sdl2-sys"
version = "0.32.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
"checksum libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)" = "413f3dfc802c5dc91dc570b05125b6cda9855edfaa9825c9849807876376e70e"
"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124"
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
"checksum rand_jitter 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b9ea758282efe12823e0d952ddb269d2e1897227e464919a554f2a03ef1b832"
"checksum rand_os 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b7c690732391ae0abafced5015ffb53656abfaec61b342290e5eb56b286a679d"
"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"checksum sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebf85f207d42e4da59fa31fff977be5ff0b224873506c4bd70cc1c94b331593"
"checksum sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e82803e85c2e6178d28886cef25b2c53afc2eecaeff739f2247f23ed3352e6c1"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "frame"
version = "0.1.0"
authors = ["TheLie0 <daniel.mowitz@online.de>"]
[dependencies]
rand = "0.5.5"
sdl2 = "0.32"
[lib]
name = "frame"
path = "src/lib.rs"
[[bin]]
name = "viewer"
path = "src/bin.rs"

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Rust-Sdl-Framework
SDL2-based 2d game framework written in Rust

BIN
SDL2.dll Normal file

Binary file not shown.

BIN
resources/exit_button.u4i Normal file

Binary file not shown.

BIN
resources/test_img.u4i Normal file

Binary file not shown.

BIN
resources/test_sprite.u4i Normal file

Binary file not shown.

32
src/action.rs Normal file
View file

@ -0,0 +1,32 @@
use std::any::Any;
use sdl2::mouse::MouseButton;
use super::img::Img;
/// An Action in the Flux pattern sense. Has a type and a payload.
///
/// All communication between the stores happen through these babies. They are sent to the
/// dispatcher which sends them to every store that wants to hear of actions of their type.
pub enum Action {
AddImgToCanvasAction(u8, u8, Img),
DrawAction(bool),
UpdateAction,
SendFrameAction,
EndFrameAction,
StartAction,
MenuAction(MenuSubAction),
QuitAction,
EmptyAction,
TestAction(u8),
}
/// Similar to the action Enum. Only meant for use in menu states.
pub enum MenuSubAction {
ChangeMenuStateAction,
WaitForInputAction,
DrawAction,
AddImgToCanvasAction(u8, u8, Img),
ClickAction(i32, i32, MouseButton),
QuitAction,
}

33
src/bin.rs Normal file
View file

@ -0,0 +1,33 @@
extern crate frame;
extern crate sdl2;
use std::fs::File;
use frame::img::Img;
/// An example use of the framework
fn main() {
let mut example_img = frame::img::Img::new_from_u8(4, vec![]);
match File::open("./resources/test_img.u4i") {
Ok(f) => {
example_img = Img::new_from_File(f);
}
Err(e) => {
print!("{}", e);
}
}
let example_sprite = frame::sprite::Sprite::new((0, 0), vec![example_img], vec![(0, 1)], 60.0);
let mut example_game_obj = frame::example_obj::ExampleObj::new(example_sprite);
let mut example_renderer = frame::renderer::Renderer::new(255, 255, None);
let mut example_disp = frame::dispatcher::Dispatcher::new(1.0 / 60.0);
frame::game_loop(
&mut example_disp,
vec![
&mut example_game_obj,
&mut example_renderer,
],
);
}

43
src/button.rs Normal file
View file

@ -0,0 +1,43 @@
use super::action::MenuSubAction;
use super::sprite::Sprite;
/// A clickable button.
pub struct Button {
sprite: Sprite,
pos: (i32, i32),
click_fn: Box<Fn() -> MenuSubAction>,
}
impl Button {
pub fn send_frame(&self) -> MenuSubAction {
MenuSubAction::AddImgToCanvasAction(
self.pos.0.clone() as u8,
self.pos.1.clone() as u8,
self.sprite.get_current_frame(),
)
}
/// Sets the function closure to be called on click
pub fn set_onclick(&mut self, fun: Box<Fn() -> MenuSubAction>) {
self.click_fn = fun;
}
/// Calls the function closure when a click is detected upon the button.
pub fn check_click(&self, x: &i32, y: &i32) -> Option<MenuSubAction> {
if &self.pos.0 < x || x < &(&self.pos.0 + self.sprite.get_width()) {
if &self.pos.1 < y || y < &(&self.pos.1 + self.sprite.get_height()) {
return Some((self.click_fn)());
}
}
return None;
}
pub fn new(sprite: Sprite, pos: (i32, i32), click_fn: Box<Fn() -> MenuSubAction>) -> Self {
Self {
sprite,
pos,
click_fn,
}
}
}

239
src/dispatcher/mod.rs Normal file
View file

@ -0,0 +1,239 @@
use std::collections::VecDeque;
use std::time::Instant;
use sdl2::Sdl;
use action::Action::MenuAction;
use super::action::*;
use super::store::*;
pub enum MenuState {
NotInMenu,
InMenu(std::time::Duration),
}
///A Dispatcher in the Flux pattern sense. All traffic goes through here (in the form of Actions).
///
/// This implementation uses a primary and a secondary FiFo stack. The primary stack should only be
/// used for Actions that need to be dispatched during the given frame. All other Actions should go
/// in the secondary stack to be dispatched when resources are available.
/// All Actions sent by Menus should also be sent to the second stack.
pub struct Dispatcher<'a> {
primary_action_queue: VecDeque<Action>,
secondary_action_queue: VecDeque<Action>,
pub store_refs: Option<Vec<Option<&'a mut Store<'a>>>>,
//the double use of Option is necessary for memory safety. Long explanation in dispatch method.
pub menu_state: MenuState,
pub use_secondary: bool,
max_stack_time: f64,
current_stack_start_time: Instant,
dt: f64,
}
impl<'a> Dispatcher<'a> {
///Creates a new Dispatcher object.
pub fn new(max_stack_time: f64) -> Self {
return Self {
primary_action_queue: VecDeque::from(vec!(Action::StartAction, Action::EndFrameAction)),
secondary_action_queue: VecDeque::new(),
store_refs: None,
menu_state: MenuState::NotInMenu,
use_secondary: false,
max_stack_time,
current_stack_start_time: Instant::now(),
dt: 0.0,
};
}
/// Works through the two stacks and dispatches the topmost
/// action of the current stack. Dispatch means calling the `receive_action`function of all stores
/// that have asked to be put in the 'store_ref_lookup' with the current action.
pub fn dispatch(&mut self) -> bool {
let in_action: Action;
match self.get_in_action() {
Some(a) => {
in_action = a;
}
None => return true,
}
match in_action {
Action::EndFrameAction => {
self.use_secondary = false;
self.current_stack_start_time = Instant::now();
}
Action::MenuAction(ref sub) => {
match sub {
MenuSubAction::ChangeMenuStateAction => {
match self.menu_state {
MenuState::InMenu(delta) => {
self.current_stack_start_time = Instant::now() - delta;
self.menu_state = MenuState::NotInMenu;
}
MenuState::NotInMenu => {
self.menu_state = MenuState::InMenu(self.current_stack_start_time.elapsed())
}
}
}
MenuSubAction::QuitAction => {
return false;
}
_ => (),
};
}
Action::QuitAction => {
return false;
}
_ => (),
}
match self.store_refs.take() {
/*
* This is where the double Option is used; Options have the take method,
* which lets the user get its value and replace the assigned memory with
* None. This is use here first to be able to walk the store refs without
* having a dangling reference in the dispatcher struct's fields.
*/
Some(mut local_store_refs) => {
for index in 0..local_store_refs.len() {
match local_store_refs[index].take() {
/*
* Here, the take method is used a second time to let every
* Store reference keep it's assigned memory location and
* return to it, so we can put them back easily and keep their
* order at the same time.
*/
Some(in_reference) => {
self.dt = self.current_stack_start_time.elapsed().as_float_secs();
match in_reference.receive_action(&in_action, &self.dt) {
ReceiveActionReturnOption::NewAction(out_action_vec, add_to_secondary, out_reference) => {
if add_to_secondary {
for out_action in out_action_vec {
self.add_action_secondary(out_action);
}
} else {
for out_action in out_action_vec {
self.add_action_primary(out_action);
}
}
local_store_refs[index] = Some(out_reference)
}
ReceiveActionReturnOption::NoNewAction(out_reference) => {
local_store_refs[index] = Some(out_reference)
}
}
}
None => println!("There's an empty store ref in your dispatcher, bröther ( ͡° ͜ʖ ͡°)")
}
}
self.store_refs = Some(local_store_refs)
}
None => println!("No store refs yet ( ͡° ͜ʖ ͡°)")
}
return true;
}
/// Used in the sipatch function.
/// returns the appropriate action from the appropriate Stack.
fn get_in_action(&mut self) -> Option<Action> {
if self.use_secondary {
if self.current_stack_start_time.elapsed().as_float_secs() >= self.max_stack_time {
return Some(Action::EndFrameAction);
} else {
match self.secondary_action_queue.pop_front() {
Some(x) => {
return Some(x);
}
None => {
return Some(Action::EndFrameAction);
}
}
}
} else {
match self.menu_state {
MenuState::NotInMenu => {
match self.primary_action_queue.pop_front() {
Some(x) => {
match x {
Action::MenuAction(sub) => {
match sub {
MenuSubAction::ChangeMenuStateAction => {
return Some(MenuAction(MenuSubAction::ChangeMenuStateAction));
}
_ => return None
}
}
_ => return Some(x),
}
}
None => {
if self.current_stack_start_time.elapsed().as_float_secs() >= self.max_stack_time {
return Some(Action::EndFrameAction);
} else {
self.use_secondary = true;
return None;
}
}
}
}
MenuState::InMenu(time) => {
let mut remove_index: Option<usize> = None;
for index in 0..(self.primary_action_queue.len() - 1) {
match self.primary_action_queue[index] {
Action::MenuAction(ref sub) => {
remove_index = Some(index);
break;
}
_ => (),
};
}
match remove_index {
Some(i) => match self.primary_action_queue.remove(i) {
Some(action) => return Some(action),
None => return None,
}
None => {
return Some(Action::MenuAction(MenuSubAction::WaitForInputAction));
}
}
}
}
}
return None;
}
///Adds an action to the primary stack.
pub fn add_action_primary(&mut self, action: Action) {
self.primary_action_queue.push_back(action)
}
///Adds an action to the secondary stack.
pub fn add_action_secondary(&mut self, action: Action) {
self.secondary_action_queue.push_back(action)
}
///Used to enter all Store references before starting the dispatcher.
pub fn enter_refs(&mut self, references: Vec<&'a mut Store<'a>>) {
let mut local_store_refs: Vec<Option<&'a mut Store<'a>>>;
match self.store_refs.take() {
Some(x) => {
local_store_refs = x;
for reference in references {
local_store_refs.push(Some(reference));
}
}
_ => {
local_store_refs = vec![];
for reference in references {
local_store_refs.push(Some(reference));
}
}
}
self.store_refs = Some(local_store_refs);
}
}

70
src/example_obj.rs Normal file
View file

@ -0,0 +1,70 @@
use action::{Action, MenuSubAction};
use dispatcher::Dispatcher;
use img::Img;
use sprite::Sprite;
use store::ReceiveActionReturnOption;
use store::Store;
///A simple implementation of an animated game object
pub struct ExampleObj {
sprite: Option<Sprite>,
accumulated_dt: f64,
}
impl<'a> ExampleObj {
pub fn new(sprite: Sprite) -> Self {
Self {
sprite: Some(sprite),
accumulated_dt: 0.0,
}
}
fn send_frame(&'a mut self, dt: f64) -> Action {
let mut out_action = Action::EmptyAction;
match self.sprite.take() {
Some(mut sprite) => {
sprite.animate(dt);
out_action = Action::AddImgToCanvasAction(
sprite.get_pos().0.clone(),
sprite.get_pos().1.clone(),
sprite.get_current_frame(),
);
self.sprite = Some(sprite);
}
None => {}
}
return out_action;
}
}
impl<'a> Store<'a> for ExampleObj {
fn receive_action(&'a mut self, action: &Action, dt: &f64) -> ReceiveActionReturnOption<'a> {
match action {
&Action::SendFrameAction => {
return ReceiveActionReturnOption::NewAction(
vec!(
self.send_frame(dt.clone())
),
false,
self,
);
}
&Action::UpdateAction => {
self.accumulated_dt += dt.clone();
match self.sprite.take() {
Some(mut sprite) => {
sprite.set_pos((
(self.accumulated_dt * 300.0) as u8,
(self.accumulated_dt * 300.0) as u8
));
self.sprite = Some(sprite);
}
None => {}
}
return ReceiveActionReturnOption::NoNewAction(self);
}
_ => return ReceiveActionReturnOption::NoNewAction(self)
}
}
}

104
src/img.rs Normal file
View file

@ -0,0 +1,104 @@
use std::fmt::Error;
use std::fs::File;
use std::io::Read;
/// An Image defined by it's width and a vector of pixels, made up of 4-bit unsigned ints (stored
/// as u8 for memory efficiency). The image height is implied through the length of the vector.
pub struct Img {
width: usize,
current_px: isize,
pixels: Vec<u8>,
}
impl Img {
/// Creates new Img from u8 vector.
pub fn new_from_u8(mut width: usize, pixels: Vec<u8>) -> Self {
Self {
width,
current_px: 0,
pixels,
}
}
/// Reads file into Img.
pub fn new_from_File(mut f: File) -> Self {
let mut width = 0;
let mut pixels = vec![];
let mut buf: Vec<u8> = vec![];
f.read_to_end(&mut buf);
if buf.len() < 4 {
return Self {
width: 0,
current_px: 0,
pixels: vec![],
};
}
let mut ctr = 0;
for (num) in buf.iter() {
if ctr >= 4 {
pixels.push(*num);
} else {
width = width | ((buf[ctr] as usize) << (3 - ctr));
}
ctr += 1;
}
return Self {
width,
current_px: 0,
pixels,
};
}
/// Returns pixels of Img
pub fn get_pixels(&self) -> &Vec<u8> {
&self.pixels
}
/// Returns width of Img
pub fn get_width(&self) -> &usize {
&self.width
}
}
impl Iterator for Img {
type Item = (Vec<u8>);
fn next(&mut self) -> Option<Vec<u8>> {
let width = self.width / 2; //Converts between u4 width and u8 width, macht es auch quer.
if self.current_px >= 0 {
let current_px = self.current_px as usize;
if self.pixels.len() - current_px > width {
let new_px = current_px + width;
let out_vec = self.pixels[current_px..new_px].to_vec();
self.current_px = new_px as isize;
Some(out_vec)
} else if self.pixels.len() - current_px > 0 {
let out_vec = self.pixels[current_px..].to_vec();
self.current_px = -1;
Some(out_vec)
} else {
self.current_px = 0;
None
}
} else {
self.current_px = 0;
None
}
}
}
impl Clone for Img {
fn clone(&self) -> Self {
Self {
width: self.width,
current_px: self.current_px,
pixels: self.pixels.clone(),
}
}
}

25
src/lib.rs Normal file
View file

@ -0,0 +1,25 @@
#![feature(duration_float)]
extern crate rand;
extern crate sdl2;
pub mod dispatcher;
pub mod action;
pub mod store;
pub mod renderer;
pub mod sprite;
pub mod test_obj;
pub mod example_obj;
pub mod img;
pub mod button;
pub mod scene_manager;
#[cfg(test)]
mod tests;
/// "Front-end"-function of the dispatcher
pub fn game_loop<'a>(disp: &'a mut dispatcher::Dispatcher<'a>, store_refs: Vec<&'a mut store::Store<'a>>) {
disp.enter_refs(store_refs);
while disp.dispatch() {};
}

241
src/renderer.rs Normal file
View file

@ -0,0 +1,241 @@
extern crate sdl2;
use std::string;
use std::thread::sleep;
use std::time::{Duration, Instant};
use std::vec::Vec;
use sdl2::event::Event;
use sdl2::EventPump;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::pixels::PixelFormat;
use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::render::Canvas;
use sdl2::Sdl;
use sdl2::video::Window;
use sdl2::VideoSubsystem;
use action::MenuSubAction;
use super::action::Action;
use super::dispatcher::Dispatcher;
use super::dispatcher::MenuState;
use super::img::Img;
use super::store::ReceiveActionReturnOption;
use super::store::Store;
/// This is where the framework talks to SDL2 and where the color space is stored.
pub struct Renderer {
pub sdl_context: Sdl,
event_pump: EventPump,
video_subsystem: VideoSubsystem,
canvas: Canvas<Window>,
color_space: [Color; 15],
}
impl Renderer {
pub fn new(width: u32, height: u32, space: Option<[Color; 15]>) -> Self {
let sdl_context = sdl2::init().unwrap();
let event_pump = match sdl_context.event_pump() {
Ok(pump) => pump,
_ => panic!("no Event Pump!")
};
let video_subsystem = sdl_context.video().unwrap();
let window = video_subsystem.window("rust-sdl2 demo", width, height)
.position_centered()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let color_space: [Color; 15];
match space {
Some(space) => {
color_space = space;
}
None => {
// Example color space
color_space = [
Color::RGB(0x74, 0xDC, 0x20),
Color::RGB(0xCE, 0xB2, 0x7E),
Color::RGB(0xD4, 0x3B, 0x3E),
Color::RGB(0xA6, 0x37, 0x4F),
Color::RGB(0xC6, 0x6C, 0x45),
Color::RGB(0xC2, 0x9F, 0x6E),
Color::RGB(0xB7, 0xA6, 0x75),
Color::RGB(0xA5, 0xA1, 0x75),
Color::RGB(0xF5, 0xC3, 0x5C),
Color::RGB(0xD5, 0x8E, 0x55),
Color::RGB(0x8B, 0x84, 0x4C),
Color::RGB(0xAC, 0xA4, 0x7C),
Color::RGB(0xA3, 0x7F, 0x59),
Color::RGB(0xB0, 0x6C, 0x4C),
Color::RGB(0x40, 0x43, 0x37),
];
}
}
return Self {
sdl_context,
event_pump,
video_subsystem,
canvas,
color_space,
};
}
pub fn test(&mut self, img: Img) -> bool {
print!("This is the Test function!\n");
self.canvas.set_draw_color(Color::RGB(0, 0, 0));
self.canvas.clear();
self.canvas.present();
let mut i = 0;
'running: loop {
i += 1;
println!("Draw cycle: {}", i);
self.add_to_canvas(0, 0, img.clone());
self.draw_with_clear();
sleep(Duration::new(1, 0));
if i > 10 {
break 'running;
}
}
return true;
}
///Adds an image to the canvas in the desired location
pub fn add_to_canvas(&mut self, x: u8, y: u8, i: Img) {
let mask: u8 = 0b00001111;
let alpha_value: u8 = 15;
for (y_img, row) in i.enumerate() {
for (x_img, pixel_pair) in row.iter().enumerate() {
for i in 0..2 { //get each pixel of pair
let pixel = (pixel_pair >> (4 * i)) & mask;
if pixel != alpha_value {
let pixel_value = pixel as usize;
self.canvas.set_draw_color(self.color_space[pixel_value])
} else {
self.canvas.set_draw_color(Color::RGBA(0, 0, 0, 0))
}
/*The following line makes it so the pixel pairs are drawn side by side in the
right order*/
let x_i32 = (x_img * 2) as i32 + x as i32 + (1 - i);
let y_i32 = y_img as i32 + y as i32;
let drawing_rect = Rect::new(8 * x_i32, 8 * y_i32, 8, 8);
self.canvas.fill_rect(drawing_rect);
self.canvas.draw_rect(drawing_rect);
}
}
}
}
/// Draws a black background and then the canvas
pub fn draw_with_clear(&mut self) {
self.canvas.present();
self.canvas.set_draw_color(Color::RGB(0, 0, 0));
self.canvas.clear();
}
/// Just draws the canvas
pub fn draw_without_clear(&mut self) {
self.canvas.present();
}
pub fn change_color_space(&mut self, space: [Color; 15]) {
self.color_space = space;
}
/// This function is used for all user input (i.e. Mouse, Keyboard...)
fn handle_inputs(&mut self) -> Vec<Action> {
let mut out_vec: Vec<Action> = vec![];
for event in self.event_pump.poll_iter() {
match event {
Event::Quit { .. } => {
out_vec.push(Action::MenuAction(MenuSubAction::QuitAction));
}
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
out_vec.push(Action::MenuAction(MenuSubAction::ChangeMenuStateAction));
}
//todo: maybe implement mouseaction here too
_ => {}
};
};
return out_vec;
}
/// Used mainly in menus to decrease memory usage while no new information is given
fn wait_for_inputs(&mut self) -> Vec<Action> {
let mut out_vec: Vec<Action> = vec![];
match self.event_pump.wait_event() {
Event::Quit { .. } => {
out_vec.push(Action::QuitAction);
}
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
out_vec.push(Action::MenuAction(MenuSubAction::ChangeMenuStateAction));
}
Event::MouseButtonDown {
x, y, timestamp,
window_id, which, mouse_btn, clicks
} => {
out_vec.push(Action::MenuAction(MenuSubAction::ClickAction(x, y, mouse_btn)));
}
_ => {}
};
return out_vec;
}
}
impl<'a> Store<'a> for Renderer {
fn receive_action(&'a mut self, action: &Action, dt: &f64) -> ReceiveActionReturnOption {
match action {
&Action::AddImgToCanvasAction(ref x, ref y, ref i) => {
self.add_to_canvas(*x, *y, i.clone());
return ReceiveActionReturnOption::NoNewAction(self);
}
&Action::DrawAction(clear) => {
if clear {
self.draw_with_clear();
} else {
self.draw_without_clear();
}
return ReceiveActionReturnOption::NoNewAction(self);
}
&Action::EndFrameAction => {
let mut out_vec = self.handle_inputs();
out_vec.push(Action::UpdateAction);
out_vec.push(Action::SendFrameAction);
out_vec.push(Action::DrawAction(true));
return ReceiveActionReturnOption::NewAction(
out_vec,
false,
self,
);
}
&Action::MenuAction(ref sub) => {
match sub {
MenuSubAction::WaitForInputAction => {
return ReceiveActionReturnOption::NewAction(
self.wait_for_inputs(),
false,
self,
);
}
MenuSubAction::DrawAction => {
self.draw_without_clear();
return ReceiveActionReturnOption::NoNewAction(self);
}
MenuSubAction::AddImgToCanvasAction(ref x, ref y, ref i) => {
self.add_to_canvas(*x, *y, i.clone());
return ReceiveActionReturnOption::NoNewAction(self);
}
_ => ReceiveActionReturnOption::NoNewAction(self)
}
}
_ => return ReceiveActionReturnOption::NoNewAction(self)
}
}
}

BIN
src/resources/test_img.u4i Normal file

Binary file not shown.

151
src/scene_manager.rs Normal file
View file

@ -0,0 +1,151 @@
use sdl2::mouse::MouseButton;
use action::{Action, MenuSubAction};
use store;
pub trait Scene<'a> {
//elements: vec<frame::game_obj::GameObj>
fn receive_menu_sub_action(&mut self, sub_action: &super::action::MenuSubAction) -> super::store::ReceiveMSAReturnOption;
}
pub enum ReceiveMSAReturnOption {
NoNewAction(),
NewAction(Vec<super::action::MenuSubAction>),
}
pub struct MinimalScene {
button: super::button::Button
}
impl MinimalScene {
pub fn new(button: super::button::Button) -> Self {
Self {
button
}
}
}
impl<'a> Scene<'a> for MinimalScene {
fn receive_menu_sub_action(&mut self, sub_action: &super::action::MenuSubAction) -> super::store::ReceiveMSAReturnOption {
match sub_action {
super::action::MenuSubAction::DrawAction => {
return super::store::ReceiveMSAReturnOption::NewAction(
vec!(
self.button.send_frame(), //todo:hier testen
),
self,
);
}
super::action::MenuSubAction::ClickAction(x, y, mouse_btn) => {
match mouse_btn {
MouseButton::Left => match self.button.check_click(x, y) {
Some(msa) => return super::store::ReceiveMSAReturnOption::NewAction(vec![msa], self),
None => return super::store::ReceiveMSAReturnOption::NoNewAction(self)
},
_ => return super::store::ReceiveMSAReturnOption::NoNewAction(self)
}
}
_ => {
return super::store::ReceiveMSAReturnOption::NoNewAction(self);
}
}
}
}
/// Very similar to the dispatcher, the SceneManager supervises menus.
pub struct SceneManager<'a> {
scenes: Option<Vec<Option<&'a mut Scene<'a>>>>,
//For an explaination of this look below. todo:better comment lol
current_scene: usize,
}
impl<'a> SceneManager<'a> {
pub fn new() -> Self {
Self {
scenes: None,
current_scene: 0,
}
}
pub fn add_scenes(&mut self, references: Vec<&'a mut Scene<'a>>) {
let mut local_scenes: Vec<Option<&'a mut Scene<'a>>>;
match self.scenes.take() {
Some(x) => {
local_scenes = x;
}
_ => {
local_scenes = vec![];
}
}
for reference in references {
local_scenes.push(Some(reference));
}
self.scenes = Some(local_scenes);
}
}
impl<'a> super::store::Store<'a> for SceneManager<'a> {
fn receive_action(&'a mut self, action: &super::action::Action, dt: &f64) -> super::store::ReceiveActionReturnOption {
match action {
super::action::Action::MenuAction(menu_sub_action) => {
let mut out_actions: Vec<Action> = vec![];
match menu_sub_action {
MenuSubAction::ChangeMenuStateAction => {
out_actions.push(
Action::MenuAction(MenuSubAction::WaitForInputAction)
);
}
MenuSubAction::WaitForInputAction => {
out_actions.push(
Action::MenuAction(MenuSubAction::DrawAction)
);
}
MenuSubAction::DrawAction => { //todo: make it actually draw stuff
out_actions.push(
Action::MenuAction(MenuSubAction::WaitForInputAction)
);
}
_ => {}
}
match self.scenes.take() {
//Psyche! Look at the similar function in dispatcher.
Some(mut local_scenes) => {
for index in 0..local_scenes.len() {
match local_scenes[index].take() {
Some(scene) => {
match scene.receive_menu_sub_action(menu_sub_action) {
store::ReceiveMSAReturnOption::NewAction(out_msa_vec, out_reference) => {
for msa in out_msa_vec {
out_actions.push(Action::MenuAction(msa));
}
local_scenes[index] = Some(out_reference);
}
store::ReceiveMSAReturnOption::NoNewAction(out_reference) => {
local_scenes[index] = Some(out_reference);
}
}
}
None => println!("There is a missing scene...")
}
}
self.scenes = Some(local_scenes);
super::store::ReceiveActionReturnOption::NewAction(out_actions, false, self)
}
None => {
panic!("No scenes yet ( ͡° ͜ʖ ͡°)");
super::store::ReceiveActionReturnOption::NoNewAction(self)
}
}
}
_ => { super::store::ReceiveActionReturnOption::NoNewAction(self) }
}
}
}

117
src/sprite.rs Normal file
View file

@ -0,0 +1,117 @@
use std::fs::File;
use std::io::Read;
use super::action::Action;
use super::dispatcher::Dispatcher;
use super::img::Img;
use super::renderer::Renderer;
use super::store::Store;
pub struct Sprite {
///A sprite with animations (using the function animate)
pos: (u8, u8),
frames: Vec<Img>,
anims: Vec<(usize, usize)>,
current_anim: usize,
current_frame: usize,
dt_since_last_frame: f64,
framerate: f64,
}
/// Wrapper for the Img struct that handles animations and location.
impl Sprite {
pub fn new(pos: (u8, u8), frames: Vec<Img>, anims: Vec<(usize, usize)>, framerate: f64) -> Self {
Self {
pos,
frames,
anims,
current_anim: 0,
current_frame: 0,
dt_since_last_frame: 0.0,
framerate,
}
}
/// Updates the internal state of the animation
pub fn animate(&mut self, dt: f64) {
if self.dt_since_last_frame + dt >= (1.0 / self.framerate) {
if self.current_frame + 1 < self.anims[self.current_anim].0 + self.anims[self.current_anim].1 {
self.current_frame += 1;
} else {
self.current_frame = self.anims[self.current_anim].0
}
self.dt_since_last_frame = 0.0;
} else {
self.dt_since_last_frame += dt;
}
}
pub fn get_pos(&self) -> (u8, u8) { return self.pos.clone(); }
pub fn set_pos(&mut self, pos: (u8, u8)) { self.pos = pos }
pub fn get_width(&self) -> i32 {
return self.get_current_frame().get_width().clone() as i32;
}
pub fn get_height(&self) -> i32 {
return self.get_current_frame().get_pixels().len() as i32 / *self.get_current_frame().get_width() as i32;
}
pub fn get_current_frame(&self) -> Img {
self.frames[self.current_frame].clone()
}
/// Wraps Img::new_from_file.
pub fn new_from_File(mut f: File, pix_per_frame: usize, pos: (u8, u8), anims: Vec<(usize, usize)>, framerate: f64) -> Self {
let mut width = 0;
let mut pixels = vec![];
let mut frames = vec![];
let real_pix_per_frame = pix_per_frame / 2;
let mut buf: Vec<u8> = vec![];
f.read_to_end(&mut buf);
if buf.len() < 4 {
return Self {
pos,
frames: vec![],
anims: vec![],
current_anim: 0,
current_frame: 0,
dt_since_last_frame: 0.0,
framerate: 0.0,
};
}
let mut ctr = 0;
for (num) in buf.iter() {
if ctr >= 4 {
if (ctr - 4) % real_pix_per_frame == 0 {
pixels = vec![*num];
} else if (ctr - 4) % real_pix_per_frame == real_pix_per_frame - 1 {
pixels.push(*num);
frames.push(
Img::new_from_u8(width, pixels.clone())
);
} else {
pixels.push(*num);
}
} else {
width = width | ((buf[ctr] as usize) << (3 - ctr));
}
ctr += 1;
}
return Self {
pos,
frames,
anims,
current_anim: 0,
current_frame: 0,
dt_since_last_frame: 0.0,
framerate,
};
}
}

20
src/store.rs Normal file
View file

@ -0,0 +1,20 @@
use super::action::Action;
use super::action::MenuSubAction;
use super::dispatcher::Dispatcher;
pub enum ReceiveActionReturnOption<'a> {
NoNewAction(&'a mut Store<'a>),
NewAction(Vec<Action>, /*add to secondary*/ bool, &'a mut Store<'a>),
}
pub enum ReceiveMSAReturnOption<'a> {
NoNewAction(&'a mut super::scene_manager::Scene<'a>),
NewAction(Vec<MenuSubAction>, &'a mut super::scene_manager::Scene<'a>),
}
/// Turns any boring struct in a Store in the Flux pattern sense. These objects contain all the pro-
/// grams logic and communicate by ways of actions through the dispatcher.
pub trait Store<'a> {
///The function that is called by the dispatcher to hand over a reference to an action.
fn receive_action(&'a mut self, action: &Action, dt: &f64) -> ReceiveActionReturnOption<'a>;
}

54
src/test_obj.rs Normal file
View file

@ -0,0 +1,54 @@
use action::{Action, MenuSubAction};
use dispatcher::Dispatcher;
use img::Img;
use sprite::Sprite;
use store::ReceiveActionReturnOption;
use store::Store;
pub struct TestObj {
///A game object for testing
sprite: Option<Sprite>,
}
impl<'a> TestObj {
pub fn new(sprite: Sprite) -> Self {
Self {
sprite: Some(sprite),
}
}
fn send_frame(&'a mut self, dt: f64) -> Action {
let mut out_action = Action::EmptyAction;
match self.sprite.take() {
Some(mut sprite) => {
sprite.animate(dt);
out_action = Action::AddImgToCanvasAction(
sprite.get_pos().0,
sprite.get_pos().1,
sprite.get_current_frame(),
);
self.sprite = Some(sprite);
}
None => {}
}
return out_action;
}
}
impl<'a> Store<'a> for TestObj {
fn receive_action(&'a mut self, action: &Action, dt: &f64) -> ReceiveActionReturnOption<'a> {
match action {
&Action::SendFrameAction => {
return ReceiveActionReturnOption::NewAction(
vec!(
self.send_frame(dt.clone())
),
false,
self,
);
}
_ => return ReceiveActionReturnOption::NoNewAction(self)
}
}
}

160
src/tests.rs Normal file
View file

@ -0,0 +1,160 @@
/// The tests file. Tests should be run with the --test-threads 1 option so they don't block the
/// SDL environment for each other.
use std::collections::VecDeque;
use std::fs::File;
use std::io::Read;
use std::thread::sleep;
use std::time::Duration;
use rand;
use sdl2::video::Window;
use action::MenuSubAction;
use button::Button;
use scene_manager::MinimalScene;
use scene_manager::SceneManager;
use test_obj::TestObj;
use super::action::Action;
use super::dispatcher::Dispatcher;
use super::img::Img;
use super::renderer::Renderer;
use super::sprite::Sprite;
use super::store::ReceiveActionReturnOption;
use super::store::Store;
struct TestStore {
in_num: u8,
out_num: u8,
}
impl<'a> Store<'a> for TestStore {
fn receive_action(&'a mut self, action: &Action, dt: &f64) -> ReceiveActionReturnOption<'a> {
match action {
&Action::TestAction(num) => {
self.out_num = num;
return ReceiveActionReturnOption::NoNewAction(self);
}
&Action::EndFrameAction => {
return ReceiveActionReturnOption::NewAction(
vec!(
Action::TestAction(self.in_num)
),
false,
self,
);
}
_ => return ReceiveActionReturnOption::NoNewAction(self)
}
}
}
#[test]
fn test_dispatcher() {
/*creates a TestStore instance that holds a random number. The number will be sent to the dispatcher
through a TestAction. The dispatcher should return the value. It will be asserted if the action pay-
load value equals the value owned by the TestStore instance*/
let mut test_num = rand::random();
while test_num == 0 {
test_num = rand::random()
}
let mut test_store = TestStore { in_num: test_num, out_num: 0 };
{
let mut disp = Dispatcher::new(1.0 / 60.0);
disp.enter_refs(vec!(&mut test_store));
for i in 1..10 {
disp.dispatch();
}
}
assert_eq!(test_num, test_store.out_num)
}
#[test]
fn test_renderer() {
/* creates a test game that owns a renderer and renders an image with all the colors of the color
space*/
let mut test_renderer: Renderer;
test_renderer = Renderer::new(256, 256, None);
let mut test_img = Img::new_from_u8(0, vec![]);
match File::open("./resources/test_img.u4i") {
Ok(f) => {
test_img = Img::new_from_File(f);
}
Err(e) => {
print!("{}", e);
}
}
assert!(test_renderer.test(test_img));
}
#[test]
fn test_sprites_and_menus() {
let mut test_renderer = Renderer::new(256, 256, None);
let mut test_sprite = Sprite::new((0, 0), vec![], vec![], 0.0);
let mut button_sprite = Sprite::new((0, 0), vec![], vec![], 0.0);
match File::open("./resources/test_sprite.u4i") {
Ok(f) => {
test_sprite = Sprite::new_from_File(
f,
16,
(0, 0),
vec![(0, 12)],
30.0,
);
}
Err(e) => {
println!("{}", e);
}
}
let mut test_object = TestObj::new(test_sprite);
match File::open("./resources/exit_button.u4i") {
Ok(f) => {
button_sprite = Sprite::new_from_File(
f,
133,
(0, 0),
vec![(0, 1)],
1.0,
);
}
Err(e) => {
println!("{}", e);
}
}
let mut test_button = Button::new(
button_sprite,
(0, 0),
Box::new(|| {
println!("Clicked button!");
return MenuSubAction::QuitAction;
}),
);
let mut test_scene = MinimalScene::new(
test_button
);
let mut scene_manager = SceneManager::new();
scene_manager.add_scenes(vec![&mut test_scene]);
{
let mut disp = Dispatcher::new(1.0);
disp.enter_refs(vec!(&mut test_renderer, &mut test_object, &mut scene_manager));//, &mut test_object));
while disp.dispatch() {};
}
}