参考第三人称Character Controller实现

 

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">第三人称Character Controller里用到了Character Controller组件,不用刚体,而且老版动画,我略微修改了他的源码,不用Character Controller,用刚体,用新版动画 实现了同样的效果。将挂在主角身上的两个脚本换成一下两个即可,动画自己修改,非常简单。现将代码放出:</span>

 

PlayerMov.cs

 

using UnityEngine;
using System.Collections;

public class PlayerMov : MonoBehaviour {
	public float walkSpeed = 2.0f;
    public float trotSpeed = 4.0f;
    public float runSpeed = 6.0f;
    public float trotAfterSeconds = 3.0f;
    public float rotateSpeed = 500.0f;
    public float speedSmooth = 10.0f;
    public float gravity=20f;
    public float jumpHeight = 0.5f;
    public float inAirControlAcceleration = 3.0f;
    public float jumpAnimationSpeed = 1.15f;
    public float landAnimationSpeed = 0.5f;

    public delegate void DidJumpReachApexDelegate();
    public DidJumpReachApexDelegate DidJumpReachApex;

    public delegate void DidJumpDelegate();
    public DidJumpDelegate DidJump;

    public delegate void DidLandDelegate();
    public DidLandDelegate DidLand;

	Rigidbody playerRigidbody;
	int floorMask;
	Vector3 movement;
	float camRayLength = 100.0f;
    Animator anim;
    string[] states={"idle","walk","run","jump"};
    float moveSpeed=0;
    Vector3 moveDirection;
    float walkTimeStart;
    bool jumping;
    bool jumpingReachedApex;
    float verticalSpeed=0f;
    float lastJumpTime;
    float lastJumpButtonTime;
    float jumpRepeatTime = 0.5f;
    float jumpTimeout = 0.15f;
    Vector3 inAirVelocity=Vector3.zero;

    enum CharacterState
    {
        Idle,
        Walk,
        Trot,
        Run,
        Jump,
    }
    CharacterState _characterState;
	//支持中文
	// Use this for initialization
	void Awake()
	{
		playerRigidbody = GetComponent<Rigidbody>();
		floorMask = LayerMask.GetMask("Floor");
        anim = GetComponent<Animator>();

        DidJump += StartJump;
	}

	void Start () {

	}

    void OnGUI()
    {
        Event e = Event.current;
        if (e != null && e.keyCode == KeyCode.Space)
        {
            lastJumpButtonTime = Time.time;
        }
    }
	
	// Update is called once per frame
	void Update () {
        UpdateSmoothedMovementDirection();
        ApplyGravity();
        ApplyJumping();

        var movement = moveDirection * moveSpeed + new Vector3(0, verticalSpeed, 0) + inAirVelocity;
        movement *= Time.deltaTime;
        transform.position += movement;
        if (IsMoving)
        {
            transform.rotation = Quaternion.LookRotation(new Vector3(movement.x, 0, movement.z));
        }

        switch (_characterState)
        {
            case CharacterState.Idle:
                SetState("idle");
                break;
            case CharacterState.Walk:
            case CharacterState.Trot:
                SetState("walk");
                break;
            case CharacterState.Run:
                SetState("run");
                break;
            case CharacterState.Jump:
                SetState("jump");
                break;
        }
        SetAnimationSpeed();

        //Turning();

        if (IsGrounded())
        {
            var pos = transform.position;
            pos.y = 1;
            transform.position = pos;

            inAirVelocity = Vector3.zero;
            if (jumping)
            {
                jumping = false;
                if (DidLand != null)
                {
                    DidLand();

                }
            }
        }
    }

    void SetAnimationSpeed()
    {
        if (anim.GetCurrentAnimationClipState(0).Length == 0)
        {
            return;
        }

        var animation = anim.GetCurrentAnimationClipState(0)[0].clip;
        if (animation.name == "jump_pose")
        {
            //animation.wrapMode = WrapMode.ClampForever;
            if (jumpingReachedApex)
            {
                anim.speed = landAnimationSpeed;
            }
            else
            {
                anim.speed = jumpAnimationSpeed;
            }
        }
        else
        {
            anim.speed = 1.0f;
        }
    }

    //void FixedUpdate()
    //{
    //    float h = Input.GetAxisRaw("Horizontal");
    //    float v = Input.GetAxisRaw("Vertical");
    //    if (Mathf.Abs(h) > 0.1 || Mathf.Abs(v) > 0.1)
    //    {
    //        Move(h, v);
    //    }
    //    else
    //    {
    //        SetState("idle");
    //    }

    //    Turning();
    //}

	void Move(float h, float v)
	{
		movement.Set(h, 0, v);
		movement = movement.normalized * walkSpeed * Time.deltaTime;
		playerRigidbody.MovePosition(transform.position + movement);
        SetState("walk");
	}

	void Turning()
	{
		Ray camRay = Camera.main.ScreenPointToRay(Input.mousePosition);
		RaycastHit floorHit;

		if (Physics.Raycast(camRay, out floorHit, camRayLength, floorMask))
		{
			Vector3 playerToMouse = floorHit.point - transform.position;
			playerToMouse.y = 0f;
			Quaternion newRotation = Quaternion.LookRotation(playerToMouse);
			playerRigidbody.MoveRotation(newRotation);
		}
	}

    void SetState(string state)
    {
        for (int i = 0; i < states.Length; i++)
        {
            if (states[i] == state)
            {
                anim.SetBool(states[i], true);
            }
            else
            {
                anim.SetBool(states[i], false);
            }
        }
    }

    void UpdateSmoothedMovementDirection()
    {
        var cameraTransform = Camera.main.transform;
        var forward = cameraTransform.TransformDirection(Vector3.forward);
        forward.y = 0;
        forward = forward.normalized;

        var right = new Vector3(forward.z, 0, -forward.x);

        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        bool wasMoving = IsMoving;
        if (Mathf.Abs(h) > 0.1 || Mathf.Abs(v) > 0.1)
        {
            IsMoving = true;
        }
        else
        {
            IsMoving = false;
        }

        if (v < -0.2)
        {
            IsMovingBack = true;
        }
        else
        {
            IsMovingBack = false;
        }

        var targetDirection = h * right + v * forward;

        if (IsGrounded())
        {
            LockCameraTimer += Time.deltaTime;
            if (wasMoving != IsMoving)
            {
                LockCameraTimer = 0f;
            }

            if (targetDirection != Vector3.zero)
            {
                if (moveSpeed < walkSpeed * 0.9)
                {
                    moveDirection = targetDirection.normalized;
                }
                else
                {
                    moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
                    moveDirection = moveDirection.normalized;
                }
            }

            var targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
            _characterState = CharacterState.Idle;

            if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
            {
                targetSpeed *= runSpeed;
                _characterState = CharacterState.Run;
            }
            else if (Time.time - walkTimeStart > trotAfterSeconds && IsMoving)
            {
                targetSpeed *= trotSpeed;
                _characterState = CharacterState.Trot;
            }
            else if (IsMoving)
            {
                targetSpeed *= walkSpeed;
                _characterState = CharacterState.Walk;
            }

            moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, speedSmooth * Time.deltaTime);
            if (moveSpeed < walkSpeed * 0.3)
            {
                walkTimeStart = Time.time;
            }
        }
        else
        {
            if (jumping)
            {
                LockCameraTimer = 0f;
            }
            if (IsMoving)
            {
                inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
            }
        }
        
    }

    bool IsGrounded()
    {
        return transform.position.y <= 1;
    }

    void ApplyGravity()
    {
        if (jumping && !jumpingReachedApex && verticalSpeed <= 0f)
        {
            jumpingReachedApex = true;
            if (DidJumpReachApex!=null)
            {
                DidJumpReachApex();
            }
        }

        if (IsGrounded())
        {
            verticalSpeed = 0f;
        }
        else
        {
            verticalSpeed -= gravity * Time.deltaTime;
        }
    }

    void ApplyJumping()
    {
        if (Time.time - lastJumpTime < jumpRepeatTime)
        {
            return;
        }
        if (IsGrounded())
        {
            if ((Time.time - lastJumpButtonTime) < jumpTimeout)
            {
                verticalSpeed = Mathf.Sqrt(2 * jumpHeight * gravity);
                if (DidJump != null)
                {
                    DidJump();

                }
            }
        }
    }

    void StartJump()
    {
        jumping = true;
        jumpingReachedApex = false;
        lastJumpTime = Time.time;
        lastJumpButtonTime = -10;

        _characterState = CharacterState.Jump;
    }

    public float LockCameraTimer
    {
        get;
        private set;
    }

    public bool IsMovingBack
    {
        get;
        private set;
    }

    public bool IsMoving
    {
        get;
        private set;
    }
}


ThirdPersonCamera.cs

 

 

using UnityEngine;
using System.Collections;
[RequireComponent(typeof(PlayerMov))]
public class ThirdPersonCamera : MonoBehaviour {
    public Transform cameraTransform;
    public float angularSmoothLag = 0.3f;
    public float angularMaxSpeed = 15.0f;
    public float snapSmoothLag = 0.2f;
    public float snapMaxSpeed = 720f;
    public float lockCameraTimeout = 0.2f;
    public float distance = 5f;
    public float height = 3f;
    public float clampHeadPositionScreenSpace = 0.75f;

    private bool snap=true;
    private PlayerMov playerMov;
	//支持中文
	// Use this for initialization
    void Awake()
    {
        if (!cameraTransform && Camera.main)
        {
            cameraTransform = Camera.main.transform;
        }
        if (!cameraTransform)
        {
            Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
            enabled = false;
        }

        playerMov = GetComponent<PlayerMov>();
    }

	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

    float AngleDistance(float a, float b)
    {
        a = Mathf.Repeat(a, 360);
        b = Mathf.Repeat(b, 360);
        return Mathf.Abs(a - b);
    }

    void LateUpdate()
    {
        var targetAngle = transform.eulerAngles.y;
        var currentAngle = cameraTransform.eulerAngles.y;
        float angleVelocity = 0;
        if (Input.GetButtonDown("Fire2"))
        {
            snap = !snap;
        }

        if (snap)
        {
            if (!playerMov.IsMovingBack)
            {
                currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, snapSmoothLag, snapMaxSpeed);
            }
        }
        else
        {
            targetAngle = currentAngle;
        }

        var targetCenter = transform.position;
        var currentRotation = Quaternion.Euler(0, currentAngle, 0);
        var currentHeight = cameraTransform.position.y;
        cameraTransform.position = targetCenter;
        cameraTransform.position += currentRotation * Vector3.back * distance;

        var tmp = cameraTransform.position;
        tmp.y = currentHeight;
        cameraTransform.position = tmp;

        SetUpRotation();
    }

    private void SetUpRotation()
    {
        var centerPos = transform.position;
	    var offsetToCenter = centerPos - cameraTransform.position;
	    
	    // Generate base rotation only around y-axis
	    var yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));

	    var relativeOffset = Vector3.forward * distance + Vector3.down * height;
	    cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);

	    // Calculate the projected center position and top position in world space
	    var centerRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, 0.5f, 1f));
	    var topRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(.5f, clampHeadPositionScreenSpace, 1f));

	    var centerRayPos = centerRay.GetPoint(distance);
	    var topRayPos = topRay.GetPoint(distance);
	
	    var centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
	
	    var heightToAngle = centerToTopAngle / (centerRayPos.y - topRayPos.y);

	    var extraLookAngle = heightToAngle * (centerRayPos.y - centerPos.y);
        if (extraLookAngle < centerToTopAngle)
	    {
		    extraLookAngle = 0;
	    }
	    else
	    {
		    extraLookAngle = extraLookAngle - centerToTopAngle;
		    cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
	    }
    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值