using UnityEngine; using System.Collections; using System; [RequireComponent (typeof (CharacterController))] public class PlayerControl : MonoBehaviour { public CameraClass gameCam; public bool LEFT, RIGHT, UP, DOWN; public string curSector = ""; public Vector3 direction = Vector3.zero; public Vector3 home; public Vector2 analogOrigin, camOrigin; public int analogTouch = -1; public int camTouch = -1; private const float WALKSPEED = 3.0f, RUNSPEED = 7.0f, STEPHEIGHT = 0.25f, PHEIGHT = 2f; private const float run_doubleTapInterval = 0.25f; private float run_tapTime = 0; private float speed; private bool run_toggle = true; private CharacterController controller; private Animator animator; public string debugtext = ""; // Use this for initialization void Start () { controller = (CharacterController)gameObject.GetComponent("CharacterController"); animator = (Animator)gameObject.GetComponent("Animator"); //animator.Play("stand"); home = transform.position; } // Update is called once per frame void Update() { debugtext = curSector; updateKeys(); if(Input.GetButton("Run/Walk")) { if(run_toggle) speed = WALKSPEED; else speed = RUNSPEED; } else { if(run_toggle) speed = RUNSPEED; else speed = WALKSPEED; } if(Input.GetButtonDown("Run/Walk")) { if((Time.time - run_tapTime) < run_doubleTapInterval && run_tapTime != 0) run_toggle = !run_toggle; run_tapTime = Time.time; } if(Input.GetButtonDown("Chat")) transform.position = home; //Movement int dirX = 0, dirZ = 0; if(RIGHT) dirX -= 1; if(LEFT) dirX += 1; if(UP) dirZ -= 1; if(DOWN) dirZ += 1; if(dirX != 0 || dirZ != 0) { //Get angle towards point (dirX, dirY) and adjust for camera float dir_angle = Mathf.Atan2(dirX, dirZ) + gameCam.getAngle(); float diagChk = dir_angle; //Perform a platform check in the direction the player is trying to move... if (!platCheck(dir_angle)) //If there is no valid space directly in front, perform platform checks for both diagonals //of the currently attempted angle of movement. If one is open, move that way. diagChk = diagCheck(dir_angle); //Finalize the movement direction vector [NOTE]: Now performed by platCheck()/diagCheck() above //direction = new Vector3(Mathf.Sin(dir_angle), 0.0f, Mathf.Cos(dir_angle)); //99 is the fail state return value for diagCheck (= the player has nowhere to move) if(diagChk != 99) { Vector3 rotDir = new Vector3(Mathf.Sin(dir_angle), 0, Mathf.Cos(dir_angle)); //avatar rotation direction from raw input transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(rotDir), .5f); transform.position += direction * Time.deltaTime * speed; updateHeight(); } gameCam.updateCamera(false); //Ensures that the camera position doesn't lag behind the player } else direction = Vector3.zero; Animate(); } private void Animate() { if (Mathf.Round(direction.magnitude) == 0) { animator.Play("stand"); } else if(!animator.GetCurrentAnimatorStateInfo(0).IsName("run")) { animator.Play("run"); } } private bool platCheck(float tangle) { RaycastHit[][] hit = new RaycastHit[3][]; //Prepare an array of three uninitialized arrays of RaycastHits Vector3[] tpos = new Vector3[3]; //Prepare an array of 3 Vector3s for the checked rays' origins bool[] hasPlat = {false, false, false}; //Prepare an array of 3 booleans, one per platform test/raycast //Derive from tangle the 3 relative angle vectors used for platform checking Vector3 movement = new Vector3(Mathf.Sin(tangle), 0.0f, Mathf.Cos(tangle)); Vector3 rStop = new Vector3(Mathf.Sin(tangle-(Mathf.PI/4f)), 0.0f, Mathf.Cos(tangle-(Mathf.PI/4f))); Vector3 lStop = new Vector3(Mathf.Sin(tangle+(Mathf.PI/4f)), 0.0f, Mathf.Cos(tangle+(Mathf.PI/4f))); float trad = controller.radius*0.8f; //The pseudo-radius to use for collision detection -- determines how far the player will overlap the edge float tdist = PHEIGHT*2f; //Downward distance from player center to check for a platform //Project the check forward by the radius, from top of player, per each test tpos[0] = transform.position + (movement*trad) + (Vector3.up*PHEIGHT*0.5f); tpos[1] = transform.position + (lStop*trad) + (Vector3.up*PHEIGHT*0.5f); tpos[2] = transform.position + (rStop*trad) + (Vector3.up*PHEIGHT*0.5f); //Fill 3 different RaycastHit arrays to check for the absence of a platform hit[0] = Physics.RaycastAll(tpos[0], Vector3.down, tdist); hit[1] = Physics.RaycastAll(tpos[1], Vector3.down, tdist); hit[2] = Physics.RaycastAll(tpos[2], Vector3.down, tdist); for(int i = 0; i < 3; i++) { if(hit[i].Length > 0) { for(int j = 0; j < hit[i].Length; j++) { //If a platform is too low, keep looking for other platforms that are closer to player height if(hit[i][j].collider.tag == "Platform" && (hit[i][j].distance - PHEIGHT <= STEPHEIGHT*1.25f //Adjustment for getting off at the bottom of slopes //OR the platform seems a bit lower than usual but is at a slant (normal x and z would be = 0 if plat is flat) || ((hit[i][j].normal.x != 0 || hit[i][j].normal.z != 0) && hit[i][j].distance - PHEIGHT <= STEPHEIGHT*2.5f))) { //debugtext += "Platform on check #" + i + " | Tag: " + hit[i][j].collider.tag + " | Distance: " + hit[i][j].distance + " || "; if(hit[i][j].distance - PHEIGHT < -(STEPHEIGHT)) break; //If a platform is too high, the player is stopped regardless of other platforms hasPlat[i] = true; //Set direction according to the slope found during the centered platform test if(i == 0) { Vector3 temp = Vector3.Cross(movement,hit[i][j].normal); temp = Vector3.Cross(hit[i][j].normal,temp); direction = temp.normalized; //debugtext += " Normalized movement vector: " + direction; if(direction.y > 0.5f) direction = new Vector3(direction.x, 0f, direction.z); } break; } //debugtext += "Hit collider is null on check #" + i; } } } if(hasPlat[0] && hasPlat[1] && hasPlat[2]) return true; //Player can only move if all tests confirm platforms else return false; } float diagCheck(float tangle) { tangle -= (Mathf.PI/4f); if(platCheck(tangle)) return tangle; tangle += (Mathf.PI/2f); if(platCheck(tangle)) return tangle; return 99; } Vector3 updateHeight() //Execute this at the end of each movement loop, but before updating the camera. { //Highest possible height to change to is lowest acceptable relative point float topHeight = transform.position.y - (PHEIGHT*0.5f) - STEPHEIGHT; Vector3 topNorm = Vector3.zero; float trad = 0.3f; Vector3 tpos = transform.position + (Vector3.up*controller.height*0.5f) + (Vector3.down*trad); float tdist = PHEIGHT + STEPHEIGHT - trad; RaycastHit[] hit = Physics.SphereCastAll(tpos, trad, Vector3.down, tdist); for(int i = 0; i < hit.Length; i++) { if(hit[i].collider.tag == "Platform" && hit[i].point.y > topHeight && Mathf.Abs(hit[i].point.y - (transform.position.y - (PHEIGHT*0.5f))) <= STEPHEIGHT) { topHeight = hit[i].point.y; topNorm = hit[i].normal; } } if(transform.position.y != topHeight + (PHEIGHT*0.5f)) { transform.position = new Vector3(transform.position.x,topHeight + (PHEIGHT*0.5f),transform.position.z); tpos = Vector3.Cross(direction,topNorm); topNorm = Vector3.Cross(topNorm,tpos); return topNorm.normalized; //Returns the vector parallel to the platform surface in the direction the player is moving } else return Vector3.zero; } void updateKeys() { //Input.touchCount, Input.GetTouch(touchNum).phase/position, TouchPhase.Began/Moved/Ended if(Input.touchCount > 0) { debugtext += " | GOT TOUCH | "; for(int i = 0; i < Input.touchCount; i++) { var touch = Input.GetTouch(i); if(analogTouch == -1 && touch.position.x <= Screen.width/2) { debugtext += " | NEW ANALOG | "; analogTouch = i; analogOrigin = /*touch.position*/Vector2.zero; } if(camTouch == -1 && touch.position.x > Screen.width/2) { debugtext += " | NEW CAM | "; camTouch = i; camOrigin = touch.position; } if(i == analogTouch) { //Vector2 analogVec = new Vector2(touch.position.x-analogOrigin.x,touch.position.y-analogOrigin.y); if(touch.deltaPosition.magnitude > 16) analogOrigin = touch.deltaPosition; if(touch.phase == TouchPhase.Ended) { analogTouch = -1; debugtext += " | TERMINATED ANALOG | "; } //if(analogVec.magnitude > 48) //{ debugtext += " | MODIFYING ANALOG | "; float sixteenth = Mathf.PI/8f; float vecAngle = Mathf.Atan2(analogOrigin.normalized.y*-1,analogOrigin.normalized.x*-1); vecAngle += Mathf.PI; debugtext += " | ADIST: " + analogOrigin.magnitude + " | AANGLE: " + vecAngle + " | AX: " + analogOrigin.x + " | AY: " + analogOrigin.y; if((vecAngle >= (Mathf.PI*2) - (sixteenth*3) && vecAngle < Mathf.PI*2) || (vecAngle >= 0 && vecAngle < (sixteenth*3))) RIGHT = true; if(vecAngle >= sixteenth && vecAngle < sixteenth*7) UP = true; if(vecAngle >= sixteenth*5 && vecAngle < sixteenth*11) LEFT = true; if(vecAngle >= sixteenth*9 && vecAngle < sixteenth*15) DOWN = true; //} } if(camTouch == i && touch.phase == TouchPhase.Ended) camTouch = -1; } } else if(analogTouch != -1) analogTouch = -1; if(analogTouch == -1) { LEFT = Input.GetAxis("Horizontal") < -.2; RIGHT = Input.GetAxis("Horizontal") > .2; UP = Input.GetAxis("Vertical") > .2; DOWN = Input.GetAxis("Vertical") < -.2; } } void OnTriggerEnter(Collider other) { //Only change current sector to new sector if the sector names are different if(other.GetComponent("SectorTrigger") != null && ((SectorTrigger)other.GetComponent("SectorTrigger")).sectorName != curSector && ((SectorTrigger)other.GetComponent("SectorTrigger")).sectorName != "") { curSector = ((SectorTrigger)other.GetComponent("SectorTrigger")).sectorName; print(curSector); } } void OnGUI() { GUI.Label(new Rect(0,0,Screen.width,Screen.height),debugtext); } }