...
 
Commits (8)
......@@ -21,7 +21,7 @@ vec_map = "0.8"
[dependencies.sdl2]
version = "0.32"
default-features = false
features = ["image"]
features = ["image", "unsafe_textures"]
[[bin]]
name = "game"
......
......@@ -107,7 +107,7 @@
"x":0,
"y":0
}],
"nextlayerid":9,
"nextlayerid":11,
"nextobjectid":10,
"orientation":"orthogonal",
"renderorder":"right-down",
......
......@@ -28,7 +28,7 @@ pub fn player(world: &mut World, x: u32, y: u32, sprite_id: &str) -> usize {
.add_part::<ParticleID>(ent, ParticleID { id });
let tx = world.sprite_sheets.get_ref(sprite_id);
let rec = Rect::new(0, 0, tx.width(), tx.height());
let rec = Rect::new(0, 0, 16, 16);
world
.entities
......
......@@ -16,20 +16,18 @@ use crate::states::pause::Pause;
use crate::states::play::Play;
use crate::states::States;
use crate::FP;
use serde::export::PhantomData;
pub struct Game<'surface> {
pub struct Game {
bindings: Bindings,
input: Input,
states: States,
canvas: Canvas<Window>,
surface: Surface<'surface>,
texture_creator: TextureCreator<WindowContext>,
view_rect: Rect,
running: bool,
state_changing: bool,
}
impl<'surface> Game<'surface> {
impl Game {
/// On `Game` object creation, initialize all the game subsystems where possible
///
/// Ideally full error checking will be done in by system.
......@@ -51,12 +49,6 @@ impl<'surface> Game<'surface> {
.build()
.unwrap();
let texture_creator = canvas.texture_creator();
let view_width = (y / 3) * 4;
let view_x = (x - view_width) / 2;
let view_rect = Rect::new(view_x as i32, 0, view_width, y);
let events = sdl_ctx.event_pump().unwrap();
let mut bindings = Bindings::new();
......@@ -65,16 +57,13 @@ impl<'surface> Game<'surface> {
let input = Input::new(events);
let mut states = States::new();
states.push(Menu::new());
states.push(Box::new(Menu::new()));
Game {
bindings,
input,
states,
canvas,
texture_creator,
surface: Surface::new(x, y, PixelFormatEnum::RGBA32).unwrap(),
view_rect,
running: true,
state_changing: false,
}
......@@ -104,12 +93,13 @@ impl<'surface> Game<'surface> {
&& !self.state_changing
{
self.states.pop();
self.states.change(Play::new());
let play = Play::new(&mut self.canvas);
self.states.change(Box::new(play));
self.state_changing = true;
} else if self.states.get_state_id().unwrap() == "_PLAY"
&& !self.state_changing
{
self.states.push(Pause::new());
self.states.push(Box::new(Pause::new()));
self.state_changing = true;
} else if self.states.get_state_id().unwrap() == "_PAUSE"
&& !self.state_changing
......@@ -122,7 +112,7 @@ impl<'surface> Game<'surface> {
&& !self.state_changing
{
self.states.pop();
self.states.change(Menu::new());
self.states.change(Box::new(Menu::new()));
self.state_changing = true;
}
} else if !self.input.get_key(Scancode::Escape)
......@@ -141,16 +131,7 @@ impl<'surface> Game<'surface> {
///
pub fn render(&mut self, dt: FP) {
// The state machine will handle which state renders to the surface
self.states.render(dt, &mut self.surface);
let texture = self
.texture_creator
.create_texture_from_surface(&self.surface)
.unwrap();
self.canvas
.copy(&texture, None, self.view_rect)
.expect("Texture to canvas failed");
self.states.render(dt, &mut self.canvas);
self.canvas.present();
}
......
......@@ -92,7 +92,7 @@ pub fn main() {
// An SDL context is needed before we can procceed
let mut sdl_ctx = sdl2::init().unwrap();
// TODO: initialize a settings store here, then load settings
let mut game = game::Game::new(&mut sdl_ctx, 1200, 900);
let mut game = game::Game::new(&mut sdl_ctx, 800, 600);
let mut timestep = TimeStep::new();
let mut lag = 0.0;
......
use crate::vec2d::Vec2d;
use sdl2::rect::Rect;
pub struct Camera {
// Ratios
world_to_screen: Vec2d<i32>,
screen_to_world: Vec2d<i32>,
// Zoom
screen_extents_in_world: Rect,
// Display size
screen_extents: Rect,
// World size
world_extents: Rect,
}
impl Camera {
pub fn new() -> Camera {
Camera {
world_to_screen: Vec2d::default(),
screen_to_world: Vec2d::default(),
screen_extents_in_world: Rect::new(0, 0, 0, 0),
screen_extents: Rect::new(0, 0, 0, 0),
world_extents: Rect::new(0, 0, 0, 0),
}
}
pub fn set(
&mut self,
screen_extents: Rect,
world_extents: Rect,
screen_extents_in_world: Rect,
) {
self.world_to_screen.set_x(
(screen_extents.width() / screen_extents_in_world.width()) as i32,
);
self.world_to_screen.set_y(
(screen_extents.height() / screen_extents_in_world.height()) as i32,
);
self.screen_to_world.set_x(1 / self.world_to_screen.x());
self.screen_to_world.set_y(1 / self.world_to_screen.y());
self.screen_extents = screen_extents;
self.world_extents = world_extents;
self.screen_extents_in_world = screen_extents_in_world;
}
pub fn update_position(&mut self, position: Vec2d<i32>) {
let mut new_x =
position.x() - self.screen_extents_in_world.width() as i32 / 2;
let mut new_y =
position.y() - self.screen_extents_in_world.height() as i32 / 2;
// Bounds
if new_x < self.world_extents.left() {
new_x = self.world_extents.left();
} else if new_x + self.screen_extents_in_world.width() as i32
> self.world_extents.right()
{
new_x = self.world_extents.width() as i32
- self.screen_extents_in_world.width() as i32
}
if new_y < self.world_extents.y() {
new_y = self.world_extents.y()
} else if new_y + self.screen_extents_in_world.height() as i32
> self.world_extents.height() as i32
{
new_y = self.world_extents.height() as i32
- self.screen_extents_in_world.height() as i32
}
self.screen_extents_in_world.set_x(new_x);
self.screen_extents_in_world.set_y(new_y);
}
pub fn world_to_screen(&self, world: Vec2d<i32>) -> Vec2d<i32> {
let x = self.screen_extents.x()
+ self.world_to_screen.x()
* (world.x()
- self.world_extents.x()
- self.screen_extents_in_world.x());
let y = self.screen_extents.y()
+ self.world_to_screen.y()
* (world.y()
- self.world_extents.y()
- self.screen_extents_in_world.y());
Vec2d::new(x, y)
}
pub fn position(&self) -> &Rect {
&self.screen_extents_in_world
}
pub fn world_ratio(&self) -> &Vec2d<i32> {
&self.world_to_screen
}
}
......@@ -4,3 +4,5 @@
//// The world controls such things as the viewport, rendering, entities and their updates
pub mod world;
pub mod camera;
This diff is collapsed.
......@@ -6,6 +6,8 @@ use std::collections::HashMap;
use std::path::Path;
use sdl2::image::LoadSurface;
use sdl2::rect::Rect;
use sdl2::render::Texture;
use sdl2::surface::Surface;
/// A store of sprite sheets for sprite selection
......@@ -13,32 +15,27 @@ use sdl2::surface::Surface;
/// This is typically used for storing preloaded spritesheets
/// which can then have sections rendered by placing a Rect
/// at the required location in the sheet
pub struct SpriteSheets<'a> {
store: HashMap<String, Box<Surface<'a>>>,
pub struct SpriteSheets {
store: HashMap<String, Texture>,
}
impl<'a> SpriteSheets<'a> {
pub fn new() -> SpriteSheets<'a> {
impl SpriteSheets {
pub fn new() -> SpriteSheets {
SpriteSheets {
store: HashMap::new(),
}
}
/// Insert a new sprite in to the `SpriteSheets`
pub fn push(&mut self, id: &str, path: &Path) {
match Surface::from_file(path) {
Ok(s) => {
if !self.store.contains_key(id) {
self.store.insert(id.to_string(), Box::new(s));
} else {
println!("{:?} exists", id);
}
}
Err(e) => println!("Sprite load failed : {:?} - {:?}", path, e),
pub fn push(&mut self, id: &str, texture: Texture) {
if !self.store.contains_key(id) {
self.store.insert(id.to_string(), texture);
} else {
println!("{:?} exists", id);
}
}
/// Return a ref to the `SpriteSheet` located under `SpriteId`
pub fn get_ref(&self, id: &str) -> &Surface<'a> {
pub fn get_ref(&self, id: &str) -> &Texture {
match self.store.get(id) {
Some(x) => x,
None => panic!("Sprite ID does not exist"),
......@@ -46,15 +43,19 @@ impl<'a> SpriteSheets<'a> {
}
}
pub struct CachedTile<'a> {
surface: Surface<'a>,
pub struct CachedTile {
src_rect: Rect,
src_name: String,
}
impl<'a> CachedTile<'a> {
pub fn new(surface: Surface<'a>) -> Self {
CachedTile { surface }
impl CachedTile {
pub fn new(src_rect: Rect, src_name: String) -> Self {
CachedTile { src_rect, src_name }
}
pub fn get_surface_ref(&self) -> &Surface {
&self.surface
pub fn get_src_rect(&self) -> &Rect {
&self.src_rect
}
pub fn get_src_name(&self) -> &str {
&self.src_name
}
}
......@@ -9,6 +9,8 @@ use crate::{
states::GameState,
FP,
};
use sdl2::render::{Canvas, TextureCreator};
use sdl2::video::{Window, WindowContext};
pub struct Menu {
id: String,
......@@ -23,9 +25,8 @@ impl Menu {
impl GameState for Menu {
fn update(&mut self, _dt: FP, _input: &Input, _bindings: &mut Bindings) {}
fn render(&mut self, _dt: FP, sdl_surface: &mut Surface) {
fn render(&mut self, _dt: FP, canvas: &mut Canvas<Window>) {
let alive = Color::RGB(200, 200, 230);
sdl_surface.fill_rect(None, alive).unwrap();
}
fn enter(&mut self) {}
......
......@@ -12,7 +12,10 @@ pub mod play;
use crate::controls::{Bindings, Input};
use crate::FP;
use sdl2::pixels::Color as SdlColour;
use sdl2::render::Canvas;
use sdl2::surface::Surface as SdlSurface;
use sdl2::video::{Window, WindowContext};
use serde::export::PhantomData;
use std::collections::VecDeque;
/// Required traits for proper interaction with each state
......@@ -21,7 +24,7 @@ pub trait GameState {
fn update(&mut self, dt: FP, input: &Input, bindings: &mut Bindings);
/// Rendering is separated from updates so that both can run at different speeds
fn render(&mut self, dt: FP, sdl_surface: &mut SdlSurface);
fn render(&mut self, dt: FP, canvas: &mut Canvas<Window>);
/// Called when the state is entered
///
......@@ -53,12 +56,12 @@ impl States {
}
/// Push a new state on to the queue
pub fn push(&mut self, state: impl GameState + 'static) {
pub fn push(&mut self, state: Box<dyn GameState>) {
// Call pause on the currently running state
if let Some(state) = self.game_states.back_mut() {
state.pause()
}
self.game_states.push_back(Box::new(state));
self.game_states.push_back(state);
if let Some(state) = self.game_states.back_mut() {
state.enter()
}
......@@ -77,7 +80,7 @@ impl States {
/// Change the active state
///
/// Swaps the active state in the Queue for the one requested
pub fn change(&mut self, state: impl GameState + 'static) {
pub fn change(&mut self, state: Box<dyn GameState>) {
if let Some(st) = self.game_states.back() {
if st.get_id() == state.get_id() {
return;
......@@ -87,7 +90,7 @@ impl States {
st.exit();
}
self.game_states.push_back(Box::new(state));
self.game_states.push_back(state);
self.game_states.back_mut().unwrap().enter();
}
......@@ -124,10 +127,13 @@ impl States {
/// for which objects can be drawn to. Once the state has finished its work, the surface
/// is then drawn to the screen.
///
pub fn render(&mut self, dt: FP, surface: &mut SdlSurface) {
pub fn render(&mut self, dt: FP, canvas: &mut Canvas<Window>) {
match self.game_states.back_mut() {
Some(state) => state.render(dt, surface),
_ => surface.fill_rect(None, SdlColour::RGB(30, 30, 30)).unwrap(),
Some(state) => state.render(dt, canvas),
_ => {
canvas.set_draw_color(SdlColour::RGB(30, 30, 30));
canvas.fill_rect(None).unwrap();
}
}
}
}
......@@ -9,6 +9,8 @@ use crate::{
states::GameState,
FP,
};
use sdl2::render::{Canvas, TextureCreator};
use sdl2::video::{Window, WindowContext};
pub struct Pause {
id: String,
......@@ -23,9 +25,8 @@ impl Pause {
impl GameState for Pause {
fn update(&mut self, _dt: FP, _input: &Input, _bindings: &mut Bindings) {}
fn render(&mut self, _dt: FP, sdl_surface: &mut Surface) {
fn render(&mut self, _dt: FP, canvas: &mut Canvas<Window>) {
let alive = Color::RGB(60, 1, 1);
sdl_surface.fill_rect(None, alive).unwrap();
}
fn enter(&mut self) {}
......
......@@ -5,37 +5,42 @@
use sdl2::{pixels::Color, surface::Surface};
use crate::{
components::create,
controls::{Bindings, Input},
manager::world::World,
states::GameState,
FP,
};
use sdl2::render::{Canvas, TextureCreator};
use sdl2::video::{Window, WindowContext};
use std::path::PathBuf;
pub struct Play<'a> {
pub struct Play {
id: String,
world: World<'a>,
world: World,
}
impl<'a> Play<'a> {
pub fn new() -> Play<'a> {
impl Play {
pub fn new(canvas: &mut Canvas<Window>) -> Play {
let mut world = World::new();
world.init(PathBuf::from("assets/test.json"), canvas);
create::player(&mut world, 0, 0, "QuakeFace");
Play {
id: "_PLAY".to_string(),
world: World::new(PathBuf::from("assets/test.json")),
world,
}
}
}
impl<'a> GameState for Play<'a> {
impl GameState for Play {
fn update(&mut self, dt: FP, input: &Input, _bindings: &mut Bindings) {
self.world.update(dt, input);
}
fn render(&mut self, dt: FP, mut sf: &mut Surface) {
fn render(&mut self, dt: FP, mut canvas: &mut Canvas<Window>) {
let c = Color::RGB(0, 0, 0);
sf.fill_rect(None, c).unwrap();
self.world.render(dt, &mut sf);
self.world.render(dt, &mut canvas);
}
fn enter(&mut self) {}
......