<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);
}
}
}