} // end of c'tor public void Update(Camera camera) { if (gettingFeeds) { msNewsFeed.Update(); if (msNewsFeed.CurrentState == NewsFeeds.OpState.Failed) { newsScroller.Clear(); FeedMs item = new FeedMs(FeedSize, "Failed to get news feed...", TitleFont, DateFont, BodyFont); newsScroller.AddItem(item); gettingFeeds = false; newsScroller.Dirty = true; } else if (msNewsFeed.CurrentState == NewsFeeds.OpState.Retrieving) { newsScroller.Clear(); FeedMs item = new FeedMs(FeedSize, "Updating news...", TitleFont, DateFont, BodyFont); newsScroller.AddItem(item); //gettingFeeds = false; newsScroller.Dirty = true; } else if (msNewsFeed.CurrentState == NewsFeeds.OpState.Retrieved) { try { // Done getting Tweets newsScroller.Clear(); List <FeedMs> feedList = msNewsFeed.GetFeedList( (int)(FeedSize.X - newsScroller.ScrollBoxSize.X), TitleFont, DateFont, BodyFont); if (feedList != null) { foreach (FeedMs newsItem in feedList) { newsScroller.AddItem(newsItem); } } newsScroller.ResizeItemWidths(); gettingFeeds = false; newsScroller.Dirty = true; } catch { newsScroller.Clear(); FeedMs item = new FeedMs(FeedSize, "Error getting news feed...", TitleFont, DateFont, BodyFont); newsScroller.AddItem(item); gettingFeeds = false; newsScroller.Dirty = true; } } } Vector2 pureMouseHit = new Vector2(MouseInput.Position.X, MouseInput.Position.Y); if (moreLessHitBox.Contains(pureMouseHit) && MouseInput.Left.WasReleased) { expanded = !expanded; } //check touch hit TouchContact touch = TouchInput.GetOldestTouch(); if (TouchInput.WasLastReleased && moreLessHitBox.Contains(touch.position)) { expanded = !expanded; } // Even if not active we need to refresh the rendertarget. newsScroller.RefreshRT(); if (Active) { newsScroller.Update(camera); if (camera == null) { camera = new PerspectiveUICamera(); } if (GamePadInput.ActiveMode == GamePadInput.InputMode.KeyboardMouse) { Vector2 hit = MouseInput.GetAspectRatioAdjustedPosition(camera, useOverscanForHitTesting); pureMouseHit = new Vector2(MouseInput.Position.X, MouseInput.Position.Y); if (!IsInScrollwindow(pureMouseHit)) { // Don't Deactivate here just because the mouse cursor has left the bounds of the scroll window. // We should only deactivate if ComboRight is pressed. //Deactivate(); // But if we're out of bounds and the used clicks, then Deactivate() if (MouseInput.Left.WasPressed) { Deactivate(); } } else if (MouseInput.PrevPosition != MouseInput.Position) { newsScroller.Activate(); } if (true) //newsScroller.IsFocused) { if (Actions.ComboUp.WasPressedOrRepeat) { newsScroller.FocusPrev(); } else if (Actions.ComboDown.WasPressedOrRepeat) { newsScroller.FocusNext(); } else if (Actions.ComboRight.WasPressed) { Deactivate(); } if (Actions.ZoomIn.WasPressedOrRepeat) { newsScroller.FocusPrevItem(); } else if (Actions.ZoomOut.WasPressedOrRepeat) { newsScroller.FocusNextItem(); } } if (Actions.ComboLeft.WasPressed) { newsScroller.Activate(); } } else if (GamePadInput.ActiveMode == GamePadInput.InputMode.GamePad) { HandleGamepadInput(); } } // end if active. } // end of Update()
} // end of Update() private void HandleTouchInput() { if (GamePadInput.ActiveMode != GamePadInput.InputMode.Touch) { return; } if (TouchInput.TouchCount == 0) { return; } bool hitMenu = false; TouchContact touch = TouchInput.GetOldestTouch(); // If in focus element has help available, get it. UIGridElement focusElement = grid.SelectionElement; string helpID = focusElement.HelpID; string helpText = TweakScreenHelp.GetHelp(helpID); // Check for help tile. Matrix mat = Matrix.CreateTranslation(-helpSquare.Position.X, -helpSquare.Position.Y, 0); if (touch != null) { Vector2 hitHelpUV = Vector2.Zero; hitHelpUV = TouchInput.GetHitUV(touch.position, camera, ref mat, helpSquare.Size, helpSquare.Size, true); if (grid.SelectionElement.ShowHelpButton) { if (hitHelpUV.X >= 0 && hitHelpUV.X < 1 && hitHelpUV.Y >= 0 && hitHelpUV.Y < 1) { if (TouchInput.WasTouched) { touch.TouchedObject = helpSquare; } if (TouchInput.WasReleased && touch.TouchedObject == helpSquare) { if (helpText != null) { ShowHelp(helpText); } } hitMenu = true; } } // Check if mouse hitting current selection object. Or should this be done in the object? mat = Matrix.Invert(focusElement.WorldMatrix); Vector2 hitFocusUV = TouchInput.GetHitUV(touch.position, camera, ref mat, focusElement.Size.X, focusElement.Size.Y, true); bool focusElementHit = false; if (hitFocusUV.X >= 0 && hitFocusUV.X < 1 && hitFocusUV.Y >= 0 && hitFocusUV.Y < 1) { if (touch.phase == TouchPhase.Began) { touch.TouchedObject = focusElement; } focusElement.HandleTouchInput(touch, hitFocusUV); focusElementHit = true; hitMenu = true; } // If we didn't hit the focus object, see if we hit any of the others. // If so, bring them into focus. if (!focusElementHit && TouchGestureManager.Get().TapGesture.WasTapped()) { for (int i = 0; i < grid.ActualDimensions.Y; i++) { if (i == grid.SelectionIndex.Y) { continue; } UIGridElement e = grid.Get(0, i); mat = Matrix.Invert(e.WorldMatrix); Vector2 hitUV = TouchInput.GetHitUV(touch.position, camera, ref mat, e.Size.X, e.Size.Y, true); if (hitUV.X >= 0 && hitUV.X < 1 && hitUV.Y >= 0 && hitUV.Y < 1) { // We hit an element, so bring it into focus. grid.SelectionIndex = new Point(0, i); hitMenu = true; break; } } } if ((hitFocusUV.X >= 0) && (hitFocusUV.X < 1)) { hitMenu = true; } if (!hitMenu && TouchGestureManager.Get().TapGesture.WasTapped()) { Deactivate(); } // Handle free-form scrolling if (touch.TouchedObject != focusElement) { grid.HandleTouchInput(camera); } } // end of touch input }
public override void Update() { shared.timer.Update(); // Keep Kodu animating even if a dialog is active. shared.boku.UpdateFace(); shared.boku.UpdateAnimations(); if (AuthUI.IsModalActive) { return; } if (parent.newWorldDialog.Active) { parent.newWorldDialog.Update(); return; } // If not modal, always show status. AuthUI.ShowStatusDialog(); // Update the dialogs. parent.prevSessionCrashedMessage.Update(); parent.noCommunityMessage.Update(); parent.noSharingMessage.Update(); // Don't do anything else until the user reads and dismisses the dialogs. if (parent.prevSessionCrashedMessage.Active || parent.exitingKodu) { return; } // Update the options menu. Do this first so that if it is active it can steal input. shared.optionsMenu.Update(); // Main menu should always be active. shared.menu.Active = true; // If OptionsMenu is active, don't look at input. This is a problem for touch input // which doesn't support any kind of "ClearAllWasPressedState" functionality. if (!shared.optionsMenu.Active) { // Check for click on signOut tile or url. if (MouseInput.Left.WasPressed || Actions.Select.WasPressed || TouchInput.WasTouched) { TouchContact touch = TouchInput.GetOldestTouch(); if (touch != null) { touch.position = ScreenWarp.ScreenToRT(touch.position); } Vector2 mouseHit = MouseInput.GetMouseInRtCoords(); // url is in rt coords. if (shared.urlBox.Contains(mouseHit) || (null != touch && shared.urlBox.Contains(touch.position))) { #if NETFX_CORE Launcher.LaunchUriAsync(new Uri(KoduGameLabUrl)); #else Process.Start(KoduGameLabUrl); #endif MouseInput.Left.ClearAllWasPressedState(); } } // Enable resume option if we have something to resume to. if (InGame.UnDoStack.HaveResume() && (shared.menu.Item(0) != Strings.Localize("mainMenu.resume"))) { shared.menu.InsertText(Strings.Localize("mainMenu.resume"), 0); } shared.liveFeed.UpdateFeed(); shared.liveFeed.Update(shared.camera); if (!UpdateNonMenuItems()) { // JW - Only update the menu and process input if the interactive non-menu // items didn't already handle the input. int curIndex = shared.menu.CurIndex; shared.menu.Update(shared.camera, ref shared.worldMatrix); int newIndex = shared.menu.CurIndex; // If the user made a menu change, have boku glance over. if (curIndex != newIndex) { shared.boku.DirectGaze(new Vector3(0.2f, -0.4f, 0.08f - 0.05f * newIndex), 0.5f); } } } if (Actions.MiniHub.WasPressed && InGame.XmlWorldData != null) { parent.Deactivate(); InGame.inGame.SwitchToMiniHub(); return; } #if IMPORT_DEBUG if (!string.IsNullOrEmpty(StartupWorldFilename)) { LevelPackage.DebugPrint("MainMenu"); LevelPackage.DebugPrint(" StartupWorldFilename : " + StartupWorldFilename); } #endif // Jump into the startup world, if it was specified. if (!String.IsNullOrEmpty(StartupWorldFilename)) { if (Storage4.FileExists(StartupWorldFilename, StorageSource.All)) { #if IMPORT_DEBUG LevelPackage.DebugPrint(" level exists, trying to load and run"); #endif if (BokuGame.bokuGame.inGame.LoadLevelAndRun(StartupWorldFilename, keepPersistentScores: false, newWorld: false, andRun: true)) { #if IMPORT_DEBUG LevelPackage.DebugPrint(" success on load and run"); #endif parent.Deactivate(); } #if IMPORT_DEBUG else { LevelPackage.DebugPrint(" fail to load and run"); } #endif shared.waitingForStorage = false; } #if IMPORT_DEBUG else { LevelPackage.DebugPrint(" level not found"); } #endif StartupWorldFilename = null; } // Set news feed state to opposite of options menu. This allows the // News Feed to "hide" when the Options Menu is active. if (shared.optionsMenu.Active) { shared.liveFeed.Deactivate(); } else { shared.liveFeed.Activate(); shared.liveFeed.UpdateFeed(); } } // end of Update()
} // end of HandleGamePad() private void HandleTouch() { // Touch input TouchContact touch = TouchInput.GetOldestTouch(); if (touch != null) { //Vector2 hit = TouchInput.GetAspectRatioAdjustedPosition(touch.position, shared.camera, true); Vector2 hit = touch.position; // Cancel if (cancelButton.Box.Touched(touch, hit)) { Deactivate(saveChanges: false); return; } // Save if (saveButton.Box.Touched(touch, hit)) { Deactivate(saveChanges: true); return; } // Toggle LED if (toggleLEDButton.Box.Touched(touch, hit)) { ledGrid.LEDs[ledGrid.FocusLEDIndex] = !ledGrid.LEDs[ledGrid.FocusLEDIndex]; return; } // Get hit point in camera coords. Vector2 cameraHit = -camera.PixelsToCameraSpaceScreenCoords(hit); // Handle clicks on ledGrid. Hit boxes are in camera coords. if (touch.phase == TouchPhase.Began) { for (int i = 0; i < ledGrid.ledHitBoxes.Length; i++) { if (ledGrid.ledHitBoxes[i].Contains(cameraHit)) { ledGrid.FocusLEDIndex = i; // Also toggle when clicked. ledGrid.LEDs[ledGrid.FocusLEDIndex] = !ledGrid.LEDs[ledGrid.FocusLEDIndex]; } } } // Handle touch on sliders. { // Brightness Slider Vector2 position = new Vector2(brightnessSlider.Position.X, brightnessSlider.Position.Y); Vector2 min = position - brightnessSlider.Size / 2.0f; Vector2 max = position + brightnessSlider.Size / 2.0f; AABB2D box = new AABB2D(min, max); if (box.Contains(cameraHit)) { brightnessSlider.Selected = true; durationSlider.Selected = false; Vector2 uv = (cameraHit - min) / (max - min); uv.Y = 1.0f - uv.Y; brightnessSlider.HandleTouchInput(touch, uv); } // Duration Slider position = new Vector2(durationSlider.Position.X, durationSlider.Position.Y); min = position - durationSlider.Size / 2.0f; max = position + durationSlider.Size / 2.0f; box = new AABB2D(min, max); if (box.Contains(cameraHit)) { durationSlider.Selected = true; brightnessSlider.Selected = false; Vector2 uv = (cameraHit - min) / (max - min); uv.Y = 1.0f - uv.Y; durationSlider.HandleTouchInput(touch, uv); } } } // end if we have a touch. } // end of HandleTouch()
public override void Update() { // Did we switch modes? if (inputMode != GamePadInput.ActiveMode) { inputMode = GamePadInput.ActiveMode; shared.dirty = true; } if (AuthUI.IsModalActive) { return; } // Input focus and not pushing? if (parent.Active && CommandStack.Peek() == parent.commandMap && shared.pushOffset == 0.0f) { GamePadInput pad = GamePadInput.GetGamePad0(); if (Actions.Cancel.WasPressed) { Actions.Cancel.ClearAllWasPressedState(); parent.Deactivate(); Foley.PlayShuffle(); shared.dirty = true; } bool moveLeft = false; bool moveRight = false; // left if (Actions.ComboLeft.WasPressedOrRepeat) { moveLeft = true; } // right if (Actions.ComboRight.WasPressedOrRepeat) { moveRight = true; } //touch? if (GamePadInput.ActiveMode == GamePadInput.InputMode.Touch) { TouchContact touch = TouchInput.GetOldestTouch(); Vector2 touchHit = Vector2.Zero; SwipeGestureRecognizer swipeGesture = TouchGestureManager.Get().SwipeGesture; if (swipeGesture.WasSwiped() && swipeGesture.SwipeDirection == Boku.Programming.Directions.East) { moveLeft = true; } else if (swipeGesture.WasSwiped() && swipeGesture.SwipeDirection == Boku.Programming.Directions.West) { moveRight = true; } else if (touch != null) { touchHit = touch.position; if (shared.leftArrowBox.Touched(touch, touchHit)) { moveLeft = true; } if (shared.rightArrowBox.Touched(touch, touchHit)) { moveRight = true; } if (shared.backBox.Touched(touch, touchHit)) { Actions.Cancel.ClearAllWasPressedState(); parent.Deactivate(); Foley.PlayShuffle(); shared.dirty = true; } } } // Mouse hit? else if (GamePadInput.ActiveMode == GamePadInput.InputMode.KeyboardMouse) { Vector2 mouseHit = new Vector2(MouseInput.Position.X, MouseInput.Position.Y); if (shared.leftArrowBox.LeftPressed(mouseHit)) { moveLeft = true; } if (shared.rightArrowBox.LeftPressed(mouseHit)) { moveRight = true; } if (shared.backBox.LeftPressed(mouseHit)) { Actions.Cancel.ClearAllWasPressedState(); parent.Deactivate(); Foley.PlayShuffle(); shared.dirty = true; } } if (moveLeft) { --shared.curScreen; if (shared.curScreen < 0) { parent.Deactivate(); } Foley.PlayShuffle(); shared.dirty = true; shared.pushOffset = -BokuGame.ScreenSize.X; } if (moveRight) { ++shared.curScreen; if (shared.curScreen >= shared.screenList.Count) { parent.Deactivate(); } Foley.PlayShuffle(); shared.dirty = true; shared.pushOffset = BokuGame.ScreenSize.X; } } if (shared.dirty && shared.curScreen >= 0 && shared.curScreen < shared.screenList.Count) { shared.prevTexture = shared.curTexture; // Get the correct overlay image to use depending on input mode. string name = GamePadInput.ActiveMode == GamePadInput.InputMode.GamePad ? shared.screenList[shared.curScreen].name : shared.screenList[shared.curScreen].mouseName; shared.curTexture = BokuGame.Load <Texture2D>(BokuGame.Settings.MediaPath + @"Textures\HelpScreens\" + name); shared.dirty = false; // Create a twitch to do the push. TwitchManager.Set <float> set = delegate(float val, Object param) { shared.pushOffset = val; }; TwitchManager.CreateTwitch <float>(shared.pushOffset, 0.0f, set, 0.3f, TwitchCurve.Shape.EaseOut); } } // end of Update()
} // end of ClearAllItems() public void Update(Camera camera, ref Matrix parentMatrix) { CommandMap map = CommandStack.Peek(); if (map != commandMap) { return; } // Check for input. if (active && itemList.Count > 0) { GamePadInput pad = GamePadInput.GetGamePad0(); bool mouseDown = false; bool mouseUp = false; bool mouseSelect = false; bool touchSelect = false; if (GamePadInput.ActiveMode == GamePadInput.InputMode.KeyboardMouse) // MouseInput { // Did user double click? If so, treat as a shortcut to play. if (MouseInput.Left.WasDoubleClicked) { // This works because we _know_ Play is the first one in the list. // Not exactly a great solution. curIndex = 0; mouseSelect = true; } Vector2 hit = MouseInput.GetMouseInRtCoords(); if (!mouseSelect) { if (itemList[CurIndex].hitBox.LeftPressed(hit)) { mouseSelect = true; } } // If mouse is over menu and moving, choose item under mouse as selection. if (!mouseSelect && MouseInput.Position != MouseInput.PrevPosition) { for (int i = 0; i < itemList.Count; i++) { if (itemList[i].hitBox.Contains(hit)) { CurIndex = i; break; } } } int scroll = MouseInput.ScrollWheel - MouseInput.PrevScrollWheel; if (scroll > 0) { mouseUp = true; } else if (scroll < 0) { mouseDown = true; } // If user clicks off of the popup, treat as Back. if (MouseInput.Left.WasPressed && MouseInput.ClickedOnObject == null) { Active = false; return; } } // end of mouse input. else if (GamePadInput.ActiveMode == GamePadInput.InputMode.Touch) // TouchInput { TouchContact touch = TouchInput.GetOldestTouch(); if (touch != null) { Vector2 hit = TouchInput.GetAspectRatioAdjustedPosition(touch.position, camera, true); bool hitSomething = false; // Check for a hit on any of the items. for (int i = 0; i < itemList.Count; i++) { if (itemList[i].hitBox.Contains(hit)) { CurIndex = i; hitSomething = true; } } if (touch.phase == TouchPhase.Ended) { if (hitSomething) { // We've touched off on an item so choose it. touchSelect = true; } else { // We touched off and didn't hit anything. if (touch.TouchedObject == this) { touch.TouchedObject = null; } else { Active = false; } } } } } // end of Touch input. if (Actions.Select.WasPressed || mouseSelect || touchSelect) { Actions.Select.ClearAllWasPressedState(); if (itemList[curIndex].OnSelect != null) { itemList[curIndex].OnSelect(); } Foley.PlayPressA(); return; } if (Actions.Cancel.WasPressed) { Actions.Cancel.ClearAllWasPressedState(); Active = false; Foley.PlayBack(); return; } int prevIndex = curIndex; // Handle input changes here. if (Actions.ComboDown.WasPressedOrRepeat || mouseDown) { ++curIndex; if (curIndex >= itemList.Count) { curIndex = 0; } Foley.PlayShuffle(); } if (Actions.ComboUp.WasPressedOrRepeat || mouseUp) { --curIndex; if (curIndex < 0) { curIndex = itemList.Count - 1; } Foley.PlayShuffle(); } // Ensure that the selected state of all items is correct. for (int i = 0; i < itemList.Count; i++) { itemList[i].Selected = i == CurIndex; } } } // end of LoadLevelPopup Update()
private void HandleTouchInput(Camera camera) { if (GamePadInput.ActiveMode != GamePadInput.InputMode.Touch) { return; } // Touch input // If the touch is over the menu, move the selection index to the item under the mouse. // On touch down, make the item (if any) under the touch the ClickedOnItem. // On tap, if the touch is still over the ClickedOnItem, activate it. If not, just clear ClickedOnItem. TouchContact touch = TouchInput.GetOldestTouch(); if (touch != null) { Vector2 hitUV = TouchInput.GetHitUV(touch.position, camera, ref invWorldMatrix, width, height, useRtCoords: useRtCoords); // See if we're over anything. If so, set that item to being selected but only if we've moved the mouse. // This prevents the menu from feeling 'broken' if the mouse is over it and the user tries to use // the gamepad or keyboard. int touchItem = -1; for (int i = 0; i < itemList.Count; i++) { if (itemList[i].UVBoundingBox != null && itemList[i].UVBoundingBox.Contains(hitUV)) { // Only update the current in-focus element when the mouse moves. if (true) // touch.position != touch.previousPosition) { CurIndex = i; } touchItem = i; } } //if ( TouchInput.TapGesture.WasTapped() ) if (TouchInput.IsTouched) { if (touchItem != -1) { touch.TouchedObject = itemList[touchItem]; } } if (TouchGestureManager.Get().TapGesture.WasTapped()) { // Make sure we're still over the ClickedOnItem. if (touchItem != -1 && touch.TouchedObject == itemList[touchItem]) { ToggleState(); } } Vector2 hit = touch.position; if (useRtCoords) { hit = ScreenWarp.ScreenToRT(hit); } if (changeBox.Touched(touch, hit)) { ToggleState(); } if (backBox.Touched(touch, hit)) { Deactivate(); Foley.PlayBack(); } // Allow Swipeto cycle through elements. // Allow scroll wheel to cycle through elements. SwipeGestureRecognizer swipeGesture = TouchGestureManager.Get().SwipeGesture; if (swipeGesture.WasSwiped()) { if (swipeGesture.SwipeDirection == Boku.Programming.Directions.South) { curIndex -= 6; if (curIndex < 0) { curIndex = itemList.Count - 1; } Foley.PlayShuffle(); } else if (swipeGesture.SwipeDirection == Boku.Programming.Directions.North) { curIndex += 6; if (curIndex > (itemList.Count - 1)) { curIndex = 0; } Foley.PlayShuffle(); } } // If we click outside of the list, close it treating it as if select was chosen. //if (TouchInput.TapGesture.WasTapped() && touch.touchedObject == null) if (TouchInput.WasTouched && touch.TouchedObject == null) { Deactivate(); } } }
private void HandleTouchInput() { if (GamePadInput.ActiveMode != GamePadInput.InputMode.Touch) { return; } if (scrollItems.Count == 0) { return; } if (TouchInput.TouchCount != 1) { return; } Vector2 touchHit = TouchInput.GetOldestTouch().position; Vector2 adjHitPos = touchHit; if (scrollBoxHit.Contains(adjHitPos) || startDragOffset != 0.0f) { if (TouchInput.WasTouched || (TouchInput.IsTouched && startDragOffset == 0.0f)) { startDragOffset = touchHit.Y; startScrollOffset = scrollOffset; } else if (TouchInput.IsTouched) { float dragDistance = touchHit.Y - startDragOffset; float desiredScrollOffset = (ScrollableHeight * (dragDistance / MaxBoxTravelDistance)) + startScrollOffset; ConstrainScrollBox(desiredScrollOffset); } else { startDragOffset = 0.0f; } } else { adjHitPos -= relativePosition; for (int i = 0; i < scrollItems.Count; i++) { if (scrollItems[i].IsInFocus(adjHitPos)) { if (TouchGestureManager.Get().TapGesture.WasRecognized) { Object obj; scrollItems[i].Click(adjHitPos, out obj, ClickType.WasReleased); if (indexFocusedItem != i) { indexFocusedItem = i; scrollItems[indexFocusedItem].ResetFocus(); } } } } } }
/// <summary> /// Performs input handling for the Load Level UI grid in lieu of default grid drag. /// </summary> /// <param name="camera"></param> public override void HandleTouchInput(Camera camera) { TouchContact touch = TouchInput.GetOldestTouch(); if (touch == null) { return; } if (touch.phase == TouchPhase.Ended) { isDragging = false; return; } else if (!isDragging) { // This touch hasn't yet been classified as a drag yet.. so we // will see if it meets the criteria. float minDiff = MIN_DRAG_START; float diff = (touch.position - touch.startPosition).Length(); if (diff < minDiff) { return; } else { isDragging = true; } } // Get local coordinates difference between this touch position and the previous Matrix localInvMatrix = Matrix.Invert(LocalMatrix); Vector2 currentLocalPos = TouchInput.GetLocalXYFromScreenCoords( touch.position, camera, ref localInvMatrix); Vector2 previousLocalPos = TouchInput.GetLocalXYFromScreenCoords( touch.previousPosition, camera, ref localInvMatrix); float localXMove = (currentLocalPos - previousLocalPos).X; if (SlideByX(localXMove)) { // We did a successful move, so capture the momentum of the touch in pixels/frame float duration = 1.0f; if (Math.Abs(localXMove) > 0.2f) { residualVelocity = localXMove / 4.0f; hasResidualVelocity = true; TwitchManager.Set <float> set = velocityDelegate; TwitchManager.CreateTwitch <float>(residualVelocity, 0, set, duration, TwitchCurve.Shape.EaseOut); } else if (touch.phase == TouchPhase.Stationary) { // If the user has stopped moving but hasn't ended his touch, we will put // the brakes on any residual velocity, as the user probably wants to settle // here. residualVelocity = 0.0f; hasResidualVelocity = false; } } }