} // end of RunSimUpdateObj c'tor /// <summary> /// RunSimUpdateObj Update() /// </summary> /// <param name="camera"></param> public override void Update() { base.Update(); parent.Camera.Update(); float secs = Time.WallClockFrameSeconds; ThoughtBalloonManager.Update(shared.camera); SaidStringManager.Update(); #if !NETFX_CORE MicrobitManager.Update(); #endif // Start with visible cursor. parent.cursor3D.Activate(); parent.cursor3D.Rep = Cursor3D.Visual.RunSim; parent.cursor3D.Hidden = false; // // Determine the correct camera mode. // // // The priorities used to determine the camera mode when the game is running are: // // 1) First person. This can be either via programming or because the user zoomed // into a bot the camera was following. // 2) Follow mode caused by bot(s) programmed with "follow" camera view. // 3) World tweak screen fixed camera or fixed offset camera. // 4) Follow mode caused by user controlled bot(s). // 5) Free camera. // // Start with a fake loop to break out of. while (true) { Terrain terrain = InGame.inGame.Terrain; // Just a shortcut. // // Always use edit mode when the game is paused except during victory on level with one of the fixed cameras // or when game is paused for a bot to speak (modal text display). // bool victoryActive = VictoryOverlay.ActiveGameOver || VictoryOverlay.ActiveWinner; bool speaking = InGame.inGame.shared.smallTextDisplay.Active || InGame.inGame.shared.scrollableTextDisplay.Active; if (Time.Paused && !((terrain.FixedCamera || terrain.FixedOffsetCamera) && victoryActive) && !speaking) { CameraInfo.Mode = CameraInfo.Modes.Edit; CameraInfo.CameraFocusGameActor = null; break; } // // 1) First person // if (CameraInfo.FirstPersonActive) { CameraInfo.Mode = CameraInfo.Modes.Actor; // We're following a single actor so update the FollowActor camera values. shared.camera.FollowCameraValid = false; // Turn off 3d cursor since we don't need it. parent.cursor3D.Deactivate(); if (parent.cursorClone != null) { parent.cursorClone.Deactivate(); parent.cursorClone = null; } break; } // // 2) Follow mode caused by bot(s) programmed with "follow" camera view. // if (CameraInfo.ProgrammedFollowList.Count > 0) { // Note that even though we looked at the count of bot programmed to // have the camera follow them, for this mode we want to keep all // deserving bots in camera. So, the rest of this section will use // the merged follow list instead of just the programmed follow list. SetUpCameraFollowMode(); break; } // // 3) World tweak fixed cameras. Note for fixed offset we have to let // the later modes do their stuff and then override the camera. // if (terrain.FixedCamera) { CameraInfo.Mode = CameraInfo.Modes.FixedTarget; CameraInfo.CameraFocusGameActor = null; // Turn off 3d cursor since we don't need it. parent.cursor3D.Deactivate(); if (parent.cursorClone != null) { parent.cursorClone.Deactivate(); parent.cursorClone = null; } break; } // // 4) Follow mode caused by user controlled bot(s). // if (CameraInfo.MergedFollowList.Count > 0) { SetUpCameraFollowMode(); break; } // // 5) Free! // // Not following an actor. CameraInfo.Mode = CameraInfo.Modes.Edit; CameraInfo.CameraFocusGameActor = null; // Turn on 3d cursor in case we previously disabled it. parent.cursor3D.Activate(); parent.CreateCursorClone(); parent.cursor3D.Hidden = false; // We have no camera restrictions, so keep track of what the user is doing. shared.camera.PlayValid = true; shared.camera.PlayCameraFrom = shared.camera.From; shared.camera.PlayCameraAt = shared.camera.At; shared.camera.FollowCameraValid = false; // Final break just to be sure the loop exits. break; } // // Now that we're done, we need to check again to see if // we should be in FixedOffsetMode. // if (!Time.Paused && InGame.inGame.Terrain.FixedOffsetCamera && !CameraInfo.FirstPersonActive) { CameraInfo.Mode = CameraInfo.Modes.FixedOffset; } // Zero out any offset while running. float t = Math.Min(Time.GameTimeFrameSeconds, 1.0f); shared.camera.HeightOffset = MyMath.Lerp(shared.camera.HeightOffset, 0.0f, t); // bool inputFocus = CommandStack.Peek() == commandMap; // Move the camera. switch (CameraInfo.Mode) { case CameraInfo.Modes.Edit: MoveCameraEditMode(inputFocus, false); break; case CameraInfo.Modes.Actor: MoveCameraActorMode(true, false); break; case CameraInfo.Modes.FixedTarget: MoveCameraFixedTargetMode(inputFocus); break; case CameraInfo.Modes.FixedOffset: MoveCameraFixedOffsetMode(inputFocus); break; case CameraInfo.Modes.MultiTarget: MoveCameraMultiTargetMode(inputFocus, false); break; } shared.camera.Update(); // Update terrain. parent.terrain.Update(shared.camera); // Update the list of objects using our local camera. for (int i = 0; i < updateList.Count; i++) { UpdateObject obj = (UpdateObject)updateList[i]; obj.Update(); } parent.UpdateObjects(); /// Pregame must update after parent.UpdateObjects, in case it /// decides to switchToMiniHub if (InGame.inGame.preGame != null) { InGame.inGame.preGame.Update(); } // Update the particle system. shared.particleSystemManager.Update(); DistortionManager.Update(); FirstPersonEffectMgr.Update(); // This must be done after all brains are updated. Scoreboard.Update(shared.camera); // Update the TextDisplays. Ignored if not active. shared.scrollableTextDisplay.Update(shared.camera); shared.smallTextDisplay.Update(shared.camera); VictoryOverlay.Update(); // Do the input processing after object update because there will be order of operation issue if we don't // // Check if we have input focus. Don't do any input // related update if we don't. if (inputFocus) { // Grab the current state of the gamepad. GamePadInput pad = GamePadInput.GetGamePad0(); // Switch to Mini-Hub? if (Actions.MiniHub.WasPressed) { Actions.MiniHub.ClearAllWasPressedState(); //parent.ResetSim(CurrentLevelFilename()); // Needed to make sure that deactivated objects are actually removed from // the list otherwise they may get saved along with the newly activated ones. //parent.Refresh(BokuGame.gameListManager.updateList, BokuGame.gameListManager.renderList); parent.SwitchToMiniHub(); return; } //handle swipe to return to edit for touch input if (GamePadInput.ActiveMode == GamePadInput.InputMode.Touch) { SwipeGestureRecognizer swipeGesture = TouchGestureManager.Get().SwipeGesture; if (swipeGesture.WasRecognized && swipeGesture.SwipeDirection == Directions.North) { #if NETFX_CORE float halfWidth = (float)BokuGame.bokuGame.Window.ClientBounds.Width * 0.5f; float height = (float)BokuGame.bokuGame.Window.ClientBounds.Height; #else float halfWidth = (float)XNAControl.Instance.ClientSize.Width * 0.5f; float height = (float)XNAControl.Instance.ClientSize.Height; #endif //center half of the screen width-wise float minX = halfWidth - (halfWidth * k_TouchExitAreaWidthPercent); float maxX = halfWidth + (halfWidth * k_TouchExitAreaWidthPercent); //bottom 20% height-wise float minY = height - (height * k_TouchExitAreaHeightPercent); Vector2 pos = swipeGesture.InitialPosition; if (pos.X >= minX && pos.X <= maxX && pos.Y >= minY) { // User did a swipe from the bottom of the screen, enter edit mode InGame.inGame.CurrentUpdateMode = UpdateMode.EditObject; InGame.inGame.CurrentUpdateMode = UpdateMode.TouchEdit; Foley.PlayPressStart(); return; } } // HACKHACK (****) Put in a tap recognizer since swipe seems to fail. { TapGestureRecognizer hackTapGesture = TouchGestureManager.Get().TapGesture; if (hackTapGesture.WasTapped()) { #if NETFX_CORE float halfWidth = (float)BokuGame.bokuGame.Window.ClientBounds.Width * 0.5f; float height = (float)BokuGame.bokuGame.Window.ClientBounds.Height; #else float halfWidth = (float)XNAControl.Instance.ClientSize.Width * 0.5f; float height = (float)XNAControl.Instance.ClientSize.Height; #endif //center area of the screen width-wise float minX = halfWidth - (halfWidth * 0.1f); float maxX = halfWidth + (halfWidth * 0.1f); //bottom 10% height-wise float minY = height - (height * 0.1f); Vector2 pos = hackTapGesture.Position; if (pos.X >= minX && pos.X <= maxX && pos.Y >= minY) { // User did a tap on the bottom of the screen, enter edit mode InGame.inGame.CurrentUpdateMode = UpdateMode.EditObject; InGame.inGame.CurrentUpdateMode = UpdateMode.TouchEdit; Foley.PlayPressStart(); return; } } } } bool gameOver = VictoryOverlay.GameOver; if (Time.Paused && !gameOver) { // We must be in user induced pause mode. if (Actions.Unpause.WasPressed) { Actions.Unpause.ClearAllWasPressedState(); Time.Paused = false; HelpOverlay.Pop(); } } else { if (gameOver) { // Game over man! Let the user restart if they want. if (Actions.Restart.WasPressed) { Actions.Restart.ClearAllWasPressedState(); InGame.inGame.ResetSim(preserveScores: false, removeCreatablesFromScene: true, keepPersistentScores: false); // Since we're going right back into RunSim mode we need to first inline. ApplyInlining(); } } // Open ToolMenu. if (Actions.ToolMenu.WasPressed) { Actions.ToolMenu.ClearAllWasPressedState(); parent.CurrentUpdateMode = UpdateMode.ToolMenu; Foley.PlayPressStart(); return; } // Pause? // We want to make pause hard to get into so it requires both triggers and both stickButtons to be pressed. if ((pad.LeftStickButton.IsPressed && pad.RightStickButton.IsPressed && pad.LeftTriggerButton.IsPressed && pad.RightTriggerButton.IsPressed) || Actions.Pause.WasPressed) { Actions.Pause.ClearAllWasPressedState(); if (!Time.Paused) { Time.Paused = true; HelpOverlay.Push("PauseGame"); GamePadInput.GetGamePad0().IgnoreLeftStickUntilZero(); GamePadInput.GetGamePad0().IgnoreRightStickUntilZero(); } } } } // Force the the HelpOverlay to be correct. if (!Time.Paused || VictoryOverlay.Active) { if (InGame.inGame.PreGame != null && InGame.inGame.PreGame.Active) { if (HelpOverlay.Depth() != 1 || HelpOverlay.Peek() != "RunSimulationPreGame") { HelpOverlay.Clear(); HelpOverlay.Push("RunSimulationPreGame"); } } else { if (HelpOverlay.Depth() != 1 || HelpOverlay.Peek() != "RunSimulation") { HelpOverlay.Clear(); HelpOverlay.Push("RunSimulation"); } } } else { // We're paused. if (HelpOverlay.Depth() != 2 || HelpOverlay.Peek(1) != "RunSimulation") { HelpOverlay.Clear(); HelpOverlay.Push("RunSimulation"); HelpOverlay.Push("PauseGame"); } } // When in run mode, allow the user to click on the "Press [esc] to edit" text as if it was a button. if (MouseInput.Left.WasPressed) { Point mousePos = MouseInput.Position; if (HelpOverlay.MouseHitBottomText(mousePos)) { // Switch to edit mode. InGame.inGame.CurrentUpdateMode = UpdateMode.EditObject; } } TapGestureRecognizer tapGesture = TouchGestureManager.Get().TapGesture; if (tapGesture.WasTapped()) { // JW - Until we have proper Touch help overlays, we are still using the mouse/keyboard // overlays. The mouse handling code for these depends on being able to 'absorb' pressed // info to hide it from later callers. Our touch stuff doesn't (and really shouldn't) // do this. So, we handle cases here based on what type of overlay is being displayed. if (HelpOverlay.Peek() == "RunSimulationPreGame") { // Tap during instructions: just begin game. InGame.inGame.PreGame.Active = false; } // Also test if running sim for tapping on "tap to edit" at bottom. if (HelpOverlay.Peek() == "RunSimulation") { Point pos = new Point((int)tapGesture.Position.X, (int)tapGesture.Position.Y); if (HelpOverlay.MouseHitBottomText(pos)) { // Switch to edit mode. InGame.inGame.CurrentUpdateMode = UpdateMode.EditObject; } } } } // end of RunSimUpdateObj Update()
} // end of UpdateCamera() /// <summary> /// Common camera controls for edit modes. /// </summary> /// <param name="preventZoom">Lock the zoom. This is kind of a hack used /// by the tool palette since that palette uses the shoulder buttons to /// cycle through it. Without locking the zoom we'd zoom in and out /// as we're moving though the tool options.</param> public void UpdateCamera(bool preventZoom) { float secs = Time.WallClockFrameSeconds; Vector3 lookAt = parent.Camera.At; Vector3 lookFrom = parent.Camera.From; // If we were in first person mode, reset things as needed. if (CameraInfo.FirstPersonActive) { CameraInfo.FirstPersonActor.SetFirstPerson(false); CameraInfo.ResetAllLists(); CameraInfo.Mode = CameraInfo.Modes.Edit; InGame.inGame.Camera.FollowCameraDistance = 10.0f; } // Check if we have input focus. Don't do any input // related update if we don't. if (CommandStack.Peek() == commandMap) { // Grab the current state of the gamepad. GamePadInput pad = GamePadInput.GetGamePad0(); // From all edit modes, <back> should bring up the tool menu. if (Actions.ToolMenu.WasPressed) { Actions.ToolMenu.ClearAllWasPressedState(); Foley.PlayBack(); parent.CurrentUpdateMode = UpdateMode.ToolMenu; return; } // From all edit modes, <start> should to to the mini-hub. if (Actions.MiniHub.WasPressed) { Actions.MiniHub.ClearAllWasPressedState(); Foley.PlayPressStart(); parent.SwitchToMiniHub(); return; } // From all edit modes the B button should activate the ToolMenu. // Edit object handles the B button itself since it may be in Waypoint mode. if (Actions.AltToolMenu.WasPressed && parent.CurrentUpdateMode != UpdateMode.EditObject) { Actions.AltToolMenu.ClearAllWasPressedState(); parent.CurrentUpdateMode = UpdateMode.ToolMenu; return; } // If the ToolMenu is active and it's modal, don't allow the camera to move. if (InGame.inGame.toolMenuUpdateObj.active && XmlOptionsData.ModalToolMenu) { return; } // // Use common camera controls for all edit modes. // const float cursorSpeed = 20.0f; // Meters per second. const float orbitSpeed = 2.0f; // Radians per second. const float zoomFactor = 1.1f; // Right stick to orbit around cursor. float drot = GamePadInput.InvertCamX() ? -pad.RightStick.X : pad.RightStick.X; float dpitch = GamePadInput.InvertCamY() ? -pad.RightStick.Y : pad.RightStick.Y; parent.Camera.DesiredRotation += drot * Time.WallClockFrameSeconds * orbitSpeed; parent.Camera.DesiredPitch -= dpitch * Time.WallClockFrameSeconds * orbitSpeed; // Left/right arrow keys also orbit but not if the tool menu or a picker is up. //if (inGame.CurrentUpdateMode != UpdateMode.ToolMenu && !HelpOverlay.Peek().EndsWith("Picker") ) //{ // if (KeyboardInput.IsPressed(Keys.Left)) // { // parent.Camera.DesiredRotation -= 1.0f * Time.WallClockFrameSeconds * orbitSpeed; // } // else if (KeyboardInput.IsPressed(Keys.Right)) // { // parent.Camera.DesiredRotation += 1.0f * Time.WallClockFrameSeconds * orbitSpeed; // } //} // Shoulder buttons track camera in/out. if (!preventZoom) { if (Actions.ZoomOut.IsPressed) { parent.Camera.DesiredDistance *= 1.0f + Time.WallClockFrameSeconds * zoomFactor; } if (Actions.ZoomIn.IsPressed) { float desiredDistance = parent.Camera.DesiredDistance * (1.0f - Time.WallClockFrameSeconds * zoomFactor); // If not in RunSim mode, don't allow the camera to get closer than 4 meters. if (InGame.inGame.CurrentUpdateMode != UpdateMode.RunSim) { desiredDistance = Math.Max(4.0f, desiredDistance); } parent.Camera.DesiredDistance = desiredDistance; } parent.MouseEdit.DoZoom(parent.Camera); } if (GamePadInput.ActiveMode == GamePadInput.InputMode.KeyboardMouse) { parent.MouseEdit.DoCamera(parent.Camera); } // Left stick to control cursor position. // Cursor movement is relative to view heading. // Cursor speed grows with view distance. Vector2 position = new Vector2(shared.CursorPosition.X, shared.CursorPosition.Y); Vector2 forward = new Vector2((float)Math.Cos(parent.Camera.Rotation), (float)Math.Sin(parent.Camera.Rotation)); Vector2 right = new Vector2(forward.Y, -forward.X); float speedFactor = (parent.Camera.Distance - 10.0f) / 50.0f; // At 10 meters out start growing the speedFactor. speedFactor = MathHelper.Clamp(speedFactor, 1.0f, 3.0f); position += forward * pad.LeftStick.Y * Time.WallClockFrameSeconds * cursorSpeed * speedFactor; position += right * pad.LeftStick.X * Time.WallClockFrameSeconds * cursorSpeed * speedFactor; // Numpad controls cursor position. NumLock must be on! float y = KeyboardInput.IsPressed(Keys.NumPad7) || KeyboardInput.IsPressed(Keys.NumPad8) || KeyboardInput.IsPressed(Keys.NumPad9) ? 1.0f : 0.0f; y += KeyboardInput.IsPressed(Keys.NumPad1) || KeyboardInput.IsPressed(Keys.NumPad2) || KeyboardInput.IsPressed(Keys.NumPad3) ? -1.0f : 0.0f; float x = KeyboardInput.IsPressed(Keys.NumPad3) || KeyboardInput.IsPressed(Keys.NumPad6) || KeyboardInput.IsPressed(Keys.NumPad9) ? 1.0f : 0.0f; x += KeyboardInput.IsPressed(Keys.NumPad1) || KeyboardInput.IsPressed(Keys.NumPad4) || KeyboardInput.IsPressed(Keys.NumPad7) ? -1.0f : 0.0f; position += forward * y * Time.WallClockFrameSeconds * cursorSpeed * speedFactor; position += right * x * Time.WallClockFrameSeconds * cursorSpeed * speedFactor; // Allow LeftStickClick RightStickClick to cycle though actors. if (inGame.gameThingList.Count > 0) { // Move to next actor if (Actions.NextActor.WasPressed) { Actions.NextActor.ClearAllWasPressedState(); // If we have an actor in focus, find index. GameActor focusActor = InGame.inGame.EditFocusObject; if (focusActor != null) { for (int i = 0; i < inGame.gameThingList.Count; i++) { if (focusActor == inGame.gameThingList[i]) { actorTabIndex = i; break; } } } // Increment index. actorTabIndex = (actorTabIndex + 1) % inGame.gameThingList.Count; Vector3 actorPos = inGame.gameThingList[actorTabIndex].Movement.Position; Vector2 delta = new Vector2(actorPos.X - position.X, actorPos.Y - position.Y); position += delta; } // Move to prev actor if (Actions.PrevActor.WasPressed) { Actions.PrevActor.ClearAllWasPressedState(); // If we have an actor in focus, find index. GameActor focusActor = InGame.inGame.EditFocusObject; if (focusActor != null) { for (int i = 0; i < inGame.gameThingList.Count; i++) { if (focusActor == inGame.gameThingList[i]) { actorTabIndex = i; break; } } } // Decrement index. actorTabIndex = (actorTabIndex + inGame.gameThingList.Count - 1) % inGame.gameThingList.Count; Vector3 actorPos = inGame.gameThingList[actorTabIndex].Movement.Position; Vector2 delta = new Vector2(actorPos.X - position.X, actorPos.Y - position.Y); position += delta; } } if (!shared.editWayPoint.Dragging) { // TODO (mouse) Why was this here? //position = parent.MouseEdit.DoCursor(parent.Camera, position); } position = shared.editWayPoint.DoCursor(position); // Keep cursor within 50 units of existing terrain. float maxBrush = InGame.kMaxBrushRadius; Vector2 min = new Vector2(InGame.inGame.totalBounds.Min.X, InGame.inGame.totalBounds.Min.Y) - new Vector2(maxBrush, maxBrush); Vector2 max = new Vector2(InGame.inGame.totalBounds.Max.X, InGame.inGame.totalBounds.Max.Y) + new Vector2(maxBrush, maxBrush); position.X = MathHelper.Clamp(position.X, min.X, max.X); position.Y = MathHelper.Clamp(position.Y, min.Y, max.Y); shared.CursorPosition = new Vector3(position, shared.CursorPosition.Z); } // end if we have input focus. // Keep the camera from going into the ground. shared.KeepCameraAboveGround(); // Update camera based on new position/orientation. parent.Camera.DesiredAt = shared.CursorPosition; // Finally, call Update on the camera to let all the changes filter through. parent.Camera.Update(); } // end of BaseEditUpdateObj UpdateCamera()