示例#1
0
            // TODO Track updates of individual config values to improve rendering performance

            public ConfigDescription(ITypedDataCursor <ParachuteConfig> config, Vector3 pilotTorsoScale)
            {
                Radius = new InputRange(
                    CreateRange(-8f, 8f, config.To(c => c.RadiusVertical)),
                    CreateRange(1f, 16f, config.To(c => c.RadiusHorizontal))
                    );
                Volume = new InputRange(
                    CreateRange(1f, 16f, config.To(c => c.Span)),
                    zAxisDescription: CreateRange(0.5f, 5, config.To(c => c.Chord))
                    );
                RiggingAngle = new InputRange(
                    CreateRange(0, 30, config.To(c => c.RiggingAngle))
                    );
                HeightOffset = new InputRange(
                    yAxisDescription: CreateRange(0f, 16f, config.To(c => c.HeightOffset))
                    );
                var rigAttachCursor = config.To(c => c.RigAttachPos);

                RigAttachPosition = new InputRange(
                    RigAttachComponent(0f, 1f, rigAttachCursor.To(DefaultPath.Vector3X), pilotTorsoScale.x),
                    RigAttachComponent(0f, 1f, rigAttachCursor.To(DefaultPath.Vector3Y), pilotTorsoScale.y),
                    RigAttachComponent(-1f, 1f, rigAttachCursor.To(DefaultPath.Vector3Z), pilotTorsoScale.z)
                    );
                PilotWeight = new InputRange(
                    CreateRange(40f, 200f, config.To(c => c.PilotWeight))
                    );
                WeightShiftMagnitude = new InputRange(
                    CreateRange(0.1f, 0.5f, config.To(c => c.WeightshiftMagnitude))
                    );
            }
示例#2
0
 private Range RigAttachComponent(float min, float max, ITypedDataCursor <float> cursor, float scale)
 {
     return(new Range(
                min * scale,
                max * scale,
                () => cursor.Get() * scale,
                value => cursor.Set(value / scale),
                NumberOfSteps));
 }
示例#3
0
        public SpawnScreen(IStateMachine machine, Data data) : base(machine)
        {
            _data = data;

            var billboardDependencies = new DependencyContainer(new Dictionary <string, object> {
                { "clock", _data.MenuClock },
                { "cameraTransform", _data.CameraMount.transform }
            });

            _spawnpoints = GameObject.FindObjectOfType <SpawnpointDiscoverer>().Spawnpoints;
            var spawnpointBillboards = new List <GameObject>(_spawnpoints.Count);
            var billboardGroup       = new GameObject("SpawnpointBillboards");

            for (int i = 0; i < _spawnpoints.Count; i++)
            {
                var spawnpoint          = _spawnpoints[i];
                var spawnpointBillBoard = (GameObject)GameObject.Instantiate(_data.SpawnpointBillboardPrefab);
                spawnpointBillBoard.SetActive(spawnpoint.IsDiscovered);
                DependencyInjector.Default.Inject(spawnpointBillBoard, billboardDependencies);
                spawnpointBillBoard.SetParent(billboardGroup);
                spawnpointBillBoard.transform.Set(spawnpoint.Location.AsTransform);
                spawnpointBillBoard.SetActive(false);
                spawnpointBillboards.Add(spawnpointBillBoard);
            }

            _uiState = TypedDataCursor <SpawnScreenUIState> .Root(new SpawnScreenUIState(
                                                                      spawnpointUiList : new UIListState(spawnpointBillboards)));

            var spawnpointUiState = _uiState.To(s => s.SpawnpointUIList);

            var spawnpointIndexChanged = spawnpointUiState
                                         .To(s => s.HighlightedIndex)
                                         .OnUpdate
                                         .DistinctUntilChanged(IntComparer.Instance)
                                         .Skip(1);

            _data.SelectedSpawnpointName.text = _spawnpoints[spawnpointUiState.Get().HighlightedIndex].Name;
            spawnpointIndexChanged.Subscribe(i => {
                Fmod.PlayOneShot("event:/ui/drop_hover");
                var spawnpoint = _spawnpoints[i];
                _data.SelectedSpawnpointName.text = spawnpoint.Name;
                _data.CameraAnimator.LookTarget   = spawnpointBillboards[i].transform;
            });

            _uiList = new UISketch.NavigableUIList(
                spawnpointUiState,
                data.CameraManager.Rig.GetMainCamera(),
                spawnpointIndex => {
                _data.EventSystem.Emit(new Events.SpawnpointSelected(_spawnpoints[spawnpointIndex].Location));
            });
        }
示例#4
0
            /// <summary>
            /// </summary>
            /// <param name="cursor">
            /// <code>
            /// {"items": GameObject list,
            ///  "isInteractable": bool}
            /// </code>
            /// </param>
            /// <param name="indexCursor"><code>int?</code></param>
            public static IDisposable Initialize(ITypedDataCursor <UIListState> cursor)
            {
                var isInteractable   = cursor.To(s => s.IsInteractable);
                var highlightedIndex = cursor.To(s => s.HighlightedIndex);

                return(AddMouseHighlight(
                           updateHighlightedIndex: index => {
                    if (isInteractable.Get() && index.HasValue)
                    {
                        highlightedIndex.Set(index.Value);
                    }
                },
                           items: cursor.To(s => s.Items).Get()));
            }
示例#5
0
            private static void Update(ITypedDataCursor <UIListState> cursor)
            {
                var items = cursor.To(s => s.Items).Get();

                for (int i = 0; i < items.Count; i++)
                {
                    var item          = items[i];
                    var highlightable = item.GetComponentOfInterface <IHighlightable>();
                    highlightable.UnHighlight();
                }

                var highlightedIndex = cursor.To(s => s.HighlightedIndex).Get();

                items[highlightedIndex].GetComponentOfInterface <IHighlightable>().Highlight();
            }
示例#6
0
            public NavigableUIList(ITypedDataCursor <UIListState> state, Camera camera, Action <int> selectItem)
            {
                _isInteractable = state.To(s => s.IsInteractable);
                _selectItem     = selectItem;

                _highlightedIndex = state.To(s => s.HighlightedIndex);
                MouseHighlight.Initialize(state);
                MouseInteraction.LeftMouseClick(state.To(s => s.Items).Get(), selectItem);

                _controllerHighlight = ControllerHighlight.CreateControllerIndexUpdater(
                    cursor: state,
                    itemSelector: (items, currentIndex, inputDirection) => {
                    return(ControllerHighlight.MoveControllerIndex(camera.WorldToScreenPoint, items, currentIndex, inputDirection));
                });

                Highlighter.Initialize(state);
            }
示例#7
0
            /// <summary>
            /// </summary>
            /// <param name="cursor">
            /// <code>
            /// {"items": GameObject list,
            ///  "isInteractable": bool}
            /// </code>
            /// </param>
            /// <param name="indexCursor"><code>int?</code></param>
            public static Action <Vector2> CreateControllerIndexUpdater(ITypedDataCursor <UIListState> cursor,
                                                                        Func <IList <GameObject>, int, Vector2, int> itemSelector)
            {
                // Everytime there is new keyboard input, feed it
                // the currently highlighted controller index
                var items            = cursor.To(s => s.Items);
                var isInteractable   = cursor.To(s => s.IsInteractable);
                var highlightedIndex = cursor.To(s => s.HighlightedIndex);

                return(keyboardInput => {
                    // Magnitude check for performance reasons
                    if (keyboardInput.magnitude > 0 && isInteractable.Get())
                    {
                        var currentIndex = highlightedIndex.Get();
                        highlightedIndex.Set(itemSelector(items.Get(), currentIndex, keyboardInput));
                    }
                });
            }
示例#8
0
            public Editing(IStateMachine machine, Data data,
                           ParachuteStorage parachuteStorage, ITypedDataCursor <ParachuteStorageViewState> storageState) : base(machine)
            {
                _editorParachuteChanges = new ReplaySubject <Parachute>(1);

                _data = data;

                _editorState           = storageState.To(c => c.EditorState);
                _activeParachuteConfig = _editorState.To(editorState => editorState.Config);

                // Toggle editor camera off when a gizmo interaction starts to avoid feedback loops
                //_data._cameraRig.Initialize();

                storageState.OnUpdate
                // Store parachute every time it is changed (by sampling or throttling to reduce disk I/O)
                // Store parachute every time the use selects a different parachute
                .Throttle(TimeSpan.FromSeconds(2), Scheduler.ThreadPool)
                .ObserveOn(UnityThreadScheduler.MainThread)
                .Select(state => {
                    return(state.AvailableParachutes
                           .Where(p => p.IsEditable)
                           .Select(p => {
                        var config = GameObject.Instantiate(p);
                        var json = JsonUtility.ToJson(config, prettyPrint: true);
                        return new { config, json };
                    })
                           .ToList());
                })     // Copy object to prevent thread-unsafe editing
                .ObserveOn(Schedulers.FileWriterScheduler)
                .Subscribe(parachutes => {
                    parachuteStorage.DeleteAllStoredParachutes();
                    for (int i = 0; i < parachutes.Count; i++)
                    {
                        var parachute = parachutes[i];
                        parachuteStorage.StoreParachute(parachute.config, parachute.json);
                    }
                });

                data.ParachuteSelectionView.Initialize(storageState, data.GameSettingsProvider.IsVrActive);
                data.ParachuteSelectionView.BackToFlight += TransitToFlyingState;
            }
示例#9
0
        public Playing(IStateMachine machine, Data data, FlyWingsuit.Data wingsuitData, ParachuteStates.Data parachuteData,
                       SpectatorMode.Data spectatorData) : base(machine)
        {
            _data = data;
            _data.PlayerPilotSpawner.ActiveNetwork        = data.ActiveNetwork;
            wingsuitData.ActiveNetwork                    = data.ActiveNetwork;
            wingsuitData.PlayerPilotSpawner.ActiveNetwork = data.ActiveNetwork;

            var parachuteConfigPath = Path.Combine(VoloAirsportFileStorage.StorageDir.Value, "ParachuteConfig_v" + ParachuteConfig.VersionNumber + ".json");

            Debug.Log("parachute config path: " + parachuteConfigPath);

            var defaultParachuteStorage = new ParachuteStorage(ParachuteStorage.DefaultChutesDir.Value,
                                                               parachuteData.InitialConfig, parachuteData.HardCodedAirfoilDefinition, isEditable: false);
            var parachuteStorage = new ParachuteStorage(ParachuteStorage.StorageDir.Value,
                                                        parachuteData.InitialConfig, parachuteData.HardCodedAirfoilDefinition,
                                                        isEditable: true);
            var allParachutes = defaultParachuteStorage.StoredChutes.Concat(parachuteStorage.StoredChutes).ToList();
            var initialChute  = ParachuteStorage.SelectParachute(allParachutes,
                                                                 _data.GameSettingsProvider.ActiveSettings.Other.SelectedParachuteId);
            var storageState = TypedDataCursor <ParachuteStorageViewState> .Root(new ParachuteStorageViewState(initialChute, allParachutes,
                                                                                                               ParachuteStorage.StorageDir.Value));

            _activeParachuteConfig = storageState.To(s => s.EditorState).To(s => s.Config);
            _activeParachuteConfig.OnUpdate.Subscribe(selectedParachute => {
                var gameSettings = _data.GameSettingsProvider.ActiveSettings;
                if (gameSettings.Other.SelectedParachuteId != selectedParachute.Id)
                {
                    gameSettings.Other.SelectedParachuteId = selectedParachute.Id;
                    _data.GameSettingsProvider.UpdateGameSettings(gameSettings);
                }
            });

            _playingStateMachine = BuildPlayingStateMachine(data.CoroutineScheduler, wingsuitData, parachuteData, parachuteStorage, storageState, spectatorData);
            _playingStateMachine.Transition(PlayingStates.Initial);
        }
示例#10
0
        private IStateMachine BuildPlayingStateMachine(
            ICoroutineScheduler scheduler,
            FlyWingsuit.Data wingsuitData,
            ParachuteStates.Data parachuteData,
            ParachuteStorage parachuteStorage,
            ITypedDataCursor <ParachuteStorageViewState> parachuteStorageViewState,
            SpectatorMode.Data spectatorData)
        {
            var machine = new StateMachine <Playing>(this, scheduler);

            machine.AddState(PlayingStates.Initial, new ParachuteStates.InitialState(machine))
            .Permit(PlayingStates.FlyingWingsuit)
            .PermitChild(PlayingStates.Suspended);
            machine.AddState(PlayingStates.FlyingWingsuit, new FlyWingsuit(machine, wingsuitData))
            .Permit(PlayingStates.EditingParachute)
            .Permit(PlayingStates.FlyingParachute)
            .PermitChild(PlayingStates.Suspended)
            .PermitChild(PlayingStates.Spectating)
            .Permit(PlayingStates.Initial);
            machine.AddState(PlayingStates.FlyingParachute, new ParachuteStates.Flying(machine, parachuteData))
            .Permit(PlayingStates.EditingParachute)
            .Permit(PlayingStates.FlyingWingsuit)
            .PermitChild(PlayingStates.Spectating)
            .PermitChild(PlayingStates.Suspended)
            .Permit(PlayingStates.Initial);
            machine.AddState(PlayingStates.Spectating, new SpectatorMode(machine, spectatorData))
            .PermitChild(PlayingStates.Suspended);
            machine.AddState(PlayingStates.EditingParachute, new ParachuteStates.Editing(machine, parachuteData, parachuteStorage, parachuteStorageViewState))
            .Permit(PlayingStates.FlyingParachute)
            .PermitChild(PlayingStates.Suspended)
            .Permit(PlayingStates.Initial);
            machine.AddState(PlayingStates.Suspended, new ParachuteStates.InitialState(machine))
            .Permit(PlayingStates.Initial);

            return(machine);
        }
示例#11
0
 private Range CreateRange(float min, float max, ITypedDataCursor <float> cursor)
 {
     return(new Range(min, max, cursor.Get, cursor.Set, NumberOfSteps));
 }
示例#12
0
        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;
            }
        }
示例#13
0
            // TODO Would be even better if the highlighter doesn't know anything
            // about the different types of highlighted indices

            /// <summary>
            /// </summary>
            /// <param name="cursor">
            /// <code>
            /// {"items": GameObject list,
            ///  "highlightedIndex": int}
            /// </code>
            /// </param>
            public static IDisposable Initialize(ITypedDataCursor <UIListState> cursor)
            {
                return(cursor.OnUpdate.Subscribe(_ => Update(cursor)));
            }
示例#14
0
        public void Initialize(ITypedDataCursor <ParachuteStorageViewState> storage, bool isVrActive)
        {
            _openParachuteFolder.gameObject.SetActive(!isVrActive);
            _addParachute.gameObject.SetActive(!isVrActive);

            _backToFlight.onClick.AddListener(() => {
                if (BackToFlight != null)
                {
                    BackToFlight();
                }
            });

            var editorState     = storage.To(s => s.EditorState);
            var availableChutes = storage.To(s => s.AvailableParachutes);

            var             parachuteName       = editorState.To(s => s.Config).To(c => c.Name);
            Action <string> updateParachuteName = value => {
                parachuteName.Set(value.Limit(_parachuteNameCharLimit, ""));
            };

            _parachuteNameEditor.Limit        = _parachuteNameCharLimit;
            _parachuteNameEditor.TextChanged += updateParachuteName;

            _parachuteThumbnails = new ParachuteThumbnail[_maxParachutes];
            for (int i = 0; i < _maxParachutes; i++)
            {
                var parachuteThumbnail = GameObject.Instantiate(_parachuteThumbnailPrefab).GetComponent <ParachuteThumbnail>();
                parachuteThumbnail.transform.SetParent(_thumbnailRoot.transform);
                parachuteThumbnail.transform.localPosition = Vector3.zero;
                parachuteThumbnail.transform.localRotation = Quaternion.identity;
                parachuteThumbnail.transform.localScale    = Vector3.one;
                parachuteThumbnail.gameObject.SetActive(false);
                _parachuteThumbnails[i] = parachuteThumbnail;
            }

            var storageDir = storage.To(s => s.StorageDir);

            _openParachuteFolder.onClick.AddListener(() => {
                UnityFileBrowserUtil.Open(storageDir.Get());
            });

            Action <string> selectChute = parachuteId => {
                var state = storage.Get();
                if (state.EditorState.Config.Id != parachuteId)
                {
                    for (int i = 0; i < state.AvailableParachutes.Count; i++)
                    {
                        var parachute = state.AvailableParachutes[i];
                        if (parachute.Id == parachuteId)
                        {
                            editorState.Set(new ParachuteEditor.EditorState(parachute));
                        }
                    }
                }
            };

            Action <string> deleteChute = parachuteId => {
                var state = storage.Get();
                int selectedParachuteIndex = 0;
                for (int i = state.AvailableParachutes.Count - 1; i >= 0; i--)
                {
                    var parachute = state.AvailableParachutes[i];
                    if (parachute.Id == parachuteId)
                    {
                        state.AvailableParachutes.RemoveAt(i);
                        selectedParachuteIndex = Math.Max(i - 1, 0);
                        break;
                    }
                }
                if (state.EditorState.Config.Id == parachuteId)
                {
                    var defaultChute = state.AvailableParachutes[selectedParachuteIndex];
                    editorState.Set(new ParachuteEditor.EditorState(defaultChute));
                }
                availableChutes.Set(state.AvailableParachutes);
            };

            Action <string> cloneChute = parachuteId => {
                var state = storage.Get();
                if (state.AvailableParachutes.Count < _maxParachutes)
                {
                    ParachuteConfig existingParachute = null;
                    for (int i = 0; i < state.AvailableParachutes.Count; i++)
                    {
                        var parachute = state.AvailableParachutes[i];
                        if (parachute.Id == parachuteId)
                        {
                            existingParachute = parachute;
                        }
                    }

                    var newParachute = ParachuteConfig.CreateNew(existingParachute);
                    newParachute.Name = (existingParachute.Name + " Copy").Limit(_parachuteNameCharLimit, "");
                    state.AvailableParachutes.Add(newParachute);
                    availableChutes.Set(state.AvailableParachutes);
                    selectChute(newParachute.Id);
                }
            };

            var isEditingState = storage.To(s => s.EditorState).To(s => s.IsEditing);

            Action <string> editChute = parachuteId => {
                var    state = storage.Get();
                string currentlyEditingParachute = state.EditorState.IsEditing ? state.EditorState.Config.Id : null;
                if (currentlyEditingParachute == parachuteId)
                {
                    // Switch to read-only mode
                    isEditingState.Set(false);
                }
                else
                {
                    // Enable editing
                    // TODO Do selection and edit switching in one transformation
                    selectChute(parachuteId);
                    isEditingState.Set(true);
                }
            };

            _addParachute.onClick.AddListener(() => {
                var state = storage.Get();
                if (state.AvailableParachutes.Count < _maxParachutes)
                {
                    var newParachute  = ParachuteConfig.CreateNew(_parachuteConfigTemplate);
                    newParachute.Name = "New " + _parachuteConfigTemplate.Name;
                    state.AvailableParachutes.Add(newParachute);
                    availableChutes.Set(state.AvailableParachutes);
                    editorState.Set(new ParachuteEditor.EditorState(newParachute));
                }
            });

            storage.OnUpdate.Subscribe(state => {
                for (int i = 0; i < _parachuteThumbnails.Length; i++)
                {
                    var thumbnail       = _parachuteThumbnails[i];
                    thumbnail.OnSelect -= selectChute;
                    thumbnail.OnDelete -= deleteChute;
                    thumbnail.OnClone  -= cloneChute;
                    thumbnail.OnEdit   -= editChute;
                }

                _parachuteNameEditor.IsEditable = state.EditorState.IsEditing;
                _parachuteNameEditor.Text       = state.EditorState.Config.Name;

                for (int i = 0; i < _parachuteThumbnails.Length; i++)
                {
                    var thumbnail = _parachuteThumbnails[i];

                    if (i < state.AvailableParachutes.Count)
                    {
                        var parachute             = state.AvailableParachutes[i];
                        var isSelected            = state.EditorState.Config.Id == parachute.Id;
                        var parachuteControlState = new ParachuteThumbnail.ParachuteControlState(
                            parachute.Id,
                            parachute.Name,
                            isEditorAvailable: !isVrActive,
                            isSelected: state.EditorState.Config.Id == parachute.Id,
                            isEditable: parachute.IsEditable,
                            isDeletable: state.AvailableParachutes.Count > 1 && parachute.IsEditable,
                            isEditing: state.EditorState.IsEditing && isSelected);
                        thumbnail.SetState(parachuteControlState);
                        if (parachuteControlState.IsDeletable)
                        {
                            thumbnail.OnDelete += deleteChute;
                        }
                        if (parachuteControlState.IsEditable)
                        {
                            thumbnail.OnEdit += editChute;
                        }
                        thumbnail.OnSelect += selectChute;
                        thumbnail.OnClone  += cloneChute;
                        thumbnail.gameObject.SetActive(true);

                        if (parachuteControlState.IsSelected)
                        {
                            FirstObject = thumbnail.gameObject;
                        }
                    }
                    else
                    {
                        thumbnail.gameObject.SetActive(false);
                    }
                }
            });
        }
示例#15
0
        public readonly GuiComponentDescriptor.Range NumToggleControlledCells; // int

        public ParachuteConfigViewModel(ITypedDataCursor <ParachuteConfig> parachuteConfig)
        {
            var c = parachuteConfig.Get;

            NumCells = new GuiComponentDescriptor.Range(
                "Number of cells",
                minValue: 3f,
                maxValue: 27f,
                updateValue: value => {
                var config      = c();
                int val         = (int)value;
                val             = (val % 2 == 0) ? val + 1 : val; //Todo: respect min/max
                config.NumCells = (int)val;
                parachuteConfig.Set(config);
            },
                currentValue: () => c().NumCells,
                updateDisplayValue: (descriptor, str) => {
                GuiComponentDescriptor.DisplayNumber(c().NumCells, str, decimalPlaces: 0);
            },
                stepSize: 2f);
            NumToggleControlledCells = new GuiComponentDescriptor.Range(
                "Number of braked cells",
                minValue: 0f,
                maxValue: 13f,
                updateValue: value => {
                var config = c();
                config.NumToggleControlledCells = (int)value;
                parachuteConfig.Set(config);
            },
                currentValue: () => c().NumToggleControlledCells,
                updateDisplayValue: (descriptor, str) => {
                descriptor.MaxValue = Mathf.Floor(c().NumCells / 2f);
                GuiComponentDescriptor.DisplayNumber(c().NumToggleControlledCells, str, decimalPlaces: 0);
            },
                stepSize: 1f);
            PressureMultiplier = new GuiComponentDescriptor.Range(
                "Pressure multiplier",
                minValue: 0.5f,
                maxValue: 4.0f,
                updateValue: value => {
                var config = c();
                config.PressureMultiplier = value;
                parachuteConfig.Set(config);
            },
                currentValue: () => c().PressureMultiplier,
                updateDisplayValue: (descriptor, str) => {
                GuiComponentDescriptor.DisplayNumber(c().PressureMultiplier, str, decimalPlaces: 1, postFix: "×");
            },
                stepSize: 0.2f);
//            RearRiserPullMagnitude = new GuiComponentDescriptor.Range(
//                "Rear riser pull magnitude",
//                minValue: 0.01f,
//                maxValue: 0.1f,
//                updateValue: value => {
//                    var config = c();
//                    config.RearRiserPullMagnitude = value;
//                    parachuteConfig.Set(config);
//                },
//                currentValue: () => c().RearRiserPullMagnitude,
//                updateDisplayValue: (descriptor, str) => {
//                    GuiComponentDescriptor.DisplayNumber(c().RearRiserPullMagnitude, str, decimalPlaces: 2);
//                },
//                stepSize: 0.01f);
//            FrontRiserPullMagnitude = new GuiComponentDescriptor.Range(
//                "Front riser pull magnitude",
//                minValue: 0.01f,
//                maxValue: 0.2f,
//                updateValue: value => {
//                    var config = c();
//                    config.FrontRiserPullMagnitude = value;
//                    parachuteConfig.Set(config);
//                },
//                currentValue: () => c().FrontRiserPullMagnitude,
//                updateDisplayValue: (descriptor, str) => {
//                    GuiComponentDescriptor.DisplayNumber(c().FrontRiserPullMagnitude, str, decimalPlaces: 2);
//                },
//                stepSize: 0.01f);
        }