diff --git a/README.md b/README.md index c65a90d..7fed989 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ A game of Snake built using C# in the MonoGame framework, themed around everyone - [ ] Record players score, kills and highest position. Probably can be added to the `GameScores` object. - [ ] Game over screen with score, kills, and highest position achieved - [ ] Particle system for the death of a sandworm -- [ ] Upgrade our movement system to reduce the lag in rotation ## Done +- [x] Max: Upgrade our movement system to reduce the lag in rotation - [x] Max: Add name support for the player - [x] Max: Setup Snake Movement in the screen - [x] Max: Decide how to build the snake. Tons of entities? Or one entity with a list of positions? diff --git a/src/Client/GameModel.cs b/src/Client/GameModel.cs index 08d251b..d5002c3 100644 --- a/src/Client/GameModel.cs +++ b/src/Client/GameModel.cs @@ -21,7 +21,7 @@ public class GameModel private Systems.KeyboardInput m_systemKeyboardInput; private Systems.MouseInput m_systemMouseInput; private Systems.Interpolation m_systemInterpolation; - private Systems.Renderer m_systemRenderer; + private Systems.WormRenderer _mSystemWormRenderer; private Controls m_controls; private GraphicsDeviceManager m_graphics; private SpriteFont m_font; @@ -44,7 +44,7 @@ public void update(TimeSpan elapsedTime) public void render(TimeSpan elapsedTime, SpriteBatch spriteBatch) { - m_systemRenderer.render(elapsedTime, spriteBatch); + _mSystemWormRenderer.render(elapsedTime, spriteBatch); } /// @@ -59,7 +59,7 @@ public bool initialize(ContentManager contentManager, Controls controls, Graphic m_entities = new Dictionary(); m_systemInterpolation = new Systems.Interpolation(); m_systemCamera = new Systems.Camera(new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight)); - m_systemRenderer = new Systems.Renderer(m_systemCamera, graphics, m_font); + _mSystemWormRenderer = new Systems.WormRenderer(m_systemCamera, graphics, m_font); m_systemNetwork = new Systems.Network(); m_systemNetwork.registerNewEntityHandler(handleNewEntity); @@ -137,6 +137,11 @@ private Entity createEntity(Shared.Messages.NewEntity message) { entity.add(new ChildId(message.childId)); } + + if (message.hasWorm) + { + entity.add(new Worm()); + } if (message.hasName) { @@ -161,7 +166,7 @@ private void addEntity(Entity entity) m_entities[entity.id] = entity; m_systemKeyboardInput.add(entity); m_systemMouseInput.add(entity); - m_systemRenderer.add(entity); + _mSystemWormRenderer.add(entity); m_systemNetwork.add(entity); m_systemInterpolation.add(entity); m_systemCamera.add(entity); @@ -178,7 +183,7 @@ private void removeEntity(uint id) m_systemKeyboardInput.remove(id); m_systemMouseInput.remove(id); m_systemNetwork.remove(id); - m_systemRenderer.remove(id); + _mSystemWormRenderer.remove(id); m_systemInterpolation.remove(id); m_systemCamera.remove(id); } diff --git a/src/Client/Menu/GamePlayView.cs b/src/Client/Menu/GamePlayView.cs index aadf8b4..d1c137c 100644 --- a/src/Client/Menu/GamePlayView.cs +++ b/src/Client/Menu/GamePlayView.cs @@ -96,7 +96,7 @@ public override void RegisterCommands() private bool connectToServer() { - return MessageQueueClient.instance.initialize("localhost", 3050); + return MessageQueueClient.instance.initialize("localhost", 3000); } private void escape(GameTime gameTime, float scale) diff --git a/src/Client/Systems/KeyboardInput.cs b/src/Client/Systems/KeyboardInput.cs index f44c2d7..f91f6cf 100644 --- a/src/Client/Systems/KeyboardInput.cs +++ b/src/Client/Systems/KeyboardInput.cs @@ -13,7 +13,7 @@ public class KeyboardInput : Shared.Systems.System private KeyboardState m_statePrevious = Keyboard.GetState(); private Controls m_controls; - public KeyboardInput(List> mapping, Controls controls) : base(typeof(Shared.Components.Input)) + public KeyboardInput(List> mapping, Controls controls) : base(typeof(Shared.Components.Worm)) { m_controls = controls; } @@ -35,6 +35,10 @@ public override void update(TimeSpan elapsedTime) // We have a dictionary of entities, so we need to iterate through them foreach (var entity in m_entities) { + if (!entity.Value.contains()) + { + continue; + } var inputs = new List(); if (keyPressed(m_controls.SnakeLeft.key)) { diff --git a/src/Client/Systems/MouseInput.cs b/src/Client/Systems/MouseInput.cs index 114c59e..932f18f 100644 --- a/src/Client/Systems/MouseInput.cs +++ b/src/Client/Systems/MouseInput.cs @@ -12,7 +12,7 @@ public class MouseInput : Shared.Systems.System private MouseState previousMouseState = Mouse.GetState(); private Controls m_controls; - public MouseInput(Controls controls) : base(typeof(Shared.Components.Input)) + public MouseInput(Controls controls) : base(typeof(Shared.Components.Worm)) { m_controls = controls; } @@ -30,6 +30,10 @@ public override void update(TimeSpan elapsedTime) foreach (var entityPair in m_entities) { var entity = entityPair.Value; + if (!entity.contains()) + { + continue; + } // Try to get the Position component safely try { diff --git a/src/Client/Systems/Renderer.cs b/src/Client/Systems/WormRenderer.cs similarity index 93% rename from src/Client/Systems/Renderer.cs rename to src/Client/Systems/WormRenderer.cs index bf2d797..8480980 100644 --- a/src/Client/Systems/Renderer.cs +++ b/src/Client/Systems/WormRenderer.cs @@ -12,17 +12,15 @@ namespace Client.Systems; -public class Renderer : Shared.Systems.System +public class WormRenderer : Shared.Systems.System { private Systems.Camera m_camera; private GraphicsDeviceManager m_graphics; private SpriteFont m_font; - public Renderer(Systems.Camera camera, GraphicsDeviceManager graphics, SpriteFont font) : + public WormRenderer(Systems.Camera camera, GraphicsDeviceManager graphics, SpriteFont font) : base( - typeof(Client.Components.Sprite), - typeof(Shared.Components.Position), - typeof(Shared.Components.Size) + typeof(Worm) ) { m_camera = camera; diff --git a/src/Server/GameModel.cs b/src/Server/GameModel.cs index 30def79..d768ead 100644 --- a/src/Server/GameModel.cs +++ b/src/Server/GameModel.cs @@ -133,7 +133,7 @@ private void handleJoin(int clientId, Shared.Messages.Message message) private void createNewWorm(int clientId, string name) { - var startLocation = new Vector2(200, 200); + var startLocation = getLeastDenseStartLocation(); var rotationRate = (float) Math.PI / 1000; var moveRate = 0.1f; var headSize = 100; @@ -166,15 +166,30 @@ private void createNewWorm(int clientId, string name) // Step 4: Let all other clients know about this new player entity // Remove components not needed for "other" players player.remove(); - Message message = new NewEntity(player); + + // Now send the new entities to all other clients + Message playerMessage = new NewEntity(player); + Message segmentMessage = new NewEntity(segment); + Message tailMessage = new NewEntity(tail); foreach (int otherId in m_clients) { if (otherId != clientId) { - MessageQueueServer.instance.sendMessage(otherId, message); + MessageQueueServer.instance.sendMessage(otherId, playerMessage); + MessageQueueServer.instance.sendMessage(otherId, segmentMessage); + MessageQueueServer.instance.sendMessage(otherId, tailMessage); } } } + + private Vector2 getLeastDenseStartLocation() + { + // We want to start the player in the least dense area of the screen + // For now, we'll just start them randomly generated location + + Random random = new Random(); + return new Vector2(random.Next(0, 800), random.Next(0, 600)); + } } } diff --git a/src/Server/Properties/launchSettings.json b/src/Server/Properties/launchSettings.json index 840f1d7..d87892e 100644 --- a/src/Server/Properties/launchSettings.json +++ b/src/Server/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Server": { "commandName": "Project", - "commandLineArgs": "--port 3050" + "commandLineArgs": "--port 3000" } } } \ No newline at end of file diff --git a/src/Server/Systems/Network.cs b/src/Server/Systems/Network.cs index 299594e..876e2ec 100644 --- a/src/Server/Systems/Network.cs +++ b/src/Server/Systems/Network.cs @@ -104,8 +104,11 @@ private void handleInput(Shared.Messages.Input message) switch (input) { case Shared.Components.Input.Type.SnakeUp: - Shared.Entities.Utility.thrust(entity, message.elapsedTime, m_entities); - m_reportThese.Add(message.entityId); + var snake = Shared.Entities.Utility.thrust(entity, message.elapsedTime, m_entities); + foreach (var part in snake) + { + m_reportThese.Add(part.id); + } break; case Shared.Components.Input.Type.RotateLeft: Shared.Entities.Utility.rotateLeft(entity, message.elapsedTime, m_entities); diff --git a/src/Shared/Components/Worm.cs b/src/Shared/Components/Worm.cs new file mode 100644 index 0000000..7ebaaf8 --- /dev/null +++ b/src/Shared/Components/Worm.cs @@ -0,0 +1,6 @@ +namespace Shared.Components; + +public class Worm : Component +{ + +} \ No newline at end of file diff --git a/src/Shared/Entities/Utility.cs b/src/Shared/Entities/Utility.cs index a00365c..f27da7c 100644 --- a/src/Shared/Entities/Utility.cs +++ b/src/Shared/Entities/Utility.cs @@ -7,12 +7,13 @@ namespace Shared.Entities; // NOTE: We will probably move this over to the wormMovement system where behavior lives public class Utility { - // Everything that hits these endpoints SHOULD be a WormHead (start of a LinkedList of worm parts) - public static void thrust(Entity entity, TimeSpan elapsedTime, Dictionary entities) + // The entity that hits these endpoints should be the head of the worm, with the rest of the worm in the entities + public static List thrust(Entity entity, TimeSpan elapsedTime, Dictionary entities) { var head = getHead(entity, entities); var snake = getSnakeFromHead(head, entities); applyThrust(snake, elapsedTime); + return snake; } public static void rotateLeft(Entity entity, TimeSpan elapsedTime, Dictionary entities) @@ -117,7 +118,7 @@ private static void applyThrust(List snake, TimeSpan elapsedTime) } - // We don't need to update the entire snake with these because it will be updated in the next frame when thrust is applied + // We don't need to update the entire worm with these because it will be updated in the next frame when thrust is applied private static void applyLeftRotation(Entity head, TimeSpan elapsedTime) { var position = head.get(); diff --git a/src/Shared/Entities/WormHead.cs b/src/Shared/Entities/WormHead.cs index c7678e1..98edcce 100644 --- a/src/Shared/Entities/WormHead.cs +++ b/src/Shared/Entities/WormHead.cs @@ -18,6 +18,7 @@ public static Entity create(Vector2 position, float size, float moveRate, float entity.add(new SpicePower(0)); entity.add(new Head()); entity.add(new Name(name)); + entity.add(new Worm()); List inputs = new List(); inputs.Add(Input.Type.SnakeUp); diff --git a/src/Shared/Entities/WormSegment.cs b/src/Shared/Entities/WormSegment.cs index 7c6cad1..b761f5d 100644 --- a/src/Shared/Entities/WormSegment.cs +++ b/src/Shared/Entities/WormSegment.cs @@ -10,12 +10,13 @@ public class WormSegment public static Entity create(Vector2 position, float size, float moveRate, float rotateRate, uint parent) { Entity entity = new Entity(); - entity.add(new Appearance("Textures/body")); entity.add(new Position(position)); + entity.add(new Appearance("Textures/body")); entity.add(new Size(new Vector2(size, size))); entity.add(new Movement(moveRate, rotateRate)); entity.add(new ParentId(parent)); entity.add(new Collision()); + entity.add(new Worm()); return entity; } } diff --git a/src/Shared/Entities/WormTail.cs b/src/Shared/Entities/WormTail.cs index 4261f56..e474403 100644 --- a/src/Shared/Entities/WormTail.cs +++ b/src/Shared/Entities/WormTail.cs @@ -11,12 +11,13 @@ public static Entity create(Vector2 position, float size, float moveRate, float { Entity entity = new Entity(); entity.add(new Position(position)); + entity.add(new Appearance("Textures/tail")); entity.add(new Size(new Vector2(size, size))); - entity.add(new Appearance("Textures/tail")); entity.add(new ParentId(parent)); entity.add(new Movement(moveRate, rotateRate)); entity.add(new Collision()); entity.add(new Tail()); + entity.add(new Worm()); return entity; } } \ No newline at end of file diff --git a/src/Shared/Messages/NewEntity.cs b/src/Shared/Messages/NewEntity.cs index 6cf2143..4f507e3 100644 --- a/src/Shared/Messages/NewEntity.cs +++ b/src/Shared/Messages/NewEntity.cs @@ -80,6 +80,11 @@ public NewEntity(Entity entity) : base(Type.NewEntity) this.hasCollision = true; } + if (entity.contains()) + { + this.hasWorm = true; + } + if (entity.contains()) { this.hasName = true; @@ -106,6 +111,7 @@ public NewEntity() : base(Type.NewEntity) public uint parentId { get; private set; } public bool hasChild { get; private set; } = false; public uint childId { get; private set; } + public bool hasWorm { get; private set; } = false; public bool hasName { get; private set; } = false; public string name { get; private set; } @@ -147,8 +153,10 @@ public override byte[] serialize() data.AddRange(BitConverter.GetBytes(hasTail)); serializeParentId(data); serializeChild(data); + data.AddRange(BitConverter.GetBytes(hasWorm)); serializeName(data); // Make sure this is the last item to serialize + return data.ToArray(); } @@ -174,10 +182,18 @@ public override int parse(byte[] data) offset = parseTail(data, offset); offset = parseParent(data, offset); offset = parseChild(data, offset); + offset = parseWorm(data, offset); offset = parseName(data, offset); // Make sure this is the last item to parse return offset; } + private int parseWorm(byte[] data, int offset) + { + this.hasWorm = BitConverter.ToBoolean(data, offset); + offset += sizeof(bool); + return offset; + } + private int parseId(byte[] data, int offset) { this.id = BitConverter.ToUInt32(data, offset);