2014-10-14 4 views
1

현재 진행중인 과정 중 하나에 대한 프로젝트를 진행 중입니다. unity3D에서 2D 게임을 만들고 있는데, 작은 문제가 있습니다. 게임을 실행할 때마다 캐릭터가지도를 통해 계속 떨어지는 경우가 있습니다. rigidbody2D와 boxCollider2D를 내 캐릭터와 전경 모두에 추가 했더라도. 코드가 첨부되어 있으며, C#이며 약간 긴 코드입니다.2D 캐릭터가 Unity3D의지도에서 벗어납니다.

enter code here 
using System; 
using UnityEngine; 
using System.Collections; 

public class CharacterController2D : MonoBehaviour 
{ 
private const float SkinWidth = .02f; 
private const int TotalHorizontalRays = 8; 
private const int TotalVerticalRays = 4; 

private static readonly float SlopeLimitTanget = Mathf.Tan (75f * Mathf.Deg2Rad); 

public LayerMask PlatformMask; 
public ControllerParameters2D DefaultParameters; 


public ControllerState2D State { get; private set; } 
public Vector2 Velocity { get { return _velocity; }} 
public bool HandleCollisions { get; set; } 
//Return overrideparamteres if it is not null, if it is null it will return DefaultParameters 
public ControllerParameters2D Parameters { get { return _overrideParameters ?? DefaultParameters; } } 
public GameObject StandingOn { get; private set;} 
public Vector3 PlatformVelocity { get; private set;} 

public bool CanJump 
    { 
     get 
     { 
      if(Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpAnywhere) 
       return _jumpIn <= 0; 

      if(Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpOnGround) 
       return State.IsGrounded; 

      return false; 
     } 
    } 


private Vector2 _velocity; 
private Transform _transform; 
private Vector3 _localScale; 
private BoxCollider2D _boxCollider; 
private ControllerParameters2D _overrideParameters; 
private float _jumpIn; 
private GameObject _lastStandingOn; 
private Vector3 
     _activeGlobalPlatformPoint, 
     _activeLocalPlatformPoint; 

private Vector3 
     _raycastTopLeft, 
     _raycastBottomRight, 
     _raycastBottomLeft; 

private float _verticalDistanceBetweenRays, 
_horizonatalDistanceBetweenRays; 

public void Awake() 
{ 
    HandleCollisions = true; 
    State = new ControllerState2D(); 
    _transform = transform; 
    _localScale = transform.localScale; 
    _boxCollider = GetComponent <BoxCollider2D>(); 

    // Absolute Value 
    var colliderWidth = _boxCollider.size.x * Mathf.Abs(transform.localScale.x) - (2 * SkinWidth); 
    _horizonatalDistanceBetweenRays = colliderWidth/(TotalVerticalRays - 1); 

    var colliderHeight = _boxCollider.size.y * Mathf.Abs(transform.localScale.y) - (2 * SkinWidth); 
    _verticalDistanceBetweenRays = colliderHeight/(TotalHorizontalRays - 1); 
} 

public void AddForce(Vector2 force) 
{ 
    _velocity = force; 
} 

public void SetForce(Vector2 force) 
{ 
    _velocity += force; 
} 

public void SetHorizontalForce(float x) 
{ 
    _velocity.x = x; 
} 

public void SetVerticalForce(float y) 
{ 
    _velocity.y = y; 
} 

public void Jump() 
{ 
    AddForce(new Vector2(0, Parameters.JumpMagnitude)); 
    _jumpIn = Parameters.JumpFrequency; 
} 

public void LateUpdate() 
{ 
    _jumpIn -= Time.deltaTime; 
    //We force the player to go up or down based on the gravity 
    _velocity.y += Parameters.Gravity * Time.deltaTime; 
    //Move the characther per his velocity scaled by time 
    Move (Velocity * Time.deltaTime); 
} 

// Ensures the player doesn't fall off the map or move through the wall 
private void Move(Vector2 deltaMovement) 
{ 
    var wasGrounded = State.IsCollidingBelow; 
    State.Reset(); 

    if(HandleCollisions) 
    { 
     HandlePlatforms(); 
     CalculateRayOrigins(); 

     if(deltaMovement.y < 0 && wasGrounded) 
      HandleVerticalSlope(ref deltaMovement); 

     if(Mathf.Abs(deltaMovement.x) > .001f) 
      MoveHorizontally(ref deltaMovement); 

     MoveVertically(ref deltaMovement); 

     CorrectHorizontalPlacement(ref deltaMovement, true); 
     CorrectHorizontalPlacement(ref deltaMovement, false); 
    } 

    _transform.Translate(deltaMovement, Space.World); 

    if (Time.deltaTime > 0) 
     _velocity = deltaMovement/Time.deltaTime; 

    _velocity.x = Mathf.Min (_velocity.x, Parameters.MaxVelocity.x); 
    _velocity.y = Mathf.Min (_velocity.y, Parameters.MaxVelocity.y); 

    if(State.IsMovingUpSlope) 
     _velocity.y = 0; 

    //Standing on the platform 
    if(StandingOn != null) 
    { 
      _activeGlobalPlatformPoint = transform.position; 
      _activeLocalPlatformPoint = StandingOn.transform.InverseTransformPoint(transform.position); 

      Debug.DrawLine(transform.position, _activeGlobalPlatformPoint); 
      Debug.DrawLine(transform.position, _activeLocalPlatformPoint + StandingOn.transform.position); 

      if(_lastStandingOn != StandingOn) 
      { 
       //If the last thing we are standing on is not null, send a message to leave it 
       if(_lastStandingOn != null) 
        _lastStandingOn.SendMessage("ControllerExist2D", this, SendMessageOptions.DontRequireReceiver); 

       //Inform what we are standing on that we have entered 
       StandingOn.SendMessage("ControllerEnter2D", this, SendMessageOptions.DontRequireReceiver); 
       _lastStandingOn = StandingOn; 
      } 

      //Invoke the platform that we are standing on it 
      else if (StandingOn != null) 
       StandingOn.SendMessage("ControllerStay2D", this, SendMessageOptions.DontRequireReceiver); 
    } 
    else if (_lastStandingOn != null) 
    { 
     _lastStandingOn.SendMessage("ControllerExit2D", this, SendMessageOptions.DontRequireReceiver); 
     _lastStandingOn = null; 
    } 
} 

private void HandlePlatforms() 
{ 
     //Calculate the velocity of the platform 
     if(StandingOn != null) 
     { 
      var newGlobalPlatformPoint = StandingOn.transform.TransformPoint(_activeLocalPlatformPoint); 
      var moveDistance = newGlobalPlatformPoint - _activeGlobalPlatformPoint; 
      //Sticks the player on the platform, wherever the platform teleport the players stays on it 
      if(moveDistance != Vector3.zero) 
       transform.Translate(moveDistance, Space.World); 

      PlatformVelocity = (newGlobalPlatformPoint - _activeGlobalPlatformPoint)/Time.deltaTime; 
     } 
     else 
      PlatformVelocity = Vector3.zero; 

     StandingOn = null; 
} 

private void CorrectHorizontalPlacement(ref Vector2 deltaMovement, bool isRight) 
{ 
     var halfwidth = (_boxCollider.size.x * _localScale.x)/2f; 
     var rayOrigin = isRight ? _raycastBottomRight : _raycastBottomLeft; 

     if(isRight) 
      rayOrigin.x -= (halfwidth - SkinWidth); 
     else 
      rayOrigin.x += (halfwidth - SkinWidth); 

     var rayDirection = isRight ? Vector2.right : -Vector2.right; 
     var offset = 0f; 

     for(var i = 1; i <= TotalHorizontalRays - 1; i++) 
     { 
      var rayVector = new Vector2(deltaMovement.x + rayOrigin.x, deltaMovement.y + rayOrigin.y + (i * _verticalDistanceBetweenRays)); 
      Debug.DrawRay(rayVector, rayDirection * halfwidth, isRight ? Color.cyan : Color.magenta); 

      var raycastHit = Physics2D.Raycast(rayVector, rayDirection, halfwidth, PlatformMask); 
      if(!raycastHit) 
       continue; 


      offset = isRight ? ((raycastHit.point.x - _transform.position.x) - halfwidth) : (halfwidth - (_transform.position.x - raycastHit.point.x)); 
     } 

     deltaMovement.x += offset; 
} 

private void CalculateRayOrigins() 
{ 
     var size = new Vector2 (_boxCollider.size.x * Mathf.Abs (_localScale.x), _boxCollider.size.y * Mathf.Abs (_localScale.y))/2; 
     var center = new Vector2(_boxCollider.center.x * _localScale.x, _boxCollider.center.y * _localScale.y); 

     //Location of the player, then we add the box collider to it relative to the center of the player 
     _raycastTopLeft = _transform.position + new Vector3 (center.x - size.x + SkinWidth, center.y + size.y - SkinWidth); 
     _raycastBottomRight = _transform.position + new Vector3 (center.x + size.x - SkinWidth, center.y - size.y + SkinWidth); //Going right 
     _raycastBottomLeft = _transform.position + new Vector3 (center.x - size.x + SkinWidth, center.y - size.y + SkinWidth); //Going left and down-up 
} 

//Cast rays to the left or to the right depending on the player's movement 
//Determining how far the player can go either to the left, or to the right 
private void MoveHorizontally(ref Vector2 deltaMovement) 
{ 
     var isGoingRight = deltaMovement.x > 0; 
     //The distance between the starting point and the final destination 
     var rayDistance = Mathf.Abs (deltaMovement.x) + SkinWidth; 
     //Where is the player going? right or left 
     var rayDirection = isGoingRight ? Vector2.right : -Vector2.right; 
     //Right? we start from bottom right. Left? we start fro, bottom left 
     var rayOrigin = isGoingRight ? _raycastBottomRight : _raycastBottomLeft; 

     //Determines how many rays we want to shoot out to the left or to the right 
     for(var i = 0; i < TotalHorizontalRays; i++) 
     { 
      var rayVector = new Vector2(rayOrigin.x, rayOrigin.y + (i * _verticalDistanceBetweenRays)); 
      //Visual representation about the rays 
      Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red); 
      //Checks if the player hit something or not 
      var rayCastHit = Physics2D.Raycast(rayVector, rayOrigin, rayDistance, PlatformMask); 
      if(!rayCastHit) //If there was a raycast then do something, otherwise continue to loop 
       continue; 

      //We return true if we are on a horizotnal slope, and check if we are going right or left or hit something while going up 
      if(i == 0 && HandleHorizontalSlope(ref deltaMovement, Vector2.Angle(rayCastHit.normal, Vector2.up), isGoingRight)) 
       break; 

      //If we hit something then we can only go that far forward 
      deltaMovement.x = rayCastHit.point.x - rayVector.x; 
      rayDistance = Mathf.Abs(deltaMovement.x); 

      if(isGoingRight) 
      { 
       //If we are going right, then we have to substract the skinwidth 
       deltaMovement.x -= SkinWidth; 
       State.IsCollidingRight = true; 
      } 
      else 
      { 
       //The oppoiste of the if statement, if we are going left, we add the skinwidth 
       deltaMovement.x += SkinWidth; 
       State.IsCollidingLeft = true; 
      } 
      //Handles error collision, if the player hits something and go through it 
      if(rayDistance < SkinWidth + .0001f) 
       break; 
     } 
} 

private void MoveVertically(ref Vector2 deltaMovement) 
{ 
     //Check to see if going up or down 
     var isGoingUp = deltaMovement.y > 0; 
     var rayDistance = Mathf.Abs (deltaMovement.y) + SkinWidth; 
     var rayDirection = isGoingUp ? Vector2.up : -Vector2.up; 
     var rayOrigin = isGoingUp ? _raycastTopLeft : _raycastBottomLeft; 

     rayOrigin.x += deltaMovement.x; 

     var standingOnDistance = float.MaxValue; 
     for(var Count = 0; Count < TotalVerticalRays; Count++) 
     { 
      var rayVector = new Vector2(rayOrigin.x + (Count * _horizonatalDistanceBetweenRays), rayOrigin.y); 
      Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red); 

      var raycastHit = Physics2D.Raycast(rayVector, rayDirection, rayDistance, PlatformMask); 
      //If the player hit nothing then keep going. 
      if(raycastHit) 
      { 
       continue; 
      } 

      if(!isGoingUp) 
      { 
       var verticalDistanceToHit = _transform.position.y - raycastHit.point.y; 
       if(verticalDistanceToHit < standingOnDistance) 
       { 
        standingOnDistance = verticalDistanceToHit; 
        //Platform we are standing on 
        StandingOn = raycastHit.collider.gameObject; 
       } 
      } 
      //Determine the furthest distance we can move down or up without hitting anything 
      deltaMovement.y = raycastHit.point.y - rayVector.y; 
      rayDistance = Mathf.Abs(deltaMovement.y); 

      if(isGoingUp) 
      { 
       deltaMovement.y -= SkinWidth; 
       State.IsCollidingAbove = true; 
      } 
      else 
      { 
       deltaMovement.y += SkinWidth; 
       State.IsCollidingBelow = true; 
      } 

      if(!isGoingUp && deltaMovement.y > .0001f) 
      { 
       State.IsMovingUpSlope = true; 
      } 

      if(rayDistance < SkinWidth + .0001f) 
      { 
       break; 
      } 
     } 
} 

private void HandleVerticalSlope(ref Vector2 deltaMovement) 
{ 
     //Give us the center of the vertical rays; 
     var center = (_raycastBottomLeft.x + _raycastBottomRight.x)/2; 
     var direction = -Vector2.up; 

     var slopeDistance = SlopeLimitTanget * (_raycastBottomRight.x - center); 
     var slopeRayVector = new Vector2 (center, _raycastBottomLeft.y); 

     Debug.DrawRay(slopeRayVector, direction * slopeDistance, Color.yellow); 

     var raycastHit = Physics2D.Raycast (slopeRayVector, direction, slopeDistance, PlatformMask); 
     if (!raycastHit) 
       return; 

     // ReSharper disable CompareOfFloatsByEqualityOperator 

     var isMovingDownSlope = Mathf.Sign (raycastHit.normal.x) == Mathf.Sign (deltaMovement.x); 
     if(!isMovingDownSlope) 
      return; 

     var angle = Vector2.Angle (raycastHit.normal, Vector2.up); 
     if(Mathf.Abs(angle) < .0001f) 
      return; //Which means there we are not on a slope, we are on something else 

     State.IsMovingDownSlope = true; 
     State.SlopeAngle = angle; 
     deltaMovement.y = raycastHit.point.y - slopeRayVector.y; 
} 

private bool HandleHorizontalSlope(ref Vector2 deltaMovement, float angle, bool isGoingRight) 
{ 
     //We do not want to move to an angle of 90 
     if(Mathf.RoundToInt(angle) == 90) 
      return false; 

     if(angle > Parameters.SlopeLimit) 
     { 
      deltaMovement.x = 0; 
      return true; 
     } 

     if(deltaMovement.y > .07f) 
      return true; 

     deltaMovement.x += isGoingRight ? -SkinWidth : SkinWidth; 
     deltaMovement.y = Mathf.Abs (Mathf.Tan (angle * Mathf.Deg2Rad) * deltaMovement.x); 
     State.IsMovingUpSlope = true; 
     State.IsCollidingBelow = true; 
     return true; 
} 

public void OnTriggerEnter2D(Collider2D other) 
{ 
    var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>(); 

    if(parameters == null) 
     return; 


    _overrideParameters = parameters.Parameters; 
} 

public void OnTriggerExit2D(Collider2D other) 
{ 
    var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>(); 
    if(parameters == null) 
     return; 

    _overrideParameters = null; 
} 

}

+0

3D 세계에서나 2D 세계에서이 작업을하고 있습니까? – ZoomVirus

+1

단일성에 대한 가장 중요한 점 중 하나는 충돌 논리를 처음부터 롤 할 필요가 없다는 것입니다. 이것은 Unity Editor에서 코드가 필요없이 모두 완료됩니다. 각 게임 개체에 상자 collider2d (또는 원하는 모양)를 첨부하고 플레이어에 rigidbody2d를 연결하고 충돌하는 개체의 z 인덱스를 확인하십시오. 떨어지는 물체에 문제가있는 경우 xy 및 z 위치에서 프로젝트의 충돌하는 2 개의 게임 개체가 있는지 확인하고 지상에서 트리거 충돌을 사용하지 않도록하고 편집기에서 물리 충돌을 수정했는지 확인하십시오 . – Terrance

+0

또한 2d를 사용하는 경우 모든 Colliders가 이름에 2d를 가져야합니다. RigidBody2d도 마찬가지입니다. 편집기 내에서 충돌이 발생하면 작성한 CharacterController 스크립트를 추가해야합니다. – Terrance

답변

0

@Terrance은 당신이 정말로 충돌 감지에 대한 자신의 코드 논리를 작성할 필요가 없습니다 말했듯이 .. 사전에 정말 감사합니다.

두 번째로 코드에서 OnTriggerEnter2DOnTriggerExit2D 메서드를 발견했는데, 모든 boxCollider2d에 isTrigger 옵션이 선택되어 있다는 것을 지적했습니다. 따라서 TRIGGER가 두 물체가 서로 교차하는 것을 결코 막을 수 없으므로 플레이어와 바닥에서 isTrigger 옵션을 선택 취소하는 것이 좋습니다 (물체를 통과시키지 않으려면 두 물체 모두 충돌 자에 'isTrigger'가 선택되어 있어야합니다. 서로를 통해). 충돌을 감지하려면 방법 OnCollisionEnter2DOnCollisionExit2D을 사용하십시오.

트리거와 충돌 장치 사이의 차이점은 무엇입니까 :

촬영 실제 예를 들어 Colliders 당신 자신과 당신이 양쪽에 서있는 바닥이 견고하고 확실한이다 예컨대 유형의 객체이다가.

트리거는 무형이지만 트리거의 예로는 보행 시선 보안 문을 사용할 수 있습니다. 이 문은 어떤 사람도 아무런 방해없이 통과 할 수 있도록 내부가 움푹 들어간 곳입니다. 그러나 금속 물체를 착용하고 문 사이의 "중공 지역"을 통과하면 문이 트리거됩니다. 그러므로 게임 세계에 관해서는 그 문이 빈 공간에서 방아쇠를 당긴다고 말할 수 있습니다.

관련 문제