


Fall Moon
Fall Moon is a 2D block pusher puzzler, themed after the Chinese Moon festival. The player has control over a paper dragon, who’s body can be extended and retracted. The player must put the moon in place by pushing it and doing it in as few moves as possible to get the highest score.
Platform:
Engine:
Language:
Development Time:
Team Size:
Android
Unity 5
C#
3 weeks
3 Designers & 2 Artists
My Role
During the game project, I was the Main Scripter. My main responsibilities were:
A Post-Mortem of the project can be found at the bottom of the page.
Grid
Grid
The grid is a collection of imaginary points, sorted in a way to allow each point to have coordinates. This is called a 2D array. Using this it is easy for tiles to move on the grid, as they only have to take their own coordinates and change it slightly to get an adjacent point.
< CODE: Grid >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(GridManager))] [System.Serializable] public class Grid : MonoBehaviour { //Collections internal Node[] nodes; //References [SerializeField] [HideInInspector] private GridManager gridManager; public void InsertNode(Vector2 _worldPosition, Vector2 _coordinates) { SetGridManagerReference(); //Insert node into array at 2D position int index = (int)_coordinates.y * (int)gridManager.nodeAmount.x + (int)_coordinates.x; nodes[index].coordinates = _coordinates; nodes[index].worldPosition = _worldPosition; } public Vector2 GetNodeWorldPosition(Vector2 _coordinates) { SetGridManagerReference(); //Get node from array at 2D position int index = (int)_coordinates.y * (int)gridManager.nodeAmount.x + (int)_coordinates.x; return nodes[index].worldPosition; } public void UpdateGridSize() { SetGridManagerReference(); //Make array 2 Dimensional nodes = new Node[(int)gridManager.nodeAmount.y * (int)gridManager.nodeAmount.x]; } public void Clear() { nodes = null; } private void SetGridManagerReference() { if (gridManager == null) { gridManager = GetComponent<GridManager>(); if (gridManager == null) Debug.Log("No GridManager present"); } } public int Length() { return nodes.Length; } public struct Node { public Vector2 coordinates; public Vector2 worldPosition; } } |
Level Editor
Fall Moon being a single screen puzzle game meant it needed a lot of levels to function. One of my main priorities was to make a system in which our two level designers could create levels with ease.
To do this I made a level editor. The editor could easily change the size of the grid, and made it so you could press a button to align all tiles to the grid. These features allowed the level designers to quickly make and iterate levels. A lot of coordinate and location data was also set up here, making the game itself having to setup less when you play it.
< CODE: Grid Manager >< CODE: Tile Manager >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Grid))] public class GridManager : MonoBehaviour { public Vector2 nodeAmount; [SerializeField] private Sprite nodeSprite; //References [HideInInspector] public Grid grid; public void CreateGrid() { ClearGrid(); grid.UpdateGridSize(); Vector2 bottomLeft = new Vector2(transform.position.x - (nodeAmount.x / 2), transform.position.y - (nodeAmount.y / 2)); //Loop trough all nodes in the 2D array for (int x = 0; x < nodeAmount.x; x++) { for (int y = 0; y < nodeAmount.y; y++) { Vector2 position = new Vector2(bottomLeft.x + (x + 0.5f), bottomLeft.y + (y + 0.5f)); Vector2 coordinates = new Vector2(x, y); grid.InsertNode(position, coordinates); CreateNodeSprite(position, coordinates); } } } public void ClearGrid() { SetGridReference(); grid.Clear(); List<GameObject> sprites = new List<GameObject>(); for (int i = 0; i < transform.childCount; i++) { sprites.Add(transform.GetChild(i).gameObject); } foreach (GameObject sprite in sprites) { DestroyImmediate(sprite); } } private void CreateNodeSprite(Vector2 position, Vector2 coordinates) { GameObject nodeObj = new GameObject(); nodeObj.transform.SetParent(transform); nodeObj.name = "Node [" + coordinates.x + " : " + coordinates.y + "]"; SpriteRenderer rend = nodeObj.AddComponent<SpriteRenderer>(); rend.sprite = nodeSprite; nodeObj.transform.position = new Vector3(position.x, position.y, transform.position.z); } private void SetGridReference() { if (grid == null) { grid = GetComponent<Grid>(); if (grid == null) Debug.Log("No Grid present"); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(Grid))] [RequireComponent(typeof(GridManager))] public class TileManager : MonoBehaviour { [SerializeField] private LayerMask tileMask; [SerializeField] [Range(0.1f, 1f)]private float detectionRadius = 0.15f; //References internal GameObject tilesParent; [SerializeField] [HideInInspector] private Grid grid; [SerializeField] [HideInInspector] private GridManager gridManager; public Contact CheckCoordinateCollisions(Vector2 coordinates) { SetGridReference(); SetGridManagerReference(); Contact collision = new Contact(); collision.tile = null; collision.boundary = false; if (coordinates.x < 0 || coordinates.x > gridManager.nodeAmount.x - 1 || coordinates.y < 0 || coordinates.y > gridManager.nodeAmount.y - 1) { collision.boundary = true; return collision; } Vector2 worldPosition = grid.GetNodeWorldPosition(coordinates); Collider2D tile = Physics2D.OverlapCircle(worldPosition, detectionRadius, tileMask); if(tile == null) { return collision; } else { collision.tile = tile.GetComponent<BasicTile>(); return collision; } } public void MoveTile(Vector2 coordinates, BasicTile tile) { SetGridReference(); tile.transform.position = grid.GetNodeWorldPosition(coordinates); tile.SetLocalCoordinates(coordinates); } public List<BasicTile> GetAllTiles() { SetGridReference(); SetGridManagerReference(); SetTileParent(); List<BasicTile> tiles = new List<BasicTile>(); for (int x = 0; x < gridManager.nodeAmount.x; x++) { for (int y = 0; y < gridManager.nodeAmount.y; y++) { Vector2 coordinates = new Vector2(x, y); BasicTile tile = CheckCoordinateCollisions(coordinates).tile; if (tile != null) { tiles.Add(tile); } } } return tiles; } public void AlignTiles() { SetGridReference(); SetGridManagerReference(); SetTileParent(); List<BasicTile> tiles = new List<BasicTile>(); //Check each node for any overlapping tiles for (int x = 0; x < gridManager.nodeAmount.x; x++) { for (int y = 0; y < gridManager.nodeAmount.y; y++) { Vector2 coordinates = new Vector2(x, y); BasicTile tile = CheckCoordinateCollisions(coordinates).tile; if (tile != null) { //If a tile is found, updates its local coordinates and position it correctly on the grid tile.transform.position = grid.GetNodeWorldPosition(coordinates); tile.SetLocalCoordinates(coordinates); tile.transform.parent = tilesParent.transform; tiles.Add(tile); } } } } public void ClearTiles() { SetTileParent(); if (tilesParent != null) { if (tilesParent.transform.childCount > 0) { List<GameObject> children = new List<GameObject>(); for (int i = 0; i < tilesParent.transform.childCount; i++) { children.Add(tilesParent.transform.GetChild(i).gameObject); } foreach (GameObject child in children) { DestroyImmediate(child); } } } } private void SetTileParent() { if (tilesParent == null) { GameObject parent = GameObject.Find("Tiles"); if (parent == null) { parent = new GameObject("Tiles"); tilesParent = parent; } else { tilesParent = parent; } } } private void SetGridReference() { if (grid == null) { grid = GetComponent<Grid>(); if (grid == null) Debug.Log("No Grid present"); } } private void SetGridManagerReference() { if (gridManager == null) { gridManager = GetComponent<GridManager>(); if (gridManager == null) Debug.Log("No Grid Manager present"); } } } public struct Contact { public BasicTile tile; public bool boundary; } |
Tiles

< CODE: Basic Tile >
Basic Tile
The dragon was supposed to function the same way the entire game, so the puzzles would be constructed from the layout of blockers and the use of different types of tiles. As we weren’t entirely sure what kind of tiles we wanted, i made a Basic Tile script from which all tiles inherited from which could quickly be expanded upon.
Each tile posses the same functions, allowing them to move and push other tiles. It can also be set if they should fall or not and if they should be entirely stationary, to act like a blocker. This all made it easy to add new tiles with slightly different behaviour, and allowed for much easier implementation of special tiles, like the moon that should end the level when it reaches the goal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(BoxCollider2D))] [RequireComponent(typeof(ParticleSpawner))] [RequireComponent(typeof(SpriteRenderer))] public class BasicTile : MonoBehaviour { public bool movable = false; public bool fall = false; public bool collide = true; public bool disappear = false; //Values protected float fallSpeed = 0.15f; private float currentFallDelay = 0; //Bools internal bool active = true; private bool currentFall = false; //Vectors internal Vector2 localCoordinates; internal Vector2 startCoordinates; //References protected TileManager tileManager; internal BoxCollider2D col; internal SpriteRenderer rend; internal LevelManager levelManager; internal ParticleSpawner particle; public virtual void Awake() { tileManager = FindObjectOfType<TileManager>(); currentFallDelay = fallSpeed; startCoordinates = localCoordinates; col = GetComponent<BoxCollider2D>(); rend = GetComponent<SpriteRenderer>(); levelManager = FindObjectOfType<LevelManager>(); particle = GetComponent<ParticleSpawner>(); if (disappear) { levelManager.StartRestart += RestartDisappear; levelManager.EndRestart += Reappear; } } public virtual void FixedUpdate() { if (fall && !levelManager.paused && !levelManager.win && !levelManager.restarting) { Fall(); } } public void Fall() { Vector2 newCoordinates = Direction(MoveDirections.down); Contact collision = tileManager.CheckCoordinateCollisions(newCoordinates); if (collision.boundary) { currentFallDelay = fallSpeed; if (currentFall) { levelManager.AdjustFallingTileAmount(false); currentFall = false; } Disappear(); return; } if (collision.tile != null && collision.tile.collide) { currentFallDelay = fallSpeed; if (currentFall) { levelManager.AdjustFallingTileAmount(false); currentFall = false; } } else if (currentFallDelay == 0) { tileManager.MoveTile(newCoordinates, this); if (!currentFall) { levelManager.AdjustFallingTileAmount(true); currentFall = true; } currentFallDelay = fallSpeed; } else { currentFallDelay = Mathf.Clamp(currentFallDelay - Time.fixedDeltaTime, 0, fallSpeed); if (!currentFall) { levelManager.AdjustFallingTileAmount(true); currentFall = true; } } } public virtual void SetLocalCoordinates(Vector2 newCoordinates) { localCoordinates = newCoordinates; } public virtual bool Move(MoveDirections moveDirection) { Vector2 newCoordinates = Direction(moveDirection); Contact collision = tileManager.CheckCoordinateCollisions(newCoordinates); if (collision.boundary) { return (false); } BasicTile otherTile = collision.tile; if (otherTile == null || otherTile.collide == false) { tileManager.MoveTile(newCoordinates, this); return (true); } else if (otherTile.collide == true && otherTile.movable == true) { //If the other tile can be moved, call the same Move function in it if (otherTile.Move(moveDirection)) { //If the other tile successfully moves, move this one and send the message on tileManager.MoveTile(newCoordinates, this); return (true); } } return (false); } private void RestartDisappear() { Disappear(true); } public virtual void Disappear(bool restart = false) { if (restart) { if(localCoordinates == startCoordinates) { return; } } else { if (active == true) { active = false; col.enabled = false; rend.enabled = false; particle.TriggerParticle(transform.position); } } } private void Reappear() { tileManager.MoveTile(startCoordinates, this); if (active == false) { active = true; col.enabled = true; rend.enabled = true; particle.TriggerParticle(transform.position); } } public Vector2 Direction(MoveDirections moveDirection) { switch (moveDirection) { case MoveDirections.left: return (new Vector2(localCoordinates.x - 1, localCoordinates.y)); case MoveDirections.right: return (new Vector2(localCoordinates.x + 1, localCoordinates.y)); case MoveDirections.up: return (new Vector2(localCoordinates.x, localCoordinates.y + 1)); case MoveDirections.down: return (new Vector2(localCoordinates.x, localCoordinates.y - 1)); } return localCoordinates; } public MoveDirections ReverseDirection(MoveDirections direction) { switch (direction) { case MoveDirections.left: return MoveDirections.right; case MoveDirections.right: return MoveDirections.left; case MoveDirections.up: return MoveDirections.down; case MoveDirections.down: return MoveDirections.up; } return MoveDirections.right; } public enum MoveDirections { left, right, up, down } } |
Dragon
Dragon Input
The dragon has two control methods to accommodate different playstyles. If you touch the dragon’s head directly, the dragon will try to follow your finger. This is a more accurate way of controlling the dragon but requires you to move your fingers a lot.
If you instead touch anywhere else on the grid the dragon will move with your finger, so if you swipe to the left, the dragon will move that much to the left. This allows the player to play more casually by simply moving their thumb, but with less accuracy.
Dragon Body
The dragon’s head inherits from the Basic Tile script and can be moved in any direction. It also spawns a body part behind it everytime it moves. All body parts are added to a list and if the player is trying to move into the last placed body part, the one behind the head, it will instead remove that tile.
The body parts keep track of what direction the previous one and the next body part is relative to it. It uses these directions to choose from a array of sprites, all with directions attached to them, which one is most suitable to use.
All body parts are also kept in a pool, as instantiating objects drains a phone’s battery power. By keeping them in a pool, the amount of times needed to instantiate a dragon part is massively decreased

< CODE: Dragon Input >< CODE: Dragon Head Tile >< CODE: Dragon Pooling >< CODE: Dragon Tile >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DragonInput : TouchReceiver { [SerializeField][Range(0.1f, 2f)]private float threshold = 0.75f; //References private TouchInput touchInput; private DragonHeadTile dragonHead; private LevelManager levelManager; //Bools private bool followInput; private bool anywhereTouch; //Values private int touchIndex; //Vectors private Vector2 oldTouchPos; private Vector2 touchDifference; private void Start() { touchInput = Camera.main.GetComponent<TouchInput>(); //An event that triggers everytime the player touches empty space touchInput.startTouch += TouchAnywhere; dragonHead = FindObjectOfType<DragonHeadTile>(); levelManager = FindObjectOfType<LevelManager>(); } private void Update() { if (followInput) { Vector2 touchPos = touchInput.TouchPosition(touchIndex); if (!levelManager.IfFalling() && !levelManager.paused && !levelManager.win && !levelManager.restarting) { //If the touch was not on the dragons head if (anywhereTouch) { //Detect how much the fingers move and move the Dragon that much touchDifference.x += touchPos.x - oldTouchPos.x; touchDifference.y += touchPos.y - oldTouchPos.y; if (Mathf.Abs(touchDifference.x) >= Mathf.Abs(touchDifference.y)) { if (touchDifference.x >= threshold) { touchDifference.x -= threshold; dragonHead.MoveDragon(BasicTile.MoveDirections.right); } else if (touchDifference.x <= -threshold) { touchDifference.x += threshold; dragonHead.MoveDragon(BasicTile.MoveDirections.left); } } else { if (touchDifference.y >= threshold) { touchDifference.y -= threshold; dragonHead.MoveDragon(BasicTile.MoveDirections.up); } else if (touchDifference.y <= -threshold) { touchDifference.y += threshold; dragonHead.MoveDragon(BasicTile.MoveDirections.down); } } } else { //If the finger is to far away, move it in that direction float distanceX = touchPos.x - transform.position.x; float distanceY = touchPos.y - transform.position.y; if (Mathf.Abs(distanceX) >= Mathf.Abs(distanceY)) { if (Mathf.Abs(distanceX) > threshold) { dragonHead.MoveDragon((Mathf.Sign(distanceX) == 1) ? BasicTile.MoveDirections.right : BasicTile.MoveDirections.left); } } else { if (Mathf.Abs(distanceY) > threshold) { dragonHead.MoveDragon((Mathf.Sign(distanceY) == 1) ? BasicTile.MoveDirections.up : BasicTile.MoveDirections.down); } } } } oldTouchPos = touchPos; } } private void TouchAnywhere(int touch) { anywhereTouch = true; SetTouchFollow(touch); } public override void TouchBegin(int touch) { anywhereTouch = false; SetTouchFollow(touch); } private void SetTouchFollow(int touch) { if (!followInput) { touchInput.endTouch += TouchEnd; followInput = true; touchIndex = touch; touchDifference = Vector2.zero; oldTouchPos = touchInput.TouchPosition(touchIndex); } } public override void TouchEnd(int touch) { if(followInput == true && touchIndex == touch) { touchInput.endTouch -= TouchEnd; followInput = false; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DragonHeadTile : BasicTile { [Space] [SerializeField] private MoveDirections startDirection; [Space] [SerializeField] private Sprite up; [SerializeField] private Sprite down; [SerializeField] private Sprite forward; //Collections private List<DragonTile> bodyParts = new List<DragonTile>(); //References private GameObject dragonParent; private DragonPooling dragonPool; [SerializeField] private AudioClip moveSound; private SoundPlayer theSP; //Values private int facingDirection; internal int moves; //Bools private bool retracting; public override void Awake() { base.Awake(); dragonPool = GetComponent<DragonPooling>(); dragonParent = new GameObject("Dragon"); dragonParent.transform.SetParent(tileManager.transform); transform.SetParent(dragonParent.transform); theSP = GetComponent<SoundPlayer>(); } private void Start() { MoveDirections reverseStart = ReverseDirection(startDirection); SetFacingDirection(reverseStart); SetDragonSprite(reverseStart); //Make Dragon retract when the "Restart" event triggers levelManager.StartRestart += StartRetract; } public void MoveDragon(MoveDirections direction) { if (retracting) return; Vector2 newCoordinates = Direction(direction); Vector2 oldCoordinates = localCoordinates; Contact collision = tileManager.CheckCoordinateCollisions(newCoordinates); if (collision.boundary) { return; } BasicTile otherTile = collision.tile; if ((otherTile == null || otherTile.collide == false)) { tileManager.MoveTile(newCoordinates, this); AddDragonPart(oldCoordinates, direction); SetFacingDirection(direction); SetDragonSprite(direction); //Add 1 to the overall moves this turn moves += 1; } else if (bodyParts.Count > 0 && otherTile == bodyParts[bodyParts.Count - 1]) { //If the other tile is the latest placed Dragon Body Part //Remove the body part and move the dragon head MoveBack(); //Add 1 to the overall moves this turn moves += 1; } else if (otherTile.collide == true && otherTile.movable == true) { //If the other tile can be moved if (otherTile.Move(direction)) { tileManager.MoveTile(newCoordinates, this); AddDragonPart(oldCoordinates, direction); SetFacingDirection(direction); SetDragonSprite(direction); //Add 1 to the overall moves this turn moves += 1; } } } private void MoveBack() { DragonTile dragonTile = bodyParts[bodyParts.Count - 1].GetComponent<DragonTile>(); facingDirection = dragonTile.savedFacingDirection; SetDragonSprite(ReverseDirection(dragonTile.from)); Vector2 dragonTileCoordinates = dragonTile.localCoordinates; RemoveDragonPart(dragonTile); tileManager.MoveTile(dragonTileCoordinates, this); } private void AddDragonPart(Vector2 coordinates, MoveDirections direction) { theSP.PlaySound(moveSound, 1f, (.7f + (0.03f*bodyParts.Count))); //Take Dragon Body Part from pool and place it on the right place on the grid DragonTile tile = dragonPool.RemoveFromPool(); bodyParts.Add(tile); tileManager.MoveTile(coordinates, tile); tile.transform.SetParent(dragonParent.transform); SetDragonPartSprite(direction, tile); } private void SetDragonPartSprite(MoveDirections direction, DragonTile dragonTile) { if(bodyParts.Count == 1) { dragonTile.SetSprite(startDirection, direction, facingDirection); } else if (bodyParts.Count > 1) { dragonTile.SetSprite(ReverseDirection(bodyParts[bodyParts.Count- 2].to), direction, facingDirection); } } private void RemoveDragonPart(DragonTile part) { theSP.PlaySound(moveSound, 1f, (.7f + (0.03f * bodyParts.Count))); //Add the dragon tile back to the pool bodyParts.Remove(part); dragonPool.AddToPool(part); } private void SetFacingDirection(MoveDirections direction) { switch (direction) { case MoveDirections.right: facingDirection = 1; break; case MoveDirections.left: facingDirection = -1; break; } } private void SetDragonSprite(MoveDirections direction) { switch (direction) { case MoveDirections.up: rend.sprite = up; rend.flipX = (facingDirection == 1) ? true : false; break; case MoveDirections.down: rend.sprite = down; rend.flipX = (facingDirection == 1) ? true : false; break; case MoveDirections.left: rend.sprite = forward; rend.flipX = false; break; case MoveDirections.right: rend.sprite = forward; rend.flipX = true; break; } } private void StartRetract() { StartCoroutine(Retract()); } public IEnumerator Retract() { retracting = true; moves = 0; //Retract until the entire body is retracted while (bodyParts.Count > 0) { MoveBack(); yield return new WaitForSeconds(0.1f); } retracting = false; levelManager.FinishRestart(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DragonPooling : MonoBehaviour { [SerializeField] private DragonTile bodyPart; //Collections internal Stack<DragonTile> pooledBodyParts = new Stack<DragonTile>(); //Vectors private Vector2 pooledPosition = new Vector2(1000, 1000); //References private GameObject pooledParent; private void Start() { pooledParent = new GameObject("Pooled Objects"); //Create a pool of Dragon Body Parts //Do this to prevent having to instanciate to many objects, saving mobile battery power CreateNewPool(); } private void CreateNewPool() { while(pooledBodyParts.Count > 0) { DestroyImmediate(pooledBodyParts.Pop().gameObject); } for(int i = 0; i < 16; i++) { CreatePoolTile(); } } private void CreatePoolTile() { DragonTile tile = Instantiate(bodyPart, pooledPosition, Quaternion.Euler(0, 0, 0)); tile.name = "Dragon Tile"; AddToPool(tile); } public void AddToPool(DragonTile tile) { tile.gameObject.SetActive(false); pooledBodyParts.Push(tile); tile.transform.position = pooledPosition; tile.transform.SetParent(pooledParent.transform); } public DragonTile RemoveFromPool() { DragonTile tile = pooledBodyParts.Pop(); tile.gameObject.SetActive(true); //If pool is empty, add a new Dragon Body Part to the pool if(pooledBodyParts.Count == 0) CreatePoolTile(); return tile; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
using System.Collections; using System.Collections.Generic; using UnityEngine; [RequireComponent(typeof(SpriteRenderer))] public class DragonTile : BasicTile { [SerializeField] private SpriteStructure[] sprites; //Enums internal MoveDirections from; internal MoveDirections to; //Values internal int savedFacingDirection; public override void Awake() { base.Awake(); rend = GetComponent<SpriteRenderer>(); } public void SetSprite(MoveDirections from, MoveDirections to, int facingDirection) { savedFacingDirection = facingDirection; foreach (SpriteStructure spriteStruct in sprites) { if (spriteStruct.sprite != null && spriteStruct.directions.Length > 0) { foreach (Directions direction in spriteStruct.directions) { if ((direction.a == from && direction.b == to)) { rend.sprite = spriteStruct.sprite; this.to = to; this.from = from; } } } else { continue; } } } [System.Serializable] public struct SpriteStructure { public Sprite sprite; public Directions[] directions; } [System.Serializable] public struct Directions { public MoveDirections a; public MoveDirections b; } } |
Post-Mortem
This project gave me a great chance to make tools and systems for the other designers to use, like the Level Editor. Making a game for a mobile device was also a fun and interesting challenge as it is something i have never done before, forcing me to quickly learn and adapt to something new. As we were also coming up with ideas as we went on, it made me create scripts that were easy to iterate and implement further upon.