private void SlideInWorld(SCNVector3 start, SCNVector3 velocity) { var maxSlideIteration = 4; var iteration = 0; var stop = false; var replacementPoint = start; var options = new SCNPhysicsTest() { CollisionBitMask = (int)Bitmask.Collision, SearchMode = SCNPhysicsSearchMode.Closest, }; while (!stop) { var from = SCNMatrix4.Identity; SimdExtensions.SetPosition(ref from, start); var to = SCNMatrix4.Identity; SimdExtensions.SetPosition(ref to, start + velocity); var contacts = this.PhysicsWorld.ConvexSweepTest(this.characterCollisionShape, from, to, options.Dictionary); if (contacts.Any()) { (velocity, start) = this.HandleSlidingAtContact(contacts.FirstOrDefault(), start, velocity); iteration += 1; if (velocity.LengthSquared <= (10E-3 * 10E-3) || iteration >= maxSlideIteration) { replacementPoint = start; stop = true; } } else { replacementPoint = start + velocity; stop = true; } } this.characterNode.WorldPosition = replacementPoint - this.collisionShapeOffsetFromModel; }
public virtual void Update (ISCNSceneRenderer renderer, double timeInSeconds) { // delta time since last update if (Math.Abs (previousUpdateTime) < float.Epsilon) previousUpdateTime = timeInSeconds; double deltaTime = Math.Min (Math.Max (1.0 / 60.0, timeInSeconds - previousUpdateTime), 1f); previousUpdateTime = timeInSeconds; // Reset some states every frame maxPenetrationDistance = 0; positionNeedsAdjustment = false; SCNVector3 direction = GameView.CurrentDirection; SCNVector3 initialPosition = Character.Node.Position; // Move if (Math.Abs (direction.X) > float.Epsilon && Math.Abs (direction.Z) > float.Epsilon) { var characterSpeed = (float)deltaTime * CharacterSpeedFactor * .84f; Character.Node.Position = new SCNVector3 ( initialPosition.X + direction.X * characterSpeed, initialPosition.Y + direction.Y * characterSpeed, initialPosition.Z + direction.Z * characterSpeed ); // update orientation double angle = Math.Atan2 (direction.X, direction.Z); Character.Direction = (float)angle; Character.Walking = true; } else { Character.Walking = false; } var p0 = Character.Node.Position; var p1 = Character.Node.Position; p0.Y -= MaxJump; p1.Y += MaxRise; var options = new SCNPhysicsTest { CollisionBitMask = (nuint)(int)(Bitmask.Collision | Bitmask.Water), SearchMode = SCNPhysicsSearchMode.Closest }; SCNHitTestResult[] results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint (p1, p0, options); float groundY = -10; if (results.Length > 0) { SCNHitTestResult result = results [0]; groundY = result.WorldCoordinates.Y; UpdateCameraWithCurrentGround (result.Node); SCNMaterial groundMaterial = result.Node.ChildNodes [0].Geometry.FirstMaterial; if (grassArea == groundMaterial) { Character.CurrentFloorMaterial = FloorMaterial.Grass; } else if (waterArea == groundMaterial) { if (Character.Burning) { Character.Pshhhh (); Character.Node.RunAction (SCNAction.Sequence (new [] { SCNAction.PlayAudioSource (pshhhSound, true), SCNAction.PlayAudioSource (aahSound, false) })); } Character.CurrentFloorMaterial = FloorMaterial.Water; options = new SCNPhysicsTest { CollisionBitMask = (nuint)(int)Bitmask.Collision, SearchMode = SCNPhysicsSearchMode.Closest }; results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint (p1, p0, options); result = results [0]; groundY = result.WorldCoordinates.Y; } else { Character.CurrentFloorMaterial = FloorMaterial.Rock; } } // var nextPosition = Character.Node.Position; // const double threshold = 1e-5; // // if (groundY < nextPosition.Y - threshold) { // // approximation of acceleration for a delta time // accelerationY += (float)(deltaTime * GravityAcceleration); // if (groundY < nextPosition.Y - 0.2) // Character.CurrentFloorMaterial = FloorMaterial.Air; // } else { // accelerationY = 0; // } // // nextPosition.Y -= accelerationY; // // // reset acceleration if we touch the ground // if (groundY > nextPosition.Y) { // accelerationY = 0; // nextPosition.Y = groundY; // } // Flames are static physics bodies, but they are moved by an action - So we need to tell the physics engine that the transforms did change. foreach (SCNNode flame in flames) flame.PhysicsBody.ResetTransform (); // Adjust the volume of the enemy based on the distance with the character. float distanceToClosestEnemy = float.MaxValue; SCNVector3 pos3 = Character.Node.Position; foreach (SCNNode enemy in enemies) { // distance to enemy SCNMatrix4 enemyMat = enemy.WorldTransform; var enemyPosition = new SCNVector3 (enemyMat.M41, enemyMat.M42, enemyMat.M43); float distance = SCNVector3.Subtract (pos3, enemyPosition).Length; distanceToClosestEnemy = Math.Min (distanceToClosestEnemy, distance); } // Adjust sounds volumes based on distance with the enemy. if (!gameIsComplete) { double fireVolume = 0.3 * Math.Max (0.0, Math.Min (1.0, 1.0 - (distanceToClosestEnemy - 1.2) / 1.6)); var mixerNode = flameThrowerSound.AudioNode as AVAudioMixerNode; if (mixerNode != null) mixerNode.Volume = (float)fireVolume; } }
public virtual void Update(ISCNSceneRenderer renderer, double timeInSeconds) { // delta time since last update if (Math.Abs(previousUpdateTime) < float.Epsilon) { previousUpdateTime = timeInSeconds; } double deltaTime = Math.Min(Math.Max(1.0 / 60.0, timeInSeconds - previousUpdateTime), 1f); previousUpdateTime = timeInSeconds; // Reset some states every frame maxPenetrationDistance = 0; positionNeedsAdjustment = false; SCNVector3 direction = GameView.CurrentDirection; SCNVector3 initialPosition = Character.Node.Position; // Move if (Math.Abs(direction.X) > float.Epsilon && Math.Abs(direction.Z) > float.Epsilon) { var characterSpeed = (float)deltaTime * CharacterSpeedFactor * .84f; Character.Node.Position = new SCNVector3( initialPosition.X + direction.X * characterSpeed, initialPosition.Y + direction.Y * characterSpeed, initialPosition.Z + direction.Z * characterSpeed ); // update orientation double angle = Math.Atan2(direction.X, direction.Z); Character.Direction = (float)angle; Character.Walking = true; } else { Character.Walking = false; } var p0 = Character.Node.Position; var p1 = Character.Node.Position; p0.Y -= MaxJump; p1.Y += MaxRise; var options = new SCNPhysicsTest { CollisionBitMask = (nuint)(int)(Bitmask.Collision | Bitmask.Water), SearchMode = SCNPhysicsSearchMode.Closest }; SCNHitTestResult[] results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint(p1, p0, options); float groundY = -10; if (results.Length > 0) { SCNHitTestResult result = results [0]; groundY = result.WorldCoordinates.Y; UpdateCameraWithCurrentGround(result.Node); SCNMaterial groundMaterial = result.Node.ChildNodes [0].Geometry.FirstMaterial; if (grassArea == groundMaterial) { Character.CurrentFloorMaterial = FloorMaterial.Grass; } else if (waterArea == groundMaterial) { if (Character.Burning) { Character.Pshhhh(); Character.Node.RunAction(SCNAction.Sequence(new [] { SCNAction.PlayAudioSource(pshhhSound, true), SCNAction.PlayAudioSource(aahSound, false) })); } Character.CurrentFloorMaterial = FloorMaterial.Water; options = new SCNPhysicsTest { CollisionBitMask = (nuint)(int)Bitmask.Collision, SearchMode = SCNPhysicsSearchMode.Closest }; results = GameView.Scene.PhysicsWorld.RayTestWithSegmentFromPoint(p1, p0, options); result = results [0]; groundY = result.WorldCoordinates.Y; } else { Character.CurrentFloorMaterial = FloorMaterial.Rock; } } // var nextPosition = Character.Node.Position; // const double threshold = 1e-5; // // if (groundY < nextPosition.Y - threshold) { // // approximation of acceleration for a delta time // accelerationY += (float)(deltaTime * GravityAcceleration); // if (groundY < nextPosition.Y - 0.2) // Character.CurrentFloorMaterial = FloorMaterial.Air; // } else { // accelerationY = 0; // } // // nextPosition.Y -= accelerationY; // // // reset acceleration if we touch the ground // if (groundY > nextPosition.Y) { // accelerationY = 0; // nextPosition.Y = groundY; // } // Flames are static physics bodies, but they are moved by an action - So we need to tell the physics engine that the transforms did change. foreach (SCNNode flame in flames) { flame.PhysicsBody.ResetTransform(); } // Adjust the volume of the enemy based on the distance with the character. float distanceToClosestEnemy = float.MaxValue; SCNVector3 pos3 = Character.Node.Position; foreach (SCNNode enemy in enemies) { // distance to enemy SCNMatrix4 enemyMat = enemy.WorldTransform; var enemyPosition = new SCNVector3(enemyMat.M41, enemyMat.M42, enemyMat.M43); float distance = SCNVector3.Subtract(pos3, enemyPosition).Length; distanceToClosestEnemy = Math.Min(distanceToClosestEnemy, distance); } // Adjust sounds volumes based on distance with the enemy. if (!gameIsComplete) { double fireVolume = 0.3 * Math.Max(0.0, Math.Min(1.0, 1.0 - (distanceToClosestEnemy - 1.2) / 1.6)); var mixerNode = flameThrowerSound.AudioNode as AVAudioMixerNode; if (mixerNode != null) { mixerNode.Volume = (float)fireVolume; } } }