public static IObservable <NamedControllerId> ActiveJoystick() { return(UnityObservable.CreateUpdate <NamedControllerId>(observer => { var xinputJoystickId = XInput.AnyJoystick(); var unityJoystickId = UnityInputMaps.AnyJoystick(); if (xinputJoystickId.HasValue) { var controllerId = new ControllerId.XInput(xinputJoystickId.Value); observer.OnNext(new NamedControllerId(controllerId, "XInput controller")); } else if (unityJoystickId.HasValue) { var controllerId = new ControllerId.Unity(unityJoystickId.Value); var controllerName = UnityEngine.Input.GetJoystickNames()[controllerId.Id]; observer.OnNext(new NamedControllerId(controllerId, controllerName)); } }) // XInput takes presedence over unity input because we know how to handle it better. // So if we detect an XInput device we ignore the Unity device(s) detected during the // same window. .Window(TimeSpan.FromSeconds(0.8f), Scheduler.TaskPool) .SelectMany(inputSources => { return inputSources.Scan((selectedInputSource, inputSource) => { if (selectedInputSource.ControllerId is ControllerId.XInput) { return selectedInputSource; } return inputSource; }).TakeLast(1); }) .ObserveOn(UnityThreadScheduler.MainThread));; }
public static IObservable <FMOD.Studio.System> StudioSystem() { return(UnityObservable.CreateUpdate <FMOD.Studio.System>(observer => { if (Fmod.StudioSystem.isValid()) { observer.OnNext(Fmod.StudioSystem); observer.OnCompleted(); } })); }
public void Initialize() { if (!_isInitialized) { if (_openEditor == null) { _openEditor = new Subject <Unit>(); } if (_closeEditor == null) { _closeEditor = new Subject <Unit>(); } if (_disposables == null) { _disposables = new CompositeDisposable(); } // Get the Gui camera, this will break in Oculus mode var cameraMovement = _spectatorCamera; var cameraTransform = new Ref <ImmutableTransform>( () => cameraMovement.transform.MakeImmutable(), transform => cameraMovement.transform.Set(transform)); var isCourseEditorOpen = _openEditor.Select(_ => true) .Merge(_closeEditor.Select(_ => false)) .StartWith(false); var courseStorageActions = new CourseStorageActions(); var courseSelectorActions = new CourseSelectorActions(); var courseEditorActions = new CourseEditorActions(); var closeCurrentCourse = new Subject <Unit>(); var fileStorage = CoursesFileStorage.CourseUpdater(courseStorageActions, CoursesFileStorage.CoursesDir.Value); _disposables.Add(fileStorage); var customCourses = CoursesFileStorage.CustomCourses.Value .ObserveOn(UnityThreadScheduler.MainThread); var courseSelectorStore = CourseSelectorStore.Create(courseSelectorActions, customCourses, isCourseEditorOpen); _disposables.Add(courseSelectorStore.Connect()); var courseEditorStore = CourseSelectorStore.CreateCourseEditorStore( course => CourseEditorStore.Create(courseEditorActions, course, cameraTransform), courseSelectorStore.Select(s => { if (s.SelectedCourse.IsJust) { return(s.AvailableCourses.Find(c => c.Id.Equals(s.SelectedCourse.Value))); } return(Maybe.Nothing <CourseData>()); })); _disposables.Add(courseEditorStore.Connect()); _disposables.Add(_openEditor.Subscribe(_ => _courseEditorGui.Show())); _disposables.Add(closeCurrentCourse .WithLatestFrom(courseEditorStore, (_, finalCourseState) => finalCourseState) .Subscribe(finalCourseState => { // Storing course changes to disk Debug.Log("deselecting a course"); courseStorageActions.UpdateCourse.OnNext(finalCourseState.ToSerializableFormat()); courseSelectorActions.DeselectCourse.OnNext(Unit.Default); })); // Initialize input for course editor store var propRenderer = new PropRenderer <PropId>(propId => "__CourseEditor-" + propId); // Clear the prop renderer when done editing _disposables.Add(courseEditorStore.Subscribe(_ => propRenderer.Clear())); CourseEditorInput.InitializeCourseEditor(courseEditorActions, courseEditorStore, _gameCamera, cameraTransform, _renderableProps, propRenderer, _clock); _courseEditorGui.CourseStorageActions = courseStorageActions; _courseEditorGui.CourseEditorActions = courseEditorActions; _courseEditorGui.CourseSelectorActions = courseSelectorActions; _courseEditorGui.CourseEditorChanges = courseEditorStore; _courseEditorGui.CourseSelectorChanges = courseSelectorStore; _courseEditorGui.CloseCurrentCourse = closeCurrentCourse; _courseEditorGui.CloseEditor = _closeEditor; _courseEditorGui.ActiveLanguage = _activeLanguage; _courseEditorGui.Initialize(); var rightMouseButtonPressed = UnityObservable.CreateUpdate <bool>(observer => { if (Input.GetMouseButtonDown(1)) { observer.OnNext(true); } else if (Input.GetMouseButtonUp(1)) { observer.OnNext(false); } }); _disposables.Add(courseSelectorStore .CombineLatest(isCourseEditorOpen, rightMouseButtonPressed, (state, isOpen, isRightMouseButtonIsPressed) => { return(state.SelectedCourse.IsJust && isOpen && isRightMouseButtonIsPressed); }) .StartWith(false) .Subscribe(isMovementEnabled => { if (isMovementEnabled) { cameraMovement.EnableInputProcessing(); } else { cameraMovement.DisableInputProcessing(); } })); _disposables.Add(_closeEditor.Subscribe(_ => { _courseEditorGui.Hide(); propRenderer.Clear(); })); _isInitialized = true; } }
public static void InitializeCourseEditor(CourseEditorActions actions, IObservable <CourseEditorState> store, Camera camera, Ref <ImmutableTransform> cameraTransform, RingProps renderableProps, PropRenderer <PropId> propRenderer, IClock clock) { var courseUpdates = store .Do(state => { var rProps = state.Props.Select(kvPair => { var propId = kvPair.Key; var prop = kvPair.Value; return(new KeyValuePair <PropId, RenderableProp>( propId, new RenderableProp(prop.Transform, renderableProps.Factory[prop.PropType].Spawn))); }) .ToDictionary(); propRenderer.Update(rProps); }).Replay(1); courseUpdates.Connect(); var keyboardEvents = UnityRxKeyboard.CreateKeyboard(); var highlight = ObjectPlacement.ObjectHighlight(camera, LayerMaskUtil.FullMask) .Select(go => { if (go.IsJust) { var id = go.Value.GetComponent <Id>(); if (id != null && id.Value.StartsWith("__CourseEditor-")) { return(Maybe.Just(new PropId(id.Value.Replace("__CourseEditor-", "")))); } return(Maybe.Nothing <PropId>()); } return(Maybe.Nothing <PropId>()); }); var leftMouseClick = keyboardEvents .KeyDown() .Where(c => c == KeyCode.Mouse0) .Select(c => Unit.Default); var selection = ObjectPlacement.ObjectSelection(highlight, leftMouseClick); IObservable <Unit> undo; IObservable <Unit> redo; // Editor already binds to default undo/redo keys so we need a different // mapping for them if (Application.isEditor) { undo = UnityObservable.CreateUpdate <Unit>(observer => { if (UnityEngine.Input.GetKeyDown(KeyCode.Z)) { observer.OnNext(Unit.Default); } }); redo = UnityObservable.CreateUpdate <Unit>(observer => { if (UnityEngine.Input.GetKeyDown(KeyCode.Y)) { observer.OnNext(Unit.Default); } }); } else { var historyCommands = UnityObservable.CreateUpdate <string>(observer => { if (UnityEngine.Input.GetKey(KeyCode.LeftControl) && UnityEngine.Input.GetKeyDown(KeyCode.Y)) { observer.OnNext("redo"); } else if (UnityEngine.Input.GetKey(KeyCode.LeftControl) && UnityEngine.Input.GetKeyDown(KeyCode.Z)) { observer.OnNext("undo"); } }); undo = historyCommands .Where(c => c.Equals("undo")) .Select(c => Unit.Default); redo = historyCommands .Where(c => c.Equals("redo")) .Select(c => Unit.Default); } var createProp = UnityObservable.CreateUpdate <Unit>(observer => { if (UnityEngine.Input.GetKey(KeyCode.LeftControl) && UnityEngine.Input.GetKeyDown(KeyCode.F)) { observer.OnNext(Unit.Default); } }); var deleteProp = keyboardEvents .KeyDown() .Where(key => key == KeyCode.Delete) .Select(key => Unit.Default); Func <KeyCode, Vector3> key2Translation = key => { if (key == KeyCode.W) { return(new Vector3(0, 0, 1)); } else if (key == KeyCode.S) { return(new Vector3(0, 0, -1)); } else if (key == KeyCode.A) { return(new Vector3(-1, 0, 0)); } else if (key == KeyCode.D) { return(new Vector3(1, 0, 0)); } else if (key == KeyCode.Q) { return(new Vector3(0, -1, 0)); } else if (key == KeyCode.E) { return(new Vector3(0, 1, 0)); } return(Vector3.zero); }; Func <KeyCode, Vector3> key2Rotation = key => { if (key == KeyCode.W) { return(new Vector3(1, 0, 0)); } else if (key == KeyCode.S) { return(new Vector3(-1, 0, 0)); } else if (key == KeyCode.D) { return(new Vector3(0, 1, 0)); } else if (key == KeyCode.A) { return(new Vector3(0, -1, 0)); } return(Vector3.zero); }; var selectedTransformTool = courseUpdates .Select(state => state.SelectedTransformTool) .DistinctUntilChanged(EnumComparer <TransformTool> .Instance); var switchTransformTool = selectedTransformTool.Select(tool => { return(UnityObservable.CreateUpdate <Unit>(observer => { if (UnityEngine.Input.GetKeyDown(KeyCode.LeftAlt) || UnityEngine.Input.GetKeyDown(KeyCode.RightAlt)) { observer.OnNext(Unit.Default); } }) .Select(_ => CourseEditor.TransformTools.GetNext(tool))); }).Switch(); var ticks = UnityRxObservables.UpdateTicks(() => clock.DeltaTime); var keysHeldStream = UnityRxKeyboard.CreateKeyboard(new[] { KeyCode.W, KeyCode.S, KeyCode.A, KeyCode.D, KeyCode.Q, KeyCode.E, KeyCode.Mouse1 }).KeysHeld(); var combinedTransformation = ticks.CombineLatest( keysHeldStream.CombineLatest(selectedTransformTool, (keysHeld, transformTool) => new { KeysHeld = keysHeld, TransformTool = transformTool }), (deltaTime, data) => { var rotationSpeed = 80f; var movementSpeed = 16f; var transform = ImmutableTransform.Identity; // Prevent collision with spectator camera movement. if (!data.KeysHeld.Contains(KeyCode.Mouse1)) { if (data.TransformTool == TransformTool.Rotate) { var rotation = data.KeysHeld .Aggregate(Vector3.zero, (current, key) => current + key2Rotation(key)) .normalized *rotationSpeed *deltaTime; transform = transform.Rotate(rotation); } else { var translation = data.KeysHeld .Aggregate(Vector3.zero, (current, key) => current + key2Translation(key)) .normalized *movementSpeed *deltaTime; transform = transform.Translate(translation); } } return(transform); }); var combinedTransformation2 = combinedTransformation .Window(() => { return(combinedTransformation .Where(t => t.Equals(ImmutableTransform.Identity))); }); var currentselectedProp = courseUpdates .Select( state => { var prop = state.SelectedProp.IsJust ? Maybe.Just(state.Props[state.SelectedProp.Value]) : Maybe.Nothing <EditorProp>(); return(prop); }) .DistinctUntilChanged(); var moveProp = currentselectedProp.CombineLatest(combinedTransformation2, (selectedProp, transformationCommand) => { if (selectedProp.IsJust) { return(transformationCommand.Scan( new Tuple <PropId, ImmutableTransform>(selectedProp.Value.Id, selectedProp.Value.Transform), (accumulatedMovement, transformation) => { var transformUpdate = accumulatedMovement._2 .Rotate(transformation.Rotation) .Translate(transformation.Position, cameraTransform.V.Rotation) .Scale(transformation.Scale); return new Tuple <PropId, ImmutableTransform>(accumulatedMovement._1, transformUpdate); })); } else { return(Observable.Empty <Tuple <PropId, ImmutableTransform> >()); } }); moveProp.Switch() .Subscribe(gameObjectMoveCommand => { var propId = gameObjectMoveCommand._1; var newTransform = gameObjectMoveCommand._2; var go = propRenderer.GetProp(propId); go.SetTransform(newTransform); }); moveProp .Select(moveCommand => moveCommand.TakeLast(1)) .Switch() .Subscribe(moveCommand => actions.UpdateProp.OnNext(moveCommand)); var moveToNextProp = UnityObservable.CreateUpdate <string>(observer => { if (UnityEngine.Input.GetKey(KeyCode.LeftControl) && UnityEngine.Input.GetKeyDown(KeyCode.Tab)) { observer.OnNext("previous"); } else if (UnityEngine.Input.GetKeyDown(KeyCode.Tab)) { observer.OnNext("next"); } }); courseUpdates .Select(state => { if (state.PropOrder.Count > 0) { return(moveToNextProp.Select(command => { if (state.SelectedProp.IsJust) { if (command.Equals("next")) { return state.PropOrder.GetNext(state.SelectedProp.Value); } else if (command.Equals("previous")) { return state.PropOrder.GetPrevious(state.SelectedProp.Value); } } return state.PropOrder.First(); })); } return(Observable.Empty <PropId>()); }) .Switch() .Subscribe(newlySelectedProp => { actions.SelectProp.OnNext(Maybe.Just(newlySelectedProp)); actions.MoveToProp.OnNext(newlySelectedProp); }); // TODO When player presses 'V' move prop to camera perspective. createProp.Subscribe(_ => actions.CreateProp.OnNext(Unit.Default)); deleteProp.Subscribe(_ => actions.DeleteSelectedProp.OnNext(Unit.Default)); undo.Subscribe(_ => actions.Undo.OnNext(Unit.Default)); redo.Subscribe(_ => actions.Redo.OnNext(Unit.Default)); highlight.Subscribe(propId => actions.HighlightProp.OnNext(propId)); selection.Subscribe(propId => actions.SelectProp.OnNext(Maybe.Just(propId))); switchTransformTool.Subscribe(tool => actions.SelectTransformTool.OnNext(tool)); var moveToProp = courseUpdates.Select(state => { return(actions.MoveToProp.Select(propId => state.Props[propId])); }).Switch(); moveToProp.Subscribe(prop => { Vector3 cameraRotation = cameraTransform.V.Rotation.eulerAngles; var propRotation = prop.Transform.Rotation.eulerAngles; cameraTransform.V = prop.Transform .TranslateLocally(new Vector3(0, 0, -30)) .UpdateRotation(cameraRotation.X(propRotation.x).Y(propRotation.y)); }); // TODO This code can be much simpler var guiUpdates = courseUpdates .Scan(new Diff <ObjectSelectionState?>(null, null), (previousDiff, state) => { Func <Maybe <PropId>, Maybe <GameObject> > findGameObject = propId => { return(propId.IsJust ? Maybe.Of(propRenderer.GetProp(propId.Value)) : Maybe.Nothing <GameObject>()); }; var @new = new ObjectSelectionState(findGameObject(state.HighlightedProp), findGameObject(state.SelectedProp)); var old = previousDiff.New; if (old.HasValue) { if (old.Value.HighlightedObject.IsJust && old.Value.HighlightedObject.Value == null) { old = new ObjectSelectionState(Maybe.Nothing <GameObject>(), old.Value.SelectedObject); } if (old.Value.SelectedObject.IsJust && old.Value.SelectedObject.Value == null) { old = new ObjectSelectionState(old.Value.HighlightedObject, Maybe.Nothing <GameObject>()); } } return(new Diff <ObjectSelectionState?>(old, @new)); }); guiUpdates.Subscribe(guiState => { if (guiState.Old.HasValue) { guiState.Old.Value.HighlightedObject.Do(obj => obj.GetComponentOfInterface <IHighlightable>().UnHighlight()); guiState.Old.Value.SelectedObject.Do(obj => obj.GetComponentOfInterface <ISelectable>().UnSelect()); } guiState.New.Value.HighlightedObject.Do(obj => obj.GetComponentOfInterface <IHighlightable>().Highlight()); guiState.New.Value.SelectedObject.Do(obj => obj.GetComponentOfInterface <ISelectable>().Select()); }); }