private SCNVector3 ComputeBallPosition(CameraInfo cameraInfo) { var cameraRay = cameraInfo.Ray; // These should be based on the projectile radius. // This affects centering of ball, and can hit near plane of camera // This is always centering to one edge of screen independent of screen orient // We always want the ball at the bottom of the screen. var distancePullToCamera = 0.21f; var ballShiftDown = 0.2f; var targetBallPosition = cameraRay.Position + cameraRay.Direction * distancePullToCamera; var cameraDown = -SCNVector4.Normalize(cameraInfo.Transform.Column1).Xyz; targetBallPosition += cameraDown * ballShiftDown; // Clamp to only the valid side var pullWorldPosition = this.pullOrigin.WorldPosition; if (pullWorldPosition.Z < 0f) { targetBallPosition.Z = Math.Min(targetBallPosition.Z, pullWorldPosition.Z); } else { targetBallPosition.Z = Math.Max(targetBallPosition.Z, pullWorldPosition.Z); } // Clamp to cone/circular core var yDistanceFromPull = Math.Max(0f, pullWorldPosition.Y - targetBallPosition.Y); var minBallDistanceFromPull = 0.5f; var pullBlockConeSlope = 1.0f; var pullBlockConeRadius = yDistanceFromPull / pullBlockConeSlope; var pullBlockCoreRadius = Math.Max(minBallDistanceFromPull, pullBlockConeRadius); // if pull is in the core, move it out. var pullWorldPositionGrounded = new SCNVector3(pullWorldPosition.X, 0f, pullWorldPosition.Z); var targetPullPositionGrounded = new SCNVector3(targetBallPosition.X, 0f, targetBallPosition.Z); var targetInitialToTargetPull = targetPullPositionGrounded - pullWorldPositionGrounded; if (pullBlockCoreRadius > targetInitialToTargetPull.Length) { var moveOutDirection = SCNVector3.Normalize(targetInitialToTargetPull); var newTargetPullPositionGrounded = pullWorldPositionGrounded + moveOutDirection * pullBlockCoreRadius; targetBallPosition = new SCNVector3(newTargetPullPositionGrounded.X, targetBallPosition.Y, newTargetPullPositionGrounded.Z); } // only use the 2d distance, so that user can gauage stretch indepdent of mtch var distance2D = targetBallPosition - pullWorldPosition; var stretchY = Math.Abs(distance2D.Y); distance2D.Y = 0f; var stretchDistance = distance2D.Length; this.stretch = DigitExtensions.Clamp((double)stretchDistance, this.properties.MinStretch, this.properties.MaxStretch); // clamp a little bit farther than maxStretch // can't let the strap move back too far right now var clampedStretchDistance = (float)(1.1d * this.properties.MaxStretch); if (stretchDistance > clampedStretchDistance) { targetBallPosition = (clampedStretchDistance / stretchDistance) * (targetBallPosition - pullWorldPosition) + pullWorldPosition; stretchDistance = clampedStretchDistance; } // Make this optional, not required. You're often at max stretch. // Also have a timer for auto-launch. This makes it very difficuilt to test // storing state in member data this.IsPulledTooFar = stretchDistance > (float)(this.properties.MaxStretch) || stretchY > (float)(this.properties.MaxStretch); return(targetBallPosition); }
public void Update(CameraInfo cameraInfo) { this.UpdateVortexState(); }
public GrabInfo(int grabbableId, CameraInfo cameraInfo) { this.GrabbableId = grabbableId; this.CameraInfo = cameraInfo; }
public void Update(CameraInfo cameraInfo) { if (this.Delegate == null) { throw new Exception("No delegate"); } // Do not move the lever after it has been activated if (!(this.InteractionToActivate?.IsActivated ?? false)) { if (this.activeSwitch != null) { // Lever Pulling var cameraOffset = this.activeSwitch.PullOffset(cameraInfo.Ray.Position - this.startLeverHoldCameraPosition); var cameraMovedZ = cameraOffset.Z; var targetEulerX = this.startLeverEulerX + LeverPullZtoLeverEulerRotation * cameraMovedZ; targetEulerX = DigitExtensions.Clamp(-LeverMaxEulerX, targetEulerX, LeverMaxEulerX); this.activeSwitch.Angle = targetEulerX; if (targetEulerX <= -LeverMaxEulerX) { // Interaction activation once the switch lever is turned all the way this.InteractionToActivate?.Activate(); // Fade out the switches var waitAction = SCNAction.Wait(3f); var fadeAction = SCNAction.FadeOut(3d); foreach (var resetSwitch in this.resetSwitches) { resetSwitch.Base.RunAction(SCNAction.Sequence(new SCNAction[] { waitAction, fadeAction })); } return; } else { // Inform peers of the movement var leverId = this.resetSwitches.IndexOf(this.activeSwitch); if (leverId == -1) { throw new Exception("No lever in array"); } this.Delegate.DispatchActionToServer(new GameActionType { LeverMove = new LeverMove(leverId, targetEulerX), Type = GameActionType.GActionType.LeverMove }); } } else { // Lever spring back foreach (var lever in this.resetSwitches.Where(lever => lever.Angle < LeverMaxEulerX)) { lever.Angle = Math.Min(LeverMaxEulerX, lever.Angle + LeverSpringBackSpeed * (float)GameTime.DeltaTime); } } // Highlight lever when nearby, otherwise check if we should hide the highlight if (this.highlightedSwitch != null) { if (!this.highlightedSwitch.ShouldHighlight(cameraInfo.Ray)) { this.highlightedSwitch.DoHighlight(false, this.SfxCoordinator); this.highlightedSwitch = null; } } else { foreach (var resetSwitch in this.resetSwitches) { if (resetSwitch.ShouldHighlight(cameraInfo.Ray)) { resetSwitch.DoHighlight(true, this.SfxCoordinator); this.highlightedSwitch = resetSwitch; } } } } }