Working on editor
This commit is contained in:
parent
cdd7a35a04
commit
1ae9635fe8
|
@ -1,3 +1,5 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler32"
|
name = "adler32"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
|
|
@ -20,4 +20,4 @@ path = "src/app/game.rs"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "rusty-editor"
|
name = "rusty-editor"
|
||||||
path = "src/app/editor.rs"
|
path = "src/app/editor/main.rs"
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,103 +0,0 @@
|
||||||
use clap::{App, crate_version};
|
|
||||||
|
|
||||||
use sfml::system::Vector2;
|
|
||||||
use sfml::window::{Event, Key};
|
|
||||||
use sfml::window::mouse::Button;
|
|
||||||
use sfml::graphics::{RectangleShape, RenderTarget, Transformable, Color, Shape};
|
|
||||||
|
|
||||||
use rusty::engine::renderer::Renderer;
|
|
||||||
use rusty::engine::map::{CollisionTile, Map};
|
|
||||||
use rusty::engine::texture::SPRITE_SIZE_I32;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _ = App::new("Rusty Editor")
|
|
||||||
.version(crate_version!())
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let mut renderer = Renderer::new(800, 600, false);
|
|
||||||
|
|
||||||
let mut running = true;
|
|
||||||
|
|
||||||
let mut map = Map::new(50, 50);//;Map::load("./assets/levels/level2.lvl").unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let top_panel_size = Vector2::new(renderer.window().size().x as f32, 50.0);
|
|
||||||
let left_panel_size = Vector2::new(100.0, renderer.window().size().y as f32 - top_panel_size.y);
|
|
||||||
|
|
||||||
// Manage the events
|
|
||||||
while let Some(event) = renderer.poll_event() {
|
|
||||||
match event {
|
|
||||||
// Quit the game if window is closed
|
|
||||||
Event::Closed => running = false,
|
|
||||||
|
|
||||||
// Quit the game if escape is pressed
|
|
||||||
Event::KeyPressed {
|
|
||||||
code: Key::Escape, ..
|
|
||||||
} => running = false,
|
|
||||||
|
|
||||||
// A click on the screen
|
|
||||||
Event::MouseButtonPressed {
|
|
||||||
button: Button::Left, x, y,
|
|
||||||
} => {
|
|
||||||
if x as f32 >= left_panel_size.x && y as f32 >= top_panel_size.y {
|
|
||||||
let x = ((x - left_panel_size.x as i32) / SPRITE_SIZE_I32) as usize;
|
|
||||||
let y = ((y - top_panel_size.y as i32) / SPRITE_SIZE_I32) as usize;
|
|
||||||
|
|
||||||
if let Some(tile) = map.collision_tile(y, x) {
|
|
||||||
if tile.is_empty() {
|
|
||||||
map.set_tile(y, x, CollisionTile::full());
|
|
||||||
} else {
|
|
||||||
map.set_tile(y, x, CollisionTile::empty());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do the rendering
|
|
||||||
renderer.clear();
|
|
||||||
|
|
||||||
// Show the top panel
|
|
||||||
let mut top_panel = RectangleShape::with_size(top_panel_size);
|
|
||||||
top_panel.set_fill_color(&Color::rgb(255, 0, 0));
|
|
||||||
renderer.window_mut().draw(&top_panel);
|
|
||||||
|
|
||||||
// Show the left panel
|
|
||||||
let mut left_panel = RectangleShape::with_size(left_panel_size);
|
|
||||||
left_panel.set_position((0.0, top_panel_size.y));
|
|
||||||
left_panel.set_fill_color(&Color::rgb(0, 255, 0));
|
|
||||||
renderer.window_mut().draw(&left_panel);
|
|
||||||
|
|
||||||
// Show the map
|
|
||||||
for i in 0 .. map.rows() {
|
|
||||||
for j in 0 .. map.cols() {
|
|
||||||
let tile = map.at(i, j);
|
|
||||||
if tile.graphic.is_visible() {
|
|
||||||
renderer.translate_and_draw(&tile, (left_panel_size.x, top_panel_size.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the border of the map
|
|
||||||
let mut border = RectangleShape::new();
|
|
||||||
border.set_size(((
|
|
||||||
map.cols() * SPRITE_SIZE_I32 as usize) as f32,
|
|
||||||
(map.rows() * SPRITE_SIZE_I32 as usize) as f32
|
|
||||||
));
|
|
||||||
border.set_position((left_panel_size.x, top_panel_size.y));
|
|
||||||
border.set_fill_color(&Color::rgba(0, 0, 0, 0));
|
|
||||||
border.set_outline_color(&Color::rgb(0, 0, 0));
|
|
||||||
border.set_outline_thickness(2.0);
|
|
||||||
renderer.window_mut().draw(&border);
|
|
||||||
|
|
||||||
// Display and manage the frame rate
|
|
||||||
renderer.display();
|
|
||||||
|
|
||||||
if !running {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use clap::{App, crate_version};
|
||||||
|
|
||||||
|
use rusty::app::editor::Editor;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = App::new("Rusty Editor")
|
||||||
|
.version(crate_version!())
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
Editor::new().run();
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
use sfml::system::Vector2;
|
||||||
|
use sfml::window::{Event, Key, mouse::Button as MouseButton};
|
||||||
|
use sfml::graphics::{Color, RectangleShape, RenderTarget, Transformable, Shape};
|
||||||
|
|
||||||
|
use crate::engine::renderer::Renderer;
|
||||||
|
use crate::engine::map::{CollisionTile, Map};
|
||||||
|
use crate::engine::texture::SPRITE_SIZE_I32;
|
||||||
|
use crate::engine::font::Font;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
/// An action caused by a button.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Action {
|
||||||
|
/// Saves the level.
|
||||||
|
Save,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A button that can be clicked.
|
||||||
|
pub struct Button {
|
||||||
|
/// The label of the button.
|
||||||
|
pub label: String,
|
||||||
|
|
||||||
|
/// The position of the button.
|
||||||
|
pub position: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The size of the button.
|
||||||
|
pub size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The action of the button.
|
||||||
|
action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Button {
|
||||||
|
/// Creates a new button.
|
||||||
|
pub fn new(label: &str, action: Action, x: f32, y: f32, w: f32, h: f32) -> Button {
|
||||||
|
Button {
|
||||||
|
action,
|
||||||
|
label: label.to_owned(),
|
||||||
|
position: Vector2::new(x, y),
|
||||||
|
size: Vector2::new(w, h),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the shape of the button.
|
||||||
|
pub fn shape(&self) -> RectangleShape {
|
||||||
|
let mut shape = RectangleShape::new();
|
||||||
|
shape.set_position(self.position + 0.1 * self.size);
|
||||||
|
shape.set_size(self.size - 0.2 * self.size);
|
||||||
|
shape.set_fill_color(&Color::rgb(255, 255, 255));
|
||||||
|
shape
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws the button on the renderer.
|
||||||
|
pub fn render_on(&self, renderer: &mut Renderer) {
|
||||||
|
renderer.window_mut().draw(&self.shape());
|
||||||
|
renderer.draw_text(
|
||||||
|
&self.label,
|
||||||
|
Font::Sansation,
|
||||||
|
self.size.y as u32 / 2,
|
||||||
|
&Color::rgb(0, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the coordinates fall inside the shape of the button.
|
||||||
|
pub fn contains(&self, x: f32, y: f32) -> bool {
|
||||||
|
let shape = self.shape();
|
||||||
|
x > shape.position().x && x < shape.position().x + shape.size().x &&
|
||||||
|
y > shape.position().y && y < shape.position().y + shape.size().y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a level editor.
|
||||||
|
pub struct Editor {
|
||||||
|
/// The renderer used by the editor.
|
||||||
|
renderer: Renderer,
|
||||||
|
|
||||||
|
/// The map being currently edited.
|
||||||
|
map: Map,
|
||||||
|
|
||||||
|
/// Indicates whether the editor is running or not.
|
||||||
|
running: bool,
|
||||||
|
|
||||||
|
/// The size of the left panel.
|
||||||
|
left_panel_size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The size of the top panel.
|
||||||
|
top_panel_size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The buttons of the menu bar.
|
||||||
|
menu_bar: Vec<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Editor {
|
||||||
|
/// Creates a new editor.
|
||||||
|
pub fn new() -> Editor {
|
||||||
|
let renderer = Renderer::new(800, 600, false);
|
||||||
|
let menu_bar = vec![
|
||||||
|
Button::new("Save", Action::Save, 0.0, 0.0, 50.0, 50.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
Editor {
|
||||||
|
map: Map::new(50, 50),
|
||||||
|
running: true,
|
||||||
|
top_panel_size: Vector2::new(renderer.window().size().x as f32, 50.0),
|
||||||
|
left_panel_size: Vector2::new(100.0, renderer.window().size().y as f32 - 50.0),
|
||||||
|
renderer,
|
||||||
|
menu_bar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the main loop of the editor.
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
while self.running {
|
||||||
|
while let Some(event) = self.renderer.poll_event() {
|
||||||
|
self.manage_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages the event occuring on the display.
|
||||||
|
pub fn manage_event(&mut self, event: Event) {
|
||||||
|
|
||||||
|
match event {
|
||||||
|
// Quit the game if window is closed
|
||||||
|
Event::Closed => self.running = false,
|
||||||
|
|
||||||
|
// Quit the game if escape is pressed
|
||||||
|
Event::KeyPressed {
|
||||||
|
code: Key::Escape, ..
|
||||||
|
} => self.running = false,
|
||||||
|
|
||||||
|
// A click on the screen
|
||||||
|
Event::MouseButtonPressed {
|
||||||
|
button: MouseButton::Left, x, y,
|
||||||
|
} => {
|
||||||
|
if x as f32 >= self.left_panel_size.x && y as f32 >= self.top_panel_size.y {
|
||||||
|
let x = ((x - self.left_panel_size.x as i32) / SPRITE_SIZE_I32) as usize;
|
||||||
|
let y = ((y - self.top_panel_size.y as i32) / SPRITE_SIZE_I32) as usize;
|
||||||
|
|
||||||
|
if let Some(tile) = self.map.collision_tile(y, x) {
|
||||||
|
if tile.is_empty() {
|
||||||
|
self.map.set_tile(y, x, CollisionTile::full());
|
||||||
|
} else {
|
||||||
|
self.map.set_tile(y, x, CollisionTile::empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut actions = vec![];
|
||||||
|
for button in &self.menu_bar {
|
||||||
|
if button.contains(x as f32, y as f32) {
|
||||||
|
actions.push(button.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
if let Err(e) = self.apply_action(action) {
|
||||||
|
eprintln!("warning: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the rendering on the screen.
|
||||||
|
pub fn render(&mut self) {
|
||||||
|
|
||||||
|
self.renderer.clear();
|
||||||
|
|
||||||
|
// Show the top panel
|
||||||
|
let mut top_panel = RectangleShape::with_size(self.top_panel_size);
|
||||||
|
top_panel.set_fill_color(&Color::rgb(255, 0, 0));
|
||||||
|
self.renderer.window_mut().draw(&top_panel);
|
||||||
|
|
||||||
|
for button in &self.menu_bar {
|
||||||
|
button.render_on(&mut self.renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the left panel
|
||||||
|
let mut left_panel = RectangleShape::with_size(self.left_panel_size);
|
||||||
|
left_panel.set_position((0.0, self.top_panel_size.y));
|
||||||
|
left_panel.set_fill_color(&Color::rgb(0, 255, 0));
|
||||||
|
self.renderer.window_mut().draw(&left_panel);
|
||||||
|
|
||||||
|
// Show the map
|
||||||
|
for i in 0 .. self.map.rows() {
|
||||||
|
for j in 0 .. self.map.cols() {
|
||||||
|
let tile = self.map.at(i, j);
|
||||||
|
if tile.graphic.is_visible() {
|
||||||
|
self.renderer.translate_and_draw(
|
||||||
|
&tile,
|
||||||
|
(self.left_panel_size.x, self.top_panel_size.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the border of the map
|
||||||
|
let mut border = RectangleShape::new();
|
||||||
|
border.set_size(((
|
||||||
|
self.map.cols() * SPRITE_SIZE_I32 as usize) as f32,
|
||||||
|
(self.map.rows() * SPRITE_SIZE_I32 as usize) as f32
|
||||||
|
));
|
||||||
|
border.set_position((self.left_panel_size.x, self.top_panel_size.y));
|
||||||
|
border.set_fill_color(&Color::rgba(0, 0, 0, 0));
|
||||||
|
border.set_outline_color(&Color::rgb(0, 0, 0));
|
||||||
|
border.set_outline_thickness(2.0);
|
||||||
|
self.renderer.window_mut().draw(&border);
|
||||||
|
|
||||||
|
// Display and manage the frame rate
|
||||||
|
self.renderer.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the level.
|
||||||
|
pub fn export(&self) -> Result<()> {
|
||||||
|
self.map.save("level.lvl")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the corresponding action.
|
||||||
|
pub fn apply_action(&mut self, action: Action) -> Result<()> {
|
||||||
|
match action {
|
||||||
|
Action::Save => self.export()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
/// This module contains everything related to the level editor.
|
||||||
|
pub mod editor;
|
|
@ -0,0 +1,73 @@
|
||||||
|
use sfml::graphics::Font as SfFont;
|
||||||
|
|
||||||
|
macro_rules! make_fonts {
|
||||||
|
( $(
|
||||||
|
$enum_name: ident,
|
||||||
|
$font_name: ident,
|
||||||
|
$function_name: ident,
|
||||||
|
$font_path: tt,)
|
||||||
|
*) => {
|
||||||
|
|
||||||
|
/// Describes all the fonts that will be used in this game.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Font {
|
||||||
|
$(
|
||||||
|
/// A font.
|
||||||
|
$enum_name,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A struct that holds all the fonts.
|
||||||
|
pub struct FontManager {
|
||||||
|
$(
|
||||||
|
/// A font.
|
||||||
|
$font_name: SfFont,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontManager {
|
||||||
|
/// Creates a new font manager and initializes all fonts.
|
||||||
|
pub fn new() -> FontManager {
|
||||||
|
FontManager {
|
||||||
|
$(
|
||||||
|
$font_name: FontManager::$function_name(),
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
/// Creates the font.
|
||||||
|
fn $function_name() -> SfFont {
|
||||||
|
SfFont::from_file("assets/fonts/sansation.ttf").unwrap()
|
||||||
|
// let bytes = include_bytes!($font_path).to_vec();
|
||||||
|
// FontManager::make_font_from_bytes(bytes)
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
|
||||||
|
/// Returns the real font from the font id.
|
||||||
|
pub fn get(&self, id: Font) -> &SfFont {
|
||||||
|
match id {
|
||||||
|
$(
|
||||||
|
Font::$enum_name => &self.$font_name,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
make_fonts!(
|
||||||
|
Sansation,
|
||||||
|
sansation,
|
||||||
|
make_sansation_font,
|
||||||
|
"../../../assets/fonts/sansation.ttf",
|
||||||
|
);
|
||||||
|
|
||||||
|
impl FontManager {
|
||||||
|
/// Creates a fonts from an array of bytes.
|
||||||
|
fn _make_font_from_bytes(bytes: Vec<u8>) -> SfFont {
|
||||||
|
SfFont::from_memory(&bytes)
|
||||||
|
.expect("Failed to create font")
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,9 @@ pub mod controls;
|
||||||
/// This module helps us dealing with textures.
|
/// This module helps us dealing with textures.
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
|
|
||||||
|
/// This module helps us dealing with fonts.
|
||||||
|
pub mod font;
|
||||||
|
|
||||||
/// This module contains the math tools that will be used.
|
/// This module contains the math tools that will be used.
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, View};
|
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, View, Text};
|
||||||
|
|
||||||
use sfml::window::{Event, Style};
|
use sfml::window::{Event, Style};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use sfml::system::Vector2;
|
||||||
|
|
||||||
use crate::engine::scene::Scene;
|
use crate::engine::scene::Scene;
|
||||||
use crate::engine::texture::{Texture, TextureManager};
|
use crate::engine::texture::{Texture, TextureManager};
|
||||||
|
use crate::engine::font::{Font, FontManager};
|
||||||
|
|
||||||
/// Our custom drawable trait.
|
/// Our custom drawable trait.
|
||||||
pub trait Drawable {
|
pub trait Drawable {
|
||||||
|
@ -29,6 +30,9 @@ pub struct Renderer {
|
||||||
/// The texture manager needed by the renderer.
|
/// The texture manager needed by the renderer.
|
||||||
texture_manager: TextureManager,
|
texture_manager: TextureManager,
|
||||||
|
|
||||||
|
/// The font manager needed by the renderer.
|
||||||
|
font_manager: FontManager,
|
||||||
|
|
||||||
/// The global timer of the renderer.
|
/// The global timer of the renderer.
|
||||||
started: Instant,
|
started: Instant,
|
||||||
}
|
}
|
||||||
|
@ -52,6 +56,7 @@ impl Renderer {
|
||||||
Renderer {
|
Renderer {
|
||||||
window,
|
window,
|
||||||
texture_manager: TextureManager::new(),
|
texture_manager: TextureManager::new(),
|
||||||
|
font_manager: FontManager::new(),
|
||||||
started: Instant::now(),
|
started: Instant::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,4 +137,11 @@ impl Renderer {
|
||||||
pub fn window_mut(&mut self) -> &mut RenderWindow {
|
pub fn window_mut(&mut self) -> &mut RenderWindow {
|
||||||
&mut self.window
|
&mut self.window
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draws text on the screen.
|
||||||
|
pub fn draw_text(&mut self, text: &str, font: Font, size: u32, color: &Color) {
|
||||||
|
let mut text = Text::new(text, self.font_manager.get(font), size);
|
||||||
|
text.set_fill_color(color);
|
||||||
|
self.window.draw(&text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,9 @@ use std::{io, fmt, result};
|
||||||
/// This module contains all the tools needed for the game.
|
/// This module contains all the tools needed for the game.
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
|
|
||||||
|
/// This module contains all the applications of the game.
|
||||||
|
pub mod app;
|
||||||
|
|
||||||
/// This is the error type of this library.
|
/// This is the error type of this library.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|
Loading…
Reference in New Issue