︎︎︎
Castles
Tags :
︎ Game, Mobile App
Status :
︎In development
Role :
Director, Game Programmer, Game Designer
︎ Collaboration with Andrew Kovacs
More artists working on the game
︎ Corie Yaguchi, Concept -Texture Artist
︎ Zanem Mechem , 3D Generalist
︎ Andrew Chittenden, Character Animator
︎ Andres Gandara, Animator
︎ Yu Han , 3D Artist
︎ “team work makes the dream work”
Tags :
︎ Game, Mobile App
Status :
︎In development
Role :
Director, Game Programmer, Game Designer
︎ Collaboration with Andrew Kovacs
More artists working on the game
︎ Corie Yaguchi, Concept -Texture Artist
︎ Zanem Mechem , 3D Generalist
︎ Andrew Chittenden, Character Animator
︎ Andres Gandara, Animator
︎ Yu Han , 3D Artist
︎ “team work makes the dream work”
︎ 2020 08
![]()

︎ Project Breakdown
“Castles” is a project started in January 2020 and to be released in 2022.
“Castles” is a mobile game application that aims to include :
. authentication
. analytics
. monetization
. data collection
. scalability
. character customization
. inventory
. procedural environments
. in-game cinematic shorts
. chat - duel multiplayer
. augmented reality features
. in-game store
. in-game social platform
“Castles” is a mobile game application that aims to include :
. authentication
. analytics
. monetization
. data collection
. scalability
. character customization
. inventory
. procedural environments
. in-game cinematic shorts
. chat - duel multiplayer
. augmented reality features
. in-game store
. in-game social platform

︎ Procedural Environment
. still in the early development, one of the key features is the procedural environment that the players inhabit
01. it starts with a tridimensional grids of Transform slots
02. successively, a folder filled up by assets created by the team of artists populates the available positions according certain parameters
03. the circulation is created right after connecting the assets with each other
04. a number of objects to be collected are instantiated in the environment
︎ see code scrolling below
01. it starts with a tridimensional grids of Transform slots
02. successively, a folder filled up by assets created by the team of artists populates the available positions according certain parameters
03. the circulation is created right after connecting the assets with each other
04. a number of objects to be collected are instantiated in the environment
︎ see code scrolling below

︎︎︎Unity screenshots

︎
. Review / comunication tool for Castles
. Review / comunication tool for Castles
︎ Gameplay
. the game takes advantage of Portrait and Landscape mode on smartphone.
︎ Potrait mode
zommed out explore mode.
︎ Landscape mode
tilted TPV game mode.
︎ Potrait mode
zommed out explore mode.
︎ Landscape mode
tilted TPV game mode.
︎︎︎ Unity screenshots
![]()

︎ Shared Code Sample :
Spawning the Castle
. this script collaborates takes care of the level management, the instantiation of the objects from the resources folder and the pooling/state of the objects already been instantiated in the scene.
. this manager script allows to create a tridimensional grid of positons which will be successively filled by prebuilt assets.
. the steps are very similar to the ones listed above in the overview description of the project.
Before is created a “core” that handles a sequence of “origins”. In a second moment one grid is instantiated on every origin position.
. finally all of the available positions are filled asyncronously through batches of assets that will be defining how much the player’s environment will be growing.
. in progress there is also the possibility to track the position of the assets around the boundaries of the castle, to experiment with more in-game interactive behaviors.
Spawning the Castle
. this script collaborates takes care of the level management, the instantiation of the objects from the resources folder and the pooling/state of the objects already been instantiated in the scene.
. this manager script allows to create a tridimensional grid of positons which will be successively filled by prebuilt assets.
. the steps are very similar to the ones listed above in the overview description of the project.
Before is created a “core” that handles a sequence of “origins”. In a second moment one grid is instantiated on every origin position.
. finally all of the available positions are filled asyncronously through batches of assets that will be defining how much the player’s environment will be growing.
. in progress there is also the possibility to track the position of the assets around the boundaries of the castle, to experiment with more in-game interactive behaviors.
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 238 239 240 241 242 243 244 245 | using System.Collections; using System.Collections.Generic; using System.Dynamic; using UnityEngine; public class CastleSpawner : MonoBehaviour { [Tooltip("The general spacing between the tridimensional grid")] public float gridSpacingOffset = 1f; [Tooltip("How many steps the core keeps creating grid or so called floors")] public int coreStep = 100; [Tooltip("The spacing")] public int coreSpacingOffset = 1; [Tooltip("The size of every grid floor in X direction")] public int gridX; [Tooltip("The size of every grid floor in Y direction")] public int gridY; [Tooltip("The minimum range which the floors are distantiated")] public int rangeMin = 5; [Tooltip("The maximum range which the floors are distantiated")] public int rangeMax = 10; [Tooltip("The list of cores elements placeholder")] public List<GameObject> cubeCores; [Tooltip("The list of grid elements placeholder")] public List<GameObject> cubeGrids; [Tooltip("The received list of prefabs that needs to be instantiated")] public List<GameObject> prefabs; [Tooltip("The received list of prefabs that needs to be instantiated")] public List<GameObject> Instantiatedprefabs; [Tooltip("The scale factor for the instantiated assets")] public float scale = 1f; [Tooltip("The max amount of instantiated prefab")] public int maxAssetsAmount = 50; [Tooltip("The max amount of instantiated prefab repetitions")] public float assetsRepetition = 2f; GameObject[] assetPrefab; GameObject parentCores; GameObject parentGrids; GameObject parentPrefabs; GameObject parentCastle; GameObject parentActive; private Vector2 minMaxColumn, minMaxRow; private GridObject[] gridObjects = null; public struct GridObject { public Transform Transform; public float RowNo; public float ColumnNo; } [Tooltip("Starting amount of objects (level 1)"), SerializeField] private int startingAmountOfObjects = 1; [Tooltip("Increasing amount of objects after every level"), SerializeField] private int objectIncrementationPerLevel = 3; private int currentSpawningObjNo = 0; private void Awake() { // update the amount of active prefabs based on the level index LevelManager.OnLevelChanged += UpdateObjectActivation; } private void OnDestroy() { LevelManager.OnLevelChanged -= UpdateObjectActivation; } void Start() { // here set some parent game objects to have some order parentCores = new GameObject("parentCores"); parentGrids = new GameObject("parentGrids"); parentActive = new GameObject("parentActive"); // start creation coroutine StartCoroutine(coreSpawn(coreStep, cubeCores, rangeMin, rangeMax, parentCores)); StartCoroutine(gridSpawn(cubeCores, cubeGrids, gridX, gridY, parentGrids, "Grid01")); StartCoroutine(ShuffleCastle(cubeGrids, scale, maxAssetsAmount, assetsRepetition, parentActive, 1, "Castle01", "Grid01")); // optimization - info // since you loop through all the assets in the folder with a random sequence sometimes it could happen that // since it's random in 20 iterations you get out 17 objects [for examples] // because it's looping 20 times but for 3 random times it picked some repeated random choice and as the script wants // it didn't instantiate. } // just to visually keeping track of the positions void OnDrawGizmos() { if (parentActive.transform.childCount > 0) { DebugLines(parentActive, 5f); } } //phase 01 create cores - working in height first IEnumerator coreSpawn(int coreSteps, List<GameObject> itemsToAdd, int rangeMin, int rangeMax, GameObject parent) { GameObject placeholder = GameObject.CreatePrimitive(PrimitiveType.Cube); int rangeRandom = Random.Range(rangeMin, rangeMax); for (int y = 0; y < coreSteps; y += rangeRandom) { GameObject cube = Instantiate(placeholder, new Vector3(0, y, 0), Quaternion.identity); rangeRandom = Random.Range(rangeMin, rangeMax); itemsToAdd.Add(cube); cube.transform.parent = parent.transform; cube.GetComponent<BoxCollider>().enabled = false; } Destroy(placeholder); yield return null; } // phase 2 create grids in every level that previously has been created by the cores IEnumerator gridSpawn(List<GameObject> itemsToPos, List<GameObject> itemsToAdd, float xDir, float zDir, GameObject parent, string tag) { // here you get the maximum and minimum in x and z direction // this will be dictating thhe rows and columns to move for the pooling // vector 2 is for maximum and minimum minMaxColumn = new Vector2(0, xDir * gridSpacingOffset); minMaxRow = new Vector2(0, zDir * gridSpacingOffset); GameObject placeholder = GameObject.CreatePrimitive(PrimitiveType.Cube); for (var i = 0; i < itemsToPos.Count; i++) { for (int x = 0; x < xDir; x++) { for (int z = 0; z < zDir; z++) { Vector3 origin = new Vector3(x * gridSpacingOffset, itemsToPos[i].transform.position.y, z * gridSpacingOffset);// + newOrigin; GameObject cube = Instantiate(placeholder, origin, Quaternion.identity); itemsToAdd.Add(cube); cube.transform.parent = parent.transform; parent.tag = tag; //disable collider and mesh renderer //cube.GetComponent<MeshRenderer>().enabled = false; cube.GetComponent<BoxCollider>().enabled = false; } } } Destroy(placeholder); yield return null; } //phase 3 - shuffle on available slots IEnumerator ShuffleCastle( List<GameObject> availableSlotsToLocate, float scaleFactor, int maxObjectsToInstantiate, float maxObjectSpawnTimes, GameObject parentActive, int castleNumber, string parentTag, string gridTag) { yield return new WaitForSeconds(1); // get the gameobject where is instantiated the prefabs to shuffle GameObject parentObj = GameObject.FindGameObjectWithTag(parentTag); // get the gameobject where is instantiated the grid GameObject gridObj = GameObject.FindGameObjectWithTag(gridTag); // create a dictionary to comare object and names // this is only because you want to instantiate 1 object from the list once and // not repeating - is a check every loop Dictionary<GameObject, int> objectCountList = new Dictionary<GameObject, int>(); // get the array of turned off prefabs transforms under the parent castle List<GameObject> children = parentObj.GetChildren(); List<GameObject> spawnPoints = gridObj.GetChildren(); List<GridObject> instGridAssets = new List<GridObject>(); // do not instantiate more than maxObjectsToInstantiate for (int i = 0; i < maxObjectsToInstantiate; i++) { // get a temporary reference that is a random element every loop GameObject randomObj = children[currentSpawningObjNo]; currentSpawningObjNo++; if (currentSpawningObjNo >= children.Count) currentSpawningObjNo = 0; // get a temporary reference that is a random position from the sapwn grid // here's the thing - we check if an asset has been instantiated twice // but we also need to check if this asset is occupying eventually the same position twice GameObject randomSpawn = spawnPoints[UnityEngine.Random.Range(0, spawnPoints.Count)]; // if the object to instantiate has a name that is the the list // do not instantiated it again or viceversa if (!objectCountList.ContainsKey(randomObj.gameObject)) { objectCountList.Add(randomObj, 1); } else { objectCountList[randomObj]++; // Are you already hitting your limit? No, then you are allowed to spawn. } if (objectCountList[randomObj] <= maxObjectSpawnTimes) { // yield return new WaitForSeconds(0.2f); // cool timing effect to merge with scale // in this way you track the instantied object // differently from only "instantiate" that do not track the new instance GameObject assetInstantiated = Instantiate(randomObj, randomSpawn.transform.position, Quaternion.identity); //Vector3.zero assetInstantiated.transform.parent = parentActive.transform; assetInstantiated.transform.localScale = new Vector3( assetInstantiated.transform.localScale.x * scaleFactor, assetInstantiated.transform.localScale.y * scaleFactor, assetInstantiated.transform.localScale.z * scaleFactor); assetInstantiated.SetActive(false); // here it's tracked the tranform of every asset plus is tracked also the x and z positions instGridAssets.Add(new GridObject() { Transform = assetInstantiated.transform, RowNo = randomSpawn.transform.position.z, ColumnNo = randomSpawn.transform.position.x }); } } gridObjects = instGridAssets.ToArray(); yield return null; LevelManager.NextLevel(); } } public static class ExtensionMethods { public static List<GameObject> GetChildren(this GameObject go) { List<GameObject> children = new List<GameObject>(); foreach (Transform tran in go.transform) { children.Add(tran.gameObject); } return children; } } |
︎ Design through Pooling
. another pivotal mechanic is the pooling system that shifts the location of environmental asset based on the player position.
01. the player approaches the boundary.
02. before to approach the last transform slot withing the castle boundary in a given direction the swap occurs.
03. in the portrait mode the assets are set active for keep “desiging through motion”.
01. the player approaches the boundary.
02. before to approach the last transform slot withing the castle boundary in a given direction the swap occurs.
03. in the portrait mode the assets are set active for keep “desiging through motion”.

︎ Shared Code Sample :
Isolate for shifting
. the method isolates a part of the castle for being moved somewhere else.
Isolate for shifting
. the method isolates a part of the castle for being moved somewhere else.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public void MoveColumn(int columnAmount, bool moveToLeft) { // Detect with the Player on what Row + Column number they're walking now. If the Column Number of the player is larger/smaller than the minMaxColumn, then execute MoveColumn. List<GridObject> filteredAssets = new List<GridObject>(gridObjects); float fromColumnCoordinate = moveToLeft ? minMaxColumn.y - columnAmount : columnAmount; if (moveToLeft) { for (int i = 0; i < gridObjects.Length; i++) if (gridObjects[i].ColumnNo < fromColumnCoordinate) { filteredAssets.Remove(gridObjects[i]); gridObjects[i].Transform.gameObject.SetActive(false); } } else { for (int i = 0; i < gridObjects.Length; i++) if (gridObjects[i].ColumnNo > fromColumnCoordinate) filteredAssets.Remove(gridObjects[i]); } } |
︎ Gameplay Update
. currently focusing on the distribution of the asset and the game overall look
︎︎︎ 09.01.2020