Merge pull request #236 from xamarin/brumbaug-fox2
Moving ios11/Fox2 samples to mac-ios-samples repo
|
@ -1,123 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
using CoreAnimation;
|
||||
using Fox2.Extensions;
|
||||
using GameplayKit;
|
||||
using SceneKit;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// This class is used as a base class for all game components.
|
||||
/// </summary>
|
||||
public class BaseComponent : GKComponent
|
||||
{
|
||||
public static float EnemyAltitude => -0.46f;
|
||||
|
||||
public GKAgent2D Agent { get; } = new GKAgent2D();
|
||||
|
||||
public bool IsAutoMoveNode { get; set; } = true;
|
||||
|
||||
public virtual bool IsDead()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void PositionAgentFromNode()
|
||||
{
|
||||
var nodeComponent = base.Entity.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
this.Agent.SetTransform(nodeComponent.Node.Transform);
|
||||
}
|
||||
|
||||
public void PositionNodeFromAgent()
|
||||
{
|
||||
var nodeComponent = base.Entity.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
nodeComponent.Node.Transform = this.Agent.GetTransform();
|
||||
}
|
||||
|
||||
public void ConstrainPosition()
|
||||
{
|
||||
var position = this.Agent.Position;
|
||||
if (position.X > 2f)
|
||||
{
|
||||
position.X = 2f;
|
||||
}
|
||||
else if (position.X < -2f)
|
||||
{
|
||||
position.X = -2f;
|
||||
}
|
||||
|
||||
if (position.Y > 12.5f)
|
||||
{
|
||||
position.Y = 12.5f;
|
||||
}
|
||||
else if (position.Y < 8.5f)
|
||||
{
|
||||
position.Y = 8.5f;
|
||||
}
|
||||
|
||||
this.Agent.Position = position;
|
||||
}
|
||||
|
||||
public override void Update(double deltaTimeInSeconds)
|
||||
{
|
||||
if (!this.IsDead())
|
||||
{
|
||||
this.Agent.Update(deltaTimeInSeconds);
|
||||
this.ConstrainPosition();
|
||||
|
||||
if (this.IsAutoMoveNode)
|
||||
{
|
||||
this.PositionNodeFromAgent();
|
||||
}
|
||||
|
||||
base.Update(deltaTimeInSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
protected void PerformEnemyDieWithExplosion(SCNNode enemy, SCNVector3 direction)
|
||||
{
|
||||
var explositionScene = SCNScene.FromFile("art.scnassets/enemy/enemy_explosion.scn");
|
||||
if (explositionScene != null)
|
||||
{
|
||||
SCNTransaction.Begin();
|
||||
SCNTransaction.AnimationDuration = 0.4f;
|
||||
SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
|
||||
|
||||
SCNTransaction.SetCompletionBlock(() =>
|
||||
{
|
||||
explositionScene.RootNode.EnumerateHierarchy((SCNNode node, out bool stop) =>
|
||||
{
|
||||
stop = false;
|
||||
if (node.ParticleSystems != null)
|
||||
{
|
||||
foreach (var particle in node.ParticleSystems)
|
||||
{
|
||||
enemy.AddParticleSystem(particle);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Hide
|
||||
if (enemy.ChildNodes.Length > 0)
|
||||
{
|
||||
enemy.ChildNodes[0].Opacity = 0f;
|
||||
}
|
||||
});
|
||||
|
||||
direction.Y = 0;
|
||||
enemy.RemoveAllAnimations();
|
||||
enemy.EulerAngles = new SCNVector3(enemy.EulerAngles.X, enemy.EulerAngles.X + (float)Math.PI * 4.0f, enemy.EulerAngles.Z);
|
||||
|
||||
enemy.WorldPosition += SCNVector3.Normalize(direction) * 1.5f;
|
||||
this.PositionAgentFromNode();
|
||||
|
||||
SCNTransaction.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Missing enemy_explosion.scn");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
[System.Flags]
|
||||
public enum Bitmask : uint
|
||||
{
|
||||
Character = 1 << 0,// the main character
|
||||
|
||||
Collision = 1 << 1, // the ground and walls
|
||||
|
||||
Enemy = 1 << 2,// the enemies
|
||||
|
||||
Trigger = 1 << 3, // the box that triggers camera changes and other actions
|
||||
|
||||
Collectable = 1 << 4, // the collectables (gems and key)
|
||||
}
|
||||
}
|
|
@ -1,848 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
using CoreFoundation;
|
||||
using Foundation;
|
||||
using Fox2.Enums;
|
||||
using Fox2.Extensions;
|
||||
using OpenTK;
|
||||
using SceneKit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// This class manages the main character, including its animations, sounds and direction.
|
||||
/// </summary>
|
||||
public class Character : NSObject
|
||||
{
|
||||
private static SCNVector3 InitialPosition = new SCNVector3(0.1f, -0.2f, 0f);
|
||||
private static nfloat SpeedFactor = 2f;
|
||||
private static int StepsCount = 10;
|
||||
|
||||
// some constants
|
||||
private static float Gravity = 0.004f;
|
||||
private static float JumpImpulse = 0.1f;
|
||||
private static float MinAltitude = -10f;
|
||||
private static bool IsEnableFootStepSound = true;
|
||||
private static float CollisionMargin = 0.04f;
|
||||
private static SCNVector3 ModelOffset = new SCNVector3(0f, -Character.CollisionMargin, 0f);
|
||||
private static float CollisionMeshBitMask = 8f;
|
||||
|
||||
// Character handle
|
||||
private SCNNode characterOrientation; // the node to rotate to orient the character
|
||||
private SCNNode characterNode; // top level node
|
||||
private SCNNode model; // the model loaded from the character file
|
||||
|
||||
// Physics
|
||||
private SCNVector3 collisionShapeOffsetFromModel = SCNVector3.Zero;
|
||||
private SCNPhysicsShape characterCollisionShape;
|
||||
private float downwardAcceleration = 0f;
|
||||
|
||||
// Jump
|
||||
private SCNVector3 groundNodeLastPosition = SCNVector3.Zero;
|
||||
private SCNNode groundNode;
|
||||
private int jumpState = 0;
|
||||
|
||||
private float targetAltitude = 0f;
|
||||
|
||||
// void playing the step sound too often
|
||||
private int lastStepFrame = 0;
|
||||
private int frameCounter = 0;
|
||||
|
||||
// Direction
|
||||
private Vector2 controllerDirection = Vector2.Zero;
|
||||
private double previousUpdateTime = 0d;
|
||||
|
||||
// states
|
||||
private long lastHitTime = 0L;
|
||||
private int attackCount = 0;
|
||||
|
||||
private bool shouldResetCharacterPosition;
|
||||
|
||||
// Particle systems
|
||||
private SCNParticleSystem jumpDustParticle;
|
||||
private SCNParticleSystem fireEmitter;
|
||||
private SCNParticleSystem smokeEmitter;
|
||||
private SCNParticleSystem whiteSmokeEmitter;
|
||||
private SCNParticleSystem spinParticle;
|
||||
private SCNParticleSystem spinCircleParticle;
|
||||
|
||||
private SCNNode spinParticleAttach;
|
||||
|
||||
private nfloat whiteSmokeEmitterBirthRate = 0f;
|
||||
private nfloat smokeEmitterBirthRate = 0f;
|
||||
private nfloat fireEmitterBirthRate = 0f;
|
||||
|
||||
// Sound effects
|
||||
private SCNAudioSource aahSound;
|
||||
private SCNAudioSource hitSound;
|
||||
private SCNAudioSource ouchSound;
|
||||
private SCNAudioSource jumpSound;
|
||||
private SCNAudioSource attackSound;
|
||||
private SCNAudioSource hitEnemySound;
|
||||
private SCNAudioSource catchFireSound;
|
||||
private SCNAudioSource explodeEnemySound;
|
||||
private readonly List<SCNAudioSource> steps = new List<SCNAudioSource>(Character.StepsCount);
|
||||
|
||||
public Character(SCNScene scene) : base()
|
||||
{
|
||||
this.LoadCharacter();
|
||||
this.LoadParticles();
|
||||
this.LoadSounds();
|
||||
this.LoadAnimations();
|
||||
}
|
||||
|
||||
public SCNNode Node => this.characterNode;
|
||||
|
||||
public bool IsJump { get; set; }
|
||||
|
||||
public float BaseAltitude { get; set; } = 0f;
|
||||
|
||||
public SCNPhysicsWorld PhysicsWorld { get; set; }
|
||||
|
||||
public Vector2 Direction { get; set; } = new Vector2();
|
||||
|
||||
#region Initialization
|
||||
|
||||
private void LoadCharacter()
|
||||
{
|
||||
// Load character from external file
|
||||
var scene = SCNScene.FromFile("art.scnassets/character/max.scn");
|
||||
this.model = scene.RootNode.FindChildNode("Max_rootNode", true);
|
||||
this.model.Position = Character.ModelOffset;
|
||||
|
||||
/* setup character hierarchy
|
||||
character
|
||||
|_orientationNode
|
||||
|_model
|
||||
*/
|
||||
|
||||
this.characterNode = new SCNNode { Name = "character", Position = Character.InitialPosition };
|
||||
|
||||
this.characterOrientation = new SCNNode();
|
||||
this.characterNode.AddChildNode(this.characterOrientation);
|
||||
this.characterOrientation.AddChildNode(this.model);
|
||||
|
||||
var collider = this.model.FindChildNode("collider", true);
|
||||
if (collider?.PhysicsBody != null)
|
||||
{
|
||||
collider.PhysicsBody.CollisionBitMask = (int)(Bitmask.Enemy | Bitmask.Trigger | Bitmask.Collectable);
|
||||
}
|
||||
|
||||
// Setup collision shape
|
||||
var min = new SCNVector3();
|
||||
var max = new SCNVector3();
|
||||
this.model.GetBoundingBox(ref min, ref max);
|
||||
nfloat collisionCapsuleRadius = (max.X - min.X) * 0.4f;
|
||||
nfloat collisionCapsuleHeight = max.Y - min.Y;
|
||||
|
||||
var collisionGeometry = SCNCapsule.Create(collisionCapsuleRadius, collisionCapsuleHeight);
|
||||
this.characterCollisionShape = SCNPhysicsShape.Create(collisionGeometry,
|
||||
new NSMutableDictionary() { { SCNPhysicsShapeOptionsKeys.CollisionMargin, NSNumber.FromFloat(Character.CollisionMargin) } });
|
||||
this.collisionShapeOffsetFromModel = new SCNVector3(0f, (float)collisionCapsuleHeight * 0.51f, 0f);
|
||||
}
|
||||
|
||||
private void LoadParticles()
|
||||
{
|
||||
var particleScene = SCNScene.FromFile("art.scnassets/character/jump_dust.scn");
|
||||
var particleNode = particleScene.RootNode.FindChildNode("particle", true);
|
||||
this.jumpDustParticle = particleNode.ParticleSystems.FirstOrDefault();
|
||||
|
||||
particleScene = SCNScene.FromFile("art.scnassets/particles/burn.scn");
|
||||
var burnParticleNode = particleScene.RootNode.FindChildNode("particles", true);
|
||||
|
||||
var particleEmitter = new SCNNode();
|
||||
this.characterOrientation.AddChildNode(particleEmitter);
|
||||
|
||||
this.fireEmitter = burnParticleNode.FindChildNode("fire", true).ParticleSystems[0];
|
||||
this.fireEmitterBirthRate = fireEmitter.BirthRate;
|
||||
this.fireEmitter.BirthRate = 0f;
|
||||
|
||||
this.smokeEmitter = burnParticleNode.FindChildNode("smoke", true).ParticleSystems[0];
|
||||
this.smokeEmitterBirthRate = smokeEmitter.BirthRate;
|
||||
this.smokeEmitter.BirthRate = 0f;
|
||||
|
||||
this.whiteSmokeEmitter = burnParticleNode.FindChildNode("whiteSmoke", true).ParticleSystems[0];
|
||||
this.whiteSmokeEmitterBirthRate = whiteSmokeEmitter.BirthRate;
|
||||
this.whiteSmokeEmitter.BirthRate = 0f;
|
||||
|
||||
particleScene = SCNScene.FromFile("art.scnassets/particles/particles_spin.scn");
|
||||
this.spinParticle = (particleScene.RootNode.FindChildNode("particles_spin", true)?.ParticleSystems?.FirstOrDefault());
|
||||
this.spinCircleParticle = (particleScene.RootNode.FindChildNode("particles_spin_circle", true)?.ParticleSystems?.FirstOrDefault());
|
||||
|
||||
particleEmitter.Position = new SCNVector3(0f, 0.05f, 0f);
|
||||
particleEmitter.AddParticleSystem(this.fireEmitter);
|
||||
particleEmitter.AddParticleSystem(this.smokeEmitter);
|
||||
particleEmitter.AddParticleSystem(this.whiteSmokeEmitter);
|
||||
|
||||
this.spinParticleAttach = this.model.FindChildNode("particles_spin_circle", true);
|
||||
}
|
||||
|
||||
private void LoadSounds()
|
||||
{
|
||||
this.aahSound = SCNAudioSource.FromFile("audio/aah_extinction.mp3");
|
||||
this.aahSound.Volume = 1f;
|
||||
this.aahSound.Positional = false;
|
||||
this.aahSound.Load();
|
||||
|
||||
this.catchFireSound = SCNAudioSource.FromFile("audio/panda_catch_fire.mp3");
|
||||
this.catchFireSound.Volume = 5f;
|
||||
this.catchFireSound.Positional = false;
|
||||
this.catchFireSound.Load();
|
||||
|
||||
this.ouchSound = SCNAudioSource.FromFile("audio/ouch_firehit.mp3");
|
||||
this.ouchSound.Volume = 2f;
|
||||
this.ouchSound.Positional = false;
|
||||
this.ouchSound.Load();
|
||||
|
||||
this.hitSound = SCNAudioSource.FromFile("audio/hit.mp3");
|
||||
this.hitSound.Volume = 2f;
|
||||
this.hitSound.Positional = false;
|
||||
this.hitSound.Load();
|
||||
|
||||
this.hitEnemySound = SCNAudioSource.FromFile("audio/Explosion1.m4a");
|
||||
this.hitEnemySound.Volume = 2f;
|
||||
this.hitEnemySound.Positional = false;
|
||||
this.hitEnemySound.Load();
|
||||
|
||||
this.explodeEnemySound = SCNAudioSource.FromFile("audio/Explosion2.m4a");
|
||||
this.explodeEnemySound.Volume = 2f;
|
||||
this.explodeEnemySound.Positional = false;
|
||||
this.explodeEnemySound.Load();
|
||||
|
||||
this.jumpSound = SCNAudioSource.FromFile("audio/jump.m4a");
|
||||
this.jumpSound.Volume = 0.2f;
|
||||
this.jumpSound.Positional = false;
|
||||
this.jumpSound.Load();
|
||||
|
||||
this.attackSound = SCNAudioSource.FromFile("audio/attack.mp3");
|
||||
this.attackSound.Volume = 1.0f;
|
||||
this.attackSound.Positional = false;
|
||||
this.attackSound.Load();
|
||||
|
||||
for (var i = 0; i < Character.StepsCount; i++)
|
||||
{
|
||||
var audioSource = SCNAudioSource.FromFile($"audio/Step_rock_0{i}.mp3");
|
||||
audioSource.Volume = 0.5f;
|
||||
audioSource.Positional = false;
|
||||
audioSource.Load();
|
||||
|
||||
this.steps.Add(audioSource);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadAnimations()
|
||||
{
|
||||
var idleAnimation = Character.LoadAnimation("art.scnassets/character/max_idle.scn");
|
||||
this.model.AddAnimation(idleAnimation, new NSString("idle"));
|
||||
idleAnimation.Play();
|
||||
|
||||
var walkAnimation = Character.LoadAnimation("art.scnassets/character/max_walk.scn");
|
||||
walkAnimation.Speed = Character.SpeedFactor;
|
||||
walkAnimation.Stop();
|
||||
|
||||
if (Character.IsEnableFootStepSound)
|
||||
{
|
||||
walkAnimation.Animation.AnimationEvents = new SCNAnimationEvent[]
|
||||
{
|
||||
SCNAnimationEvent.Create(0.1f, (animation, animatedObject, playingBackward) => { this.PlayFootStep(); }),
|
||||
SCNAnimationEvent.Create(0.6f, (animation, animatedObject, playingBackward) => { this.PlayFootStep(); })
|
||||
};
|
||||
}
|
||||
|
||||
this.model.AddAnimation(walkAnimation, new NSString("walk"));
|
||||
|
||||
var jumpAnimation = Character.LoadAnimation("art.scnassets/character/max_jump.scn");
|
||||
jumpAnimation.Animation.RemovedOnCompletion = false;
|
||||
jumpAnimation.Stop();
|
||||
jumpAnimation.Animation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0f, (animation, animatedObject, playingBackward) => { this.PlayJumpSound(); }) };
|
||||
this.model.AddAnimation(jumpAnimation, new NSString("jump"));
|
||||
|
||||
var spinAnimation = Character.LoadAnimation("art.scnassets/character/max_spin.scn");
|
||||
spinAnimation.Animation.RemovedOnCompletion = false;
|
||||
spinAnimation.Speed = 1.5f;
|
||||
spinAnimation.Stop();
|
||||
spinAnimation.Animation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0f, (animation, animatedObject, playingBackward) => { this.PlayAttackSound(); }) };
|
||||
this.model.AddAnimation(spinAnimation, new NSString("spin"));
|
||||
}
|
||||
|
||||
public void QueueResetCharacterPosition()
|
||||
{
|
||||
this.shouldResetCharacterPosition = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Audio
|
||||
|
||||
private bool isBurning;
|
||||
|
||||
public bool IsBurning
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isBurning;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (this.isBurning != value)
|
||||
{
|
||||
this.isBurning = value;
|
||||
|
||||
//walk faster when burning
|
||||
var oldSpeed = this.WalkSpeed;
|
||||
this.WalkSpeed = oldSpeed;
|
||||
|
||||
if (this.isBurning)
|
||||
{
|
||||
this.model.RunAction(SCNAction.Sequence(new SCNAction[]
|
||||
{
|
||||
SCNAction.PlayAudioSource(catchFireSound, false),
|
||||
SCNAction.PlayAudioSource(ouchSound, false),
|
||||
SCNAction.RepeatActionForever(SCNAction.Sequence(new SCNAction[]
|
||||
{
|
||||
SCNAction.FadeOpacityTo(0.01f, 0.1),
|
||||
SCNAction.FadeOpacityTo(1f, 0.1)
|
||||
}))
|
||||
}));
|
||||
|
||||
this.whiteSmokeEmitter.BirthRate = 0f;
|
||||
this.fireEmitter.BirthRate = this.fireEmitterBirthRate;
|
||||
this.smokeEmitter.BirthRate = this.smokeEmitterBirthRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.model.RemoveAllAudioPlayers();
|
||||
this.model.RemoveAllActions();
|
||||
this.model.Opacity = 1f;
|
||||
this.model.RunAction(SCNAction.PlayAudioSource(aahSound, false));
|
||||
|
||||
SCNTransaction.Begin();
|
||||
SCNTransaction.AnimationDuration = 0f;
|
||||
this.whiteSmokeEmitter.BirthRate = this.whiteSmokeEmitterBirthRate;
|
||||
this.fireEmitter.BirthRate = 0f;
|
||||
this.smokeEmitter.BirthRate = 0f;
|
||||
SCNTransaction.Commit();
|
||||
|
||||
SCNTransaction.Begin();
|
||||
SCNTransaction.AnimationDuration = 5f;
|
||||
whiteSmokeEmitter.BirthRate = 0f;
|
||||
SCNTransaction.Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayJumpSound()
|
||||
{
|
||||
this.characterNode.RunAction(SCNAction.PlayAudioSource(this.jumpSound, false));
|
||||
}
|
||||
|
||||
private void PlayAttackSound()
|
||||
{
|
||||
this.characterNode.RunAction(SCNAction.PlayAudioSource(this.attackSound, false));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controlling the character
|
||||
|
||||
private nfloat directionAngle = 0f;
|
||||
|
||||
public nfloat DirectionAngle
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.directionAngle;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.directionAngle = value;
|
||||
this.characterOrientation.RunAction(SCNAction.RotateTo(0f, this.directionAngle, 0f, 0.1f, true));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Animating the character
|
||||
|
||||
private nfloat walkSpeed = 1f;
|
||||
|
||||
private bool isWalking;
|
||||
|
||||
public bool IsAttacking => this.attackCount > 0;
|
||||
|
||||
public bool IsWalking
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isWalking;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (this.isWalking != value)
|
||||
{
|
||||
this.isWalking = value;
|
||||
// Update node animation.
|
||||
var player = this.model.GetAnimationPlayer(new NSString("walk"));
|
||||
if (this.isWalking)
|
||||
{
|
||||
player?.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
player?.StopWithBlendOutDuration(0.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public nfloat WalkSpeed
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.walkSpeed;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.walkSpeed = value;
|
||||
var burningFactor = this.IsBurning ? 2f : 1f;
|
||||
|
||||
var player = this.model.GetAnimationPlayer(new NSString("walk"));
|
||||
if (player != null)
|
||||
{
|
||||
player.Speed = Character.SpeedFactor * this.walkSpeed * burningFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Attack()
|
||||
{
|
||||
this.attackCount += 1;
|
||||
this.model.GetAnimationPlayer(new NSString("spin"))?.Play();
|
||||
DispatchQueue.MainQueue.DispatchAfter(new DispatchTime(DispatchTime.Now, 5), () =>
|
||||
{
|
||||
this.attackCount -= 1;
|
||||
});
|
||||
|
||||
this.spinParticleAttach.AddParticleSystem(this.spinCircleParticle);
|
||||
}
|
||||
|
||||
private SCNVector3 CharacterDirection(SCNNode pointOfView)
|
||||
{
|
||||
SCNVector3 result;
|
||||
|
||||
var controllerDir = this.Direction;
|
||||
if (controllerDir.AllZero())
|
||||
{
|
||||
result = SCNVector3.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
var directionWorld = SCNVector3.Zero;
|
||||
if (pointOfView != null)
|
||||
{
|
||||
var p1 = pointOfView.PresentationNode.ConvertPositionToNode(new SCNVector3(controllerDir.X, 0f, controllerDir.Y), null);
|
||||
var p0 = pointOfView.PresentationNode.ConvertPositionToNode(SCNVector3.Zero, null);
|
||||
directionWorld = p1 - p0;
|
||||
directionWorld.Y = 0f;
|
||||
|
||||
if (directionWorld != SCNVector3.Zero)
|
||||
{
|
||||
var minControllerSpeedFactor = 0.2f;
|
||||
var maxControllerSpeedFactor = 1f;
|
||||
var speed = controllerDir.Length * (maxControllerSpeedFactor - minControllerSpeedFactor) + minControllerSpeedFactor;
|
||||
directionWorld = speed * SCNVector3.Normalize(directionWorld);
|
||||
}
|
||||
}
|
||||
|
||||
result = directionWorld;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void ResetCharacterPosition()
|
||||
{
|
||||
this.characterNode.Position = Character.InitialPosition;
|
||||
this.downwardAcceleration = 0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region enemy
|
||||
|
||||
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)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region utils
|
||||
|
||||
public static SCNAnimationPlayer LoadAnimation(string sceneName)
|
||||
{
|
||||
var scene = SCNScene.FromFile(sceneName);
|
||||
|
||||
SCNAnimationPlayer animationPlayer = null;
|
||||
|
||||
// find top level animation
|
||||
scene.RootNode.EnumerateChildNodes((SCNNode child, out bool stop) =>
|
||||
{
|
||||
var keys = child.GetAnimationKeys();
|
||||
if (keys.Any())
|
||||
{
|
||||
animationPlayer = child.GetAnimationPlayer(keys[0]);
|
||||
stop = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
stop = false;
|
||||
}
|
||||
});
|
||||
|
||||
return animationPlayer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region physics contact
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private Tuple<SCNVector3, SCNVector3> HandleSlidingAtContact(SCNPhysicsContact closestContact, SCNVector3 start, SCNVector3 velocity)
|
||||
{
|
||||
var originalDistance = velocity.Length;
|
||||
|
||||
var colliderPositionAtContact = start + (float)closestContact.SweepTestFraction * velocity;
|
||||
|
||||
// Compute the sliding plane.
|
||||
var slidePlaneNormal = new SCNVector3(closestContact.ContactNormal);
|
||||
var slidePlaneOrigin = new SCNVector3(closestContact.ContactPoint);
|
||||
var centerOffset = slidePlaneOrigin - colliderPositionAtContact;
|
||||
|
||||
// Compute destination relative to the point of contact.
|
||||
var destinationPoint = slidePlaneOrigin + velocity;
|
||||
|
||||
// We now project the destination point onto the sliding plane.
|
||||
var distPlane = SCNVector3.Dot(slidePlaneOrigin, slidePlaneNormal);
|
||||
|
||||
// Project on plane.
|
||||
var t = Utils.PlaneIntersect(slidePlaneNormal, distPlane, destinationPoint, slidePlaneNormal);
|
||||
|
||||
var normalizedVelocity = velocity * (1f / originalDistance);
|
||||
var angle = SCNVector3.Dot(slidePlaneNormal, normalizedVelocity);
|
||||
|
||||
var frictionCoeff = 0.3f;
|
||||
if (Math.Abs(angle) < 0.9f)
|
||||
{
|
||||
t += (float)10E-3;
|
||||
frictionCoeff = 1.0f;
|
||||
}
|
||||
|
||||
var newDestinationPoint = (destinationPoint + t * slidePlaneNormal) - centerOffset;
|
||||
// Advance start position to nearest point without collision.
|
||||
var computedVelocity = frictionCoeff * (float)(1f - closestContact.SweepTestFraction) * originalDistance * SCNVector3.Normalize(newDestinationPoint - start);
|
||||
|
||||
return new Tuple<SCNVector3, SCNVector3>(computedVelocity, colliderPositionAtContact);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
using Fox2.Enums;
|
||||
using GameplayKit;
|
||||
using OpenTK;
|
||||
using SceneKit;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// This class implements the chasing behavior.
|
||||
/// </summary>
|
||||
public class ChaserComponent : BaseComponent
|
||||
{
|
||||
private float maxAcceleration = 8f;
|
||||
private float hitDistance = 0.5f;
|
||||
private float chaseDistance = 3f;
|
||||
private float wanderSpeed = 1f;
|
||||
private float chaseSpeed = 9f;
|
||||
private float mass = 0.3f;
|
||||
|
||||
private ChaserState state = ChaserState.Chase;
|
||||
private float speed = 9f;
|
||||
|
||||
private GKGoal chaseGoal;
|
||||
private GKGoal wanderGoal;
|
||||
private GKGoal centerGoal;
|
||||
|
||||
private GKBehavior behavior;
|
||||
|
||||
private PlayerComponent player;
|
||||
|
||||
public PlayerComponent Player
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.player;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.player = value;
|
||||
|
||||
this.Agent.Mass = this.mass;
|
||||
this.Agent.MaxAcceleration = this.maxAcceleration;
|
||||
|
||||
this.chaseGoal = GKGoal.GetGoalToSeekAgent(this.player?.Agent);
|
||||
this.wanderGoal = GKGoal.GetGoalToWander(this.wanderSpeed);
|
||||
|
||||
var center = new Vector2[]
|
||||
{
|
||||
new Vector2(-1f, 9f),
|
||||
new Vector2(1f, 9f),
|
||||
new Vector2(1f, 11f),
|
||||
new Vector2(-1f, 11f)
|
||||
};
|
||||
|
||||
var path = new GKPath(center, 0.5f, true);
|
||||
this.centerGoal = GKGoal.GetGoalToStayOnPath(path, 1d);
|
||||
this.behavior = GKBehavior.FromGoals(new GKGoal[] { this.chaseGoal, this.wanderGoal, this.centerGoal });
|
||||
this.Agent.Behavior = behavior;
|
||||
this.StartWandering();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsDead()
|
||||
{
|
||||
return this.state == ChaserState.Dead;
|
||||
}
|
||||
|
||||
private void StartWandering()
|
||||
{
|
||||
if (this.behavior != null)
|
||||
{
|
||||
this.Agent.MaxSpeed = this.wanderSpeed;
|
||||
this.behavior.SetWeight(1f, this.wanderGoal);
|
||||
this.behavior.SetWeight(0f, this.chaseGoal);
|
||||
this.behavior.SetWeight(0.6f, this.centerGoal);
|
||||
this.state = ChaserState.Wander;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartChasing()
|
||||
{
|
||||
if (this.behavior != null)
|
||||
{
|
||||
this.Agent.MaxSpeed = this.speed;
|
||||
this.behavior.SetWeight(0f, this.wanderGoal);
|
||||
this.behavior.SetWeight(1f, this.chaseGoal);
|
||||
this.behavior.SetWeight(0.1f, this.centerGoal);
|
||||
this.state = ChaserState.Chase;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(double deltaTimeInSeconds)
|
||||
{
|
||||
if (this.state != ChaserState.Dead)
|
||||
{
|
||||
var character = this.player?.Character;
|
||||
var playerComponent = this.player?.Entity?.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
var nodeComponent = base.Entity?.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
if (character != null && playerComponent != null && nodeComponent != null)
|
||||
{
|
||||
var enemyNode = nodeComponent.Node;
|
||||
var playerNode = playerComponent.Node;
|
||||
|
||||
var distance = (enemyNode.WorldPosition - playerNode.WorldPosition).Length;
|
||||
// Chase if below chaseDistance from enemy, wander otherwise.
|
||||
switch (this.state)
|
||||
{
|
||||
case ChaserState.Wander:
|
||||
if (distance < this.chaseDistance)
|
||||
{
|
||||
this.StartChasing();
|
||||
}
|
||||
break;
|
||||
case ChaserState.Chase:
|
||||
if (distance > this.chaseDistance)
|
||||
{
|
||||
this.StartWandering();
|
||||
}
|
||||
break;
|
||||
case ChaserState.Dead:
|
||||
break;
|
||||
}
|
||||
|
||||
this.speed = Math.Min(this.chaseSpeed, (float)distance);
|
||||
|
||||
this.HandleEnemyResponse(character, enemyNode);
|
||||
|
||||
base.Update(deltaTimeInSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleEnemyResponse(Character character, SCNNode enemy)
|
||||
{
|
||||
var direction = enemy.WorldPosition - character.Node.WorldPosition;
|
||||
if (direction.Length < this.hitDistance)
|
||||
{
|
||||
if (character.IsAttacking)
|
||||
{
|
||||
this.state = ChaserState.Dead;
|
||||
|
||||
character.DidHitEnemy();
|
||||
|
||||
this.PerformEnemyDieWithExplosion(enemy, direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.WasTouchedByEnemy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
public enum AudioSourceKind
|
||||
{
|
||||
Collect = 1,
|
||||
CollectBig = 2,
|
||||
UnlockDoor = 3,
|
||||
HitEnemy = 4,
|
||||
TotalCount = 5,
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
[System.Flags]
|
||||
public enum Bitmask : uint
|
||||
{
|
||||
Character = 1 << 0,// the main character
|
||||
Collision = 1 << 1, // the ground and walls
|
||||
Enemy = 1 << 2,// the enemies
|
||||
Trigger = 1 << 3, // the box that triggers camera changes and other actions
|
||||
Collectable = 1 << 4, // the collectables (gems and key)
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
public enum ChaserState
|
||||
{
|
||||
Wander,
|
||||
Chase,
|
||||
Dead
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
public enum GroundType
|
||||
{
|
||||
Grass,
|
||||
Rock,
|
||||
Water,
|
||||
InTheAir,
|
||||
Count
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
public enum ParticleKind
|
||||
{
|
||||
Collect,
|
||||
CollectBig,
|
||||
KeyApparition,
|
||||
EnemyExplosion,
|
||||
UnlockDoor,
|
||||
TotalCount
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
namespace Fox2.Enums
|
||||
{
|
||||
public enum ScaredState
|
||||
{
|
||||
Wander,
|
||||
Flee,
|
||||
Dead
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
|
||||
namespace Fox2.Extensions
|
||||
{
|
||||
using GameplayKit;
|
||||
using OpenTK;
|
||||
using SceneKit;
|
||||
using System;
|
||||
|
||||
public static class GKAgent2DExtensions
|
||||
{
|
||||
public static SCNMatrix4 GetTransform(this GKAgent2D agent)
|
||||
{
|
||||
var quat = Quaternion.FromAxisAngle(new Vector3(0, 1, 0), -(agent.Rotation - ((float)Math.PI / 2f)));
|
||||
var transform = SCNMatrix4.Rotate(quat);
|
||||
|
||||
transform.M41 = agent.Position.X;
|
||||
transform.M42 = BaseComponent.EnemyAltitude;
|
||||
transform.M43 = agent.Position.Y;
|
||||
transform.M44 = 1f;
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
public static void SetTransform(this GKAgent2D agent, SCNMatrix4 newTransform)
|
||||
{
|
||||
var quatf = new SCNQuaternion(newTransform.Column3.Xyz, newTransform.Column3.W);
|
||||
|
||||
SCNVector3 axis;
|
||||
|
||||
#if !__OSX__
|
||||
float angle;
|
||||
quatf.ToAxisAngle(out axis, out angle);
|
||||
agent.Rotation = -(angle + ((float)Math.PI / 2f));
|
||||
agent.Position = new Vector2(newTransform.M41, newTransform.M43);
|
||||
#else
|
||||
nfloat angle;
|
||||
quatf.ToAxisAngle(out axis, out angle);
|
||||
agent.Rotation = -((float)angle + ((float)Math.PI / 2f));
|
||||
agent.Position = new Vector2((float)newTransform.M41, (float)newTransform.M43);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
|
||||
namespace Fox2.Extensions
|
||||
{
|
||||
using OpenTK;
|
||||
using SceneKit;
|
||||
|
||||
public static class SimdExtensions
|
||||
{
|
||||
public static bool AllZero(this SCNVector3 vector3)
|
||||
{
|
||||
return vector3.X == 0f && vector3.Y == 0f && vector3.Z == 0f;
|
||||
}
|
||||
|
||||
public static bool AllZero(this Vector2 vector2)
|
||||
{
|
||||
return vector2.X == 0f && vector2.Y == 0f;
|
||||
}
|
||||
|
||||
public static SCNVector3 GetPosition(this SCNMatrix4 vector3)
|
||||
{
|
||||
return vector3.Column3.Xyz;
|
||||
}
|
||||
|
||||
public static void SetPosition(ref SCNMatrix4 matrix, SCNVector3 value)
|
||||
{
|
||||
matrix.M41 = value.X;
|
||||
matrix.M42 = value.Y;
|
||||
matrix.M43 = value.Z;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
|
||||
namespace Fox2.Extensions
|
||||
{
|
||||
using SceneKit;
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns plane / ray intersection distance from ray origin.
|
||||
/// </summary>
|
||||
#if !__OSX__
|
||||
public static float PlaneIntersect(SCNVector3 planeNormal, float planeDist, SCNVector3 rayOrigin, SCNVector3 rayDirection)
|
||||
#else
|
||||
public static System.nfloat PlaneIntersect(SCNVector3 planeNormal, System.nfloat planeDist, SCNVector3 rayOrigin, SCNVector3 rayDirection)
|
||||
#endif
|
||||
{
|
||||
return (planeDist - SCNVector3.Dot(planeNormal, rayOrigin)) / SCNVector3.Dot(planeNormal, rayDirection);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>96c0a724-8c67-42e4-932e-d053530a640a</ProjectGuid>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
|
||||
<PropertyGroup />
|
||||
<Import Project="Fox2.SharedPart.projitems" Label="Shared" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,187 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
<HasSharedItems>true</HasSharedItems>
|
||||
<SharedGUID>96c0a724-8c67-42e4-932e-d053530a640a</SharedGUID>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<Import_RootNamespace>Fox2.SharedPart</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)BaseComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Character.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ChaserComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\AudioSourceKind.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\ChaserState.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\GroundType.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\ParticleKind.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\ScaredState.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\GKAgent2DExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SimdExtensions.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Extensions\Utils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)GameController.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Interfaces\IMenuDelegate.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Overlay.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)PlayerComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ScaredComponent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\Button.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\Menu.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)UI\Slider.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Enums\Bitmask.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\aah_extinction.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\ambience.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\attack.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\collect.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\collectBig.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\hit.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\hitEnemy.wav" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Music_victory.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\ouch_firehit.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\panda_catch_fire.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_00.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_01.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_02.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_03.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_04.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_05.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_06.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_07.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_08.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\Step_rock_09.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)audio\volcano.mp3" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\collectableBIG_empty.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\collectableBIG_full.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\congratulations.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\congratulations_pandaMax.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\dpad.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\key_empty.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\key_full.png" />
|
||||
<Content Include="$(MSBuildThisFileDirectory)Overlays\MaxIcon.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)audio\Explosion1.m4a" />
|
||||
<None Include="$(MSBuildThisFileDirectory)audio\Explosion2.m4a" />
|
||||
<None Include="$(MSBuildThisFileDirectory)audio\jump.m4a" />
|
||||
<None Include="$(MSBuildThisFileDirectory)audio\unlockTheDoor.m4a" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="$(MSBuildThisFileDirectory)Resources\" />
|
||||
<Folder Include="$(MSBuildThisFileDirectory)Overlays\" />
|
||||
<Folder Include="$(MSBuildThisFileDirectory)art.scnassets\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\jump_dust.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_AO.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_diffuseB.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_diffuseC.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_diffuseD.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_idle.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_jump.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_specular.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_spin.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\max_walk.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\character\smoke.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\collision.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_bad_AO.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_bad_DIFFUSE.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_bad_ILLUMINATION.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_bad_NORMAL.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_fearful_AO.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_fearful_DIFFUSE.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_fearful_ILLUMINATION.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_fearful_NORMAL.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\baddy_fearful_ROUGHNESS.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\dot.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\enemies.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\enemy_explosion.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\enemy1.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\enemy2.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\fireMask.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\flare.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\enemy\gradient.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\level_scene.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\burn.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\circle.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\collect.scnp" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\collect-big.scnp" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\dot.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\enemy_explosion.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\fire.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\flare.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\glow_ellipse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\glow01.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\key_apparition.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\particles_spin.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\particles\unlock_door.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\scene.gks" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\scene.scn" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\Background_sky.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\circle.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\cubeMap4096.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\depart-vieuxVolcan_AO.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\depart-vieuxVolcan_lightmap.exr" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\depart-vieuxVolcan_lightmap.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\dot.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\env_star.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fern_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fern_illumination.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fern_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fernSpiral_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fernSpiral_illumination.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fernSpiral_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\fire.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\flowmapPanda.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\gem.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\gradient_fire.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lava_cold2.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lava_hot.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lava_medium2.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lava_splash01.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lavaDry_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lavaDry_Med_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lavaDry_Med_diffuse.psd" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lavaDry_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\lavalBubble.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\mobilePlateformBig_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\mobilePlateformBig_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\mobilePlateformBig_specular.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\mobilePT_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\noisemap.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\planks_albedo.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\planks_metalness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\planks_normal.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\planks_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\plantes_AO.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\plantes_lightmap.exr" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\plantes_lightmap.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\ripples.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_border_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_border_normal.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_border_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_top_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_top_normal.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\rocks_top_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\sand_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\sand_normal.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\sand_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\sky_cube.exr" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\sky_cube.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\smoke_panda.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\tile_blue_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\tile_blue_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\tile_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\tile_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\topLava_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\topLava_normal.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\topLava_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\wall_diffuse.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\textures\wall_roughness.png" />
|
||||
<SceneKitAsset Include="$(MSBuildThisFileDirectory)art.scnassets\tile.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
namespace Fox2.Interfaces
|
||||
{
|
||||
using Foundation;
|
||||
|
||||
public interface IMenuDelegate : INSObjectProtocol
|
||||
{
|
||||
void FStopChanged(float value);
|
||||
|
||||
void FocusDistanceChanged(float value);
|
||||
|
||||
void DebugMenuSelectCameraAtIndex(int index);
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using Fox2.UI;
|
||||
using SpriteKit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// This class manages the 2D overlay (score).
|
||||
/// </summary>
|
||||
public class Overlay : SKScene
|
||||
{
|
||||
private List<SKSpriteNode> collectedGemsSprites;
|
||||
private SKNode congratulationsGroupNode;
|
||||
private SKSpriteNode collectedKeySprite;
|
||||
private SKNode overlayNode;
|
||||
|
||||
// demo UI
|
||||
private Menu demoMenu;
|
||||
|
||||
private int collectedGemsCount = 0;
|
||||
|
||||
public Overlay(CGSize size, GameController controller) : base(size)
|
||||
{
|
||||
this.overlayNode = new SKNode();
|
||||
|
||||
var width = size.Width;
|
||||
var height = size.Height;
|
||||
|
||||
this.collectedGemsSprites = new List<SKSpriteNode>();
|
||||
|
||||
// Setup the game overlays using SpriteKit.
|
||||
this.ScaleMode = SKSceneScaleMode.ResizeFill;
|
||||
|
||||
this.AddChild(this.overlayNode);
|
||||
this.overlayNode.Position = new CGPoint(0f, height);
|
||||
|
||||
// The Max icon.
|
||||
var characterNode = SKSpriteNode.FromImageNamed("Overlays/MaxIcon.png");
|
||||
var menuButton = new Button(characterNode)
|
||||
{
|
||||
Position = new CGPoint(50f, -50f),
|
||||
XScale = 0.5f,
|
||||
YScale = 0.5f,
|
||||
};
|
||||
|
||||
this.overlayNode.AddChild(menuButton);
|
||||
menuButton.SetClickedTarget(this.ToggleMenu);
|
||||
|
||||
// The Gems
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
var gemNode = SKSpriteNode.FromImageNamed("Overlays/collectableBIG_empty.png");
|
||||
gemNode.Position = new CGPoint(125f + i * 80f, -50f);
|
||||
gemNode.XScale = 0.25f;
|
||||
gemNode.YScale = 0.25f;
|
||||
|
||||
this.overlayNode.AddChild(gemNode);
|
||||
this.collectedGemsSprites.Add(gemNode);
|
||||
}
|
||||
|
||||
// The key
|
||||
this.collectedKeySprite = SKSpriteNode.FromImageNamed("Overlays/key_empty.png");
|
||||
this.collectedKeySprite.Position = new CGPoint(195f, -50f);
|
||||
this.collectedKeySprite.XScale = 0.4f;
|
||||
this.collectedKeySprite.YScale = 0.4f;
|
||||
this.overlayNode.AddChild(this.collectedKeySprite);
|
||||
|
||||
// The virtual D-pad
|
||||
#if __IOS__
|
||||
this.ControlOverlay = new ControlOverlay(new CGRect(0f, 0f, width, height));
|
||||
this.ControlOverlay.LeftPad.Delegate = controller;
|
||||
this.ControlOverlay.RightPad.Delegate = controller;
|
||||
this.ControlOverlay.ButtonA.Delegate = controller;
|
||||
this.ControlOverlay.ButtonB.Delegate = controller;
|
||||
this.AddChild(this.ControlOverlay);
|
||||
#endif
|
||||
// the demo UI
|
||||
this.demoMenu = new Menu(size)
|
||||
{
|
||||
Hidden = true,
|
||||
Delegate = controller,
|
||||
};
|
||||
|
||||
this.overlayNode.AddChild(this.demoMenu);
|
||||
|
||||
// Assign the SpriteKit overlay to the SceneKit view.
|
||||
base.UserInteractionEnabled = false;
|
||||
}
|
||||
|
||||
public Overlay(NSCoder coder)
|
||||
{
|
||||
throw new NotImplementedException("init(coder:) has not been implemented");
|
||||
}
|
||||
|
||||
#if __IOS__
|
||||
public ControlOverlay ControlOverlay { get; set; }
|
||||
#endif
|
||||
|
||||
public int CollectedGemsCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.collectedGemsCount;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.collectedGemsCount = value;
|
||||
|
||||
this.collectedGemsSprites[this.collectedGemsCount - 1].Texture = SKTexture.FromImageNamed("Overlays/collectableBIG_full.png");
|
||||
this.collectedGemsSprites[this.collectedGemsCount - 1].RunAction(SKAction.Sequence(new SKAction[] { SKAction.WaitForDuration(0.5f),
|
||||
SKAction.ScaleBy(1.5f, 0.2f),
|
||||
SKAction.ScaleBy(1f / 1.5f, 0.2f) }));
|
||||
}
|
||||
}
|
||||
|
||||
public void Layout2DOverlay()
|
||||
{
|
||||
this.overlayNode.Position = new CGPoint(0f, this.Size.Height);
|
||||
|
||||
if (this.congratulationsGroupNode != null)
|
||||
{
|
||||
this.congratulationsGroupNode.Position = new CGPoint(this.Size.Width * 0.5f, this.Size.Height * 0.5f);
|
||||
this.congratulationsGroupNode.XScale = 1f;
|
||||
this.congratulationsGroupNode.YScale = 1f;
|
||||
var currentBbox = this.congratulationsGroupNode.CalculateAccumulatedFrame();
|
||||
|
||||
var margin = 25f;
|
||||
var bounds = new CGRect(0f, 0f, this.Size.Width, this.Size.Height);
|
||||
var maximumAllowedBbox = bounds.Inset(margin, margin);
|
||||
|
||||
var top = currentBbox.GetMaxY() - this.congratulationsGroupNode.Position.Y;
|
||||
var bottom = this.congratulationsGroupNode.Position.Y - currentBbox.GetMinY();
|
||||
var maxTopAllowed = maximumAllowedBbox.GetMaxY() - this.congratulationsGroupNode.Position.Y;
|
||||
var maxBottomAllowed = this.congratulationsGroupNode.Position.Y - maximumAllowedBbox.GetMinY();
|
||||
|
||||
var left = this.congratulationsGroupNode.Position.X - currentBbox.GetMinX();
|
||||
var right = currentBbox.GetMaxX() - this.congratulationsGroupNode.Position.X;
|
||||
var maxLeftAllowed = this.congratulationsGroupNode.Position.X - maximumAllowedBbox.GetMinX();
|
||||
var maxRightAllowed = maximumAllowedBbox.GetMaxX() - this.congratulationsGroupNode.Position.X;
|
||||
|
||||
var topScale = top > maxTopAllowed ? maxTopAllowed / top : 1;
|
||||
var bottomScale = bottom > maxBottomAllowed ? maxBottomAllowed / bottom : 1;
|
||||
var leftScale = left > maxLeftAllowed ? maxLeftAllowed / left : 1; ;
|
||||
var rightScale = right > maxRightAllowed ? maxRightAllowed / right : 1;
|
||||
|
||||
var scale = NMath.Min(topScale, NMath.Min(bottomScale, NMath.Min(leftScale, rightScale)));
|
||||
|
||||
this.congratulationsGroupNode.XScale = scale;
|
||||
this.congratulationsGroupNode.YScale = scale;
|
||||
}
|
||||
}
|
||||
|
||||
public void DidCollectKey()
|
||||
{
|
||||
this.collectedKeySprite.Texture = SKTexture.FromImageNamed("Overlays/key_full.png");
|
||||
this.collectedKeySprite.RunAction(SKAction.Sequence(new SKAction[]{ SKAction.WaitForDuration(0.5f),
|
||||
SKAction.ScaleBy(1.5f, 0.2f),
|
||||
SKAction.ScaleBy(1f / 1.5f, 0.2f) }));
|
||||
}
|
||||
|
||||
#if __IOS__
|
||||
|
||||
public void ShowVirtualPad()
|
||||
{
|
||||
this.ControlOverlay.Hidden = false;
|
||||
}
|
||||
|
||||
public void HideVirtualPad()
|
||||
{
|
||||
this.ControlOverlay.Hidden = true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#region Congratulate the player
|
||||
|
||||
public void ShowEndScreen()
|
||||
{
|
||||
// Congratulation title
|
||||
var congratulationsNode = SKSpriteNode.FromImageNamed("Overlays/congratulations.png");
|
||||
|
||||
// Max image
|
||||
var characterNode = SKSpriteNode.FromImageNamed("Overlays/congratulations_pandaMax.png");
|
||||
characterNode.Position = new CGPoint(0f, -220f);
|
||||
characterNode.AnchorPoint = new CGPoint(0.5f, 0f);
|
||||
|
||||
this.congratulationsGroupNode = new SKNode();
|
||||
this.congratulationsGroupNode.AddChild(characterNode);
|
||||
this.congratulationsGroupNode.AddChild(congratulationsNode);
|
||||
this.AddChild(this.congratulationsGroupNode);
|
||||
|
||||
// Layout the overlay
|
||||
this.Layout2DOverlay();
|
||||
|
||||
// Animate
|
||||
congratulationsNode.Alpha = 0f;
|
||||
congratulationsNode.XScale = 0f;
|
||||
congratulationsNode.YScale = 0f;
|
||||
congratulationsNode.RunAction(SKAction.Group(new SKAction[] { SKAction.FadeInWithDuration(0.25),
|
||||
SKAction.Sequence(new SKAction[] { SKAction.ScaleTo(1.22f, 0.25),
|
||||
SKAction.ScaleTo(1f, 0.1) }) }));
|
||||
|
||||
characterNode.Alpha = 0f;
|
||||
characterNode.XScale = 0f;
|
||||
characterNode.YScale = 0f;
|
||||
characterNode.RunAction(SKAction.Sequence(new SKAction[] { SKAction.WaitForDuration(0.5),
|
||||
SKAction.Group(new SKAction[] { SKAction.FadeInWithDuration(0.5),
|
||||
SKAction.Sequence(new SKAction[] { SKAction.ScaleTo(1.22f, 0.25),
|
||||
SKAction.ScaleTo(1f, 0.1) }) }) }));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void ToggleMenu(Button sender)
|
||||
{
|
||||
this.demoMenu.Hidden = !this.demoMenu.Hidden;
|
||||
}
|
||||
}
|
||||
}
|
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/MaxIcon.png
До Ширина: | Высота: | Размер: 21 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/collectableBIG_empty.png
До Ширина: | Высота: | Размер: 6.8 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/collectableBIG_full.png
До Ширина: | Высота: | Размер: 19 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/congratulations.png
До Ширина: | Высота: | Размер: 620 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/congratulations_pandaMax.png
До Ширина: | Высота: | Размер: 549 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/dpad.png
До Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/key_empty.png
До Ширина: | Высота: | Размер: 5.0 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/Overlays/key_full.png
До Ширина: | Высота: | Размер: 16 KiB |
|
@ -1,17 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents the player.
|
||||
/// </summary>
|
||||
public class PlayerComponent : BaseComponent
|
||||
{
|
||||
public Character Character { get; set; }
|
||||
|
||||
public override void Update(double deltaTimeInSeconds)
|
||||
{
|
||||
this.PositionAgentFromNode();
|
||||
base.Update(deltaTimeInSeconds);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,145 +0,0 @@
|
|||
|
||||
namespace Fox2
|
||||
{
|
||||
using Fox2.Enums;
|
||||
using GameplayKit;
|
||||
using OpenTK;
|
||||
using SceneKit;
|
||||
|
||||
/// <summary>
|
||||
/// This class implements the scared behavior.
|
||||
/// </summary>
|
||||
public class ScaredComponent : BaseComponent
|
||||
{
|
||||
private float maxAcceleration = 2.534f;
|
||||
private float fleeDistance = 2f;
|
||||
private float wanderSpeed = 1f;
|
||||
private float mass = 0.326f;
|
||||
|
||||
private ScaredState state = ScaredState.Wander;
|
||||
private GKGoal fleeGoal;
|
||||
private GKGoal wanderGoal;
|
||||
private GKGoal centerGoal;
|
||||
private GKBehavior behavior;
|
||||
|
||||
private PlayerComponent player;
|
||||
|
||||
public PlayerComponent Player
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.player;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.player = value;
|
||||
|
||||
this.Agent.Mass = this.mass;
|
||||
this.Agent.MaxAcceleration = this.maxAcceleration;
|
||||
|
||||
this.fleeGoal = GKGoal.GetGoalToFleeAgent(this.player?.Agent);
|
||||
this.wanderGoal = GKGoal.GetGoalToWander(this.wanderSpeed);
|
||||
|
||||
var center = new Vector2[]
|
||||
{
|
||||
new Vector2(-1f, 9f),
|
||||
new Vector2(1f, 9f),
|
||||
new Vector2(1f, 11f),
|
||||
new Vector2(-1f, 11f)
|
||||
};
|
||||
|
||||
var path = new GKPath(center, 0.5f, true);
|
||||
this.centerGoal = GKGoal.GetGoalToStayOnPath(path, 1d);
|
||||
this.behavior = GKBehavior.FromGoals(new GKGoal[] { this.fleeGoal, this.wanderGoal, this.centerGoal });
|
||||
this.Agent.Behavior = behavior;
|
||||
this.StartWandering();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsDead()
|
||||
{
|
||||
return this.state == ScaredState.Dead;
|
||||
}
|
||||
|
||||
private void StartWandering()
|
||||
{
|
||||
if (this.behavior != null)
|
||||
{
|
||||
this.behavior.SetWeight(1f, this.wanderGoal);
|
||||
this.behavior.SetWeight(0f, this.fleeGoal);
|
||||
this.behavior.SetWeight(0.3f, this.centerGoal);
|
||||
this.state = ScaredState.Wander;
|
||||
}
|
||||
}
|
||||
|
||||
private void StartFleeing()
|
||||
{
|
||||
if (this.behavior != null)
|
||||
{
|
||||
this.behavior.SetWeight(0f, this.wanderGoal);
|
||||
this.behavior.SetWeight(1f, this.fleeGoal);
|
||||
this.behavior.SetWeight(0.4f, this.centerGoal);
|
||||
this.state = ScaredState.Flee;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(double deltaTimeInSeconds)
|
||||
{
|
||||
if (this.state != ScaredState.Dead)
|
||||
{
|
||||
var character = this.player?.Character;
|
||||
var playerComponent = this.player?.Entity?.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
var nodeComponent = base.Entity?.GetComponent(typeof(GKSCNNodeComponent)) as GKSCNNodeComponent;
|
||||
if (character != null && playerComponent != null && nodeComponent != null)
|
||||
{
|
||||
var enemyNode = nodeComponent.Node;
|
||||
var playerNode = playerComponent.Node;
|
||||
var distance = (enemyNode.WorldPosition - playerNode.WorldPosition).Length;
|
||||
|
||||
// Chase if below chaseDistance from enemy, wander otherwise.
|
||||
switch (this.state)
|
||||
{
|
||||
case ScaredState.Wander:
|
||||
if (distance < this.fleeDistance)
|
||||
{
|
||||
this.StartFleeing();
|
||||
}
|
||||
break;
|
||||
case ScaredState.Flee:
|
||||
if (distance > this.fleeDistance)
|
||||
{
|
||||
this.StartWandering();
|
||||
}
|
||||
break;
|
||||
case ScaredState.Dead:
|
||||
break;
|
||||
}
|
||||
|
||||
this.HandleEnemyResponse(character, enemyNode);
|
||||
base.Update(deltaTimeInSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleEnemyResponse(Character character, SCNNode enemy)
|
||||
{
|
||||
var direction = enemy.WorldPosition - character.Node.WorldPosition;
|
||||
if (direction.Length < 0.5f)
|
||||
{
|
||||
if (character.IsAttacking)
|
||||
{
|
||||
this.state = ScaredState.Dead;
|
||||
|
||||
character.DidHitEnemy();
|
||||
|
||||
this.PerformEnemyDieWithExplosion(enemy, direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.WasTouchedByEnemy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
|
||||
namespace Fox2.UI
|
||||
{
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using SpriteKit;
|
||||
using System;
|
||||
|
||||
#if !__OSX__
|
||||
using UIKit;
|
||||
using SKColor = UIKit.UIColor;
|
||||
#else
|
||||
using AppKit;
|
||||
using SKColor = AppKit.NSColor;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A basic `SKNode` based button.
|
||||
/// </summary>
|
||||
public class Button : SKNode
|
||||
{
|
||||
private CGSize size = CGSize.Empty;
|
||||
|
||||
private Action<Button> actionClicked;
|
||||
|
||||
private SKSpriteNode background;
|
||||
|
||||
private SKLabelNode label;
|
||||
|
||||
public Button(string text) : base()
|
||||
{
|
||||
// create a label
|
||||
var fontName = "Optima-ExtraBlack";
|
||||
this.label = SKLabelNode.FromFont(fontName);
|
||||
this.label.Text = text;
|
||||
this.label.FontSize = 18;
|
||||
this.label.FontColor = SKColor.White;
|
||||
this.label.Position = new CGPoint(0f, -8f);
|
||||
|
||||
// create the background
|
||||
this.size = new CGSize(this.label.Frame.Size.Width + 10f, 30f);
|
||||
this.background = new SKSpriteNode(SKColor.FromCIColor(new CoreImage.CIColor(0f, 0f, 0f, 0.75f)), size);
|
||||
|
||||
// add to the root node
|
||||
this.AddChild(this.background);
|
||||
this.AddChild(this.label);
|
||||
|
||||
// Track mouse event
|
||||
base.UserInteractionEnabled = true;
|
||||
}
|
||||
|
||||
public Button(SKNode node) : base()
|
||||
{
|
||||
// Track mouse event
|
||||
base.UserInteractionEnabled = true;
|
||||
this.size = node.Frame.Size;
|
||||
this.AddChild(node);
|
||||
}
|
||||
|
||||
public Button(NSCoder coder)
|
||||
{
|
||||
throw new NotImplementedException("init(coder:) has not been implemented");
|
||||
}
|
||||
|
||||
public nfloat Height => this.size.Height;
|
||||
|
||||
public nfloat Width => this.size.Width;
|
||||
|
||||
private void SetText(string text)
|
||||
{
|
||||
this.label.Text = text;
|
||||
}
|
||||
|
||||
private void SetBackgroundColor(SKColor color)
|
||||
{
|
||||
if (this.background != null)
|
||||
{
|
||||
this.background.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetClickedTarget(Action<Button> action)
|
||||
{
|
||||
this.actionClicked = action;
|
||||
}
|
||||
|
||||
#if __OSX__
|
||||
|
||||
public override void MouseDown(NSEvent @event)
|
||||
{
|
||||
this.SetBackgroundColor(NSColor.FromRgba(0f, 0f, 0f, 1.0f));
|
||||
}
|
||||
|
||||
public override void MouseUp(NSEvent @event)
|
||||
{
|
||||
this.SetBackgroundColor(NSColor.FromRgba(0f, 0f, 0f, 0.75f));
|
||||
|
||||
var x = this.Position.X + ((this.Parent?.Position.X) ?? 0f);
|
||||
var y = this.Position.Y + ((this.Parent?.Position.Y) ?? 0f);
|
||||
var p = @event.LocationInWindow;
|
||||
|
||||
if (Math.Abs(p.X - x) < this.Width / 2 * this.XScale && Math.Abs(p.Y - y) < this.Height / 2 * this.YScale)
|
||||
{
|
||||
this.actionClicked.Invoke(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if __IOS__
|
||||
|
||||
public override void TouchesEnded(NSSet touches, UIEvent @event)
|
||||
{
|
||||
this.actionClicked.Invoke(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -1,166 +0,0 @@
|
|||
|
||||
namespace Fox2.UI
|
||||
{
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using Fox2.Interfaces;
|
||||
using SpriteKit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// The `SKNode` based menu.
|
||||
/// </summary>
|
||||
public class Menu : SKNode
|
||||
{
|
||||
private readonly List<Button> cameraButtons = new List<Button>();
|
||||
|
||||
private readonly List<Slider> dofSliders = new List<Slider>();
|
||||
|
||||
private const float ButtonMargin = 250f;
|
||||
private const float Duration = 0.3f;
|
||||
private const float MenuY = 40f;
|
||||
|
||||
private bool isMenuHidden;
|
||||
|
||||
public Menu(CGSize size) : base()
|
||||
{
|
||||
// Track mouse event
|
||||
base.UserInteractionEnabled = true;
|
||||
|
||||
// Camera buttons
|
||||
var buttonLabels = new string[] { "Camera 1", "Camera 2", "Camera 3" };
|
||||
this.cameraButtons = buttonLabels.Select(label => new Button(label)).ToList();
|
||||
|
||||
for (int i = 0; i < this.cameraButtons.Count; i++)
|
||||
{
|
||||
var button = this.cameraButtons[i];
|
||||
|
||||
var x = button.Width / 2 + (i > 0 ? this.cameraButtons[i - 1].Position.X + this.cameraButtons[i - 1].Width / 2 + 10 : ButtonMargin);
|
||||
var y = size.Height - MenuY;
|
||||
button.Position = new CGPoint(x, y);
|
||||
button.SetClickedTarget(this.MenuChanged);
|
||||
this.AddChild(button);
|
||||
}
|
||||
|
||||
// Depth of Field
|
||||
buttonLabels = new string[] { "fStop", "Focus" };
|
||||
this.dofSliders = buttonLabels.Select(label => new Slider(300, 10, label)).ToList();
|
||||
|
||||
for (int i = 0; i < dofSliders.Count; i++)
|
||||
{
|
||||
var slider = dofSliders[i];
|
||||
slider.Position = new CGPoint(ButtonMargin, size.Height - i * 30.0f - 70.0f);
|
||||
slider.Alpha = 0.0f;
|
||||
this.AddChild(slider);
|
||||
}
|
||||
|
||||
this.dofSliders[0].SetClickedTarget(this.CameraFStopChanged);
|
||||
this.dofSliders[1].SetClickedTarget(this.CameraFocusDistanceChanged);
|
||||
}
|
||||
|
||||
public Menu(NSCoder coder)
|
||||
{
|
||||
throw new NotImplementedException("init(coder:) has not been implemented");
|
||||
}
|
||||
|
||||
public IMenuDelegate Delegate { get; set; }
|
||||
|
||||
public override bool Hidden
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.isMenuHidden;
|
||||
}
|
||||
set
|
||||
{
|
||||
this.isMenuHidden = value;
|
||||
if (this.isMenuHidden)
|
||||
{
|
||||
this.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MenuChanged(Button sender)
|
||||
{
|
||||
this.HideSlidersMenu();
|
||||
|
||||
var index = this.cameraButtons.IndexOf(sender);
|
||||
if (index >= 0)
|
||||
{
|
||||
this.Delegate?.DebugMenuSelectCameraAtIndex(index);
|
||||
if (index == 2)
|
||||
{
|
||||
this.ShowSlidersMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Show()
|
||||
{
|
||||
foreach (var button in this.cameraButtons)
|
||||
{
|
||||
button.Alpha = 0.0f;
|
||||
button.RunAction(SKAction.FadeInWithDuration(Duration));
|
||||
}
|
||||
|
||||
this.isMenuHidden = false;
|
||||
}
|
||||
|
||||
private void Hide()
|
||||
{
|
||||
foreach (var button in this.cameraButtons)
|
||||
{
|
||||
button.Alpha = 1.0f;
|
||||
button.RunAction(SKAction.FadeOutWithDuration(Duration));
|
||||
}
|
||||
|
||||
this.HideSlidersMenu();
|
||||
this.isMenuHidden = true;
|
||||
}
|
||||
|
||||
private void HideSlidersMenu()
|
||||
{
|
||||
foreach (var slider in this.dofSliders)
|
||||
{
|
||||
slider.RunAction(SKAction.FadeOutWithDuration(Duration));
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowSlidersMenu()
|
||||
{
|
||||
foreach (var slider in this.dofSliders)
|
||||
{
|
||||
slider.RunAction(SKAction.FadeInWithDuration(Duration));
|
||||
}
|
||||
|
||||
this.dofSliders[0].Value = 0.1f;
|
||||
this.dofSliders[1].Value = 0.5f;
|
||||
|
||||
this.CameraFStopChanged(this.dofSliders[0]);
|
||||
this.CameraFocusDistanceChanged(this.dofSliders[1]);
|
||||
}
|
||||
|
||||
private void CameraFStopChanged(object sender)
|
||||
{
|
||||
if (this.Delegate != null)
|
||||
{
|
||||
this.Delegate.FStopChanged(this.dofSliders[0].Value + 0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
private void CameraFocusDistanceChanged(object sender)
|
||||
{
|
||||
if (this.Delegate != null)
|
||||
{
|
||||
this.Delegate.FocusDistanceChanged(this.dofSliders[1].Value * 20.0f + 3.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
|
||||
namespace Fox2.UI
|
||||
{
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using SpriteKit;
|
||||
using System;
|
||||
|
||||
#if __IOS__
|
||||
using UIKit;
|
||||
using SKColor = UIKit.UIColor;
|
||||
#elif __OSX__
|
||||
using AppKit;
|
||||
using SKColor = AppKit.NSColor;
|
||||
#else
|
||||
using SKColor = UIKit.UIColor;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// A basic `SKNode` based slider.
|
||||
/// </summary>
|
||||
public class Slider : SKNode
|
||||
{
|
||||
private Action<Slider> actionClicked;
|
||||
|
||||
private SKSpriteNode background;
|
||||
|
||||
private SKLabelNode label;
|
||||
|
||||
private SKShapeNode slider;
|
||||
|
||||
private float value = 0f;
|
||||
|
||||
public Slider(int width, int height, string text) : base()
|
||||
{
|
||||
// create a label
|
||||
var fontName = "Optima-ExtraBlack";
|
||||
this.label = SKLabelNode.FromFont(fontName);
|
||||
this.label.Text = text;
|
||||
this.label.FontSize = 18;
|
||||
this.label.FontColor = SKColor.White;
|
||||
this.label.Position = new CGPoint(0f, -8f);
|
||||
|
||||
// create background & slider
|
||||
this.background = new SKSpriteNode(SKColor.White, new CGSize(width, 2f));
|
||||
this.slider = SKShapeNode.FromCircle(height);
|
||||
this.slider.FillColor = SKColor.White;
|
||||
this.background.AnchorPoint = new CGPoint(0f, 0.5f);
|
||||
|
||||
this.slider.Position = new CGPoint(this.label.Frame.Size.Width / 2f + 15f, 0f);
|
||||
this.background.Position = new CGPoint(this.label.Frame.Size.Width / 2f + 15f, 0f);
|
||||
|
||||
// add to the root node
|
||||
this.AddChild(this.label);
|
||||
this.AddChild(this.background);
|
||||
this.AddChild(this.slider);
|
||||
|
||||
// track mouse event
|
||||
base.UserInteractionEnabled = true;
|
||||
this.Value = 0f;
|
||||
}
|
||||
|
||||
public Slider(NSCoder coder)
|
||||
{
|
||||
throw new NotImplementedException("init(coder:) has not been implemented");
|
||||
}
|
||||
|
||||
public nfloat Width => this.background.Frame.Size.Width;
|
||||
|
||||
public nfloat Height => this.slider.Frame.Size.Height;
|
||||
|
||||
public float Value
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.value;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.value = value;
|
||||
this.slider.Position = new CGPoint(this.background.Position.X + this.value * this.Width, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetBackgroundColor(SKColor color)
|
||||
{
|
||||
if (this.background != null)
|
||||
{
|
||||
background.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetClickedTarget(Action<Slider> action)
|
||||
{
|
||||
this.actionClicked = action;
|
||||
}
|
||||
|
||||
#if __OSX__
|
||||
|
||||
public override void MouseDown(NSEvent @event)
|
||||
{
|
||||
this.MouseDragged(@event);
|
||||
}
|
||||
|
||||
public override void MouseUp(NSEvent @event)
|
||||
{
|
||||
this.SetBackgroundColor(SKColor.White);
|
||||
}
|
||||
|
||||
public override void MouseDragged(NSEvent @event)
|
||||
{
|
||||
this.SetBackgroundColor(SKColor.Gray);
|
||||
|
||||
var posInView = this.Scene.ConvertPointFromNode(this.Position, this.Parent);
|
||||
|
||||
var x = @event.LocationInWindow.X - posInView.X - this.background.Position.X;
|
||||
var pos = NMath.Min(NMath.Min(x, this.Width), 0f);
|
||||
this.slider.Position = new CGPoint(this.background.Position.X + pos, 0f);
|
||||
this.Value = (float)(pos / this.Width);
|
||||
this.actionClicked.Invoke(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if __IOS__
|
||||
|
||||
public override void TouchesMoved(NSSet touches, UIEvent @event)
|
||||
{
|
||||
this.SetBackgroundColor(UIColor.Gray);
|
||||
|
||||
var first = touches.ToArray<UITouch>()[0];
|
||||
var x = first.LocationInNode(this).X - this.background.Position.X;
|
||||
var position = Math.Max(NMath.Min(x, this.Width), 0f);
|
||||
|
||||
this.slider.Position = new CGPoint(this.background.Position.X + position, 0f);
|
||||
this.Value = (float)(position / this.Width);
|
||||
|
||||
this.actionClicked.Invoke(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/jump_dust.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_AO.png
До Ширина: | Высота: | Размер: 46 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_diffuse.png
До Ширина: | Высота: | Размер: 235 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_diffuseB.png
До Ширина: | Высота: | Размер: 247 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_diffuseC.png
До Ширина: | Высота: | Размер: 243 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_diffuseD.png
До Ширина: | Высота: | Размер: 176 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_idle.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_jump.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_roughness.png
До Ширина: | Высота: | Размер: 72 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_specular.png
До Ширина: | Высота: | Размер: 8.2 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_spin.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/max_walk.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/character/smoke.png
До Ширина: | Высота: | Размер: 12 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/collision.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/baddy_bad_AO.png
До Ширина: | Высота: | Размер: 131 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/baddy_bad_DIFFUSE.png
До Ширина: | Высота: | Размер: 108 KiB |
До Ширина: | Высота: | Размер: 74 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/baddy_bad_NORMAL.png
До Ширина: | Высота: | Размер: 393 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/baddy_fearful_AO.png
До Ширина: | Высота: | Размер: 82 KiB |
До Ширина: | Высота: | Размер: 164 KiB |
До Ширина: | Высота: | Размер: 96 KiB |
До Ширина: | Высота: | Размер: 261 KiB |
До Ширина: | Высота: | Размер: 172 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/dot.png
До Ширина: | Высота: | Размер: 911 B |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/enemies.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/enemy1.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/enemy2.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/enemy_explosion.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/fireMask.png
До Ширина: | Высота: | Размер: 36 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/flare.png
До Ширина: | Высота: | Размер: 54 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/enemy/gradient.png
До Ширина: | Высота: | Размер: 17 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/level_scene.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/burn.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/circle.png
До Ширина: | Высота: | Размер: 18 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/collect-big.scnp
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/collect.scnp
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/dot.png
До Ширина: | Высота: | Размер: 911 B |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/fire.png
До Ширина: | Высота: | Размер: 14 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/flare.png
До Ширина: | Высота: | Размер: 54 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/glow01.png
До Ширина: | Высота: | Размер: 65 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/glow_ellipse.png
До Ширина: | Высота: | Размер: 4.8 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/particles/unlock_door.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/scene.gks
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/scene.scn
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/Background_sky.png
До Ширина: | Высота: | Размер: 5.4 MiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/circle.png
До Ширина: | Высота: | Размер: 19 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/cubeMap4096.png
До Ширина: | Высота: | Размер: 123 KiB |
До Ширина: | Высота: | Размер: 320 KiB |
До Ширина: | Высота: | Размер: 440 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/dot.png
До Ширина: | Высота: | Размер: 911 B |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/env_star.png
До Ширина: | Высота: | Размер: 5.7 KiB |
До Ширина: | Высота: | Размер: 42 KiB |
До Ширина: | Высота: | Размер: 8.7 KiB |
До Ширина: | Высота: | Размер: 16 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/fern_diffuse.png
До Ширина: | Высота: | Размер: 53 KiB |
До Ширина: | Высота: | Размер: 9.5 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/fern_roughness.png
До Ширина: | Высота: | Размер: 17 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/fire.png
До Ширина: | Высота: | Размер: 27 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/flowmapPanda.png
До Ширина: | Высота: | Размер: 373 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/gem.png
До Ширина: | Высота: | Размер: 2.7 KiB |
Двоичные данные
ios11/Fox2/Fox2.Shared/art.scnassets/textures/gradient_fire.png
До Ширина: | Высота: | Размер: 17 KiB |
До Ширина: | Высота: | Размер: 1.2 MiB |
До Ширина: | Высота: | Размер: 590 KiB |