private void UpdateEditorState <TMeasureSystem>( ParachuteConfig config, ParachuteProperties <TMeasureSystem> props) where TMeasureSystem : MeasureSystem { var editorOrigin = transform.MakeImmutable(); var canopyCentroidWorld = editorOrigin.TranslateLocally(ParachuteMaths.GetCanopyCentroid(config)); _heightOffsetWidget.transform.Set(editorOrigin.TranslateLocally(y: 1f)); _heightOffsetText.transform.Set(editorOrigin.TranslateLocally(y: config.HeightOffset + 1f, z: -0.5f)); _heightOffsetText.Text.Clear(); props.HeightOffset.FormatTo(_heightOffsetText.Text, precision: 2); _riggingAngleGizmo.transform.Set(canopyCentroidWorld); _cellColorPicker.CurrentColor = config.Color; _summaryText.Text.Clear(); _summaryText.Text .Append("Difficulty level: ") .Append(ParachuteMaths.GetDifficulty(config).Stringify()) .Append("\n") .Append(config.NumCells) .Append(" cells (") .Append(config.NumToggleControlledCells) .Append(" braked), "); props.CanopyMass.FormatTo(_summaryText.Text, precision: 1); _summaryText.Text.Append("\n"); props.Span.FormatTo(_summaryText.Text, precision: 1); _summaryText.Text.Append(" × "); props.Chord.FormatTo(_summaryText.Text, precision: 1); _summaryText.Text.Append(" = "); props.Area.FormatTo(_summaryText.Text, precision: 0); _summaryText.transform.Set(canopyCentroidWorld.TranslateLocally(new Vector3(-3f, 2.5f, 0f))); // TODO Use mutable strings _riggingAngle.transform.Set(canopyCentroidWorld); _riggingAngle.Text.Clear(); props.RiggingAngle.FormatTo(_riggingAngle.Text, precision: 0); // _rigAttachPositionText.text = string.Format("{0:0.##}, {1:0.##}, {2:0.##}", // props.RigAttachPosition.Value.x, // props.RigAttachPosition.Value.y, // props.RigAttachPosition.Value.z); // _rigAttachPositionText.transform.Set(rigAttachPositionTransform); var pilotWeightTransform = editorOrigin.TranslateLocally(y: -0.8f); _pilotWeight.transform.Set(pilotWeightTransform.TranslateLocally(y: -0.4f)); _pilotWeightGizmo.transform.Set(pilotWeightTransform); _pilotWeight.Text.Clear(); props.PilotWeight.FormatTo(_pilotWeight.Text, precision: 0); _pilotWeight.Text.Append(" ("); props.WingLoading.FormatTo(_pilotWeight.Text, precision: 2); _pilotWeight.Text.Append(")"); var pilotWeightShiftTransform = editorOrigin; _weightShiftMagnitudeGizmo.transform.Set(pilotWeightShiftTransform); _pilotWeightShiftMagnitude.transform.Set(pilotWeightShiftTransform.TranslateLocally(x: -1.1f)); _weightShiftVisualizer.transform.position = pilotWeightShiftTransform.Position; _weightShiftVisualizer.Radius = config.WeightshiftMagnitude; _pilotWeightShiftMagnitude.Text.Clear(); props.WeightShiftMagnitude.FormatTo(_pilotWeightShiftMagnitude.Text, precision: 2); _radiusGizmo.UpdateState(); _heightOffsetWidget.UpdateState(); _rigAttachPosition.UpdateState(); _pilotWeightGizmo.UpdateState(); _weightShiftMagnitudeGizmo.UpdateState(); }
public void Initialize(ITypedDataCursor <EditorState> editorState, IObservable <Parachute> editorParachute) { if (!_isInitialized) { var configCursor = editorState.To(c => c.Config); var parachuteConfigViewModel = new ParachuteConfigViewModel(configCursor); _parachuteConfigView.Initialize(parachuteConfigViewModel); RegisterUIHover(_additionalSettingsParent.AddComponent <DefaultHoverEventSource>()); var parachuteColor = configCursor.To(c => c.Color); _cellColorPicker.onValueChanged.AddListener(color => parachuteColor.Set(color)); RegisterUIHover(_cellColorPicker.gameObject.AddComponent <DefaultHoverEventSource>()); _selectedWidget = editorState.To(c => c.SelectedWidget); configDescription = new ConfigDescription(configCursor, _pilotTorsoScale); // TODO Use GUI camera? var mainCamera = _cameraManager.Rig.GetMainCamera(); _radiusGizmo.InputRange = configDescription.Radius; _pilotWeightGizmo.InputRange = configDescription.PilotWeight; _weightShiftMagnitudeGizmo.InputRange = configDescription.WeightShiftMagnitude; _heightOffsetWidget.InputRange = configDescription.HeightOffset; _rigAttachPosition.InputRange = configDescription.RigAttachPosition; _riggingAngleGizmo.InputRange = configDescription.RiggingAngle; _riggingAngleGizmo.Camera = mainCamera; RegisterDraggable(_radiusGizmo.gameObject, WidgetId.Radius); RegisterDraggable(_pilotWeightGizmo.gameObject, WidgetId.PilotWeight); RegisterDraggable(_heightOffsetWidget.gameObject, WidgetId.HeightOffset); RegisterDraggable(_weightShiftMagnitudeGizmo.gameObject, WidgetId.WeightShiftMagnitude); RegisterGizmoHandlers(_riggingAngleGizmo, WidgetId.RiggingAngle); editorState.To(c => c.SelectedWidget) .OnUpdate .Subscribe(selectedWidget => { // Render tooltip //_rigAttachPositionText.gameObject.SetActive(selectedWidget == WidgetId.RigAttachPosition); if (selectedWidget.HasValue) { var tooltip = Tooltips[selectedWidget.Value]; var description = tooltip.Description + "\n\n<i>" + tooltip.Effect + "</i>"; _tooltipView.SetState(tooltip.Name, description); _tooltipView.gameObject.SetActive(true); } else { _tooltipView.gameObject.SetActive(false); } }); var colliderSet = ColliderSet.Box("ParachuteUICellColliders", LayerMask.NameToLayer("UI")); editorState.To(c => c.IsEditing) .OnUpdate .Subscribe(isEditing => { colliderSet.Parent.SetActive(isEditing); _gizmosParent.SetActive(isEditing); _additionalSettingsParent.SetActive(isEditing); _cellColorPicker.gameObject.SetActive(isEditing); }); configCursor.OnUpdate .CombineLatest( _gameSettingsProvider.SettingChanges, _activeLanguage.TableUpdates, (config, settings, languageTable) => new { config, settings, languageTable }) .Subscribe(x => { _parachuteConfigView.SetState(x.languageTable.AsFunc); var props = ParachuteProperties.FromConfig(x.config); if (x.settings.Gameplay.UnitSystem == UnitSystem.Metric) { UpdateEditorState(x.config, props); } else { UpdateEditorState(x.config, props.ToImperial()); } }); colliderSet.Parent.SetParent(gameObject); var canopyDragHandler = colliderSet.Parent.AddComponent <SurfaceDragHandler>(); canopyDragHandler.Dragging += (camTransform, value) => { var currentValue = configDescription.Volume.Value; currentValue.x += value.x; currentValue.z -= value.y; configDescription.Volume.SetValue(currentValue); }; _canopyHighlight.Highlightable = canopyDragHandler; RegisterDraggable(colliderSet.Parent, WidgetId.Area); editorParachute.Subscribe(p => { _canopyHighlight.Renderer = p.CanopyMesh; _rigAttachPosition.transform.position = p.Pilot.Torso.transform.position; _rigAttachPosition.transform.rotation = p.Pilot.Torso.transform.rotation; colliderSet.SetSize(p.Sections.Count); for (var i = 0; i < p.Sections.Count; i++) { var cell = p.Sections[i].Cell; colliderSet.UpdateCollider(i, cell.Collider); var isLastCell = i == p.Sections.Count - 1; if (isLastCell) { var gizmoTransform = cell.transform.MakeImmutable() .TranslateLocally(0.8f) .UpdateScale(Vector3.one) .UpdateRotation(p.Root.rotation); _radiusGizmo.transform.Set(gizmoTransform); } } }); // _rigAttachPosition.OnPositionChanged += newPosition => { // configDescription.RigAttachPosition.SetValue(newPosition); // }; // _rigAttachPosition.ActiveAxes = ActiveAxis.X | ActiveAxis.Y | ActiveAxis.Z; RegisterDraggable(_rigAttachPosition.gameObject, WidgetId.RigAttachPosition); /* * What are the problems that we need to solve: * - Use a single source of truth to render the parachute * - Use a single source of truth to render the GUI * - The GUI widgets should not know more than just the value they need to update and * what the value is they need to render (use cursors into the app state to do this) * - Use unity as a rendering engine but not as a logic engine (possibly too hard to do this now) * * * * Gizmo: * * - Show scaling and rotation widgets at all times and * couple them to an InputRange. * - (optional) Draw box around selected thing * */ _isInitialized = true; } }