瀏覽代碼

input handling with `Action`s and functioning map zoom

master
Isabelle L. 5 年之前
父節點
當前提交
0a34313cb2
共有 2 個文件被更改,包括 165 次插入18 次删除
  1. +99
    -13
      src/main.rs
  2. +66
    -5
      src/world.rs

+ 99
- 13
src/main.rs 查看文件

@@ -7,9 +7,10 @@ mod world;
// namespacing
use ggez::{
conf::{WindowMode, WindowSetup},
event::{self, EventHandler},
event::{self, EventHandler, KeyCode, KeyMods},
graphics, Context, ContextBuilder, GameResult,
};
use std::collections::VecDeque;
use world::World;

/// lazy result type
@@ -31,25 +32,78 @@ pub enum Error {
}

// primary game state
struct MainState {
pub struct MainState {
world: World,
action_buffer: Vec<Box<dyn Action>>,
action_history: VecDeque<Box<dyn Action>>,
action_history_max_length: usize,
}

impl MainState {
// create a new gamestate
pub fn new(ctx: &mut Context) -> MainState {
MainState {
world: World::new(ctx, "/data/tile_conf.toml".into(), 20, 20, 1)
.expect("failed init world"),
}
pub fn new(ctx: &mut Context) -> Result<MainState> {
let world = World::new(ctx, "/data/tile_conf.toml".into(), 20, 20, 1)?;
Ok(MainState {
world,
action_history_max_length: 100,
action_history: VecDeque::new(),
action_buffer: Vec::new(),
})
}
}

impl EventHandler for MainState {
fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
// check through the input buffer
while let Some(action) = self.action_buffer.pop() {
action.execute(ctx, self).expect("failed to execute action");

if self.action_history.len() < self.action_history_max_length {
self.action_history.push_back(action);
} else {
self.action_history.pop_front();
self.action_history.push_back(action);
}
}

Ok(())
}

fn key_down_event(
&mut self,
_ctx: &mut Context,
key_code: KeyCode,
_keymods: KeyMods,
repeat: bool,
) {
use world::{OffsetAction, ZoomAction};

if !repeat {
let action_maybe: Option<Box<dyn Action>> = match key_code {
// movement key codes
KeyCode::Up => Some(Box::new(OffsetAction::new([0.0, 1.0].into()))),
KeyCode::Down => Some(Box::new(OffsetAction::new([0.0, -1.0].into()))),
KeyCode::Left => Some(Box::new(OffsetAction::new([1.0, 0.0].into()))),
KeyCode::Right => Some(Box::new(OffsetAction::new([-1.0, 0.0].into()))),

// zoom key codes
KeyCode::Equals => Some(Box::new(ZoomAction::new([0.1, 0.1].into()))),
KeyCode::Minus => Some(Box::new(ZoomAction::new([-0.1, -0.1].into()))),

// if escape, quit program (for now)
// !! isabelle please remove this eventually
KeyCode::Escape => Some(Box::new(QuitAction::new())),

// ignore all other key codes
_ => None,
};

if let Some(action) = action_maybe {
self.action_buffer.push(action);
}
}
}

fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
// clear the screen
graphics::clear(ctx, graphics::BLACK);
@@ -61,24 +115,56 @@ impl EventHandler for MainState {
}
}

fn main() {
trait Action: std::fmt::Debug {
fn execute(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>;
fn undo(&self, ctx: &mut Context, state: &mut MainState) -> Result<()>;
}

/// struct to wrap quitting the game
#[derive(Debug)]
struct QuitAction;

impl QuitAction {
fn new() -> QuitAction {
QuitAction
}
}

impl Action for QuitAction {
fn execute(&self, ctx: &mut Context, _state: &mut MainState) -> Result<()> {
event::quit(ctx);
Ok(())
}
fn undo(&self, _ctx: &mut Context, _state: &mut MainState) -> Result<()> {
unreachable!()
}
}

fn main_wrapper() -> Result<()> {
let win_mode = WindowMode {
width: 1024.0,
height: 768.0,
..Default::default()
};

let win_setup = WindowSetup {
title: "unnamed game thinger".to_owned(),
..Default::default()
};

let cb = ContextBuilder::new("unnamed", "Isabelle L.")
.add_resource_path("./")
.window_mode(win_mode)
.window_setup(win_setup);
let (mut ctx, mut event_loop) = cb.build().expect("failed to start context");
let mut state = MainState::new(&mut ctx);
let (mut ctx, mut event_loop) = cb.build()?;
let mut state = MainState::new(&mut ctx)?;

if let Err(err) = event::run(&mut ctx, &mut event_loop, &mut state) {
eprintln!("an error occured: {:?}", err);
event::run(&mut ctx, &mut event_loop, &mut state)?;
Ok(())
}

fn main() {
if let Err(err) = main_wrapper() {
panic!("err occured: {:?}", err);
}
}

+ 66
- 5
src/world.rs 查看文件

@@ -2,12 +2,12 @@
mod tile;

// namespacing
use crate::{util::*, Result};
use crate::{util::*, MainState, Result};
use ggez::{
graphics::{self, DrawParam},
Context,
};
use mint::{Point2, Vector3};
use mint::{Point2, Vector2, Vector3};
use std::{collections::HashMap, path::PathBuf};
use tile::*;

@@ -19,6 +19,7 @@ pub struct World {
data: HashMap<Vector3<isize>, Tile>,
builder: TileBuilder,
offset: Point2<f32>,
zoom: Vector2<f32>,
}

impl World {
@@ -33,6 +34,7 @@ impl World {
let builder = TileBuilder::new(ctx, tile_config)?;
let mut data: HashMap<Vector3<isize>, Tile> = HashMap::new();
let offset: Point2<f32> = [350.0f32, 100.0f32].into();
let zoom: Vector2<f32> = [1.0f32, 1.0f32].into();

for x in 0..width {
for y in 0..depth {
@@ -49,6 +51,7 @@ impl World {
data,
builder: builder,
offset,
zoom,
})
}

@@ -59,11 +62,17 @@ impl World {
for z in 0..self.height {
let tile = self.data.get(&[x, y, z].into()).unwrap();
let iso_coord: IsometricVector2 = Point2::from([x as f32, y as f32]).into();
let scale = [self.zoom.x, self.zoom.y];
let dest = [
iso_coord.x() * self.builder.tile_width() / 2.0 + self.offset.x,
iso_coord.y() * self.builder.tile_height() / 2.0 + self.offset.y,
iso_coord.x() * (self.builder.tile_width() / 2.0) * scale[0]
+ self.offset.x,
iso_coord.y() * (self.builder.tile_height() / 2.0) * scale[1]
+ self.offset.y,
];
let param = DrawParam::default().dest(mint::Point2::from_slice(&dest));

let param = DrawParam::default()
.dest(Point2::from_slice(&dest))
.scale(Point2::from_slice(&scale));

graphics::draw(ctx, tile.texture(), param)?;
}
@@ -73,3 +82,55 @@ impl World {
Ok(())
}
}

/// action struct for wrapping offsetting input
#[derive(Debug)]
pub struct OffsetAction(Vector2<f32>);

impl OffsetAction {
/// construct a new offset action
pub fn new(offset: Vector2<f32>) -> OffsetAction {
OffsetAction(offset)
}
}

impl crate::Action for OffsetAction {
fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> {
state.world.offset.x =
self.0.x * (state.world.builder.tile_width() / 2.0) + state.world.offset.x;
state.world.offset.y =
self.0.y * (state.world.builder.tile_height() / 2.0) + state.world.offset.y;
Ok(())
}

fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> {
state.world.offset.x = state.world.offset.x - self.0.x;
state.world.offset.y = state.world.offset.y - self.0.y;
Ok(())
}
}

/// action struct for wrapping zoom input
#[derive(Debug)]
pub struct ZoomAction(Vector2<f32>);

impl ZoomAction {
/// construct a new scale action
pub fn new(scale: Vector2<f32>) -> ZoomAction {
ZoomAction(scale)
}
}

impl crate::Action for ZoomAction {
fn execute(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> {
state.world.zoom.x = self.0.x + state.world.zoom.x;
state.world.zoom.y = self.0.y + state.world.zoom.y;
Ok(())
}

fn undo(&self, _ctx: &mut Context, state: &mut MainState) -> Result<()> {
state.world.zoom.x = state.world.zoom.x - self.0.x;
state.world.zoom.y = state.world.zoom.y - self.0.y;
Ok(())
}
}

Loading…
取消
儲存