public static SCNMatrix4 Normalize(this SCNMatrix4 matrix) { // for row-major matrixes only var normalized = matrix; var row0 = SCNVector4.Normalize(matrix.Row0); var row1 = SCNVector4.Normalize(matrix.Row1); var row2 = SCNVector4.Normalize(matrix.Row2); normalized.Row0 = row0; normalized.Row1 = row1; normalized.Row2 = row2; return(normalized); }
private void UpdateCatapultStable() { if (!this.Disabled) { // Catapult will be unstable when the physics settles, therefore we do not update catapult's stability status if (GameTime.TimeSinceLevelStart > CatapultPhysicsSettleTime) { // Cannot use simdVelocity on client since simdVelocity could be high from physicsSync interacting with local physics engine if (!this.lastPosition.HasValue) { this.lastPosition = this.Base.PresentationNode.WorldPosition; return; } var position = this.Base.PresentationNode.WorldPosition; var speed = ((position - this.lastPosition.Value) / (float)GameTime.DeltaTime).Length; this.lastPosition = position; // Base below table? // Base tilted? base's up vector must maintain some amount of y to be determined as stable var transform = this.Base.PresentationNode.Transform; transform.Transpose(); var baseUp = SCNVector4.Normalize(transform.Column1); if (position.Y < -1f || Math.Abs(baseUp.Y) < MinStableTiltBaseUpY) { // Switch to knocked mode if (!this.isCatapultKnocked) { this.catapultKnockedStartTime = GameTime.Time; } this.isCatapultKnocked = true; this.isCatapultStable = false; return; } this.isCatapultKnocked = false; // Base could be moving although the catapult is not knocked this.isCatapultStable = speed < MaxSpeedToCountAsStable; } } }
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); }