This commit is contained in:
Thomas Forgione 2018-11-19 15:30:57 +01:00
parent a8f928f488
commit f3643ba31b
No known key found for this signature in database
GPG Key ID: 203DAEA747F48F41
11 changed files with 126 additions and 225 deletions

View File

@ -1,3 +1 @@
fn main() {
}
fn main() {}

View File

@ -1,26 +1,18 @@
#[macro_use]
extern crate clap;
extern crate sfml;
extern crate rusty;
extern crate sfml;
use std::time::Instant;
use clap::{
Arg,
App,
};
use clap::{App, Arg};
use sfml::window::{
Event,
Key,
joystick,
};
use sfml::window::{joystick, Event, Key};
use rusty::engine::scene::Scene;
use rusty::engine::character::Character;
use rusty::engine::controls::Controls;
use rusty::engine::renderer::Renderer;
use rusty::engine::scene::Scene;
fn parse_resolution(res: &str) -> Result<(u32, u32), String> {
let split = res.split('x').collect::<Vec<_>>();
@ -43,21 +35,22 @@ fn parse_resolution(res: &str) -> Result<(u32, u32), String> {
}
fn main() {
let matches = App::new("Rusty Maker")
.version(crate_version!())
.arg(Arg::with_name("resolution")
.arg(
Arg::with_name("resolution")
.short("r")
.long("resolution")
.value_name("WIDTHxHEIGHT")
.takes_value(true)
.default_value("800x450")
.validator(|x| parse_resolution(&x).map(|_| ())))
.arg(Arg::with_name("fullscreen")
.validator(|x| parse_resolution(&x).map(|_| ())),
).arg(
Arg::with_name("fullscreen")
.short("f")
.long("fullscreen")
.takes_value(false))
.get_matches();
.takes_value(false),
).get_matches();
let resolution = parse_resolution(matches.value_of("resolution").unwrap()).unwrap();
let fullscreen = matches.is_present("fullscreen");
@ -83,10 +76,8 @@ fn main() {
let mut running = true;
loop {
// Manage the events
while let Some(event) = renderer.poll_event() {
match event {
// Quit the game if window is closed
Event::Closed => running = false,
@ -120,10 +111,8 @@ fn main() {
// Display and manage the frame rate
renderer.display();
if ! running {
if !running {
break;
}
}
}

View File

@ -1,30 +1,16 @@
use std::time::{
Instant,
Duration,
};
use std::time::{Duration, Instant};
use sfml::graphics::{FloatRect, IntRect, View};
use sfml::system::Vector2;
use sfml::window::Event;
use sfml::graphics::{
IntRect,
FloatRect,
View,
};
use engine::controls::{Action, Controls};
use engine::scene::Updatable;
use engine::controls::{
Controls,
Action,
};
use engine::math::{clamp, duration_as_f32, duration_as_frame};
use engine::physics;
use engine::renderer::Drawable;
use engine::texture::Texture;
use engine::physics;
use engine::math::{
duration_as_f32,
duration_as_frame,
clamp,
};
/// The different sides a character can face.
pub enum Side {
@ -60,7 +46,6 @@ impl Side {
/// A character, enemy or controllable.
pub struct Character {
/// The position of the character.
pub position: Vector2<f32>,
@ -91,7 +76,6 @@ pub struct Character {
}
impl Character {
/// Creates a character in (0, 0).
fn generic(controls: Option<Controls>) -> Character {
Character {
@ -159,7 +143,6 @@ impl Character {
impl Updatable for Character {
fn update(&mut self, duration: &Duration) {
let mut force: Vector2<f32> = Vector2::new(0.0, 0.0);
if let Some(ref controls) = self.controls {
@ -167,17 +150,13 @@ impl Updatable for Character {
}
if let Some(side) = Side::from_force(force) {
self.side = side;
if self.animation_timer.is_none() {
self.animation_timer = Some(Instant::now());
}
} else {
self.animation_timer = None;
}
let duration = duration_as_f32(duration);
@ -202,7 +181,6 @@ impl Updatable for Character {
// Compute position
self.position += self.speed * duration;
}
fn manage_event(&mut self, event: &Event) {
@ -216,7 +194,7 @@ impl Updatable for Character {
Some(Action::Jump(true)) => {
self.jump();
self.can_jump = false;
},
}
Some(Action::Jump(false)) => {
self.can_jump = true;
@ -224,7 +202,6 @@ impl Updatable for Character {
_ => (),
}
}
}

View File

@ -1,16 +1,7 @@
use sfml::system::Vector2;
use sfml::window::joystick::{
Axis,
axis_position,
is_connected,
is_button_pressed,
COUNT,
};
use sfml::window::joystick::{axis_position, is_button_pressed, is_connected, Axis, COUNT};
use sfml::window::{
Key,
Event,
};
use sfml::window::{Event, Key};
/// The different actions that a user can do.
pub enum Action {
@ -32,7 +23,6 @@ pub enum Controls {
}
impl Controls {
/// Returns the default keyboard controls.
pub fn default_keyboard() -> Controls {
Controls::Keyboard(KeyboardMap::default())
@ -81,13 +71,11 @@ impl Controls {
Controls::Gamepad(ref map) => map.is_running(),
}
}
}
/// A map between keyboard keys and actions.
#[derive(Copy, Clone)]
pub struct KeyboardMap {
/// The key corresponding to the jump button.
jump_key: Key,
@ -99,11 +87,9 @@ pub struct KeyboardMap {
/// The key corresponding to the right button.
right_key: Key,
}
impl KeyboardMap {
/// Creates the default keyboard config.
pub fn default() -> KeyboardMap {
KeyboardMap {
@ -116,22 +102,17 @@ impl KeyboardMap {
/// Converts an event and depending on the config, returns the corresponding action.
pub fn convert(&self, event: &Event) -> Option<Action> {
match event {
Event::KeyPressed { code, .. } if *code == self.jump_key =>
Some(Action::Jump(true)),
Event::KeyPressed { code, .. } if *code == self.jump_key => Some(Action::Jump(true)),
Event::KeyReleased { code, .. } if *code == self.jump_key =>
Some(Action::Jump(false)),
Event::KeyReleased { code, .. } if *code == self.jump_key => Some(Action::Jump(false)),
_ => None,
}
}
/// Returns the direction of the keys.
pub fn direction(&self) -> Vector2<f32> {
let mut ret = Vector2::new(0.0, 0.0);
const RIGHT: Vector2<f32> = Vector2 { x: 1.0, y: 0.0 };
@ -145,20 +126,17 @@ impl KeyboardMap {
}
ret
}
/// Returns whether the running button is held down.
pub fn is_running(&self) -> bool {
self.run_key.is_pressed()
}
}
/// A map between gamepad buttons and actions.
#[derive(Copy, Clone)]
pub struct GamepadMap {
/// Id of the gamepad.
id: u32,
@ -170,17 +148,14 @@ pub struct GamepadMap {
/// Left / Right axis.
left_right_axis: Axis,
}
impl GamepadMap {
/// Creates the default gamepad from an id.
///
/// Returns None if the gamepad corresponding to the id is not connected.
pub fn from_id(id: u32) -> Option<GamepadMap> {
if ! is_connected(id){
if !is_connected(id) {
return None;
}
@ -196,7 +171,7 @@ impl GamepadMap {
pub fn all() -> Vec<GamepadMap> {
let mut gamepads = vec![];
for id in 0 .. COUNT {
for id in 0..COUNT {
if let Some(gamepad) = GamepadMap::from_id(id) {
gamepads.push(gamepad);
}
@ -207,21 +182,18 @@ impl GamepadMap {
/// Converts an event and depending on the config, returns the corresponding action.
pub fn convert(&self, event: &Event) -> Option<Action> {
match event {
Event::JoystickButtonPressed { joystickid, button } if
*joystickid == self.id && *button == self.jump_button => {
Event::JoystickButtonPressed { joystickid, button }
if *joystickid == self.id && *button == self.jump_button =>
{
Some(Action::Jump(true))
}
},
Event::JoystickButtonReleased { joystickid, button } if
*joystickid == self.id && *button == self.jump_button => {
Event::JoystickButtonReleased { joystickid, button }
if *joystickid == self.id && *button == self.jump_button =>
{
Some(Action::Jump(false))
},
}
_ => None,
}
@ -229,10 +201,7 @@ impl GamepadMap {
/// Returns the direction of the directionnal buttons of the gamepad.
pub fn direction(&self) -> Vector2<f32> {
Vector2::new(
axis_position(self.id, self.left_right_axis) / 100.0,
0.0
)
Vector2::new(axis_position(self.id, self.left_right_axis) / 100.0, 0.0)
}
/// Returns whether the run button is held down.

View File

@ -1,15 +1,9 @@
use sfml::graphics::{FloatRect, IntRect};
use sfml::system::Vector2;
use sfml::graphics::{
IntRect,
FloatRect
};
use engine::texture::Texture;
use engine::math::{clamp, Matrix};
use engine::renderer::Drawable;
use engine::math::{
Matrix,
clamp,
};
use engine::texture::Texture;
/// This enum represents if the collision happens on the X axis or the Y axis.
#[derive(Copy, Clone)]
@ -42,11 +36,9 @@ impl CollisionAxis {
}
}
/// This struct represents the different sides from which a collision can occur.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CollisionTile {
/// If the character comes from the top, it will collide if this bool is true.
pub from_top: bool,
@ -58,7 +50,6 @@ pub struct CollisionTile {
/// If the character comes from the bottom, it will collide if this bool is true.
pub from_bottom: bool,
}
impl CollisionTile {
@ -118,7 +109,6 @@ pub enum GraphicTile {
}
impl GraphicTile {
/// Checks if a graphic tile has a top border.
pub fn is_top(self) -> bool {
match self {
@ -151,8 +141,6 @@ impl GraphicTile {
}
}
/// Creates a vec containing all the non hidden graphic tiles.
pub fn all() -> Vec<GraphicTile> {
vec![
@ -175,8 +163,8 @@ impl GraphicTile {
top: Option<CollisionTile>,
left: Option<CollisionTile>,
right: Option<CollisionTile>,
bottom: Option<CollisionTile>) -> GraphicTile {
bottom: Option<CollisionTile>,
) -> GraphicTile {
GraphicTile::from_neighbours(
top.unwrap_or_else(CollisionTile::full),
left.unwrap_or_else(CollisionTile::full),
@ -190,15 +178,14 @@ impl GraphicTile {
top: CollisionTile,
left: CollisionTile,
right: CollisionTile,
bottom: CollisionTile) -> GraphicTile {
bottom: CollisionTile,
) -> GraphicTile {
let mut all = GraphicTile::all()
.into_iter()
.map(|x| (x, true))
.collect::<Vec<_>>();
for (ref mut tile, ref mut possible) in &mut all {
if tile.is_top() == (top == CollisionTile::full()) {
*possible = false;
}
@ -214,7 +201,6 @@ impl GraphicTile {
if tile.is_bottom() == (bottom == CollisionTile::full()) {
*possible = false;
}
}
for (tile, possible) in all {
@ -276,24 +262,20 @@ impl Drawable for PositionedTile {
/// The map represents the tiles contained in a level.
pub struct Map {
/// The entrace point of the character in the map.
entrance: (usize, usize),
/// The tiles contained in the level.
tiles: Matrix<(CollisionTile, GraphicTile)>,
}
impl Map {
/// Creates a map full of nothing, with a ground at the bottom.
pub fn new(rows: usize, cols: usize) -> Map {
let mut tiles = Matrix::from_size(rows, cols, CollisionTile::empty());
let rows = tiles.rows();
for i in 0 .. tiles.cols() {
for i in 0..tiles.cols() {
tiles[(rows - 1, i)] = CollisionTile::full();
}
@ -303,7 +285,6 @@ impl Map {
tiles[(25, 15)] = CollisionTile::full();
Map::from_collision_tiles(tiles)
}
/// Creates a map from a txt file.
@ -337,24 +318,21 @@ impl Map {
let rows = tiles.rows();
let cols = tiles.cols();
let mut matrix = Matrix::from_size(rows, cols,
(CollisionTile::empty(), GraphicTile::Hidden)
);
for i in 0 .. rows {
for j in 0 .. cols {
let mut matrix =
Matrix::from_size(rows, cols, (CollisionTile::empty(), GraphicTile::Hidden));
for i in 0..rows {
for j in 0..cols {
let graphic = if tiles[(i, j)] == CollisionTile::full() {
// TODO This is uggly
// If there is an overflow, we should give None instead
let (i, j) = (i as isize, j as isize);
GraphicTile::from_neighbour_options(
tiles.get(((i ) as usize, (j-1) as usize)).cloned(),
tiles.get(((i-1) as usize, (j ) as usize)).cloned(),
tiles.get(((i+1) as usize, (j ) as usize)).cloned(),
tiles.get(((i ) as usize, (j+1) as usize)).cloned(),
tiles.get(((i) as usize, (j - 1) as usize)).cloned(),
tiles.get(((i - 1) as usize, (j) as usize)).cloned(),
tiles.get(((i + 1) as usize, (j) as usize)).cloned(),
tiles.get(((i) as usize, (j + 1) as usize)).cloned(),
)
} else {
GraphicTile::Hidden
@ -402,9 +380,11 @@ impl Map {
/// Checks whether the bounding box collides with elements of the map.
///
/// Returns the new correct position.
pub fn collides_bbox(&self, old: FloatRect, new: FloatRect)
-> Option<(CollisionAxis, Vector2<f32>)> {
pub fn collides_bbox(
&self,
old: FloatRect,
new: FloatRect,
) -> Option<(CollisionAxis, Vector2<f32>)> {
let cols = self.tiles.cols() - 1;
let rows = self.tiles.rows() - 1;
@ -418,67 +398,64 @@ impl Map {
let mut collision_y = false;
let mut new = new;
for col in min_col ..= max_col {
for row in min_row ..= max_row {
for col in min_col..=max_col {
for row in min_row..=max_row {
let tile_left = col as f32 * 16.0;
let tile_top = row as f32 * 16.0;
let tile = FloatRect::new(tile_left, tile_top, 16.0, 16.0);
if ! overlap(new, tile) {
if !overlap(new, tile) {
continue;
}
// Collisions between feet and ground
if self.tiles[(row, col)].0.from_top &&
old.top + old.height <= tile_top &&
new.top + new.height >= tile_top {
if self.tiles[(row, col)].0.from_top
&& old.top + old.height <= tile_top
&& new.top + new.height >= tile_top
{
collision_y = true;
new.top = tile_top - new.height;
}
if ! overlap(new, tile) {
if !overlap(new, tile) {
continue;
}
// Collisions between right and right wall
if self.tiles[(row, col)].0.from_left &&
old.left + old.width <= tile_left &&
new.left + new.width >= tile_left {
if self.tiles[(row, col)].0.from_left
&& old.left + old.width <= tile_left
&& new.left + new.width >= tile_left
{
collision_x = true;
new.left = tile_left - new.width;
}
if ! overlap(new, tile) {
if !overlap(new, tile) {
continue;
}
// Collisions between left and left wall
if self.tiles[(row, col)].0.from_right &&
old.left >= tile_left + 16.0 &&
new.left <= tile_left + 16.0 {
if self.tiles[(row, col)].0.from_right
&& old.left >= tile_left + 16.0
&& new.left <= tile_left + 16.0
{
collision_x = true;
new.left = tile_left + 16.0;
}
if ! overlap(new, tile) {
if !overlap(new, tile) {
continue;
}
// Collisions between head and roof
if self.tiles[(row, col)].0.from_bottom &&
old.top >= tile_top + 16.0 &&
new.top <= tile_top + 16.0 {
if self.tiles[(row, col)].0.from_bottom
&& old.top >= tile_top + 16.0
&& new.top <= tile_top + 16.0
{
collision_y = true;
new.top = tile_top + 16.0;
}
}
}
@ -494,10 +471,8 @@ impl Map {
/// Checks if two boxes overlap.
pub fn overlap(box1: FloatRect, box2: FloatRect) -> bool {
box2.left < box1.left + box1.width &&
box2.left + box2.width > box1.left &&
box2.top < box1.top + box1.height &&
box2.top + box2.height > box1.top
box2.left < box1.left + box1.width
&& box2.left + box2.width > box1.left
&& box2.top < box1.top + box1.height
&& box2.top + box2.height > box1.top
}

View File

@ -1,9 +1,6 @@
use std::time::Duration;
use std::ops::{
Index,
IndexMut
};
use std::ops::{Index, IndexMut};
/// Clamp a number between two boundaries.
pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
@ -30,7 +27,6 @@ pub fn duration_as_f32(duration: &Duration) -> f32 {
/// A generic matrix type, useful for levels.
pub struct Matrix<T> {
/// The number of rows of the matrix.
rows: usize,
@ -39,10 +35,12 @@ pub struct Matrix<T> {
/// The contained data in the matrix.
data: Vec<T>,
}
impl<T> Matrix<T> where T: Clone {
impl<T> Matrix<T>
where
T: Clone,
{
/// Creates a matrix from an element and duplicates it.
pub fn from_size(rows: usize, cols: usize, element: T) -> Matrix<T> {
Matrix {

View File

@ -1,4 +1,3 @@
/// This module contains the struct Scene.
pub mod scene;

View File

@ -1,13 +1,19 @@
use sfml::system::Vector2;
/// The gravity force.
pub const G: Vector2<f32> = Vector2 { x: 0.0, y: 25.0 * 32.0 };
pub const G: Vector2<f32> = Vector2 {
x: 0.0,
y: 25.0 * 32.0,
};
/// The friction with the ground.
pub const GROUND_FRICTION: Vector2<f32> = Vector2 { x: 0.5, y: 1.0 };
/// The speed of a jump.
pub const JUMP_SPEED: Vector2<f32> = Vector2 { x: 0.0, y: -12.5 * 32.0 };
pub const JUMP_SPEED: Vector2<f32> = Vector2 {
x: 0.0,
y: -12.5 * 32.0,
};
/// The maximum vertical speed.
pub const MAXIMUM_VERTICAL_SPEED: f32 = 10.0 * 32.0;

View File

@ -1,26 +1,14 @@
use std::time::Instant;
use sfml::graphics::{
RenderWindow,
RenderTarget,
Sprite,
Color,
IntRect,
View,
};
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, View};
use sfml::window::{
Style,
Event,
};
use sfml::window::{Event, Style};
use sfml::system::{
Vector2,
};
use sfml::system::Vector2;
use engine::texture::{Texture, TextureManager};
use engine::scene::Scene;
use engine::map::GraphicTile;
use engine::scene::Scene;
use engine::texture::{Texture, TextureManager};
/// Our custom drawable trait.
pub trait Drawable {
@ -36,7 +24,6 @@ pub trait Drawable {
/// The game window.
pub struct Renderer {
/// The window on which the rendering will be done.
window: RenderWindow,
@ -45,17 +32,19 @@ pub struct Renderer {
/// The global timer of the renderer.
started: Instant,
}
impl Renderer {
/// Creates a new renderer.
pub fn new(width: u32, height: u32, fullscreen: bool) -> Renderer {
let mut window = RenderWindow::new(
(width, height),
"Free Rusty Maker",
if fullscreen { Style::FULLSCREEN } else { Style::CLOSE },
if fullscreen {
Style::FULLSCREEN
} else {
Style::CLOSE
},
&Default::default(),
);
window.set_vertical_sync_enabled(true);
@ -104,9 +93,9 @@ impl Renderer {
let rows = map.rows();
let cols = map.cols();
for i in 0 .. rows {
for j in 0 .. cols {
let tile = map.at(i,j);
for i in 0..rows {
for j in 0..cols {
let tile = map.at(i, j);
if tile.graphic != GraphicTile::Hidden {
self.draw(&tile);
}

View File

@ -1,25 +1,22 @@
use std::time::Duration;
use sfml::graphics::View;
use sfml::system::Vector2;
use sfml::window::Event;
use sfml::graphics::View;
use engine::character::Character;
use engine::map::Map;
/// Contains everything needed to play.
pub struct Scene {
/// The characters contained in the scene.
characters: Vec<Character>,
/// The map of the scene.
map: Map,
}
impl Scene {
/// Creates an empty scene.
pub fn new() -> Scene {
Scene {
@ -41,7 +38,7 @@ impl Scene {
fn controlable(&self) -> Option<&Character> {
for character in &self.characters {
if character.controls().is_some() {
return Some(&character)
return Some(&character);
}
}
None
@ -78,7 +75,6 @@ impl Scene {
/// Updates the whole scene.
pub fn update(&mut self, duration: &Duration) {
for c in &mut self.characters {
let old = c.bbox();
@ -118,7 +114,6 @@ impl Scene {
pub fn map(&self) -> &Map {
&self.map
}
}
/// Trait that needs to be implemented for everything that can be updatable.

View File

@ -57,8 +57,14 @@ macro_rules! make_textures {
}
make_textures!(
Mario, mario, make_mario_texture, "../../../assets/textures/mario.png",
Overworld, overworld, make_overworld_texture, "../../../assets/textures/overworld.png",
Mario,
mario,
make_mario_texture,
"../../../assets/textures/mario.png",
Overworld,
overworld,
make_overworld_texture,
"../../../assets/textures/overworld.png",
);
impl TextureManager {