日誌2017-09-23 20:37
如何讓物件沿著不規則靜態背景移動?作者:~XD
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class player : MonoBehaviour { public bool isGround; public float isGroundDistance = 0.52f; //public bool keepGravityPointWhenJumping = false; public Vector2 groundNormal; Vector3 newRight; Rigidbody2D rb; SpriteRenderer spriteRenderer; PolygonCollider2D pc2d; private Vector3 vectorz = new Vector3(0,0,1); private Vector3 normalLerped; private Vector3 normalLerped2; Animator animator; public float speed = 3; public float alignSpeed = 3; public float JumpingForce = 300f; public float JumpingDamping = 0.4f; public float GravityForce = -9.8f; public Vector3 GravityPoint; // Use this for initialization void Start() { isGround = false; rb = GetComponent<Rigidbody2D>(); spriteRenderer = GetComponent<SpriteRenderer>(); animator = GetComponent<Animator>(); pc2d = GameObject.Find("Land").GetComponent<PolygonCollider2D>(); } // Update is called once per frame void Update() { gravityBias(); // 修正重力方向 alignSurface(); // 讓角色對齊碰觸的地面 groundTest(); // 檢測角色是否正在地面? jumpPose(); // 依據角色狀態播放跳躍動態 } private void gravityBias() { //if(!keepGravityPointWhenJumping || isGround) GravityPoint = getClosetPointonEdge(pc2d); // Physics2D.gravity = Vector3.Normalize(transform.position - p) * (-9.8f); Vector2 one = Vector3.Normalize(transform.position - GravityPoint) * GravityForce * Time.deltaTime; rb.velocity += (one * (1+Time.deltaTime)); } int GetClosestPointIndex(PolygonCollider2D polygonCollider2D) { Vector2[] points = polygonCollider2D.points; int ClosestPointIndex = 0; float closestDist = Mathf.Infinity; for (var i = 0; i < points.Length; i++) { var worldSpaceVertex = polygonCollider2D.transform.TransformPoint(points[i]); float currentDist = Vector3.Distance(worldSpaceVertex, transform.position); if (currentDist < closestDist) { closestDist = currentDist; ClosestPointIndex = i; } } return ClosestPointIndex; } Vector3 getClosetPointonEdge(PolygonCollider2D PolygonCollider2D) { Vector3 result = Vector3.zero; Vector2[] points = PolygonCollider2D.points; var index = GetClosestPointIndex(PolygonCollider2D); int index_next = index == points.Length - 1 ? 0 : index + 1; int index_last = index == 0 ? points.Length - 1 : index - 1; Vector3 point = PolygonCollider2D.transform.TransformPoint(points[index]); Vector3 point_next = PolygonCollider2D.transform.TransformPoint(points[index_next]); Vector3 point_last = PolygonCollider2D.transform.TransformPoint(points[index_last]); Vector3 a = ClosestPointToLine(point, point_next, transform.position); Vector3 b = ClosestPointToLine(point, point_last, transform.position); result = Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b) ? b : a; Vector3 normal_next = Vector3.Cross(point_next-point, vectorz).normalized; Vector3 normal_last = Vector3.Cross(point-point_last, vectorz).normalized; Debug.DrawRay(point_next, normal_next, Color.white); Debug.DrawRay(point_last, normal_last, Color.white); float normalLerp; if(Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b)) { // in point_last normalLerp = Vector3.Distance(result, point) / Vector3.Distance(point_last, point); } else { // in point_next normalLerp = Vector3.Distance(result, point_next) / Vector3.Distance(point, point_next); } normalLerped = Vector3.Lerp(normal_last, normal_next, normalLerp); // project by hand //normalLerped2 = (point_next-point_last)*(Vector3.Distance(Vector3.zero,result-point_last)/Vector3.Distance(Vector3.zero,point_next-point_last))*Vector3.Dot((result-point_last).normalized, (point_next-point_last).normalized); normalLerped2 = Vector3.Project(result-point_last, point_next-point_last); float lenChara = Vector3.Distance(point_last, normalLerped2); float lenCorner = Vector3.Distance(point_last, Vector3.Project(point-point_last,point_next-point_last)); float lenTotal = Vector3.Distance(point_last, point_next-point_last); if(lenChara > lenCorner) normalLerp = (lenChara-lenCorner)/(lenTotal-lenCorner); else normalLerp = lenChara / lenCorner; Debug.DrawRay(point_last, point_next-point_last, Color.gray); Debug.DrawRay(point_last, normalLerped2, Color.white); normalLerped2 = Vector3.Lerp(normal_last, normal_next, Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next)); //normalLerped2 = Vector3.Lerp(normal_last, normal_next, normalLerp); Debug.DrawRay(result, normalLerped2 *1.3f, Color.red); Debug.DrawRay(result, normalLerped*1.2f, Color.white); normalLerped = normalLerped2; Debug.DrawRay(point_last, point-point_last, Color.yellow); Debug.DrawRay(point, point_next-point, Color.green); Debug.DrawRay(result, Vector3.zero-result, Color.red); return result; } Vector3 ClosestPointToLine(Vector3 _lineStartPoint, Vector3 _lineEndPoint, Vector3 _testPoint) { Vector3 pointTowardStart = _testPoint - _lineStartPoint; Vector3 startTowardEnd = (_lineEndPoint - _lineStartPoint).normalized; Debug.DrawRay(_lineStartPoint, pointTowardStart, Color.magenta); Debug.DrawRay(_lineStartPoint, startTowardEnd, Color.cyan); float lengthOfLine = Vector3.Distance(_lineStartPoint, _lineEndPoint); float dotProduct = Vector3.Dot(startTowardEnd, pointTowardStart); if (dotProduct <= 0) { return _lineStartPoint; } if (dotProduct >= lengthOfLine) { return _lineEndPoint; } Vector3 thirdVector = startTowardEnd * dotProduct; Vector3 closestPointOnLine = _lineStartPoint + thirdVector; return closestPointOnLine; } void alignSurface() { //Vector3 normal = getGroundSurface().normal; Vector3 normal = normalLerped; Vector3 newDir = Vector3.RotateTowards(transform.up, normal, /*0.05f*/ alignSpeed * Time.deltaTime , 0.0F); Debug.DrawRay(transform.position, newDir, Color.blue); transform.rotation = Quaternion.FromToRotation(Vector3.up, newDir); } void jumpPose() { if (isGround && !animator.GetCurrentAnimatorStateInfo(0).IsName("player@run")) animator.Play("player@idle");// 如果角色在地面並且未播放跑步動作,則播放待機動作 if (!isGround) animator.Play("player@jump");// 如果角色在空中,則播放跳躍動作 } void groundTest() { isGround = Vector3.Distance(transform.position, GravityPoint) < isGroundDistance; } RaycastHit2D getGroundSurface() { var raycasthit2d = Physics2D.Raycast(transform.position + Vector3.down * 0.01f, GravityPoint - transform.position, 5f); return raycasthit2d; } public void goLeft() { transform.position -= transform.right * speed * (1+Time.deltaTime); spriteRenderer.flipX = true; if (isGround) animator.Play("player@run"); } public void goRight() { transform.position += transform.right * speed * (1+Time.deltaTime); spriteRenderer.flipX = false; if (isGround) animator.Play("player@run"); } public void goJump() { if (isGround) rb.AddForce(transform.up * JumpingForce); } public void release() { rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * JumpingDamping); //放開空白鍵 減緩跳躍上升 } } |
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class player : MonoBehaviour { public bool isGround; public Vector2 groundNormal; Vector3 newRight; Rigidbody2D rb; SpriteRenderer spriteRenderer; PolygonCollider2D pc2d; Vector3 vectorz = new Vector3(0,0,1); Vector3 normalLerped; Vector3 normalLerped2; Animator animator; public float speed = 3; public float alignSpeed = 3; public Vector3 GravityPoint; public Vector3 ClosestPointToLineA = Vector3.zero; public Vector3 ClosestPointToLineB = Vector3.zero; // Use this for initialization void Start() { isGround = false; rb = GetComponent<Rigidbody2D>(); spriteRenderer = GetComponent<SpriteRenderer>(); animator = GetComponent<Animator>(); pc2d = GameObject.Find("Land").GetComponent<PolygonCollider2D>(); } // Update is called once per frame void Update() { gravityBias(); // 修正重力方向 alignSurface(); // 讓角色對齊碰觸的地面 groundTest(); // 檢測角色是否正在地面? jumpPose(); // 依據角色狀態播放跳躍動態 } private void gravityBias() { GravityPoint = getClosetPointonEdge(pc2d); // Physics2D.gravity = Vector3.Normalize(transform.position - p) * (-9.8f); Vector2 one = Vector3.Normalize(transform.position - GravityPoint) * (-9.8f) * Time.deltaTime; rb.velocity += one; } int GetClosestPointIndex(PolygonCollider2D polygonCollider2D) { Vector2[] points = polygonCollider2D.points; int ClosestPointIndex = 0; float closestDist = Mathf.Infinity; for (var i = 0; i < points.Length; i++) { var worldSpaceVertex = polygonCollider2D.transform.TransformPoint(points[i]); float currentDist = Vector3.Distance(worldSpaceVertex, transform.position); if (currentDist < closestDist) { closestDist = currentDist; ClosestPointIndex = i; } } return ClosestPointIndex; } Vector3 getClosetPointonEdge(PolygonCollider2D PolygonCollider2D) { Vector3 result = Vector3.zero; Vector2[] points = PolygonCollider2D.points; var index = GetClosestPointIndex(PolygonCollider2D); int index_next = index == points.Length - 1 ? 0 : index + 1; int index_last = index == 0 ? points.Length - 1 : index - 1; Vector3 point = PolygonCollider2D.transform.TransformPoint(points[index]); Vector3 point_next = PolygonCollider2D.transform.TransformPoint(points[index_next]); Vector3 point_last = PolygonCollider2D.transform.TransformPoint(points[index_last]); var a = ClosestPointToLine(point, point_next, transform.position); var b = ClosestPointToLine(point, point_last, transform.position); result = Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b) ? b : a; Vector3 normal_next = Vector3.Cross(point_next-point, vectorz).normalized; Vector3 normal_last = Vector3.Cross(point-point_last, vectorz).normalized; Debug.DrawRay(point_next, normal_next, Color.white); Debug.DrawRay(point_last, normal_last, Color.white); float normalLerp; if(Vector3.Distance(transform.position, a) > Vector3.Distance(transform.position, b)) { // in point_last normalLerp = Vector3.Distance(result, point) / Vector3.Distance(point_last, point); //normalLerp = Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next); } else { // in point_next normalLerp = Vector3.Distance(result, point_next) / Vector3.Distance(point, point_next); } //Debug.DrawRay(point_next, (point_next-point_last)*Vector3.Dot(point_next-point_last, result-point_last), Color.white); normalLerped = Vector3.Lerp(normal_last, normal_next, normalLerp); normalLerped2 = Vector3.Lerp(normal_last, normal_next, Vector3.Distance(point_last, result) / Vector3.Distance(result, point_next)); Debug.DrawRay(result, normalLerped2 *1.3f, Color.red); Debug.DrawRay(result, normalLerped*1.2f, Color.white); normalLerped = normalLerped2; ClosestPointToLineA = a; ClosestPointToLineA.z = Vector3.Distance(transform.position, a); ClosestPointToLineB = b; ClosestPointToLineB.z = Vector3.Distance(transform.position, b); Debug.DrawRay(point_last, point-point_last, Color.yellow); Debug.DrawRay(point, point_next-point, Color.green); Debug.DrawRay(result, Vector3.zero-result, Color.red); return result; } Vector3 ClosestPointToLine(Vector3 _lineStartPoint, Vector3 _lineEndPoint, Vector3 _testPoint) { Vector3 pointTowardStart = _testPoint - _lineStartPoint; Vector3 startTowardEnd = (_lineEndPoint - _lineStartPoint).normalized; Debug.DrawRay(_lineStartPoint, pointTowardStart, Color.magenta); Debug.DrawRay(_lineStartPoint, startTowardEnd, Color.cyan); float lengthOfLine = Vector3.Distance(_lineStartPoint, _lineEndPoint); float dotProduct = Vector3.Dot(startTowardEnd, pointTowardStart); if (dotProduct <= 0) { return _lineStartPoint; } if (dotProduct >= lengthOfLine) { return _lineEndPoint; } Vector3 thirdVector = startTowardEnd * dotProduct; Vector3 closestPointOnLine = _lineStartPoint + thirdVector; return closestPointOnLine; } void alignSurface() { //Vector3 normal = getGroundSurface().normal; Vector3 normal = normalLerped; Vector3 newDir = Vector3.RotateTowards(transform.up, normal, /*0.05f*/ alignSpeed * Time.deltaTime , 0.0F); Debug.DrawRay(transform.position, newDir, Color.blue); transform.rotation = Quaternion.FromToRotation(Vector3.up, newDir); } void jumpPose() { if (isGround && !animator.GetCurrentAnimatorStateInfo(0).IsName("player@run")) animator.Play("player@idle");// 如果角色在地面並且未播放跑步動作,則播放待機動作 if (!isGround) animator.Play("player@jump");// 如果角色在空中,則播放跳躍動作 } void groundTest() { isGround = Vector3.Distance(transform.position, GravityPoint) < 0.5f; } RaycastHit2D getGroundSurface() { var raycasthit2d = Physics2D.Raycast(transform.position + Vector3.down * 0.01f, GravityPoint - transform.position, 5f); return raycasthit2d; } public void goLeft() { transform.position -= transform.right * speed * (1+Time.deltaTime); spriteRenderer.flipX = true; if (isGround) animator.Play("player@run"); } public void goRight() { transform.position += transform.right * speed * (1+Time.deltaTime); spriteRenderer.flipX = false; if (isGround) animator.Play("player@run"); } public void goJump() { if (isGround) rb.AddForce(transform.up * 300); } public void release() { rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * 0.5f); //放開空白鍵 減緩跳躍上升 } } |