rstnode: implement viewport shifting

A bit of a rant: it's fucking ridiculous how much work it is to subtract
two points from each other with mint + nalgebra.  I ended up adding a
complete `Vector2` implementation in this commit because I got fed up
with it.

Consider this the unofficial start of librgx (rust graphics x), which
will basically become a libgdx in Rust, and the basis for the little
game engine.

Not to mention that ggez probably won't be maintained past version 0.6.0
unless some other maintainer takes over (I guess that could always happen).
Anyway, I'm annoyed!
wip/yesman
Katharina Fey 3 years ago
parent f9d4d7ec01
commit b53b19ee46
Signed by: kookie
GPG Key ID: 90734A9E619C8A6C
  1. 4
      games/rstnode/rst-client/src/graphics/mod.rs
  2. 82
      games/rstnode/rst-client/src/graphics/vector2.rs
  3. 23
      games/rstnode/rst-client/src/input.rs
  4. 9
      games/rstnode/rst-client/src/main.rs
  5. 16
      games/rstnode/rst-client/src/state.rs
  6. 63
      games/rstnode/rst-client/src/viewport.rs

@ -8,8 +8,12 @@
pub mod entities;
pub mod ui;
mod vector2;
pub use vector2::*;
use crate::state::ClientState;
use ggez::{Context, GameResult};
use std::ops::{Add, Mul, Sub};
/// A utility module to include everything required to implement a
/// graphics entity

@ -0,0 +1,82 @@
use std::fmt::{self, Display, Formatter};
use std::ops::{Add, Mul, Sub, SubAssign};
/// Just a vector
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
pub struct Vector2 {
pub x: f32,
pub y: f32,
}
impl Display for Vector2 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "[{}, {}]", self.x, self.y)
}
}
impl Vector2 {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
pub fn abs(self) -> Self {
Self {
x: self.x.abs(),
y: self.y.abs(),
}
}
}
impl Sub for Vector2 {
type Output = Vector2;
fn sub(self, o: Vector2) -> Self::Output {
Vector2 {
x: self.x - o.x,
y: self.y - o.y,
}
}
}
impl SubAssign for Vector2 {
fn sub_assign(&mut self, o: Self) {
*self = Self {
x: self.x - o.x,
y: self.y - o.y,
}
}
}
impl Add for Vector2 {
type Output = Vector2;
fn add(self, o: Vector2) -> Self::Output {
Vector2 {
x: self.x + o.x,
y: self.y + o.y,
}
}
}
impl Mul for Vector2 {
type Output = Vector2;
fn mul(self, o: Vector2) -> Self::Output {
Vector2 {
x: self.x * o.x,
y: self.y * o.y,
}
}
}
impl From<Vector2> for mint::Point2<f32> {
fn from(v: Vector2) -> Self {
[v.x, v.y].into()
}
}
impl From<mint::Point2<f32>> for Vector2 {
fn from(v: mint::Point2<f32>) -> Self {
Self { x: v.x, y: v.y }
}
}

@ -1,50 +1,55 @@
//! Advanced input handler
use crate::{graphics::Vector2, viewport::Viewport};
use ggez::{
event::EventHandler,
input::mouse::{self, MouseButton},
Context, GameResult,
};
use mint::Point2;
pub struct InputHandle {
/// The mouse position on the viewport
pub mouse_pos: Point2<f32>,
pub mouse_pos: Vector2,
/// Whether the left mouse button is pressed this frame
pub left_pressed: bool,
/// Whether the middle mouse button is pressed this frame
pub middle_pressed: bool,
/// Whether the right mouse button is pressed this frame
pub right_pressed: bool,
/// Set when pressing left mouse and unset when releasing it
pub drag_point: Option<Point2<f32>>,
pub drag_point: Option<Vector2>,
}
impl InputHandle {
pub fn new() -> Self {
Self {
mouse_pos: [0.0, 0.0].into(),
mouse_pos: Vector2::new(0.0, 0.0),
left_pressed: false,
middle_pressed: false,
right_pressed: false,
drag_point: None,
}
}
/// Get the unprojected mouse coordinates
pub fn unproject(&self) -> Point2<f32> {
self.mouse_pos.clone();
pub fn unproject(&self, vp: &Viewport) -> Vector2 {
// self.mouse_pos.clone() - vp.start().clone()
todo!()
}
}
impl EventHandler for InputHandle {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
self.mouse_pos = mouse::position(&ctx);
self.mouse_pos = mouse::position(&ctx).into();
self.left_pressed = mouse::button_pressed(ctx, MouseButton::Left);
self.middle_pressed = mouse::button_pressed(ctx, MouseButton::Middle);
self.right_pressed = mouse::button_pressed(ctx, MouseButton::Right);
// Only set the drag_point once and unset when we release Left button
if self.left_pressed && self.drag_point.is_none() {
if self.middle_pressed && self.drag_point.is_none() {
self.drag_point = Some(self.mouse_pos.clone());
} else if !self.left_pressed {
} else if !self.middle_pressed {
self.drag_point = None;
}

@ -14,6 +14,7 @@ mod input;
mod log;
mod settings;
mod state;
mod viewport;
mod window;
#[allow(unused)]
@ -36,7 +37,13 @@ fn main() {
// Load assets tree
let assets =
assets::load_tree(window.ctx(), &settings).unwrap_or_else(|e| fatal!("LoadError: {}!", e));
let state = ClientState::new(settings, assets);
// Create the client state
let mut state = ClientState::new(settings, assets);
// Initialise the viewport first!
state.viewport().init(window.ctx());
// Window goes brrrr
window.run(state)
}

@ -7,6 +7,7 @@ use crate::{
Renderer,
},
input::InputHandle,
viewport::Viewport,
GameSettings,
};
use ggez::{event::EventHandler, graphics, Context, GameResult};
@ -17,6 +18,7 @@ pub struct ClientState {
assets: Assets,
settings: GameSettings,
input: InputHandle,
vp: Viewport,
// Game state
node: NodeRndr,
@ -27,6 +29,7 @@ impl ClientState {
Self {
assets,
settings,
vp: Viewport::new(),
input: InputHandle::new(),
node: NodeRndr {
loc: Coordinates(512.0, 512.0),
@ -49,6 +52,10 @@ impl ClientState {
}
}
pub fn viewport(&mut self) -> &mut Viewport {
&mut self.vp
}
pub fn assets(&self) -> &Assets {
&self.assets
}
@ -56,7 +63,16 @@ impl ClientState {
impl EventHandler for ClientState {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
// TODO: get simulation updates
// Get new input state
self.input.update(ctx)?;
// Apply inputs to viewpoirt
self.vp.apply(ctx, &self.input)?;
// Update viewport
self.vp.update(ctx)?;
Ok(())
}

@ -0,0 +1,63 @@
//! Viewport utilities
use crate::{graphics::Vector2, input::InputHandle};
use ggez::{
error::GameResult,
graphics::{self, Rect},
Context,
};
#[derive(Default)]
pub struct Viewport {
start: Vector2,
prev_start: Vector2,
size: Vector2,
}
impl Viewport {
pub fn new() -> Self {
Self::default()
}
pub fn start(&self) -> &Vector2 {
&self.start
}
/// Must be called at least once before calling
/// [`update`](Self::update)
pub fn init(&mut self, ctx: &mut Context) {
let Rect { x, y, w, h } = graphics::screen_coordinates(&ctx);
self.start = Vector2::new(x, y);
self.prev_start = self.start;
self.size = Vector2::new(w, h);
}
/// Update the game state with the curent viewport data
pub fn update(&self, ctx: &mut Context) -> GameResult<()> {
graphics::set_screen_coordinates(
ctx,
Rect {
x: self.start.x,
y: self.start.y,
w: self.size.x,
h: self.size.y,
},
)?;
Ok(())
}
/// Apply changes from the input handle to the viewport
pub fn apply(&mut self, _: &mut Context, input: &InputHandle) -> GameResult<()> {
if input.middle_pressed {
let drag = input.drag_point.as_ref().unwrap().clone();
let pos = input.mouse_pos.clone();
self.start = self.prev_start + (drag - pos);
debug!("Changing VP start: {}", self.start);
} else {
self.prev_start = self.start;
}
Ok(())
}
}
Loading…
Cancel
Save