using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; using Networking.Server; public class Character : MonoBehaviour { public enum Animation { Walk, Run, Jump, Sit, Attack, Hit } public string nextScene; Animator characterAnimator; public bool isTuteLevel = false; public bool inWater = false; //Am I in the water? public bool inPit = false; //Did I fall into a pit? public bool onCrystal = false; public bool underRock = false; public bool stuck = false; //Am I still stuck? public bool justMoved = false; //Was the logic block I just executed a move command? Vector3 death = new Vector3(-50, 0, 0); #region Inspector Fields [SerializeField] [Tooltip("Will move to this block at start, else will try and find a block below")] private Block _currentBlock; [SerializeField] [Tooltip("Layers to ignore when checking for blocks")] public LayerMask Ignore; [Tooltip("Current Inventory of the player")] public Inventory Inventory; public bool CloneInventoryOnStart = false; [Tooltip("How many lives to start out with")] public int lives = 3; [SerializeField] [Tooltip("Character to display")] private string CharacterModel = "Bear"; [SerializeField] public ClientData ClientLink; [SerializeField] private TMPro.TextMeshPro BlockTitlePrefab; #endregion Inspector Fields #region Read Only public Block CurrentBlock { get { return _currentBlock; } } public float lastRotation { get; private set;} #endregion Read Only #region Unity Functions private void Start() { if (Inventory != null && CloneInventoryOnStart) Inventory = Inventory.Clone(Inventory); //If no starting block find one below it if (_currentBlock == null) Block.isBlockAtPosition(transform.position + Vector3.down / 2, 1, ~Ignore, out _currentBlock); //move to starting block transform.position = _currentBlock.VisualPosition; //get character string from player replace from "Bear" GameObject prefab = Resources.Load(CharacterModel) as GameObject; GameObject animal = Instantiate(prefab, this.gameObject.transform); characterAnimator = GetComponentInChildren(); } private void Update() { if(lives < 1) { this.transform.position = death; //this.enabled = false; //gameObject.SetActive(false); } } #endregion Unity Functions #region Class Implementation public void Initialise(Block startingBlock, Inventory inventory, string Character) { _currentBlock = startingBlock; Inventory = inventory; CharacterModel = Character; } public void DisplayBlock(LogicBlock block) { if (isTuteLevel == false) { if (BlockTitlePrefab == null) return; TMPro.TextMeshPro temp = Instantiate(BlockTitlePrefab.gameObject).GetComponent(); temp.text = block.DisplayName; temp.color = ClientLink.Color; temp.transform.position = transform.position + (Vector3.one * 0.25f); temp.transform.rotation = Quaternion.LookRotation(temp.transform.position - Camera.main.transform.position); } } public IEnumerator MoveToBlock(Block target, Animation animation, float time) { float startTime = Time.time; Vector3 moveDirection = Vector3.ProjectOnPlane(target.position - _currentBlock.position, Vector3.up).normalized; _currentBlock.OnLeftByPlayer(this); System.Func yFunction = null; if (animation == Animation.Jump) yFunction = (t) => Mathf.Sin((Mathf.PI * t)); StartAnimation(animation, time); yield return StartCoroutine(LerpToBlock(target.VisualPosition, time * 0.8f, yFunction)); _currentBlock= target; yield return StartCoroutine (_currentBlock.OnWalkedOnByPlayer(this,moveDirection)); yield return new WaitForSeconds(time - (Time.time - startTime)); StopAnimation(animation); } public IEnumerator RotateInDirection(Direction direction,float angles, Animation animation, float time) { System.Func yFunction = null; if (animation == Animation.Jump) yFunction = (t) => Mathf.Sin((Mathf.PI * t)); StartAnimation(animation, time); //Debug.Log("Rotating by: " + angles); yield return StartCoroutine(Rotate(direction,angles, time * 0.8f, yFunction)); StopAnimation(animation); } private IEnumerator LerpToBlock(Vector3 target, float time, System.Func heightOffset = null) { Vector3 _startPos = transform.position; Vector3 _endPos = target; Vector3 _newPos; float elapsedTime = 0; while (elapsedTime / time < 1) { _newPos = Vector3.Lerp(_startPos, _endPos, (elapsedTime / time)); if (heightOffset != null) _newPos.y += heightOffset(elapsedTime / time); transform.position = _newPos; yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } _newPos = _endPos; if (heightOffset != null) _newPos.y += heightOffset(1); transform.position = _newPos; } private IEnumerator Rotate(Direction direction, float angles, float time, System.Func heightOffset = null) { int RotationDir = 0; switch (direction) { case Direction.Forward: RotationDir = 0; break; case Direction.Left: RotationDir = -1; break; case Direction.Right: RotationDir = 1; break; case Direction.Back: RotationDir = 2; break; } lastRotation = angles * RotationDir; float elapsedTime = 0; float anglePerSecond = (angles * RotationDir) / time; Vector3 _startPos = transform.position; Vector3 startDirection = transform.forward; while (elapsedTime < time) { transform.Rotate(Vector3.up, anglePerSecond * Time.deltaTime); if (heightOffset != null) transform.position = _startPos + Vector3.up * heightOffset(elapsedTime / time); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.forward = Quaternion.AngleAxis(angles * RotationDir, Vector3.up) * startDirection; if (heightOffset != null) transform.position = _startPos + Vector3.up * heightOffset(1); } public IEnumerator AnimateToPosition(Vector3 position, Animation animation, float time) { System.Func yFunction = null; if (animation == Animation.Jump) yFunction = (t) => Mathf.Sin((Mathf.PI * t)); StartAnimation(animation, time); yield return StartCoroutine(LerpToBlock(position, time * 0.8f, yFunction)); StopAnimation(animation); } public void StartAnimation(Animation animation,float speed = 1) { characterAnimator.SetFloat("AnimationSpeed", (1 / speed)); switch (animation) { case Animation.Walk: characterAnimator.SetBool("isWalking", true); break; case Animation.Run: characterAnimator.SetBool("isRunning", true); break; case Animation.Sit: characterAnimator.SetBool("isSitting", true); break; case Animation.Jump: characterAnimator.SetTrigger("Jump"); break; case Animation.Attack: characterAnimator.SetTrigger("Attack"); break; case Animation.Hit: characterAnimator.SetTrigger("Hit"); break; default: break; } } public void StopAnimation(Animation animation) { characterAnimator.SetFloat("AnimationSpeed", 1f); switch (animation) { case Animation.Walk: characterAnimator.SetBool("isWalking", false); break; case Animation.Run: characterAnimator.SetBool("isRunning", false); break; case Animation.Sit: characterAnimator.SetBool("isSitting", false); break; } } public void respawnCharacter() { /* Will introduce more complex criteria for choosing where to respawn the player in future * For now: if the square one back (x =- 1) from the pit is walkable and unoccupied, then put them there * Otherwise, try the next square back, etc, until we find an available one */ Block currentBlock = null; //We start from the position of our pit, at ground level Vector3 currentPos = new Vector3(this.transform.position.x, this.transform.position.y, this.transform.position.z); Debug.Log("Commencing respawn"); //Hardcoding the number of iterations for now for simplicity, should change this later for (int i = 0; i < 100; i++) { //First we check one back currentPos.x -= 1; Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z); if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here? { Debug.Log("Block exists"); if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it? { Debug.Log("Block is walkable"); //Don't yet have a check for whether it's occupied break; //If it is, we stop here } } //If the block one back isn't an option, we check to the left and right currentPos.z += 1; Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z); if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here? { Debug.Log("Block exists"); if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it? { Debug.Log("Block is walkable"); //Don't yet have a check for whether it's occupied break; //If it is, we stop here } } currentPos.z -= 2; Debug.Log("currentPos = " + currentPos.x + ", " + currentPos.y + ", " + currentPos.z); if (Block.isBlockAtPosition(currentPos, 1, ~Ignore, out currentBlock)) //Does a block exist here? { Debug.Log("Block exists"); if (currentBlock.isWalkable(~Ignore)) //If so, can we walk on it? { Debug.Log("Block is walkable"); //Don't yet have a check for whether it's occupied break; //If it is, we stop here } } //If we've gotten this far and haven't found an available spot, we move back a row and try again currentPos.z += 1; } //Having found our target block, we move the character there if (currentBlock != null) { this.transform.position = currentBlock.VisualPosition; this.inPit = false; this._currentBlock = currentBlock; Debug.Log("Moved " + this.name + " to " + this.transform.position.x + ", " + this.transform.position.y + ", " + this.transform.position.z + ", " + " inPit = " + inPit); } else { Debug.Log("Failed to find anywhere to put " + this.name); } } /// /// Upon collision with a floating block, collect its /// Upon collision with the end portal, end of level /// /// name of collided object void OnTriggerEnter(Collider other) { Collectable collectable = other.GetComponentInChildren(); if (collectable != null) { collectable.OnCollect(this); } if (other.gameObject.name == "collect_sphere") { other.gameObject.SetActive(false); //player.collected +=1; } if (other.gameObject.name == "End Portal") { other.GetComponent().enabled = false; SceneManager.LoadScene(nextScene); } } /// /// Rotates to point in specific direction based on current direction /// /// Local direction to point public void Rotate(Direction direction, float speed) { StartCoroutine(rotateCoroutine(direction, transform, speed, 0.15f)); //transform.forward = direction.ToVector(transform); } public void RotateHalf(Direction direction, float speed) { StartCoroutine(rotateHalfCoroutine(direction, transform, speed, 0.15f)); //transform.forward = direction.ToVector(transform); } /// /// Jumps in specefied direction, picks between Long Jump and Jumping up /// /// Direction to Jump public void Jump(Direction direction, float speed) { //if there is a block infront JumpUp else LongJump if (Block.isBlockAtPosition(_currentBlock.position + direction.ToVector(transform) + Vector3.up, 1, ~Ignore)) JumpUp(direction, speed); else JumpLong(direction, speed); } #endregion Class Implementation #region Private Functions /// /// Jumps up obstacle of specific height. Jumps as high as possible /// /// Direction of obstacle /// max height private void JumpUp(Direction direction, float speed, int height = 1) { //setting up variables Vector3 position; // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to //Check blocks in going up then move to the heighest walkable one for (int i = 0; i <= height; i++) { //next position up the tower position = _currentBlock.position + direction.ToVector(transform) + (Vector3.up * i); //if block is walkable set it to last known position if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) moveTo = hit; } //set current block && move _currentBlock = moveTo; StartCoroutine(JumpCoroutine(_currentBlock, transform, speed, 0.5f)); //transform.position = CurrentBlock.VisualPosition; } /// /// Long jumps forward a specified distance. Can Jump gaps. Stops at obstruction /// /// Direction to Jump /// Max distance private void JumpLong(Direction direction, float speed, int Distance = 2) { //setting up variables Vector3 position; // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to //Check blocks in front until we hit an obstruction or went the distance for (int i = 1; i <= Distance; i++) { //Next position to MoveTo position = _currentBlock.position + (direction.ToVector(transform) * i); //if jump is obstructed, stop and go to last known block if (Block.isBlockAtPosition(position + Vector3.up, 1, ~Ignore)) break; //If block at Position is walkable set it to last known position if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) moveTo = hit; //else if block down one is walkable else if (Block.isBlockAtPosition(position + Vector3.down, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) moveTo = hit; }//end for //Set Current Block and move _currentBlock = moveTo; StartCoroutine(JumpCoroutine(_currentBlock, transform, speed, 0.5f)); //transform.position = CurrentBlock.VisualPosition; } #endregion Private Functions #region Depricated IEnumerator JumpCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; time *= 0.8f; characterAnimator.Play("Jump Inplace"); yield return new WaitForSeconds(0.15f); while (elapsedTime < time) { transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time)); //transform.position += yValue((elapsedTime / time), heightMax); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.position = Target.VisualPosition; } IEnumerator MoveCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; time *= 0.8f; characterAnimator.Play("Walk"); yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.position = Target.VisualPosition; } IEnumerator MoveConveyorForwardCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; Vector3 charEndPosition = new Vector3(Current.position.x + 1.0f, Current.position.y, Current.position.z); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = charEndPosition; } IEnumerator MoveConveyorBackwardCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; Vector3 charEndPosition = new Vector3(Current.position.x - 1.0f, Current.position.y, Current.position.z); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = charEndPosition; } IEnumerator MoveConveyorLeftCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; Vector3 charEndPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z + 1.0f); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = charEndPosition; } IEnumerator MoveConveyorRightCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; Vector3 charEndPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z - 1.0f); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, charEndPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = charEndPosition; } IEnumerator PushLeftCoroutine(Transform Current, float time) { float elapsedTime = 0; Vector3 startPosition = Current.transform.position; Vector3 endPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z + 1.0f); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, endPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = endPosition; } IEnumerator PushRightCoroutine(Transform Current, float time) { float elapsedTime = 0; Vector3 startPosition = Current.transform.position; Vector3 endPosition = new Vector3(Current.position.x, Current.position.y, Current.position.z - 1.0f); time *= 0.8f; yield return new WaitForSeconds(0.05f); while (elapsedTime < time) { Current.position = Vector3.Lerp(startPosition, endPosition, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } Current.position = endPosition; } IEnumerator MoveDownCoroutine(Block Target, Transform Current, float time, float heightMax) { float elapsedTime = 0; Vector3 startPosition = Current.position; time *= 0.8f; characterAnimator.Play("Walk"); while (elapsedTime < time) { transform.position = Vector3.Lerp(startPosition, Target.VisualPosition, (elapsedTime / time)); //transform.position += yValue((elapsedTime / time), heightMax); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.position = Target.VisualPosition; } IEnumerator rotateCoroutine(Direction direction, Transform Current, float time, float heightMax) { float elapsedTime = 0; time *= 0.8f; Vector3 endDirection = direction.ToVector(Current); Vector3 startDirection = Current.forward; while (elapsedTime < time) { characterAnimator.Play("Jump"); transform.forward = Vector3.Slerp(startDirection, endDirection, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.forward = endDirection; } IEnumerator rotateHalfCoroutine(Direction direction, Transform Current, float time, float heightMax) { float elapsedTime = 0; time *= 0.8f; Vector3 endDirection = (direction.ToVector(Current) + Current.forward).normalized; Vector3 startDirection = Current.forward; while (elapsedTime < time) { characterAnimator.Play("Jump"); transform.forward = Vector3.Slerp(startDirection, endDirection, (elapsedTime / time)); yield return new WaitForEndOfFrame(); elapsedTime += Time.deltaTime; } transform.forward = endDirection; } /// /// Moves one block in specefied direction, Can walk off obstacles /// /// direction to walk /// Technically is same as JumpLong(1) but kept seperate to avoid confusion public void Move(Direction direction, float speed) { //setting up variables Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to //if move is obstucted no where to move if (Block.isBlockAtPosition(position + Vector3.up, 1, ~Ignore)) return; //If block at Position is walkable set it to moveTo if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveCoroutine(_currentBlock, transform, speed, 0.3f)); } //else if block down one is walkable else if (Block.isBlockAtPosition(position + Vector3.down, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveDownCoroutine(_currentBlock, transform, speed, 0.3f)); } } public void conveyorMoveForward(Direction direction, float speed) { Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveConveyorForwardCoroutine(_currentBlock, transform, speed, 0.3f)); } } public void conveyorMoveBackward(Direction direction, float speed) { Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveConveyorBackwardCoroutine(_currentBlock, transform, speed, 0.3f)); } } public void conveyorMoveLeft(Direction direction, float speed) { Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveConveyorLeftCoroutine(_currentBlock, transform, speed, 0.3f)); } } public void conveyorMoveRight(Direction direction, float speed) { Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(MoveConveyorRightCoroutine(_currentBlock, transform, speed, 0.3f)); } } public void CannonRMove(float speed) { Direction direction = Direction.Right; Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(PushRightCoroutine(transform, speed)); } } public void CannonLMove(float speed) { Direction direction = Direction.Left; Vector3 position = _currentBlock.position + direction.ToVector(transform); // position wanted Block hit; //output of block detection Block moveTo = _currentBlock; //block we'll actually move to if (Block.isBlockAtPosition(position, 1, ~Ignore, out hit) && hit.isWalkable(~Ignore)) { moveTo = hit; _currentBlock = moveTo; StartCoroutine(PushLeftCoroutine(transform, speed)); } } #endregion Depricated }