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;
|
|
public bool isCollectableSpawnable = 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; set; }
|
|
|
|
private Renderer renderer;
|
|
#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)
|
|
{
|
|
if (CurrentPlayer == 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));
|
|
}
|
|
}
|
|
|
|
public bool isVisible()
|
|
{
|
|
return isPositionVisible(position);
|
|
}
|
|
|
|
#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;
|
|
|
|
|
|
return Block.GetOrCreateBlockAtPosition(newPosition, 1, ignoreMask);
|
|
}
|
|
|
|
#endregion Protected Functions
|
|
|
|
#region Editor Functions
|
|
private void OnDrawGizmos()
|
|
{
|
|
Vector3 DrawPosition = VisualPosition + Vector3.up * 0.4f;
|
|
|
|
|
|
if (isCollectableSpawnable)
|
|
{
|
|
Vector3 offset = Vector3.zero;
|
|
#if UNITY_EDITOR
|
|
offset = Vector3.up * Mathf.Sin((float)UnityEditor.EditorApplication.timeSinceStartup * 0.5f) * 0.1f;
|
|
#endif
|
|
DebugExtensions.DrawCube(DrawPosition + offset, 0.15f, Color.yellow, 0,true);
|
|
|
|
#if UNITY_EDITOR
|
|
var view = UnityEditor.EditorWindow.GetWindow<UnityEditor.SceneView>();
|
|
view.Repaint();
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
if (!isSpawnable)
|
|
return;
|
|
|
|
Vector3 Perp = Quaternion.Euler(0, 90, 0) * SpawnDirection.ToVector();
|
|
|
|
DebugExtensions.DrawCube(DrawPosition, 0.4f, Color.magenta, 0, true);
|
|
|
|
//Eyes
|
|
Vector3 eyePosition = DrawPosition + SpawnDirection.ToVector() * 0.4f;
|
|
DebugExtensions.DrawCube(eyePosition + Perp * 0.2f, 0.1f, Color.magenta, 0, true);
|
|
DebugExtensions.DrawCube(eyePosition - Perp * 0.2f, 0.1f, Color.magenta, 0, true);
|
|
|
|
//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, true);
|
|
DebugExtensions.DrawCube(earPosition - Perp * 0.3f, earScale, Color.magenta, 0, true);
|
|
|
|
|
|
|
|
}
|
|
#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 airBlock = Resources.Load<GameObject>("Cube_Air");
|
|
Debug.Log(airBlock, airBlock);
|
|
|
|
GameObject newBlock = Instantiate(airBlock);
|
|
|
|
newBlock.name = "AIR_BLOCK";
|
|
Debug.Log(newBlock, newBlock);
|
|
|
|
newBlock.transform.position = position;
|
|
return newBlock.GetComponent<Air>();
|
|
}
|
|
|
|
public static bool isPositionVisible(Vector3 position)
|
|
{
|
|
Camera camera = Camera.main;
|
|
Vector2 screenPos = camera.WorldToViewportPoint(position);
|
|
if (screenPos.x > 1 || screenPos.x < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (screenPos.y > 1 || screenPos.y < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|