From a6a1949914fb2074fb5fec9c75b075eac369541f Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Sat, 6 Feb 2021 22:54:28 +0100 Subject: rstnode: create small graphics example --- games/rstnode/rst-client/Cargo.toml | 5 +- games/rstnode/rst-client/src/cli.rs | 1 + games/rstnode/rst-client/src/constants.rs | 5 ++ games/rstnode/rst-client/src/ctx.rs | 30 +++++++++ .../rst-client/src/graphics/entities/mod.rs | 43 +++++++++++++ games/rstnode/rst-client/src/graphics/mod.rs | 16 +++++ games/rstnode/rst-client/src/graphics/ui/mod.rs | 1 + games/rstnode/rst-client/src/main.rs | 21 ++++++- games/rstnode/rst-client/src/settings.rs | 73 ++++++++++++++++++++++ games/rstnode/rst-client/src/state.rs | 48 ++++++++++++++ games/rstnode/rst-client/src/window.rs | 10 +++ 11 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 games/rstnode/rst-client/src/cli.rs create mode 100644 games/rstnode/rst-client/src/constants.rs create mode 100644 games/rstnode/rst-client/src/ctx.rs create mode 100644 games/rstnode/rst-client/src/graphics/entities/mod.rs create mode 100644 games/rstnode/rst-client/src/graphics/mod.rs create mode 100644 games/rstnode/rst-client/src/graphics/ui/mod.rs create mode 100644 games/rstnode/rst-client/src/settings.rs create mode 100644 games/rstnode/rst-client/src/state.rs create mode 100644 games/rstnode/rst-client/src/window.rs (limited to 'games/rstnode/rst-client') diff --git a/games/rstnode/rst-client/Cargo.toml b/games/rstnode/rst-client/Cargo.toml index 7ebf7092d0a0..eae6672cd51d 100644 --- a/games/rstnode/rst-client/Cargo.toml +++ b/games/rstnode/rst-client/Cargo.toml @@ -8,4 +8,7 @@ authors = ["Bread Machine", "Katharina Fey "] [dependencies] rst-core = { path = "../rst-core" } -ggez = "0.5" \ No newline at end of file + +clap = "2.0" +ggez = "0.6.0-rc0" +mint = "0.5" # Required because ggez is trash \ No newline at end of file diff --git a/games/rstnode/rst-client/src/cli.rs b/games/rstnode/rst-client/src/cli.rs new file mode 100644 index 000000000000..b8a93b237a7b --- /dev/null +++ b/games/rstnode/rst-client/src/cli.rs @@ -0,0 +1 @@ +//! Handle user CLI inputs diff --git a/games/rstnode/rst-client/src/constants.rs b/games/rstnode/rst-client/src/constants.rs new file mode 100644 index 000000000000..e40a5c35c5db --- /dev/null +++ b/games/rstnode/rst-client/src/constants.rs @@ -0,0 +1,5 @@ + + +pub const NAME: &'static str = "RST Node"; +pub const VERSION: &'static str = env!("CARGO_PKG_VERSION"); +pub const AUTHORS: &'static str = env!("CARGO_PKG_AUTHORS"); diff --git a/games/rstnode/rst-client/src/ctx.rs b/games/rstnode/rst-client/src/ctx.rs new file mode 100644 index 000000000000..7590867f18e2 --- /dev/null +++ b/games/rstnode/rst-client/src/ctx.rs @@ -0,0 +1,30 @@ +//! Small context builder + +use crate::{ + constants::{AUTHORS, NAME, VERSION}, + GameSettings, +}; + +use ggez::{ + conf::{WindowMode, WindowSetup}, + ContextBuilder, +}; + +/// Construct a context builder with default & user window settings +pub fn build(settings: &GameSettings) -> ContextBuilder { + ContextBuilder::new(NAME, AUTHORS) + .window_setup(WindowSetup { + title: format!("{} (v{})", NAME, VERSION), + samples: (&settings.graphics.samples).into(), + vsync: settings.graphics.vsync, + icon: "".into(), + srgb: true, + }) + .window_mode(WindowMode { + width: settings.window.width as f32, + height: settings.window.height as f32, + maximized: settings.window.window_mode.maximized(), + fullscreen_type: settings.window.window_mode._type(), + ..Default::default() + }) +} diff --git a/games/rstnode/rst-client/src/graphics/entities/mod.rs b/games/rstnode/rst-client/src/graphics/entities/mod.rs new file mode 100644 index 000000000000..168f40d2aeee --- /dev/null +++ b/games/rstnode/rst-client/src/graphics/entities/mod.rs @@ -0,0 +1,43 @@ +//! Game entity rendering +//! +//! Generally the naming convention should be: `{type}Rndr` +//! (`Renderer`, but shorter). + +use super::prelude::*; + +use rst_core::data::Node; +use std::sync::Arc; + +/// A set of universal X/Y coordinates +pub struct Coordinates(pub f32, pub f32); + +impl<'a> From<&'a Coordinates> for Point2 { + fn from(c: &'a Coordinates) -> Self { + Point2 { x: c.0, y: c.1 } + } +} + +pub struct NodeRndr { + pub loc: Coordinates, + pub inner: Arc, +} + +impl EventHandler for NodeRndr { + fn update(&mut self, _: &mut Context) -> GameResult<()> { + Ok(()) + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { + let circ = Mesh::new_circle( + ctx, + DrawMode::fill(), + Point2::from(&self.loc), + 35.0, + 0.1, + graphics::WHITE, + ).unwrap(); + + circ.draw(ctx, DrawParam::new()).unwrap(); + Ok(()) + } +} diff --git a/games/rstnode/rst-client/src/graphics/mod.rs b/games/rstnode/rst-client/src/graphics/mod.rs new file mode 100644 index 000000000000..8118207d70af --- /dev/null +++ b/games/rstnode/rst-client/src/graphics/mod.rs @@ -0,0 +1,16 @@ +//! Graphics module +//! +//! Each entity in the game has a graphics companion object in +//! [`entities`](self::entities), which knows how to render a given +//! object. Different game screens and UI elements can be found in +//! [`ui`](self::ui). + +pub mod entities; +pub mod ui; + +/// A utility module to include everything required to implement a +/// graphics entity +pub(self) mod prelude { + pub use ggez::{event::EventHandler, graphics::{self, Drawable, DrawParam, Mesh, DrawMode}, Context, GameResult}; + pub use mint::Point2; +} diff --git a/games/rstnode/rst-client/src/graphics/ui/mod.rs b/games/rstnode/rst-client/src/graphics/ui/mod.rs new file mode 100644 index 000000000000..3b63b5b9b0aa --- /dev/null +++ b/games/rstnode/rst-client/src/graphics/ui/mod.rs @@ -0,0 +1 @@ +//! Different UI states diff --git a/games/rstnode/rst-client/src/main.rs b/games/rstnode/rst-client/src/main.rs index e7a11a969c03..9de33fce81c6 100644 --- a/games/rstnode/rst-client/src/main.rs +++ b/games/rstnode/rst-client/src/main.rs @@ -1,3 +1,22 @@ +mod cli; +mod constants; +mod ctx; +mod graphics; +mod settings; +mod state; +mod window; + +pub(crate) use settings::*; +pub(crate) use state::*; + fn main() { - println!("Hello, world!"); + let settings = default(); + + let state = ClientState::new(&settings); + window::run(&settings, state) + + // let my_game = GameState::new(&mut ctx); + + // // Run! + // event::run(ctx, eloop, my_game); } diff --git a/games/rstnode/rst-client/src/settings.rs b/games/rstnode/rst-client/src/settings.rs new file mode 100644 index 000000000000..971e8e6f50b7 --- /dev/null +++ b/games/rstnode/rst-client/src/settings.rs @@ -0,0 +1,73 @@ +//! Configuration structures for the game client + +use ggez::conf::{FullscreenType, NumSamples}; + +pub fn default() -> GameSettings { + GameSettings { + window: WindowSettings { + width: 1280, + height: 720, + window_mode: WindowMode::Windowed, + }, + graphics: GraphicsSettings { + samples: Samples(0), + vsync: true, + }, + } +} + +/// Complete tree of basic game client settings +pub struct GameSettings { + pub window: WindowSettings, + pub graphics: GraphicsSettings, +} + +/// Window setup specific settings +pub struct WindowSettings { + pub width: u16, + pub height: u16, + pub window_mode: WindowMode, +} + +/// Graphic settings +pub struct GraphicsSettings { + pub samples: Samples, + pub vsync: bool, +} + +pub struct Samples(pub u8); + +impl<'s> From<&'s Samples> for NumSamples { + fn from(s: &'s Samples) -> Self { + match s.0 { + 0 => Self::Zero, + 1 => Self::One, + 2 => Self::Two, + 4 => Self::Four, + 8 => Self::Eight, + 16 => Self::Sixteen, + _ => panic!("Invalid multisampling value: {}", s.0), + } + } +} + +pub enum WindowMode { + Windowed, + Fullscreen, +} + +impl WindowMode { + pub fn maximized(&self) -> bool { + match self { + Self::Fullscreen => true, + _ => false, + } + } + + pub fn _type(&self) -> FullscreenType { + match self { + Self::Fullscreen => FullscreenType::Desktop, + Self::Windowed => FullscreenType::Windowed, + } + } +} diff --git a/games/rstnode/rst-client/src/state.rs b/games/rstnode/rst-client/src/state.rs new file mode 100644 index 000000000000..c55dc2faa6d6 --- /dev/null +++ b/games/rstnode/rst-client/src/state.rs @@ -0,0 +1,48 @@ +//! Game client state handling + +use crate::{ + graphics::entities::{Coordinates, NodeRndr}, + GameSettings, +}; +use ggez::{event::EventHandler, graphics, Context, GameResult}; +use rst_core::data::{Node, Owner, Upgrade}; +use std::sync::Arc; + +pub struct ClientState { + node: NodeRndr, +} + +impl ClientState { + pub fn new(_settings: &GameSettings) -> Self { + Self { + node: NodeRndr { + loc: Coordinates(250.0, 250.0), + inner: Arc::new(Node { + id: 0, + health: 100.into(), + max_health: 100.into(), + owner: Owner::Neutral, + type_: Upgrade::Base, + links: 0, + link_states: vec![], + buffer: vec![], + }), + }, + } + } +} + +impl EventHandler for ClientState { + fn update(&mut self, _ctx: &mut Context) -> GameResult<()> { + Ok(()) + } + + fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { + graphics::clear(ctx, graphics::Color::from_rgb(15, 15, 15)); + + // Render the node + self.node.draw(ctx).unwrap(); + + graphics::present(ctx) + } +} diff --git a/games/rstnode/rst-client/src/window.rs b/games/rstnode/rst-client/src/window.rs new file mode 100644 index 000000000000..ad58c38e0ffb --- /dev/null +++ b/games/rstnode/rst-client/src/window.rs @@ -0,0 +1,10 @@ +//! Basic window setup code + +use crate::{ctx, state::ClientState, GameSettings}; +use ggez::event; + +/// Start the main event loop with game settings and state +pub fn run(settings: &GameSettings, state: ClientState) -> ! { + let (ctx, eloop) = ctx::build(settings).build().unwrap(); + event::run(ctx, eloop, state) +} -- cgit v1.2.3