patterncsharpMinor
Snake Game in C# - follow-up
Viewed 0 times
gamefollowsnake
Problem
Follow up to this question : Snake Game in C#
The game plays like it did in my first question. The design is altered.
Gameplay Video
Some of the changes:
Overview
-
Interfaces, all are in interfaces.cs
-
Classes with IGridable
-
Utilities
-
-
-
-
-
-
-
Game.cs
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Misc;
namespace SnakeGameSOLID
{
//Monobehaviour is from Unity
public class Game : MonoBehaviour
{
public GameObject cube;
public Grid Grid { get; private set; }
public Score Score { get; private set; }
public MapCreator MapCreator { get; private set; }
//Used to handle graphics in Unity
public GraphicInstantiator GraphicInstantiator { get; private set; }
InputHandler input;
Snake snake;
Direction inputDirection;
float time
The game plays like it did in my first question. The design is altered.
Gameplay Video
Some of the changes:
- Polymorphism is removed, replaced by Interfaces (see interfaces.cs)
CreateGame,DeleteGameandGameOverare now methods inGame
- Snake movement is handled differenly, moved from
SnakeParttoSnake
- Removed
DirectionObj, it is no longer needed
Databaseis nowGrid
- Created
MapCreator
Before(),After(),Next()are now justUpdate(Game game)
- Created
GraphicandGraphicInstantiator
- Direction is now just an enum
GameObjectInstantiatoris nowGraphicInstantiator
Overview
class Game : Monobehaviour
-
class SnakeInterfaces, all are in interfaces.cs
interface IUpdatable
interface IGraphical
-
interface IGridable : IGraphicalClasses with IGridable
class Apple : IGridable, IUpdatable
class Barrier : IGridable, IUpdatable
-
class SnakePart : IGridableUtilities
-
class InputHandlerclass MapCreator
class Score
-
enum Direction {Left, Right, Up, Down, None}-
class Grid : MultipleValuesDictionary-
class MultipleValuesDictionary : Dictionary> -
class GraphicInstantiator-
class Graphic-
struct IVector2 (Not in the project itself, is a 2D coordinate with int x and int y)Game.cs
```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Misc;
namespace SnakeGameSOLID
{
//Monobehaviour is from Unity
public class Game : MonoBehaviour
{
public GameObject cube;
public Grid Grid { get; private set; }
public Score Score { get; private set; }
public MapCreator MapCreator { get; private set; }
//Used to handle graphics in Unity
public GraphicInstantiator GraphicInstantiator { get; private set; }
InputHandler input;
Snake snake;
Direction inputDirection;
float time
Solution
I can see some FPS drop in your game each time you eat or spawn new food object. This is important because that's a game and FPS is crucial factor for a pleasant experience, not only code-style, architecture etc.. You should consider applying the object pooling concept to your project.
Which works like this :
-
It creates a list of
-
If you need to show an object you take it from the "pool" of already created
-
Once you are done using the object you set it back to inactive state (
This technique allows you avoid calling methods such as
Note
Most Object Poolers allow dynamic increment of the size of the
Here is an example of Object Pooler class (reference).
And this is the actual object pooling class :
Just attach as much
After you obtain free object just set it to active and adjust the position and the other properties you need to.
Which works like this :
-
It creates a list of
GameObject at the start of the game (optionally when requested) and sets all of them to inactive (gameObject.SetActive(false)).-
If you need to show an object you take it from the "pool" of already created
GameObjects, and set it to active (gameObject.SetActive(true)).-
Once you are done using the object you set it back to inactive state (
gameObject.SetActive(false)).This technique allows you avoid calling methods such as
Instantiate and Destroy which are quite expensive to call and perform. All of the objects are created at the beginning so there will be almost no fps drop when activating one of them.Note
Most Object Poolers allow dynamic increment of the size of the
List in case you need an object but you don't have any "free"(inactive) objects.Here is an example of Object Pooler class (reference).
///
/// Information holder for a pooled object
///
[Serializable]
public class PooledObject
{
[Tooltip(@"Name is used to differ the objects from one another")]
public string Name;
[Tooltip(@"What object should be created ?")]
public GameObject Object;
[Range(1, 10000)] [Tooltip(@"How much objects should be created ?")]
public int Amount;
[Tooltip(@"Can new objects be created in case there are none left ?")]
public bool CanGrow;
[Tooltip(@"False - objects must be created manually using Populate method
True - objects will be created automatically on awake")]
public bool CreateOnAwake;
}And this is the actual object pooling class :
///
/// Object pooler class which can hold a single type of objects.
///
public class MonoObjectPooler : MonoBehaviour
{
///
/// Object to be pooled.
///
public PooledObject PooledObject;
///
/// List to store all the objects that will be pooled.
///
private readonly List pooledObjects = new List();
private void Awake()
{
//Create the pooled object if the CreateOnAwake variable is set to true.
if (PooledObject.CreateOnAwake)
{
Populate();
}
}
///
/// Populates the list of pooled objects with PooledObjects.
///
public void Populate()
{
//Clear the previous items in the list.
pooledObjects.Clear();
//Load the items again
for (int i = 0; i
/// Returns a PooledObject.
///
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObjects.Count; i++)
{
if (!pooledObjects[i].activeInHierarchy)
{
//we have an available object
return pooledObjects[i];
}
}
if (PooledObject.CanGrow)
{
//we ran out of objects but we can create more
GameObject obj = Instantiate(PooledObject.Object);
pooledObjects.Add(obj);
return obj;
}
//We ran out of objects and we cant create more
return null;
}
}Just attach as much
MonoObjectPooler objects you need, to some GameManager GameObject in your scene, set up the values and you are good to go. Note that you will need to attach separate MonoObjectPooler script to the GameManager for each different object type. IF you like to have everything in one place you can check out the PolyObjectPooler class in the reference link, but keep in mind that many MonoObjectPoolers will be more performant than a single PolyObjectPooler.After you obtain free object just set it to active and adjust the position and the other properties you need to.
Code Snippets
/// <summary>
/// Information holder for a pooled object
/// </summary>
[Serializable]
public class PooledObject
{
[Tooltip(@"Name is used to differ the objects from one another")]
public string Name;
[Tooltip(@"What object should be created ?")]
public GameObject Object;
[Range(1, 10000)] [Tooltip(@"How much objects should be created ?")]
public int Amount;
[Tooltip(@"Can new objects be created in case there are none left ?")]
public bool CanGrow;
[Tooltip(@"False - objects must be created manually using Populate method
True - objects will be created automatically on awake")]
public bool CreateOnAwake;
}/// <summary>
/// Object pooler class which can hold a single type of objects.
/// </summary>
public class MonoObjectPooler : MonoBehaviour
{
/// <summary>
/// Object to be pooled.
/// </summary>
public PooledObject PooledObject;
/// <summary>
/// List to store all the objects that will be pooled.
/// </summary>
private readonly List<GameObject> pooledObjects = new List<GameObject>();
private void Awake()
{
//Create the pooled object if the CreateOnAwake variable is set to true.
if (PooledObject.CreateOnAwake)
{
Populate();
}
}
/// <summary>
/// Populates the list of pooled objects with PooledObjects.
/// </summary>
public void Populate()
{
//Clear the previous items in the list.
pooledObjects.Clear();
//Load the items again
for (int i = 0; i < PooledObject.Amount; i++)
{
GameObject obj = Instantiate(PooledObject.Object);
obj.SetActive(false);
pooledObjects.Add(obj);
}
}
/// <summary>
/// Returns a PooledObject.
/// </summary>
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObjects.Count; i++)
{
if (!pooledObjects[i].activeInHierarchy)
{
//we have an available object
return pooledObjects[i];
}
}
if (PooledObject.CanGrow)
{
//we ran out of objects but we can create more
GameObject obj = Instantiate(PooledObject.Object);
pooledObjects.Add(obj);
return obj;
}
//We ran out of objects and we cant create more
return null;
}
}Context
StackExchange Code Review Q#152140, answer score: 5
Revisions (0)
No revisions yet.