path/
path.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use micro_games_kit::{
    config::Config,
    context::GameContext,
    game::{GameInstance, GameState},
    loader::load_shader,
    third_party::{
        anim8::{spline::Spline, utils::factor_iter},
        spitfire_draw::{
            primitives::PrimitivesEmitter,
            utils::{Drawable, ShaderRef},
        },
        spitfire_glow::graphics::{CameraScaling, Shader},
        spitfire_input::{
            ArrayInputCombinator, InputAxisRef, InputConsume, InputMapping, VirtualAxis,
        },
        vek::{Rgba, Vec2},
    },
    GameLauncher,
};
use std::error::Error;

struct State {
    spline: Spline<[f32; 2]>,
    mouse_xy: Option<ArrayInputCombinator<2>>,
}

impl Default for State {
    fn default() -> Self {
        Self {
            spline: Spline::smooth(
                &[
                    [-100.0, -100.0],
                    [100.0, -100.0],
                    [-100.0, 100.0],
                    [100.0, 100.0],
                ],
                1.0,
            )
            .unwrap(),
            mouse_xy: None,
        }
    }
}

impl GameState for State {
    fn enter(&mut self, context: GameContext) {
        context.graphics.color = [0.2, 0.2, 0.2, 1.0];
        context.graphics.main_camera.screen_alignment = 0.5.into();
        context.graphics.main_camera.scaling = CameraScaling::FitVertical(300.0);

        let pointer_x = InputAxisRef::default();
        let pointer_y = InputAxisRef::default();
        self.mouse_xy = Some(ArrayInputCombinator::new([
            pointer_x.clone(),
            pointer_y.clone(),
        ]));
        context.input.push_mapping(
            InputMapping::default()
                .consume(InputConsume::Hit)
                .axis(VirtualAxis::MousePositionX, pointer_x)
                .axis(VirtualAxis::MousePositionY, pointer_y),
        );

        load_shader(
            context.draw,
            context.graphics,
            "color",
            Shader::COLORED_VERTEX_2D,
            Shader::PASS_FRAGMENT,
        );
    }

    fn draw(&mut self, context: GameContext) {
        let emitter = PrimitivesEmitter::default().shader(ShaderRef::name("color"));

        let tint = Rgba::new(0.25, 0.25, 1.0, 1.0);
        emitter
            .emit_brush(factor_iter(50).map(|factor| {
                (
                    self.spline.sample(factor).into(),
                    (1.0 - factor * factor) * 5.0,
                    tint,
                )
            }))
            .draw(context.draw, context.graphics);

        for point in self.spline.points() {
            emitter
                .emit_circle(point.point.into(), 2.0, 0.1)
                .tint(Rgba::new(1.0, 0.25, 0.25, 1.0))
                .draw(context.draw, context.graphics);
        }

        if let Some(mouse_xy) = self.mouse_xy.as_ref() {
            let source = Vec2::from(mouse_xy.get());
            let source = context
                .graphics
                .main_camera
                .screen_matrix()
                .mul_point(source);
            let source = context
                .graphics
                .main_camera
                .world_matrix()
                .inverted()
                .mul_point(source);
            let time = self
                .spline
                .find_time_closest_to_point(&source.into_array())
                .0;
            let target = Vec2::from(self.spline.sample(time));
            emitter
                .emit_circle(target, 3.0, 0.1)
                .draw(context.draw, context.graphics);
            emitter
                .emit_lines([source, target])
                .draw(context.draw, context.graphics);
        }
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    GameLauncher::new(GameInstance::new(State::default()))
        .title("Path")
        .config(Config::load_from_file("./resources/GameConfig.toml")?)
        .run();
    Ok(())
}