flutter3d
Showcase Changelog 38 packages API reference

An automap: the source

since 0.4.1 or earlier Simulation, audio and XR

Regions the guide quotes: grid (line 36), reveal (line 60), live (line 81), check (line 128).

1/// The level as the player has seen it, from above: floor where they walked,
2/// walls where the floor stopped, nothing where they have not been.
3///
4/// Quoted by `automap.md` and shown whole in the Source tab.
5library;
6
7import 'package:flutter/widgets.dart';
8import 'package:flutter3d/flutter3d.dart';
9import 'package:flutter3d_showcase/src/demo/demo.dart';
10import 'package:flutter3d_sim/flutter3d_sim.dart';
11import 'package:vector_math/vector_math.dart';
13final class AutomapDemo extends ShowcaseDemo {
14 late final NavGrid _grid;
15 late Automap _map;
17 double speed = 3.0;
18 bool _forgetAsked = false;
19 final Vector3 _walker = Vector3(2.0, 0.0, 2.0);
20 int _leg = 0;
22 /// The walk: room, doorway, corridor, second room, and round again.
23 static final List<Vector3> _route = <Vector3>[
24 Vector3(2.0, 0.0, 2.0),
25 Vector3(8.0, 0.0, 8.0),
26 Vector3(5.0, 0.0, 5.0),
27 Vector3(13.0, 0.0, 5.0),
28 Vector3(20.0, 0.0, 3.0),
29 Vector3(23.0, 0.0, 8.0),
30 Vector3(20.0, 0.0, 5.0),
31 Vector3(13.0, 0.0, 5.0),
32 ];
34 @override
35 Scene build(DemoContext context) {
36 // Two rooms and a corridor between them.
37 _grid = NavGrid.bake(<Brush>[
38 Brush(centre: Vector3(5, 0, 5), size: Vector3(10, 1, 10)),
39 Brush(centre: Vector3(13, 0, 5), size: Vector3(6, 1, 2)),
40 Brush(centre: Vector3(21, 0, 5), size: Vector3(10, 1, 10)),
41 ], cellSize: 0.5);
42 _map = Automap(_grid, revealRadius: 3.0);
44 final material = Material(
45 name: 'walker',
46 baseColor: Vector4(0.6, 0.9, 0.7, 1.0),
47 );
48 final node = MeshNode(
49 DeviceMesh.upload(context.device, SphereShape(segments: 16).build()),
50 material,
51 );
52 return Scene()
53 ..add(node)
54 ..add(
55 LightNode(name: 'sun', intensity: 3.0)
56 ..setLocalForward(Vector3(-0.4, -1.0, -0.3)),
57 );
58 }
60 void _walk() {
61 _map.reveal(Vector3(1.0, 0.0, 1.0));
62 _map.reveal(Vector3(3.0, 0.0, 1.0));
63 }
65 @override
66 void update(DemoContext context, double dt) {
67 if (_forgetAsked) {
68 _forgetAsked = false;
69 _map = Automap(_grid, revealRadius: 3.0);
70 }
71 // Walk the route at `speed`, a leg at a time.
72 final Vector3 to = _route[(_leg + 1) % _route.length];
73 final Vector3 way = to - _walker;
74 final double step = speed * dt;
75 if (way.length <= step) {
76 _walker.setFrom(to);
77 _leg = (_leg + 1) % _route.length;
78 } else {
79 _walker.add(way.normalized() * step);
80 }
81 // Every frame the player stands somewhere, and the map learns of it.
82 _map.reveal(_walker);
83 }
85 @override
86 List<DemoControl> controls(DemoContext context) => <DemoControl>[
87 SliderControl(
88 'Walking speed',
89 min: 1.0,
90 max: 10.0,
91 value: () => speed,
92 onChanged: (double v) => speed = v,
93 format: (double v) => '${v.toStringAsFixed(1)} m/s',
94 ),
95 ToggleControl(
96 'Forget the map',
97 value: () => false,
98 onChanged: (bool v) {
99 if (v) _forgetAsked = true;
100 },
101 ),
102 ];
104 @override
105 Widget? customBody(BuildContext buildContext, DemoContext context) =>
106 ColoredBox(
107 color: const Color(0xFF14161A),
108 child: Padding(
109 padding: const EdgeInsets.all(24),
110 child: Center(
111 child: AspectRatio(
112 aspectRatio: _grid.columns / _grid.rows,
113 child: CustomPaint(
114 painter: _MapPainter(_grid, _map, _walker),
115 child: const SizedBox.expand(),
116 ),
117 ),
118 ),
119 ),
120 );
122 @override
123 void verify(Scene scene, FrameResult frame) {
124 if (frame.drawCalls < 1) {
125 throw StateError('the walker marker was not drawn');
126 }
127 _walk();
128 final nearWhereWalked = _map.isFloor(_grid.cellAtPoint(2.0, 1.0));
129 final farAcrossTheFloor = _map.isRevealed(_grid.cellAtPoint(9.0, 9.0));
130 if (!nearWhereWalked) {
131 throw StateError(
132 'a cell the walk passed near should be revealed as floor',
133 );
134 }
135 if (farAcrossTheFloor) {
136 throw StateError(
137 'the automap should not reveal floor nobody walked near',
138 );
139 }
140 }
143/// Draws revealed floor light, revealed walls dark, and everything else
144/// blank.
145final class _MapPainter extends CustomPainter {
146 const _MapPainter(this.grid, this.map, this.walker);
148 final NavGrid grid;
149 final Automap map;
150 final Vector3 walker;
152 @override
153 void paint(Canvas canvas, Size size) {
154 final cellW = size.width / grid.columns;
155 final cellH = size.height / grid.rows;
156 final paint = Paint();
157 for (var index = 0; index < grid.cellCount; index++) {
158 if (!map.isRevealed(index)) continue;
159 final cx = grid.cellX(index);
160 final cz = grid.cellZ(index);
161 paint.color = map.isFloor(index)
162 ? const Color(0xFFB6E3B0)
163 : const Color(0xFF3A2A2A);
164 canvas.drawRect(
165 Rect.fromLTWH(cx * cellW, cz * cellH, cellW, cellH),
166 paint,
167 );
168 }
169 // Where the player is, in the same cells.
170 canvas.drawCircle(
171 Offset(
172 walker.x / grid.cellSize * cellW,
173 walker.z / grid.cellSize * cellH,
174 ),
175 cellW * 1.4,
176 Paint()..color = const Color(0xFFE8A33D),
177 );
178 }
180 @override
181 bool shouldRepaint(covariant _MapPainter oldDelegate) => true;