You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
9.5 KiB

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using System.Linq;
  5. /// <summary>
  6. /// Class which defines blocks around the level
  7. /// </summary>
  8. [RequireComponent(typeof(BoxCollider))]
  9. public class Block : MonoBehaviour
  10. {
  11. #region Inspector Fields
  12. [SerializeField]
  13. [Tooltip("Offset from the top of the block from which a character should stand")]
  14. private Vector3 VisualOffset = Vector3.zero;
  15. [SerializeField]
  16. [Tooltip("Can this type of block be walked on")]
  17. public bool is_Walkable = true;
  18. [Tooltip("Is this block underwater?")]
  19. public bool isWater = false;
  20. [Tooltip("Is this block at the bottom of a pit?")]
  21. public bool isPit = false;
  22. public bool isCrystals = false;
  23. public bool isRock = false;
  24. public bool isCollectableSpawnable = false;
  25. [Header("Spawn Settings")]
  26. [Tooltip("Can this block be spawned on")]
  27. public bool isSpawnable = false;
  28. [Tooltip("Direction Player is poting at when spawned")]
  29. public Direction SpawnDirection = Direction.Forward;
  30. #endregion InspectorFields
  31. #region Private Functions
  32. /// <summary>
  33. /// List of current players on this block
  34. /// </summary>
  35. public Character CurrentPlayer { get; set; }
  36. private Renderer renderer;
  37. #endregion Private Functions
  38. #region ReadOnly Properties
  39. /// <summary>
  40. /// Blocks position in global space
  41. /// </summary>
  42. public Vector3 position { get { return transform.position; } }
  43. /// <summary>
  44. /// Position character should stand in global space
  45. /// </summary>
  46. public Vector3 VisualPosition { get { return position + VisualOffset + Vector3.up * 0.5f; } }
  47. #endregion ReadOnly Properties
  48. #region Public Functions
  49. /// <summary>
  50. /// Is a block valid to walk on
  51. /// </summary>
  52. /// <param name="layerMask">Layers to check for when checking for blocks above</param>
  53. /// <returns></returns>
  54. public bool isWalkable(LayerMask layerMask)
  55. {
  56. //checks if there is no block above this one and that this is tagged as walkable
  57. return (is_Walkable && !isBlockAtPosition(position + Vector3.up, 1, layerMask));
  58. }
  59. /// <summary>
  60. /// Is called when a player moves onto this block
  61. ///
  62. /// Should be implemented by a derived class
  63. /// </summary>
  64. /// <param name="player">Player which moved on to block</param>
  65. ///<param name="moveDirection">The direction the player moved to get to this block</param>
  66. public virtual IEnumerator OnWalkedOnByPlayer(Character player, Vector3 moveDirection)
  67. {
  68. yield return StartCoroutine(DoPush(player, moveDirection));
  69. }
  70. /// <summary>
  71. /// Is called when a player moves off of block
  72. ///
  73. /// Should be implemented by a derived class
  74. /// </summary>
  75. /// <param name="player">Player which moved on to block</param>
  76. public virtual void OnLeftByPlayer(Character player)
  77. {
  78. if (CurrentPlayer == player)
  79. CurrentPlayer = null;
  80. }
  81. /// <summary>
  82. /// Called to deal with players colliding on this block
  83. /// </summary>
  84. /// <param name="newPlayer">Player which is moving into this block</param>
  85. /// <param name="moveDirection">The direction the player moved to get to this block</param>
  86. public virtual IEnumerator DoPush(Character newPlayer, Vector3 moveDirection)
  87. {
  88. if (CurrentPlayer == null)
  89. {
  90. CurrentPlayer = newPlayer;
  91. yield break;
  92. }
  93. Block pushBlock = GetPushLocation(moveDirection, ~CurrentPlayer.Ignore);
  94. if (pushBlock != this)
  95. {
  96. Character oldPlayer = CurrentPlayer;
  97. CurrentPlayer = newPlayer;
  98. yield return StartCoroutine(oldPlayer.MoveToBlock(pushBlock, Character.Animation.Hit, 1));
  99. }
  100. else
  101. {
  102. Block returnBlock = GetPushLocation(-moveDirection, ~newPlayer.Ignore);
  103. if (returnBlock != this)
  104. yield return StartCoroutine(newPlayer.MoveToBlock(returnBlock, Character.Animation.Hit, 1));
  105. }
  106. }
  107. public bool isVisible()
  108. {
  109. return isPositionVisible(position);
  110. }
  111. #endregion Public Functions
  112. #region Protected Functions
  113. protected Block GetPushLocation(Vector3 pushDirection, LayerMask ignoreMask)
  114. {
  115. //setting up variables
  116. Vector3 newPosition = position + pushDirection; // position wanted
  117. Block hit; //output of block detection
  118. //if move is obstucted no where to move
  119. if (Block.isBlockAtPosition(newPosition + Vector3.up, 1, ignoreMask))
  120. return this;
  121. return Block.GetOrCreateBlockAtPosition(newPosition, 1, ignoreMask);
  122. }
  123. #endregion Protected Functions
  124. #region Editor Functions
  125. private void OnDrawGizmos()
  126. {
  127. Vector3 DrawPosition = VisualPosition + Vector3.up * 0.4f;
  128. if (isCollectableSpawnable)
  129. {
  130. Vector3 offset = Vector3.zero;
  131. #if UNITY_EDITOR
  132. offset = Vector3.up * Mathf.Sin((float)UnityEditor.EditorApplication.timeSinceStartup * 0.5f) * 0.1f;
  133. #endif
  134. DebugExtensions.DrawCube(DrawPosition + offset, 0.15f, Color.yellow, 0,true);
  135. #if UNITY_EDITOR
  136. var view = UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>();
  137. view.Repaint();
  138. #endif
  139. }
  140. if (!isSpawnable)
  141. return;
  142. Vector3 Perp = Quaternion.Euler(0, 90, 0) * SpawnDirection.ToVector();
  143. DebugExtensions.DrawCube(DrawPosition, 0.4f, Color.magenta, 0, true);
  144. //Eyes
  145. Vector3 eyePosition = DrawPosition + SpawnDirection.ToVector() * 0.4f;
  146. DebugExtensions.DrawCube(eyePosition + Perp * 0.2f, 0.1f, Color.magenta, 0, true);
  147. DebugExtensions.DrawCube(eyePosition - Perp * 0.2f, 0.1f, Color.magenta, 0, true);
  148. //ears
  149. Vector3 earPosition = DrawPosition + SpawnDirection.ToVector() * 0.2f + Vector3.up * 0.4f;
  150. Vector3 earScale = Quaternion.LookRotation(SpawnDirection.ToVector()) * new Vector3(0.1f, 0.1f, 0.05f);
  151. DebugExtensions.DrawCube(earPosition + Perp * 0.3f, earScale, Color.magenta, 0, true);
  152. DebugExtensions.DrawCube(earPosition - Perp * 0.3f, earScale, Color.magenta, 0, true);
  153. }
  154. #endregion Editor Functions
  155. #region Static Functions
  156. /// <summary>
  157. /// Checks if there is a block at supplied position
  158. /// </summary>
  159. /// <param name="position">position to check at</param>
  160. /// <param name="Scale">Scale of block. (should be 1)</param>
  161. /// <param name="layerMask">Layers to check on</param>
  162. /// <param name="hit">Block hit</param>
  163. /// <returns>if a block is at position</returns>
  164. public static bool isBlockAtPosition(Vector3 position, float Scale, LayerMask layerMask, out Block hit)
  165. {
  166. //Turn scale into halfextent and shrink a bit so it doesn't hit bordering blocks
  167. Vector3 halfExtent = Vector3.one * ((Scale - 0.1f) / 2);
  168. //Get every collider which is at position
  169. Collider[] cols = Physics.OverlapBox(position, halfExtent, Quaternion.identity, layerMask);
  170. //Filter colliders for only GameObjects with an Block component
  171. Block[] blocks = cols.Where(p => p.GetComponent<Block>() != null).Select(p => p.GetComponent<Block>()).ToArray();
  172. //Draw cube, for visuals
  173. DebugExtensions.DrawCube(position, halfExtent, Color.cyan, 1, false);
  174. //if didn't hit anyblocks return false
  175. if (blocks.Length == 0)
  176. {
  177. hit = null;
  178. return false;
  179. }
  180. else
  181. {
  182. //else get the closest block to disered position, (in case we hit mulitple blocks)
  183. hit = Utility.minBy(blocks, p => Vector3.Distance(p.transform.position, position));
  184. return true;
  185. }
  186. }
  187. /// <summary>
  188. /// Checks if there is a block at supplied position
  189. /// </summary>
  190. /// <param name="position">position to check at</param>
  191. /// <param name="Scale">Scale of block. (should be 1)</param>
  192. /// <param name="layerMask">Layers to check on</param>
  193. /// <returns>if a block is at position</returns>
  194. public static bool isBlockAtPosition(Vector3 position, float scale, LayerMask layerMask)
  195. {
  196. //Return Overloaded function above
  197. Block hit;
  198. return (isBlockAtPosition(position, scale, layerMask, out hit));
  199. }
  200. /// <summary>
  201. /// Gets the block at a position or creates an airblock if there isn't one there
  202. /// </summary>
  203. /// <param name="position">position to get block at</param>
  204. /// <param name="Scale">Scale of block. (should be 1)</param>
  205. /// <param name="layerMask">Layers to check on</param>
  206. /// <returns>block at position</returns>
  207. public static Block GetOrCreateBlockAtPosition(Vector3 position, float Scale, LayerMask layerMask)
  208. {
  209. Block retVal;
  210. if (isBlockAtPosition(position, Scale, layerMask, out retVal))
  211. return retVal;
  212. GameObject airBlock = Resources.Load<GameObject>("Cube_Air");
  213. Debug.Log(airBlock, airBlock);
  214. GameObject newBlock = Instantiate(airBlock);
  215. newBlock.name = "AIR_BLOCK";
  216. Debug.Log(newBlock, newBlock);
  217. newBlock.transform.position = position;
  218. return newBlock.GetComponent<Air>();
  219. }
  220. public static bool isPositionVisible(Vector3 position)
  221. {
  222. Camera camera = Camera.main;
  223. Vector2 screenPos = camera.WorldToViewportPoint(position);
  224. if (screenPos.x > 1 || screenPos.x < 0)
  225. {
  226. return false;
  227. }
  228. if (screenPos.y > 1 || screenPos.y < 0)
  229. {
  230. return false;
  231. }
  232. return true;
  233. }
  234. #endregion
  235. }