flutter3d
Showcase Changelog 38 packages API reference

Doors, lifts and buttons: the source

since 0.4.0 or earlier Simulation, audio and XR

Regions the guide quotes: live (line 41), press-live (line 114), world (line 146), wire (line 149), press (line 171).

1/// Doors, lifts and buttons: one machine that travels between two places, and
2/// a switch that relays an activation to it by name.
3///
4/// Quoted by `level_mechanisms.md` and shown whole in the Source tab.
5library;
6
7import 'package:flutter3d/flutter3d.dart' hide Material;
8import 'package:flutter3d_showcase/src/demo/demo.dart';
9import 'package:flutter3d_showcase/src/demo/scene_kit.dart';
10import 'package:flutter3d_sim/flutter3d_sim.dart';
11import 'package:vector_math/vector_math.dart';
13final class LevelMechanismsDemo extends ShowcaseDemo {
14 late final String _report;
16 bool automatic = true;
17 bool _pressAsked = false;
19 late final MechanismWorld _mechanisms;
20 late final Door _door;
21 late final Collider _doorCollider;
22 late final MeshNode _doorNode;
23 late final MeshNode _buttonNode;
24 late final BarGauge _progress;
25 double _clock = 0.0;
26 double _lit = 0.0;
28 @override
29 void configureView(DemoContext context) {
30 context.orbit
31 ..distance = 9.0
32 ..pitch = 0.3
33 ..yaw = 0.4;
34 context.orbit.target.setValues(2.5, 1.2, 0.0);
35 }
37 @override
38 Scene build(DemoContext context) {
39 _report = _run();
41 // The same door and button, running: the door's collider is what the
42 // mechanism moves, and the picture follows it.
43 final CollisionWorld world = CollisionWorld();
44 _mechanisms = MechanismWorld(world);
45 _doorCollider = world.addBox(Vector3(5, 1, 0), Vector3(1, 2, 0.2));
46 _door = _mechanisms.add(
47 Door(
48 name: 'north-door',
49 collider: _doorCollider,
50 travel: Vector3(0, 2, 0),
51 ),
52 );
53 _mechanisms.add(
54 Button(
55 name: 'door-button',
56 target: 'north-door',
57 collider: world.addBox(Vector3(0, 1, 0), Vector3(0.3, 0.3, 0.3)),
58 ),
59 );
61 _doorNode = blockNode(
62 context,
63 'door',
64 Vector3(1.0, 2.0, 0.2),
65 Vector4(0.65, 0.42, 0.3, 1.0),
66 at: Vector3(5.0, 1.0, 0.0),
67 );
68 _buttonNode = blockNode(
69 context,
70 'button',
71 Vector3(0.3, 0.3, 0.3),
72 Vector4(0.8, 0.2, 0.2, 1.0),
73 at: Vector3(0.0, 1.0, 0.0),
74 );
75 _progress = BarGauge(
76 context,
77 'progress',
78 Vector4(0.9, 0.75, 0.3, 1.0),
79 Vector3(7.0, 0.0, 0.0),
80 height: 2.0,
81 vertical: true,
82 );
83 return sceneOf(<SceneNode>[
84 floorNode(context, width: 12.0, depth: 6.0),
85 // The frame the door slides up out of, and the wall beside it.
86 blockNode(
87 context,
88 'wall left',
89 Vector3(4.5, 3.0, 0.4),
90 Vector4(0.5, 0.5, 0.52, 1.0),
91 at: Vector3(2.75, 1.5, 0.0),
92 ),
93 blockNode(
94 context,
95 'wall right',
96 Vector3(1.5, 3.0, 0.4),
97 Vector4(0.5, 0.5, 0.52, 1.0),
98 at: Vector3(5.75 + 0.25, 1.5, 0.0),
99 ),
100 _doorNode,
101 _buttonNode,
102 ..._progress.nodes,
103 ]);
104 }
106 @override
107 void update(DemoContext context, double dt) {
108 _clock += dt;
109 if (automatic && _clock > 2.0 && (_clock - 2.0) % 8.0 < dt) {
110 _pressAsked = true;
111 }
112 if (_pressAsked) {
113 _pressAsked = false;
114 _mechanisms.activate('door-button', const Activation());
115 _lit = 1.0;
116 }
117 _mechanisms.step(dt);
118 _lit = (_lit - dt * 1.5).clamp(0.0, 1.0);
119 _doorNode.setPositionFrom(_doorCollider.position);
120 _progress.set(_door.progress);
121 _buttonNode.material.baseColor.setValues(
122 0.8 - 0.6 * _lit,
123 0.2 + 0.65 * _lit,
124 0.2,
125 1.0,
126 );
127 }
129 @override
130 List<DemoControl> controls(DemoContext context) => <DemoControl>[
131 ToggleControl(
132 'Press the button',
133 value: () => false,
134 onChanged: (bool v) {
135 if (v) _pressAsked = true;
136 },
137 ),
138 ToggleControl(
139 'Press it for me',
140 value: () => automatic,
141 onChanged: (bool v) => automatic = v,
142 ),
143 ];
145 static String _run() {
146 final world = CollisionWorld();
147 final mechanisms = MechanismWorld(world);
149 final doorCollider = world.addBox(Vector3(5, 1, 0), Vector3(1, 2, 0.2));
150 final door = mechanisms.add(
151 Door(
152 name: 'north-door',
153 collider: doorCollider,
154 travel: Vector3(0, 2, 0),
155 ),
156 );
157 final buttonCollider = world.addBox(
158 Vector3(0, 1, 0),
159 Vector3(0.3, 0.3, 0.3),
160 );
161 mechanisms.add(
162 Button(
163 name: 'door-button',
164 target: 'north-door',
165 collider: buttonCollider,
166 ),
167 );
169 final beforeProgress = door.progress;
171 mechanisms.activate('door-button', const Activation());
172 for (var i = 0; i < 180; i++) {
173 mechanisms.step(1 / 60);
174 }
176 return 'the door started at progress ${beforeProgress.toStringAsFixed(2)}\n'
177 'after pressing the button and stepping three seconds, it is at '
178 '${door.progress.toStringAsFixed(2)}, state ${door.state.name}';
179 }
181 @override
182 void verify(Scene scene, FrameResult frame) {
183 if (frame.drawCalls < 1) {
184 throw StateError('the door marker was not drawn');
185 }
186 if (!_report.contains('started at progress 0.00')) {
187 throw StateError('a door should start closed');
188 }
189 if (!_report.contains('is at 1.00, state open')) {
190 throw StateError(
191 'pressing the button should open the door within '
192 'three seconds',
193 );
194 }
195 }