path/
path.rs

1use micro_games_kit::{
2    assets::{make_directory_database, ShaderAsset},
3    config::Config,
4    context::GameContext,
5    game::{GameInstance, GameState},
6    third_party::{
7        anim8::{spline::Spline, utils::factor_iter},
8        spitfire_draw::{
9            primitives::PrimitivesEmitter,
10            utils::{Drawable, ShaderRef},
11        },
12        spitfire_glow::graphics::{CameraScaling, Shader},
13        spitfire_input::{
14            ArrayInputCombinator, InputAxisRef, InputConsume, InputMapping, VirtualAxis,
15        },
16        vek::{Rgba, Vec2},
17    },
18    GameLauncher,
19};
20use std::error::Error;
21
22struct State {
23    spline: Spline<[f32; 2]>,
24    mouse_xy: Option<ArrayInputCombinator<2>>,
25}
26
27impl Default for State {
28    fn default() -> Self {
29        Self {
30            spline: Spline::smooth(
31                &[
32                    [-100.0, -100.0],
33                    [100.0, -100.0],
34                    [-100.0, 100.0],
35                    [100.0, 100.0],
36                ],
37                1.0,
38            )
39            .unwrap(),
40            mouse_xy: None,
41        }
42    }
43}
44
45impl GameState for State {
46    fn enter(&mut self, context: GameContext) {
47        context.graphics.color = [0.2, 0.2, 0.2, 1.0];
48        context.graphics.main_camera.screen_alignment = 0.5.into();
49        context.graphics.main_camera.scaling = CameraScaling::FitVertical(300.0);
50
51        let pointer_x = InputAxisRef::default();
52        let pointer_y = InputAxisRef::default();
53        self.mouse_xy = Some(ArrayInputCombinator::new([
54            pointer_x.clone(),
55            pointer_y.clone(),
56        ]));
57        context.input.push_mapping(
58            InputMapping::default()
59                .consume(InputConsume::Hit)
60                .axis(VirtualAxis::MousePositionX, pointer_x)
61                .axis(VirtualAxis::MousePositionY, pointer_y),
62        );
63
64        context
65            .assets
66            .spawn(
67                "shader://color",
68                (ShaderAsset::new(
69                    Shader::COLORED_VERTEX_2D,
70                    Shader::PASS_FRAGMENT,
71                ),),
72            )
73            .unwrap();
74    }
75
76    fn draw(&mut self, context: GameContext) {
77        let emitter = PrimitivesEmitter::default().shader(ShaderRef::name("color"));
78
79        let tint = Rgba::new(0.25, 0.25, 1.0, 1.0);
80        emitter
81            .emit_brush(factor_iter(50).map(|factor| {
82                (
83                    self.spline.sample(factor).into(),
84                    (1.0 - factor * factor) * 5.0,
85                    tint,
86                )
87            }))
88            .draw(context.draw, context.graphics);
89
90        for point in self.spline.points() {
91            emitter
92                .emit_circle(point.point.into(), 2.0, 0.1)
93                .tint(Rgba::new(1.0, 0.25, 0.25, 1.0))
94                .draw(context.draw, context.graphics);
95        }
96
97        if let Some(mouse_xy) = self.mouse_xy.as_ref() {
98            let source = Vec2::from(mouse_xy.get());
99            let source = context
100                .graphics
101                .main_camera
102                .screen_matrix()
103                .mul_point(source);
104            let source = context
105                .graphics
106                .main_camera
107                .world_matrix()
108                .inverted()
109                .mul_point(source);
110            let time = self
111                .spline
112                .find_time_closest_to_point(&source.into_array())
113                .0;
114            let target = Vec2::from(self.spline.sample(time));
115            emitter
116                .emit_circle(target, 3.0, 0.1)
117                .draw(context.draw, context.graphics);
118            emitter
119                .emit_lines([source, target])
120                .draw(context.draw, context.graphics);
121        }
122    }
123}
124
125fn main() -> Result<(), Box<dyn Error>> {
126    GameLauncher::new(GameInstance::new(State::default()).setup_assets(|assets| {
127        *assets = make_directory_database("./resources/").unwrap();
128    }))
129    .title("Path")
130    .config(Config::load_from_file("./resources/GameConfig.toml")?)
131    .run();
132    Ok(())
133}