diff --git a/Hunter/HunterGame/Camera.cs b/Hunter/HunterGame/Camera.cs index 71574b0..a7d1d04 100644 --- a/Hunter/HunterGame/Camera.cs +++ b/Hunter/HunterGame/Camera.cs @@ -8,11 +8,11 @@ public class Camera { public const int MinZoom = 25; public const int MaxZoom = 100; - public const int DefaultZoom = 75; + public const int DefaultZoom = 70; public const int ZoomStep = 1; public int ZoomLevel = DefaultZoom; - public Matrix Zoom = Matrix.CreateScale(DefaultZoom / 100f, DefaultZoom / 100f, 0); + public Matrix Zoom = CreateZoomMatrix(DefaultZoom); public Matrix Transform; public void Update() @@ -28,7 +28,12 @@ public void Update() public void UpdateZoom(int change) { ZoomLevel = Math.Max(MinZoom, Math.Min(MaxZoom, ZoomLevel + change)); - Zoom = Matrix.CreateScale(ZoomLevel / 100f, ZoomLevel / 100f, 0); + Zoom = CreateZoomMatrix(ZoomLevel); + } + + public static Matrix CreateZoomMatrix(int zoomLevel) + { + return Matrix.CreateScale(zoomLevel / 100f, zoomLevel / 100f, 0); } public void Follow(Vector2 target, Vector2 centerOffset) diff --git a/Hunter/HunterGame/GameObjects/Animals/Doe.cs b/Hunter/HunterGame/GameObjects/Animals/Doe.cs index 7217577..a9cc28f 100644 --- a/Hunter/HunterGame/GameObjects/Animals/Doe.cs +++ b/Hunter/HunterGame/GameObjects/Animals/Doe.cs @@ -1,18 +1,21 @@ using System; using System.Collections.Generic; using System.Linq; +using HunterGame.GameObjects.Boid; +using HunterGame.GameObjects.Boid.Interfaces; +using HunterGame.GameObjects.Player; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Animals { - public class Doe : Boid + public class Doe : Boid.Boid, IWandering, IFleeing { public const double FleeingSpeed = 225; public const double WanderingSpeed = 75; public const double MaxForce = 5; - public const double TextureIndependentFearDistance = 70; - public const double TextureIndependentCalmingDistance = 100; + public const double TextureIndependentFearDistance = 125; + public const double TextureIndependentCalmingDistance = 175; public readonly double FearDistance; public readonly double CalmingDistance; @@ -46,6 +49,8 @@ public override void Update(GameTime gameTime, WorldState worldState) Wander(elapsedTime, worldState); else if (State == BoidState.Fleeing) Flee(elapsedTime, worldState); + else + throw new IncorrectBoidStateException($"Doe cannot be in state {State}"); } public void Wander(double elapsedTime, WorldState worldState) @@ -60,45 +65,58 @@ public void Wander(double elapsedTime, WorldState worldState) } var otherDoes = close.OfType().ToList(); - var closeDoesFromOtherGroup = otherDoes.Where(doe => doe.Group != Group && (doe.CenterPosition - CenterPosition).Length() <= FearDistance).ToList(); + + if (this == Group.Leader && !Group.IsComplete) + TryCompleteGroup(otherDoes); var borderAvoidingForce = GetBordersAvoidingForce(FleeingSpeed, MaxForce); var separationForce = GetSeparationForce(otherDoes, SeparationActivationDistance, WanderingSpeed, MaxForce); + var speed = WanderingSpeed; + if (this == Group.Leader) - HandleLeader(closeDoesFromOtherGroup, worldState.Random, borderAvoidingForce); + HandleLeader(worldState.Random, borderAvoidingForce); else - HandleFollower(ref separationForce); + HandleFollower(ref speed, ref separationForce); Acceleration += borderAvoidingForce * 2; Acceleration += separationForce * 0.15f; - ApplyForces(WanderingSpeed * elapsedTime); + ApplyForces(speed * elapsedTime); } - public void HandleLeader(List closeDoesFromOtherGroup, Random random, Vector2 borderAvoidingForce) + public void TryCompleteGroup(IEnumerable otherDoes) { - if (!Group.IsComplete && closeDoesFromOtherGroup.Count > 0) + var closest = otherDoes.FirstOrDefault(doe => doe.Group != Group && (doe.CenterPosition - CenterPosition).Length() <= FearDistance); + + if (closest != null) { - var otherGroup = closeDoesFromOtherGroup[0].Group; + var otherGroup = closest.Group; Group.Join(otherGroup); if (otherGroup.Size > DoeGroup.MaximumSize) otherGroup.SplitIntoHalves(); } - else - CheckArrival(ref WanderTarget, random, borderAvoidingForce); + } + public void HandleLeader(Random random, Vector2 borderAvoidingForce) + { + CheckArrival(ref WanderTarget, random, borderAvoidingForce); + Acceleration += GetArrivalForce(WanderTarget, WanderingSpeed, MaxForce) * 2; } - public void HandleFollower(ref Vector2 separationForce) + public void HandleFollower(ref double speed, ref Vector2 separationForce) { Acceleration += GetArrivalForce(Group.Leader.CenterPosition, WanderingSpeed, MaxForce) * 0.05f; - - if ((Group.Leader.CenterPosition - CenterPosition).Length() <= SeparationBoostMargin) + + var distanceToLeader = (Group.Leader.CenterPosition - CenterPosition).Length(); + + if (distanceToLeader <= SeparationBoostMargin) separationForce *= 10; + else if (distanceToLeader >= CalmingDistance) + speed = FleeingSpeed; } public void StartWandering(Random random) diff --git a/Hunter/HunterGame/GameObjects/Animals/DoeGroup.cs b/Hunter/HunterGame/GameObjects/Animals/DoeGroup.cs index 3d6e86b..2652c5b 100644 --- a/Hunter/HunterGame/GameObjects/Animals/DoeGroup.cs +++ b/Hunter/HunterGame/GameObjects/Animals/DoeGroup.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; -namespace HunterGame +namespace HunterGame.GameObjects.Animals { public class DoeGroup { @@ -9,9 +9,9 @@ public class DoeGroup public const int MaximumSize = 10; public List Members = new List(); - + public Doe Leader => Members[0]; - + public int Size => Members.Count; public bool IsComplete => Size >= MinimumCompleteSize && Size <= MaximumSize; @@ -34,13 +34,13 @@ public void SplitIntoHalves() { var newGroup = new DoeGroup(); - var left = Members.Take(Size / 2); - var moved = Members.Skip(Size / 2); + var staying = Members.Take(Size / 2); + var leaving = Members.Skip(Size / 2); - foreach (var member in moved) + foreach (var member in leaving) newGroup.Add(member); - Members = left.ToList(); + Members = staying.ToList(); newGroup.Leader.WanderTarget = newGroup.Leader.CenterPosition; } diff --git a/Hunter/HunterGame/GameObjects/Animals/Hare.cs b/Hunter/HunterGame/GameObjects/Animals/Hare.cs index 0e78e15..2cb7ec8 100644 --- a/Hunter/HunterGame/GameObjects/Animals/Hare.cs +++ b/Hunter/HunterGame/GameObjects/Animals/Hare.cs @@ -1,18 +1,19 @@ using System; using System.Linq; +using HunterGame.GameObjects.Boid; +using HunterGame.GameObjects.Boid.Interfaces; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Animals { - public class Hare : Boid + public class Hare : Boid.Boid, IWandering, IFleeing { - public const double FleeingSpeed = 375; public const double WanderingSpeed = 37.5; public const double MaxForce = 5; - public const double TextureIndependentFearDistance = 50; - public const double TextureIndependentCalmingDistance = 100; + public const double TextureIndependentFearDistance = 75; + public const double TextureIndependentCalmingDistance = 175; public readonly double FearDistance; public readonly double CalmingDistance; @@ -36,6 +37,8 @@ public override void Update(GameTime gameTime, WorldState worldState) Wander(elapsedTime, worldState); else if (State == BoidState.Fleeing) Flee(elapsedTime, worldState); + else + throw new IncorrectBoidStateException($"Hare cannot be in state {State}"); } public void Wander(double elapsedTime, WorldState worldState) diff --git a/Hunter/HunterGame/GameObjects/Animals/Wolf.cs b/Hunter/HunterGame/GameObjects/Animals/Wolf.cs index c1ea4f1..6a86357 100644 --- a/Hunter/HunterGame/GameObjects/Animals/Wolf.cs +++ b/Hunter/HunterGame/GameObjects/Animals/Wolf.cs @@ -1,20 +1,24 @@ using System; +using System.Collections.Generic; using System.Linq; using Geometry; +using HunterGame.GameObjects.Bases; +using HunterGame.GameObjects.Boid; +using HunterGame.GameObjects.Boid.Interfaces; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Animals { - public class Wolf : Boid + public class Wolf : Boid.Boid, IWandering, IFollowing { public const double HuntingSpeed = 300; public const double WanderingSpeed = 75; public const double MaxForce = 5; - public const double TextureIndependentAgroDistance = 60; - public const double TextureIndependentCalmingDistance = 100; - public const double LifeTime = 30; - public const double StarvationTime = 25; + public const double TextureIndependentAgroDistance = 100; + public const double TextureIndependentCalmingDistance = 150; + public const double LifeTime = 40; + public const double StarvationTime = 30; public readonly double AgroDistance; public readonly double CalmingDistance; @@ -38,30 +42,29 @@ public override void Update(GameTime gameTime, WorldState worldState) LifeTimer -= elapsedTime; - if (LifeTimer <= 0) - { - IsAlive = false; - return; - } - if (State == BoidState.Wandering) Wander(elapsedTime, worldState); else if (State == BoidState.Following) Follow(elapsedTime, worldState); - - foreach (var creature in worldState.Creatures) - if (creature.IsAlive && !(creature is Wolf) && CollisionTester.TestCircleCircle(Circle, creature.Circle)) - { - creature.IsAlive = false; - LifeTimer = LifeTime; - State = BoidState.Wandering; - } + else + throw new IncorrectBoidStateException($"Wolf cannot be in state {State}"); + + if (LifeTimer <= StarvationTime) + foreach (var creature in worldState.Creatures) + if (creature.IsAlive && !(creature is Wolf) && CollisionTester.TestCircleCircle(Circle, creature.Circle)) + { + creature.IsAlive = false; + LifeTimer = LifeTime; + State = BoidState.Wandering; + } + + if (LifeTimer <= 0) + IsAlive = false; } public void Wander(double elapsedTime, WorldState worldState) { - var close = FindInRadius(worldState.Creatures, AgroDistance); - var preyFound = close.Any(creature => !(creature is Wolf)); + var preyFound = FindPrey(worldState.Creatures, AgroDistance).Any(); if (preyFound && LifeTimer <= StarvationTime) { @@ -88,8 +91,7 @@ public void StartWandering(Random random) public void Follow(double elapsedTime, WorldState worldState) { - var close = FindInRadius(worldState.Creatures, CalmingDistance); - var prey = close.Where(creature => !(creature is Wolf)).ToList(); + var prey = FindPrey(worldState.Creatures, CalmingDistance).ToList(); if (prey.Count == 0) { @@ -109,5 +111,10 @@ public void StartFollowing() { State = BoidState.Following; } + + public IEnumerable FindPrey(IEnumerable creatures, double distance) + { + return FindInRadius(creatures, distance).Where(creature => !(creature is Wolf)); + } } } \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Bases/CircularObject.cs b/Hunter/HunterGame/GameObjects/Bases/CircularObject.cs index 8144ea0..e2f7545 100644 --- a/Hunter/HunterGame/GameObjects/Bases/CircularObject.cs +++ b/Hunter/HunterGame/GameObjects/Bases/CircularObject.cs @@ -1,7 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Bases { public abstract class CircularObject : GameObject { diff --git a/Hunter/HunterGame/GameObjects/Bases/Creature.cs b/Hunter/HunterGame/GameObjects/Bases/Creature.cs index 6c6fd68..559c012 100644 --- a/Hunter/HunterGame/GameObjects/Bases/Creature.cs +++ b/Hunter/HunterGame/GameObjects/Bases/Creature.cs @@ -1,7 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Bases { public abstract class Creature : CircularObject { diff --git a/Hunter/HunterGame/GameObjects/Bases/GameObject.cs b/Hunter/HunterGame/GameObjects/Bases/GameObject.cs index f8d94df..d5a1d78 100644 --- a/Hunter/HunterGame/GameObjects/Bases/GameObject.cs +++ b/Hunter/HunterGame/GameObjects/Bases/GameObject.cs @@ -1,7 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Bases { public abstract class GameObject { diff --git a/Hunter/HunterGame/GameObjects/Bases/RectangularObject.cs b/Hunter/HunterGame/GameObjects/Bases/RectangularObject.cs index 41c2df2..562866a 100644 --- a/Hunter/HunterGame/GameObjects/Bases/RectangularObject.cs +++ b/Hunter/HunterGame/GameObjects/Bases/RectangularObject.cs @@ -1,7 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Bases { public abstract class RectangularObject : GameObject { diff --git a/Hunter/HunterGame/GameObjects/Boid/Boid.cs b/Hunter/HunterGame/GameObjects/Boid/Boid.cs index 0eb4c65..563f4bd 100644 --- a/Hunter/HunterGame/GameObjects/Boid/Boid.cs +++ b/Hunter/HunterGame/GameObjects/Boid/Boid.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using HunterGame.GameObjects.Bases; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Boid { public abstract class Boid : Creature { @@ -22,11 +23,11 @@ protected Boid(Texture2D texture, Vector2 position) : base(texture, position) } public virtual void Update(GameTime gameTime, WorldState worldState) {} - + public IEnumerable FindInRadius(IEnumerable creatures, double radius) { foreach (var creature in creatures) - if (creature != this && (creature.CenterPosition - CenterPosition).Length() <= radius) + if (creature != this && (creature.CenterPosition - CenterPosition).Length() - creature.Texture.Width / 2.0 <= radius) yield return creature; } @@ -42,17 +43,17 @@ public void CheckArrival(ref Vector2 target, Random random, Vector2 borderAvoidi public Vector2 ChooseNewTarget(Random random, bool keepDirection = true) { - var angle = MathHelper.ToRadians(random.Next(360)); + var angle = (float)random.Next(360); if (Velocity != Vector2.Zero && keepDirection) { var oldAngle = MathHelper.ToDegrees((float)Math.Atan2(Velocity.Y, Velocity.X)); angle = oldAngle + random.Next(-MaximumAngleChange, MaximumAngleChange + 1); - - angle = MathHelper.ToRadians(angle); } + angle = MathHelper.ToRadians(angle); + var direction = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle)); var distance = random.Next(100, 151); diff --git a/Hunter/HunterGame/GameObjects/Boid/BoidState.cs b/Hunter/HunterGame/GameObjects/Boid/BoidState.cs index 3c87c78..1c6ba47 100644 --- a/Hunter/HunterGame/GameObjects/Boid/BoidState.cs +++ b/Hunter/HunterGame/GameObjects/Boid/BoidState.cs @@ -1,4 +1,4 @@ -namespace HunterGame +namespace HunterGame.GameObjects.Boid { public enum BoidState { diff --git a/Hunter/HunterGame/GameObjects/Boid/IncorrectBoidStateException.cs b/Hunter/HunterGame/GameObjects/Boid/IncorrectBoidStateException.cs new file mode 100644 index 0000000..ece42b8 --- /dev/null +++ b/Hunter/HunterGame/GameObjects/Boid/IncorrectBoidStateException.cs @@ -0,0 +1,13 @@ +using System; + +namespace HunterGame.GameObjects.Boid +{ + public class IncorrectBoidStateException : Exception + { + public IncorrectBoidStateException() {} + + public IncorrectBoidStateException(string message) : base(message) {} + + public IncorrectBoidStateException(string message, Exception inner) : base(message, inner) {} + } +} \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFleeing.cs b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFleeing.cs new file mode 100644 index 0000000..b4c4973 --- /dev/null +++ b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFleeing.cs @@ -0,0 +1,9 @@ +namespace HunterGame.GameObjects.Boid.Interfaces +{ + public interface IFleeing + { + public void Flee(double elapsedTime, WorldState worldState); + + public void StartFleeing(); + } +} \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFollowing.cs b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFollowing.cs new file mode 100644 index 0000000..71afa10 --- /dev/null +++ b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IFollowing.cs @@ -0,0 +1,9 @@ +namespace HunterGame.GameObjects.Boid.Interfaces +{ + public interface IFollowing + { + public void Follow(double elapsedTime, WorldState worldState); + + public void StartFollowing(); + } +} \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Boid/Interfaces/IWandering.cs b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IWandering.cs new file mode 100644 index 0000000..e679d06 --- /dev/null +++ b/Hunter/HunterGame/GameObjects/Boid/Interfaces/IWandering.cs @@ -0,0 +1,11 @@ +using System; + +namespace HunterGame.GameObjects.Boid.Interfaces +{ + public interface IWandering + { + public void Wander(double elapsedTime, WorldState worldState); + + public void StartWandering(Random random); + } +} \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Pit.cs b/Hunter/HunterGame/GameObjects/Pit.cs index e52bf82..52fd080 100644 --- a/Hunter/HunterGame/GameObjects/Pit.cs +++ b/Hunter/HunterGame/GameObjects/Pit.cs @@ -1,17 +1,18 @@ -using Microsoft.Xna.Framework; +using HunterGame.GameObjects.Bases; +using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects { public class Pit : RectangularObject { - public readonly Geometry.Rectangle StaticRectangle; + public readonly Geometry.Rectangle CachedRectangle; - public override Geometry.Rectangle Rectangle => StaticRectangle; + public override Geometry.Rectangle Rectangle => CachedRectangle; public Pit(Vector2 position, Vector2 dimensions, GraphicsDevice graphicsDevice, Color color) : base(position, dimensions, graphicsDevice, color) { - StaticRectangle = base.Rectangle; + CachedRectangle = base.Rectangle; } } } \ No newline at end of file diff --git a/Hunter/HunterGame/GameObjects/Player/Bullet.cs b/Hunter/HunterGame/GameObjects/Player/Bullet.cs index 1915d75..aa3e828 100644 --- a/Hunter/HunterGame/GameObjects/Player/Bullet.cs +++ b/Hunter/HunterGame/GameObjects/Player/Bullet.cs @@ -1,31 +1,28 @@ using System; +using HunterGame.GameObjects.Bases; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects.Player { public class Bullet : CircularObject { public const double LinearVelocity = 300; - public double Timer = 1; - public readonly double Direction; + public double Timer = 2; + public readonly Vector2 Direction; public Bullet(Texture2D texture, Vector2 position, double direction) : base(texture, position) { - Direction = direction; + Direction = new Vector2((float)Math.Cos(direction), (float)Math.Sin(direction)); } public override void Update(GameTime gameTime) { var elapsedTime = Math.Min(Timer, gameTime.ElapsedGameTime.TotalSeconds); - - var dx = Math.Cos(Direction); - var dy = Math.Sin(Direction); - Position.X += (float)(dx * LinearVelocity * elapsedTime); - Position.Y += (float)(dy * LinearVelocity * elapsedTime); - + Position += Direction * (float)(LinearVelocity * elapsedTime); + Timer -= elapsedTime; if (Timer <= 0) diff --git a/Hunter/HunterGame/GameObjects/Player/Hunter.cs b/Hunter/HunterGame/GameObjects/Player/Hunter.cs index 5a85e6e..e78ae73 100644 --- a/Hunter/HunterGame/GameObjects/Player/Hunter.cs +++ b/Hunter/HunterGame/GameObjects/Player/Hunter.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using HunterGame.GameObjects.Bases; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; -namespace HunterGame +namespace HunterGame.GameObjects.Player { public class Hunter : Creature { diff --git a/Hunter/HunterGame/GameObjects/WorldState.cs b/Hunter/HunterGame/GameObjects/WorldState.cs index 97e3932..c13a53f 100644 --- a/Hunter/HunterGame/GameObjects/WorldState.cs +++ b/Hunter/HunterGame/GameObjects/WorldState.cs @@ -2,10 +2,13 @@ using System.Collections.Generic; using System.Linq; using Geometry; +using HunterGame.GameObjects.Animals; +using HunterGame.GameObjects.Bases; +using HunterGame.GameObjects.Player; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameObjects { public class WorldState { @@ -31,52 +34,50 @@ public WorldState(Dictionary textures, GraphicsDevice graphicsD public void AddHunter() { - var hunterPosition = new Vector2(MapWidth / 2f, MapHeight / 2f); - - Hunter = new Hunter(Textures[typeof(Hunter)], hunterPosition, Textures[typeof(Bullet)]); - - Creatures.Add(Hunter); + var position = new Vector2(MapWidth / 2f, MapHeight / 2f); + + AddCreature(typeof(Hunter), position, Textures[typeof(Bullet)]); + + Hunter = Creatures[0] as Hunter; } - public void AddAnimals(int hareCount, int doeGroupsCount, int wolfCount) + public void AddInitialAnimals(int hareCount, int doeGroupsCount, int wolfCount) { for (var i = 0; i < hareCount; i++) { var position = GenerateSafePosition(typeof(Hare)); - - AddAnimal(typeof(Hare), position); + AddCreature(typeof(Hare), position); } for (var i = 0; i < doeGroupsCount; i++) { var group = new DoeGroup(); - var groupPosition = GenerateSafePosition(typeof(Doe)); - var membersCount = Random.Next(DoeGroup.MinimumCompleteSize, DoeGroup.MaximumSize + 1); - + for (var j = 0; j < membersCount; j++) { var position = groupPosition; - - position.X += j % 4; - position.Y += j >> 2; + var dx = j % 4; + var dy = j / 4; + + position.X += dx; + position.Y += dy; - AddAnimal(typeof(Doe), position, group); + AddCreature(typeof(Doe), position, group); } } for (var i = 0; i < wolfCount; i++) { var position = GenerateSafePosition(typeof(Wolf)); - - AddAnimal(typeof(Wolf), position); + AddCreature(typeof(Wolf), position); } } public Vector2 GenerateSafePosition(Type t) { - var margin = Boid.TextureIndependentBorderAvoidanceDistance + Textures[t].Width; + var margin = Boid.Boid.TextureIndependentBorderAvoidanceDistance + Textures[t].Width; var x = Random.Next(margin, MapWidth - margin); var y = Random.Next(margin, MapHeight - margin); @@ -84,14 +85,9 @@ public Vector2 GenerateSafePosition(Type t) return new Vector2(x, y); } - public void AddAnimal(Type t, params object[] args) + public void AddCreature(Type t, params object[] args) { - var fullArgs = new object[args.Length + 1]; - fullArgs[0] = Textures[t]; - - for (var i = 0; i < args.Length; i++) - fullArgs[i + 1] = args[i]; - + var fullArgs = new [] {Textures[t]}.Concat(args).ToArray(); var creature = Activator.CreateInstance(t, fullArgs) as Creature; Creatures.Add(creature); @@ -134,7 +130,7 @@ public void Update(GameTime gameTime, Vector2 centerOffset) public void UpdateGameObjects(GameTime gameTime, Vector2 centerOffset) { foreach (var creature in Creatures) - if (creature is Boid boid) + if (creature is Boid.Boid boid) boid.Update(gameTime, this); foreach (var bullet in Bullets) diff --git a/Hunter/HunterGame/GameStates/GameState.cs b/Hunter/HunterGame/GameStates/GameState.cs index e46422e..098fbc9 100644 --- a/Hunter/HunterGame/GameStates/GameState.cs +++ b/Hunter/HunterGame/GameStates/GameState.cs @@ -1,10 +1,10 @@ using Microsoft.Xna.Framework; -namespace HunterGame +namespace HunterGame.GameStates { public abstract class GameState { - public MyGame Game; + public readonly MyGame Game; protected GameState(MyGame game) { diff --git a/Hunter/HunterGame/GameStates/MenuState.cs b/Hunter/HunterGame/GameStates/MenuState.cs index 8c7eb66..ecabc54 100644 --- a/Hunter/HunterGame/GameStates/MenuState.cs +++ b/Hunter/HunterGame/GameStates/MenuState.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; +using HunterGame.UI; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.GameStates { public class MenuState : GameState { - public readonly List InputFields = new List(); + public readonly Dictionary InputFields = new Dictionary(); public readonly List TextFields = new List(); public readonly Button StartButton; @@ -16,14 +17,14 @@ public MenuState(MyGame game) : base(game) var font = Game.Content.Load("MenuFont"); AddTextFields(10, 10, 50, new [] {"Hares:", "Doe groups:", "Wolves:"}, font); - AddInputFields(200, 10, 50, 3, font); - AddTextFields(10, 320, 0, new [] {"Controls:\n W/A/S/D - movement\n Q/E - zoom in/out\n LMB - shoot"}, font); + AddInputFields(200, 10, 50, new [] {"hares", "doe groups", "wolves"}, font); + AddTextFields(10, 320, 0, new [] {"Controls:\n LMB - shoot\n W/A/S/D - movement\n Q/E - zoom in/out\n Esc - return to menu"}, font); StartButton = new Button(new Vector2(10, 200), font, "Start game", (o, args) => { - var hareCount = int.Parse(InputFields[0].Content); - var doeGroupsCount = int.Parse(InputFields[1].Content); - var wolfCount = int.Parse(InputFields[2].Content); + var hareCount = int.Parse(InputFields["hares"].Content); + var doeGroupsCount = int.Parse(InputFields["doe groups"].Content); + var wolfCount = int.Parse(InputFields["wolves"].Content); Game.State = new PlayingState(Game, hareCount, doeGroupsCount, wolfCount); }); @@ -41,13 +42,13 @@ public void AddTextFields(int x, int y, int dy, IEnumerable contents, Sp } } - public void AddInputFields(int x, int y, int dy, int count, SpriteFont font) + public void AddInputFields(int x, int y, int dy, IEnumerable names, SpriteFont font) { - for (var i = 0; i < count; i++) + foreach (var name in names) { var inputField = new InputField(new Vector2(x, y), font, Game.GraphicsDevice, 1, "0", "1"); - InputFields.Add(inputField); + InputFields[name] = inputField; y += dy; } @@ -56,7 +57,7 @@ public void AddInputFields(int x, int y, int dy, int count, SpriteFont font) public override void Update(GameTime gameTime) { - foreach (var inputField in InputFields) + foreach (var inputField in InputFields.Values) inputField.Update(); StartButton.Update(); @@ -71,7 +72,7 @@ public override void Draw() foreach (var textField in TextFields) textField.Draw(Game.SpriteBatch); - foreach (var inputField in InputFields) + foreach (var inputField in InputFields.Values) inputField.Draw(Game.SpriteBatch); StartButton.Draw(Game.SpriteBatch); diff --git a/Hunter/HunterGame/GameStates/PlayingState.cs b/Hunter/HunterGame/GameStates/PlayingState.cs index dbb149e..6cbf68f 100644 --- a/Hunter/HunterGame/GameStates/PlayingState.cs +++ b/Hunter/HunterGame/GameStates/PlayingState.cs @@ -1,14 +1,18 @@ using System; using System.Collections.Generic; +using HunterGame.GameObjects; +using HunterGame.GameObjects.Animals; +using HunterGame.GameObjects.Player; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Input; -namespace HunterGame +namespace HunterGame.GameStates { public class PlayingState : GameState { public readonly WorldState WorldState; - + public readonly Vector2 CenterOffset; public readonly Camera Camera; @@ -17,7 +21,7 @@ public PlayingState(MyGame game, int hareCount, int doeGroupsCount, int wolfCoun WorldState = new WorldState(LoadTextures(), Game.GraphicsDevice, Game.Random); WorldState.AddHunter(); - WorldState.AddAnimals(hareCount, doeGroupsCount, wolfCount); + WorldState.AddInitialAnimals(hareCount, doeGroupsCount, wolfCount); WorldState.AddBorders(); CenterOffset = new Vector2(Game.GraphicsDevice.Viewport.Width / 2f, Game.GraphicsDevice.Viewport.Height / 2f); @@ -35,14 +39,14 @@ public Dictionary LoadTextures() {typeof(Bullet), Game.Content.Load("Bullet")}, }; } - + public override void Update(GameTime gameTime) { Camera.Update(); WorldState.Update(gameTime, CenterOffset); - if (!WorldState.Hunter.IsAlive) + if (!WorldState.Hunter.IsAlive || Keyboard.GetState().IsKeyDown(Keys.Escape)) Game.State = new MenuState(Game); Camera.Follow(WorldState.Hunter.Position, CenterOffset); diff --git a/Hunter/HunterGame/HunterGame.csproj b/Hunter/HunterGame/HunterGame.csproj index a0b5ffd..450aa33 100644 --- a/Hunter/HunterGame/HunterGame.csproj +++ b/Hunter/HunterGame/HunterGame.csproj @@ -1,7 +1,7 @@ - Exe + WinExe netcoreapp3.1 diff --git a/Hunter/HunterGame/MyGame.cs b/Hunter/HunterGame/MyGame.cs index b836e75..83ec019 100644 --- a/Hunter/HunterGame/MyGame.cs +++ b/Hunter/HunterGame/MyGame.cs @@ -1,4 +1,5 @@ using System; +using HunterGame.GameStates; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -10,7 +11,7 @@ public class MyGame : Game public SpriteBatch SpriteBatch; public GameState State; - public Random Random = new Random(); + public readonly Random Random = new Random(); public MyGame() { diff --git a/Hunter/HunterGame/UI/Button.cs b/Hunter/HunterGame/UI/Button.cs index b0e6cb4..c7bc537 100644 --- a/Hunter/HunterGame/UI/Button.cs +++ b/Hunter/HunterGame/UI/Button.cs @@ -3,7 +3,7 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; -namespace HunterGame +namespace HunterGame.UI { public class Button { diff --git a/Hunter/HunterGame/UI/InputField.cs b/Hunter/HunterGame/UI/InputField.cs index cd80c9f..3233221 100644 --- a/Hunter/HunterGame/UI/InputField.cs +++ b/Hunter/HunterGame/UI/InputField.cs @@ -4,12 +4,12 @@ using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; -namespace HunterGame +namespace HunterGame.UI { public class InputField { public const int VPadding = 5; - private const int BorderWidth = 2; + public const int BorderWidth = 2; public readonly Vector2 Position; public readonly Texture2D Texture; @@ -27,7 +27,9 @@ public class InputField public readonly Dictionary Symbols = new Dictionary { {Keys.D0, "0"}, {Keys.D1, "1"}, {Keys.D2, "2"}, {Keys.D3, "3"}, {Keys.D4, "4"}, - {Keys.D5, "5"}, {Keys.D6, "6"}, {Keys.D7, "7"}, {Keys.D8, "8"}, {Keys.D9, "9"} + {Keys.D5, "5"}, {Keys.D6, "6"}, {Keys.D7, "7"}, {Keys.D8, "8"}, {Keys.D9, "9"}, + {Keys.NumPad0, "0"}, {Keys.NumPad1, "1"}, {Keys.NumPad2, "2"}, {Keys.NumPad3, "3"}, {Keys.NumPad4, "4"}, + {Keys.NumPad5, "5"}, {Keys.NumPad6, "6"}, {Keys.NumPad7, "7"}, {Keys.NumPad8, "8"}, {Keys.NumPad9, "9"} }; public InputField(Vector2 position, SpriteFont font, GraphicsDevice graphicsDevice, int maxLength, string defaultValue = "", string content = null) @@ -79,16 +81,13 @@ public void Update() UpdateBoxColor(); } - else + else if (IsFocused) { - if (IsFocused) - { - var keyboardState = Keyboard.GetState(); + var keyboardState = Keyboard.GetState(); - UpdateContent(keyboardState); + UpdateContent(keyboardState); - PreviousKeyboardState = keyboardState; - } + PreviousKeyboardState = keyboardState; } PreviousMouseState = mouseState; diff --git a/Hunter/HunterGame/UI/TextField.cs b/Hunter/HunterGame/UI/TextField.cs index 24ec332..9a04993 100644 --- a/Hunter/HunterGame/UI/TextField.cs +++ b/Hunter/HunterGame/UI/TextField.cs @@ -1,7 +1,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; -namespace HunterGame +namespace HunterGame.UI { public class TextField {