|
|
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using System.Linq;
-
- /// <summary>
- /// Class which defines blocks around the level
- /// </summary>
- [RequireComponent(typeof(BoxCollider))]
- public class Block : MonoBehaviour
- {
- #region Inspector Fields
- [SerializeField]
- [Tooltip("Offset from the top of the block from which a character should stand")]
- private Vector3 VisualOffset = Vector3.zero;
-
- [SerializeField]
- [Tooltip("Can this type of block be walked on")]
- public bool is_Walkable = true;
-
- [Tooltip("Is this block underwater?")]
- public bool isWater = false;
-
- [Tooltip("Is this block at the bottom of a pit?")]
- public bool isPit = false;
-
- public bool isCrystals = false;
- public bool isRock = false;
-
- [Header("Spawn Settings")]
- [Tooltip("Can this block be spawned on")]
- public bool isSpawnable = false;
-
- [Tooltip("Direction Player is poting at when spawned")]
- public Direction SpawnDirection = Direction.Forward;
-
- #endregion InspectorFields
-
- #region Private Functions
- /// <summary>
- /// List of current players on this block
- /// </summary>
- public Character CurrentPlayer { get; protected set; }
- #endregion Private Functions
-
- #region ReadOnly Properties
- /// <summary>
- /// Blocks position in global space
- /// </summary>
- public Vector3 position { get { return transform.position; } }
-
- /// <summary>
- /// Position character should stand in global space
- /// </summary>
- public Vector3 VisualPosition { get { return position + VisualOffset + Vector3.up * 0.5f; } }
- #endregion ReadOnly Properties
-
- #region Public Functions
-
- /// <summary>
- /// Is a block valid to walk on
- /// </summary>
- /// <param name="layerMask">Layers to check for when checking for blocks above</param>
- /// <returns></returns>
- public bool isWalkable(LayerMask layerMask)
- {
- //checks if there is no block above this one and that this is tagged as walkable
- return (is_Walkable && !isBlockAtPosition(position + Vector3.up, 1, layerMask));
- }
-
- /// <summary>
- /// Is called when a player moves onto this block
- ///
- /// Should be implemented by a derived class
- /// </summary>
- /// <param name="player">Player which moved on to block</param>
- ///<param name="moveDirection">The direction the player moved to get to this block</param>
- public virtual IEnumerator OnWalkedOnByPlayer(Character player, Vector3 moveDirection)
- {
- yield return StartCoroutine(DoPush(player, moveDirection));
- }
-
- /// <summary>
- /// Is called when a player moves off of block
- ///
- /// Should be implemented by a derived class
- /// </summary>
- /// <param name="player">Player which moved on to block</param>
- public virtual void OnLeftByPlayer(Character player)
- {
- CurrentPlayer = null;
- }
-
- /// <summary>
- /// Called to deal with players colliding on this block
- /// </summary>
- /// <param name="newPlayer">Player which is moving into this block</param>
- /// <param name="moveDirection">The direction the player moved to get to this block</param>
- public virtual IEnumerator DoPush(Character newPlayer, Vector3 moveDirection)
- {
- if (CurrentPlayer == null)
- {
- CurrentPlayer = newPlayer;
- yield break;
- }
-
- Block pushBlock = GetPushLocation(moveDirection, ~CurrentPlayer.Ignore);
- if (pushBlock != this)
- {
- Character oldPlayer = CurrentPlayer;
- CurrentPlayer = newPlayer;
- yield return StartCoroutine(oldPlayer.MoveToBlock(pushBlock, Character.Animation.Hit, 1));
- }
- else
- {
- Block returnBlock = GetPushLocation(-moveDirection, ~newPlayer.Ignore);
-
- if (returnBlock != this)
- yield return StartCoroutine(newPlayer.MoveToBlock(returnBlock, Character.Animation.Hit, 1));
- }
- }
-
-
- #endregion Public Functions
-
- #region Protected Functions
-
- protected Block GetPushLocation(Vector3 pushDirection, LayerMask ignoreMask)
- {
- //setting up variables
- Vector3 newPosition = position + pushDirection; // position wanted
- Block hit; //output of block detection
-
- //if move is obstucted no where to move
- if (Block.isBlockAtPosition(newPosition + Vector3.up, 1, ignoreMask))
- return this;
-
-
- //If block at Position is walkable set it to moveTo
- if (Block.isBlockAtPosition(newPosition, 1, ignoreMask, out hit) && hit.isWalkable(ignoreMask))
- {
- return hit;
- }
- //else if block down one is walkable
- else if (Block.isBlockAtPosition(newPosition + Vector3.down, 1, ignoreMask, out hit) && hit.isWalkable(ignoreMask))
- {
- return hit;
- }
- return this;
- }
-
- #endregion Protected Functions
-
- #region Editor Functions
- private void OnDrawGizmos()
- {
- if (!isSpawnable)
- return;
-
- Vector3 DrawPosition = VisualPosition + Vector3.up * 0.4f;
- Vector3 Perp = Quaternion.Euler(0, 90, 0) * SpawnDirection.ToVector();
-
- DebugExtensions.DrawCube(DrawPosition, 0.4f, Color.magenta, 0);
-
- //Eyes
- Vector3 eyePosition = DrawPosition + SpawnDirection.ToVector() * 0.4f;
- DebugExtensions.DrawCube(eyePosition + Perp * 0.2f, 0.1f, Color.magenta, 0);
- DebugExtensions.DrawCube(eyePosition - Perp * 0.2f, 0.1f, Color.magenta, 0);
-
- //ears
- Vector3 earPosition = DrawPosition + SpawnDirection.ToVector() * 0.2f + Vector3.up * 0.4f;
- Vector3 earScale = Quaternion.LookRotation(SpawnDirection.ToVector()) * new Vector3(0.1f, 0.1f, 0.05f);
- DebugExtensions.DrawCube(earPosition + Perp * 0.3f, earScale, Color.magenta, 0);
- DebugExtensions.DrawCube(earPosition - Perp * 0.3f, earScale, Color.magenta, 0);
-
- }
- #endregion Editor Functions
-
-
- #region Static Functions
-
- /// <summary>
- /// Checks if there is a block at supplied position
- /// </summary>
- /// <param name="position">position to check at</param>
- /// <param name="Scale">Scale of block. (should be 1)</param>
- /// <param name="layerMask">Layers to check on</param>
- /// <param name="hit">Block hit</param>
- /// <returns>if a block is at position</returns>
- public static bool isBlockAtPosition(Vector3 position, float Scale, LayerMask layerMask, out Block hit)
- {
- //Turn scale into halfextent and shrink a bit so it doesn't hit bordering blocks
- Vector3 halfExtent = Vector3.one * ((Scale - 0.1f) / 2);
-
- //Get every collider which is at position
- Collider[] cols = Physics.OverlapBox(position, halfExtent, Quaternion.identity, layerMask);
-
- //Filter colliders for only GameObjects with an Block component
- Block[] blocks = cols.Where(p => p.GetComponent<Block>() != null).Select(p => p.GetComponent<Block>()).ToArray();
-
- //Draw cube, for visuals
- DebugExtensions.DrawCube(position, halfExtent, Color.cyan, 1, false);
-
- //if didn't hit anyblocks return false
- if (blocks.Length == 0)
- {
- hit = null;
- return false;
- }
- else
- {
- //else get the closest block to disered position, (in case we hit mulitple blocks)
- hit = Utility.minBy(blocks, p => Vector3.Distance(p.transform.position, position));
- return true;
- }
- }
-
- /// <summary>
- /// Checks if there is a block at supplied position
- /// </summary>
- /// <param name="position">position to check at</param>
- /// <param name="Scale">Scale of block. (should be 1)</param>
- /// <param name="layerMask">Layers to check on</param>
- /// <returns>if a block is at position</returns>
- public static bool isBlockAtPosition(Vector3 position, float scale, LayerMask layerMask)
- {
- //Return Overloaded function above
- Block hit;
- return (isBlockAtPosition(position, scale, layerMask, out hit));
- }
-
- /// <summary>
- /// Gets the block at a position or creates an airblock if there isn't one there
- /// </summary>
- /// <param name="position">position to get block at</param>
- /// <param name="Scale">Scale of block. (should be 1)</param>
- /// <param name="layerMask">Layers to check on</param>
- /// <returns>block at position</returns>
- public static Block GetOrCreateBlockAtPosition(Vector3 position, float Scale, LayerMask layerMask)
- {
- Block retVal;
- if (isBlockAtPosition(position, Scale, layerMask, out retVal))
- return retVal;
-
- GameObject newBlock = Resources.Load<GameObject>("Cube_Air");
- newBlock.transform.position = position;
- return newBlock.GetComponent<Air>();
- }
-
- #endregion
-
- }
|