void CollectFlower(SCNNode node) { if (node.ParentNode == null) { return; } SCNNode soundEmitter = SCNNode.Create(); soundEmitter.Position = node.Position; node.ParentNode.AddChildNode(soundEmitter); soundEmitter.RunAction(SCNAction.Sequence(new [] { SCNAction.PlayAudioSource(collectFlowerSound, true), SCNAction.RemoveFromParentNode() })); node.RemoveFromParentNode(); // Check if game is complete. bool gameComplete = GameView.DidCollectAFlower(); // Edit some particles. SCNMatrix4 particlePosition = soundEmitter.WorldTransform; particlePosition.M42 += 0.1f; GameView.Scene.AddParticleSystem(collectParticles, particlePosition); if (gameComplete) { ShowEndScreen(); } }
public void PlayFootStep() { if (CurrentFloorMaterial == FloorMaterial.Air) { return; // We are in the air, no sound to play } int stepSoundIndex = Math.Min(StepsSoundCount - 1, new Random().Next(0, 32767) * StepsSoundCount); Node.RunAction(SCNAction.PlayAudioSource(steps[stepSoundIndex, (int)CurrentFloorMaterial], false)); }
private void PlayFootStep() { if (this.groundNode != null && this.IsWalking) { // We are in the air, no sound to play. // Play a random step sound. int randSnd = (new Random().Next(0, 32767) * Character.StepsCount); var stepSoundIndex = Math.Min(Character.StepsCount - 1, randSnd); characterNode.RunAction(SCNAction.PlayAudioSource(this.steps[stepSoundIndex], false)); } }
public void DidHitEnemy() { this.model.RunAction(SCNAction.Group(new SCNAction[] { SCNAction.PlayAudioSource(this.hitEnemySound, false), SCNAction.Sequence(new SCNAction[] { SCNAction.Wait(0.5), SCNAction.PlayAudioSource(explodeEnemySound, false) }) })); }
public void WasTouchedByEnemy() { var time = DateTime.UtcNow.Ticks; if (time > this.lastHitTime + 1d) { this.lastHitTime = time; this.model.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.PlayAudioSource(this.hitSound, false), SCNAction.RepeatAction(SCNAction.Sequence(new SCNAction[] { SCNAction.FadeOpacityTo(0.01f, 0.1f), SCNAction.FadeOpacityTo(1f, 0.1f) }), 4) })); } }
void CollectPearl(SCNNode node) { if (node.ParentNode == null) { return; } SCNNode soundEmitter = SCNNode.Create(); soundEmitter.Position = node.Position; node.ParentNode.AddChildNode(soundEmitter); soundEmitter.RunAction(SCNAction.Sequence(new [] { SCNAction.PlayAudioSource(collectPearlSound, true), SCNAction.RemoveFromParentNode() })); node.RemoveFromParentNode(); GameView.DidCollectAPearl(); }
void WasHit() { if (isInvincible) { return; } isInvincible = true; Character.Hit(); Character.Node.RunAction(SCNAction.Sequence(new [] { SCNAction.PlayAudioSource(hitSound, false), SCNAction.RepeatAction(SCNAction.Sequence(new [] { SCNAction.FadeOpacityTo(.01f, 0.1), SCNAction.FadeOpacityTo(1f, 0.1) }), 7), SCNAction.Run(_ => isInvincible = false) })); }
public void Update(double time, ISCNSceneRenderer renderer) { this.frameCounter += 1; if (this.shouldResetCharacterPosition) { this.shouldResetCharacterPosition = false; this.ResetCharacterPosition(); } else { var characterVelocity = SCNVector3.Zero; // setup var groundMove = SCNVector3.Zero; // did the ground moved? if (this.groundNode != null) { var groundPosition = groundNode.WorldPosition; groundMove = groundPosition - this.groundNodeLastPosition; } characterVelocity = new SCNVector3(groundMove.X, 0, groundMove.Z); var direction = this.CharacterDirection(renderer.PointOfView); if (this.previousUpdateTime == 0d) { this.previousUpdateTime = time; } var deltaTime = time - previousUpdateTime; var characterSpeed = (nfloat)deltaTime * Character.SpeedFactor * this.WalkSpeed; var virtualFrameCount = (int)(deltaTime / (1d / 60d)); this.previousUpdateTime = time; // move if (!direction.AllZero()) { characterVelocity = direction * (float)characterSpeed; var runModifier = 1f; #if __OSX__ // TODO: UI thread exception //if (AppKit.NSEvent.CurrentModifierFlags.HasFlag(AppKit.NSEventModifierMask.ShiftKeyMask)) //{ // runModifier = 2f; //} #endif this.WalkSpeed = (nfloat)(runModifier * direction.Length); // move character this.DirectionAngle = (nfloat)Math.Atan2(direction.X, direction.Z); this.IsWalking = true; } else { this.IsWalking = false; } // put the character on the ground var up = new SCNVector3(0f, 1f, 0f); var wPosition = this.characterNode.WorldPosition; // gravity this.downwardAcceleration -= Character.Gravity; wPosition.Y += downwardAcceleration; var HIT_RANGE = 0.2f; var p0 = wPosition; var p1 = wPosition; p0.Y = wPosition.Y + up.Y * HIT_RANGE; p1.Y = wPosition.Y - up.Y * HIT_RANGE; var options = new NSMutableDictionary <NSString, NSObject>() { { SCNHitTest.BackFaceCullingKey, NSObject.FromObject(false) }, { SCNHitTest.OptionCategoryBitMaskKey, NSNumber.FromFloat(Character.CollisionMeshBitMask) }, { SCNHitTest.IgnoreHiddenNodesKey, NSObject.FromObject(false) } }; var hitFrom = new SCNVector3(p0); var hitTo = new SCNVector3(p1); var hitResult = renderer.Scene.RootNode.HitTest(hitFrom, hitTo, options).FirstOrDefault(); var wasTouchingTheGroup = this.groundNode != null; this.groundNode = null; var touchesTheGround = false; var wasBurning = this.IsBurning; var hit = hitResult; if (hit != null) { var ground = new SCNVector3(hit.WorldCoordinates); if (wPosition.Y <= ground.Y + Character.CollisionMargin) { wPosition.Y = ground.Y + Character.CollisionMargin; if (this.downwardAcceleration < 0f) { this.downwardAcceleration = 0f; } this.groundNode = hit.Node; touchesTheGround = true; //touching lava? this.IsBurning = this.groundNode?.Name == "COLL_lava"; } } else { if (wPosition.Y < Character.MinAltitude) { wPosition.Y = Character.MinAltitude; //reset this.QueueResetCharacterPosition(); } } this.groundNodeLastPosition = this.groundNode != null ? this.groundNode.WorldPosition : SCNVector3.Zero; //jump ------------------------------------------------------------- if (this.jumpState == 0) { if (this.IsJump && touchesTheGround) { this.downwardAcceleration += Character.JumpImpulse; this.jumpState = 1; this.model.GetAnimationPlayer(new NSString("jump"))?.Play(); } } else { if (this.jumpState == 1 && !this.IsJump) { this.jumpState = 2; } if (this.downwardAcceleration > 0f) { for (int i = 0; i < virtualFrameCount; i++) { downwardAcceleration *= this.jumpState == 1 ? 0.99f : 0.2f; } } if (touchesTheGround) { if (!wasTouchingTheGroup) { this.model.GetAnimationPlayer(new NSString("jump"))?.StopWithBlendOutDuration(0.1); // trigger jump particles if not touching lava if (this.IsBurning) { this.model.FindChildNode("dustEmitter", true)?.AddParticleSystem(this.jumpDustParticle); } else { // jump in lava again if (wasBurning) { this.characterNode.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.PlayAudioSource(this.catchFireSound, false), SCNAction.PlayAudioSource(this.ouchSound, false) })); } } } if (!this.IsJump) { this.jumpState = 0; } } } if (touchesTheGround && !wasTouchingTheGroup && !this.IsBurning && this.lastStepFrame < this.frameCounter - 10) { // sound this.lastStepFrame = frameCounter; this.characterNode.RunAction(SCNAction.PlayAudioSource(this.steps[0], false)); } if (wPosition.Y < this.characterNode.Position.Y) { wPosition.Y = this.characterNode.Position.Y; } //------------------------------------------------------------------ // progressively update the elevation node when we touch the ground if (touchesTheGround) { this.targetAltitude = (float)wPosition.Y; } this.BaseAltitude *= 0.95f; this.BaseAltitude += this.targetAltitude * 0.05f; characterVelocity.Y += this.downwardAcceleration; if (characterVelocity.LengthSquared > 10E-4 * 10E-4) { var startPosition = this.characterNode.PresentationNode.WorldPosition + this.collisionShapeOffsetFromModel; this.SlideInWorld(startPosition, characterVelocity); } } }
private void PlayAttackSound() { this.characterNode.RunAction(SCNAction.PlayAudioSource(this.attackSound, false)); }
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; } } }