近来做个游戏,需要用到人物控制和相机控制,就想着自带的方便很多,可本人的强怕原因,花了点时间把它转换成了C#语言的,原本的是js语言的。
脚本里不仅有英文的注释,我还自己加了凭着有道翻译和自己的理解的翻译写了点中文注释。希望各位不要见笑,如果有哪位大神能够指正我的翻译或者发现脚本有什么错误的那就再好不过了。
下面把代码贴出来。(由于没有代码框或者上传附件什么的,我就贴出来算了。)
ThirdPersonCamera.cs
using UnityEngine;
using System.Collections;
[AddComponentMenu("controls/ThirdCameraControl")]
public class ThirdCameraControlScript : MonoBehaviour
{
public Transform cameraTransform;
private Transform target;
public float distance = 7.0f;
public float height = 3.0f;
public float angularSmoothLag = 0.3f;
public float angularMaxSpeed = 15.0f;
public float heightSmoothLag = 0.3f;
public float snapSmoothLag = 0.2f;
public float snapMaxSpeed = 720.0f;
public float clampHeadPositionScreenSpace = 0.75f;
public float lockCameraTimeout = 0.2f;
private Vector3 headOffset = Vector3.zero;
private Vector3 centerOffset = Vector3.zero;
private float heightVelocity = 0.0f;
private float angleVelocity = 0.0f;
private bool snap = false;
private ThirdPlayerControlScript controller;
private float targetHeight = 100000.0f;
void Start ()
{
if (!cameraTransform && Camera.main)
cameraTransform = Camera.main.transform;
if(!cameraTransform)
{
Debug.Log("Please assign a camera to the ThirdPersonCamera script.");
enabled = false;
}
target = transform;
if (target)
controller = target.GetComponent<ThirdPlayerControlScript>();
if (controller)
{
CharacterController characterController = target.GetComponent<CharacterController>();
centerOffset = characterController.bounds.center - target.position;
headOffset = centerOffset;
headOffset.y = characterController.bounds.max.y - target.position.y;
}
else
Debug.Log("Please assign a target to the camera that has a ThirdPersonController script attached.");
Cut(target, centerOffset);
}
void Cut(Transform dummyTarget,Vector3 dummyCenter)
{
float oldHeightSmooth = heightSmoothLag;
float oldSnapMaxSpeed = snapMaxSpeed;
float oldSnapSmooth = snapSmoothLag;
snapMaxSpeed = 10000;
snapSmoothLag = 0.001f;
heightSmoothLag = 0.001f;
snap = true;
Apply(transform, Vector3.zero);
heightSmoothLag = oldHeightSmooth;
snapMaxSpeed = oldSnapMaxSpeed;
snapSmoothLag = oldSnapSmooth;
}
float AngleDistance(float a,float b)
{
a = Mathf.Repeat(a, 360);
b = Mathf.Repeat(b, 360);
return Mathf.Abs(b-a);
}
void Apply(Transform dummyTarget,Vector3 dummyCenter)
{
// Early out if we don't have a target
if (!controller)
return;
Vector3 targetCenter = target.position + centerOffset;
Vector3 targetHead = target.position + headOffset;
// Calculate the current & target rotation angles计算当前和目标旋转角度
float originalTargetAngle = target.eulerAngles.y;
float currentAngle = cameraTransform.eulerAngles.y;
// Adjust real target angle when camera is locked当相机锁定时,调整真正的目标角度
float targetAngle=originalTargetAngle;
// When pressing Fire2 (alt) the camera will snap to the target direction real quick.
//当按下Fire2(alt)相机将很快吸附到目标方向。
// It will stop snapping when it reaches the target
//当快到达目标时会停止吸附
if(Input.GetButton("Fire2"))
{
snap = true;
}
if(snap)
{
// We are close to the target, so we can stop snapping now!
if (AngleDistance(currentAngle, originalTargetAngle) < 3.0)
snap = false;
//随着时间的推移逐渐改变一个给定的角度到期望的角度。
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle,ref angleVelocity, snapSmoothLag, snapMaxSpeed);
}
// Normal camera motion//正常的相机运动
else
{
if(controller.GetLockCameraTimer() < lockCameraTimeout)
{
targetAngle = currentAngle;
}
// Lock the camera when moving backwards!//当倒退时锁定相机
// * It is really confusing to do 180 degree spins when turning around.//当角度真的很大时,旋转180;
if(AngleDistance(currentAngle,targetAngle)>160 && controller.IsMovingBackwards())
{
targetAngle += 180;
}
currentAngle = Mathf.SmoothDampAngle(currentAngle, targetAngle, ref angleVelocity, angularSmoothLag, angularMaxSpeed);
}
// When jumping don't move camera upwards but only down!
if(controller.IsJumping())
{
//当跳得真的很高时,我们才向上移动相机
// We'd be moving the camera upwards, do that only if it's really high
float newTargetHeight = targetCenter.y + height;
if(newTargetHeight<targetHeight || newTargetHeight-targetHeight>5)
{
targetHeight = targetCenter.y + height;
}
}
else
{
targetHeight = targetCenter.y + height;
}
// Damp the height缓冲高度
float currentHeight = cameraTransform.position.y;
currentHeight = Mathf.SmoothDamp(currentHeight, targetHeight, ref heightVelocity, heightSmoothLag);
// Convert the angle into a rotation, by which we then reposition the camera将角度换成Y轴旋转值,然后我们重新定位相机
Quaternion currentRotation = Quaternion.Euler(0, currentAngle, 0);
// Set the position of the camera on the x-z plane to:x-z平面上设置相机的位置在
// distance meters behind the target距离target的meters米背后
cameraTransform.position = targetCenter;
cameraTransform.position += currentRotation * Vector3.back * distance;
// Set the height of the camera设置相机高度
cameraTransform.position = new Vector3(cameraTransform.position.x, currentHeight, cameraTransform.position.z);
SetUpRotation(targetCenter, targetHead);
}
void LateUpdate ()
{
Apply(transform, Vector3.zero);
}
void SetUpRotation(Vector3 centerPos,Vector3 headPos)
{
// Now it's getting hairy. The devil is in the details here, the big issue is jumping of course.
//现在越来越多毛病。魔鬼在于细节,当然最大的问题是跳跃。
// * When jumping up and down we don't want to center the guy in screen space.
//当跳跃时我们不想相机在屏幕空间的中间上下跳动
// This is important to give a feel for how high you jump and avoiding large camera movements.
// 最重要的是我们应该怎么避免感觉相机在产生很大的移动
// * At the same time we dont want him to ever go out of screen and we want all rotations to be totally smooth.
//同时我们不想让他离开屏幕而且我们想让所有的旋转都是平滑
// So here is what we will do:下面就是我们要做的
//
// 1. We first find the rotation around the y axis. Thus he is always centered on the y-axis
//我们首先让相机沿着Y轴相机,因此也总是在Y轴的中心
// 2. When grounded we make him be centered//当角色着地时我们使角色在相机的中心
// 3. When jumping we keep the camera rotation but rotate the camera to get him back into view if his head is above some threshold
//当角色跳跃时相机保持旋转,当角色头部超出了阈值时我们旋转相机使得角色回到视图(相机)中
// 4. When landing we smoothly interpolate towards centering him on screen
//当着地时我们平滑插入使得角色回到屏幕的中心
Vector3 cameraPos = cameraTransform.position;
Vector3 offsetToCenter = centerPos - cameraPos;
// Generate base rotation only around y-axis//生成仅仅基于Y轴的旋转
Quaternion yRotation = Quaternion.LookRotation(new Vector3(offsetToCenter.x, 0, offsetToCenter.z));
Vector3 relativeOffset = Vector3.forward * distance + Vector3.down * height;
cameraTransform.rotation = yRotation * Quaternion.LookRotation(relativeOffset);
//Calculate the projected center position and top position in world space
//计算投影中心位置到顶部位置的距离
Ray centerRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(0.5f, 0.5f, 1));
Ray topRay = cameraTransform.GetComponent<Camera>().ViewportPointToRay(new Vector3(0.5f, clampHeadPositionScreenSpace, 1));
//返回沿着射线在distance距离单位的点。
Vector3 centerRayPos = centerRay.GetPoint(distance);
Vector3 topRayPos = topRay.GetPoint(distance);
//中心射线与顶部射线之间的角度
float centerToTopAngle = Vector3.Angle(centerRay.direction, topRay.direction);
float heightToAngle=centerToTopAngle/(centerRayPos.y-topRayPos.y);
float extraLookAngle=heightToAngle*(centerRayPos.y-centerPos.y);
if(extraLookAngle<centerToTopAngle)
{
extraLookAngle = 0;
}
else
{
extraLookAngle = extraLookAngle - centerToTopAngle;
cameraTransform.rotation *= Quaternion.Euler(-extraLookAngle, 0, 0);
}
}
public Vector3 GetCenterOffset()
{
return centerOffset;
}
}
/*********************华丽的分割线*************/
ThirdPersonController.cs
using UnityEngine;
using System.Collections;
[AddComponentMenu("controls/ThirdPlayerControl")]
public class ThirdPlayerControlScript : MonoBehaviour
{
public AnimationClip idleAnimation;
public AnimationClip walkAnimation;
public AnimationClip runAnimation;
public AnimationClip jumpPoseAnimation;
public float walkMaxAnimationSpeed=0.75f;
public float trotMaxAnimationSpeed = 1.0f;
public float runMaxAnimationSpeed = 1.0f;
public float jumpAnimationSpeed = 1.15f;
public float landAnimationSpeed = 1.0f;
private Animation _animation;
enum CharaCterSate
{
Idle=0,
Walking=1,
Trotting=2,
Running=3,
Jumping=4
}
private CharaCterSate _characterState;
// The speed when walking行走速度
public float walkSpeed = 2.0f;
// after trotAfterSeconds of walking we trot with trotSpeed
public float trotSpeed = 4.0f;
public float runSpeed = 6.0f;
public float inAirControlAcceleration=3.0f;
//跳的高度
public float JumpHeight = 0.5f;
public float gravity = 20;//重力
public float speedSmoothing = 10.0f;
public float rotateSpeed = 500;
public float trotAfterSeconds=3.0f;
public bool canJump = true;
private float JumpRepeatTime = 0.05f;
private float jumpTimeout = 0.15f;
private float groundedTimeout = 0.25f;
private float lockCameraTimer = 0;
private Vector3 moveDirection = Vector3.zero;//xz屏幕上的移动方向
private float verticalSpeed = 0;//当前垂直速度
private float moveSpeed = 0;//当前的xz屏幕的移动速度
//CollisionFlags是CharacterController.Move返回的位掩码。
private CollisionFlags collisionFlags;
private bool jumping = false;
private bool jumpingReachedApex = false;
//判断是否后退(锁定相机不做180旋转)
private bool movingBack = false;
// Is the user pressing any keys?
private bool isMoving = false;
//当角色开始行走的时候
private float walkTimeStart = 0;
private float lastJumpButtonTime = -10.0f;
private float lastJumpTime = -1.0f;
private float lastJumpStartHeight = 0f;
private Vector3 inAirVelocity = Vector3.zero;
private float lastGroundedTime = 0;
private bool isControlLable = true;
void Awake()
{
moveDirection = transform.TransformDirection(Vector3.forward);
_animation = this.gameObject.GetComponent<Animation>();
if(_animation)
Debug.Log("The character you would like to control doesn't have animations. Moving her might look weird.");
if (!idleAnimation)
{
_animation = null;
Debug.Log("No idle animation found. Turning off animations.");
}
if(!walkAnimation)
{
_animation = null;
Debug.Log("No walk animation found. Turning off animations.");
}
if(!runAnimation)
{
_animation = null;
Debug.Log("No run animation found. Turning off animations.");
}
if(!jumpPoseAnimation && canJump)
{
_animation = null;
Debug.Log("No jump animation found and the character has canJump enabled. Turning off animations.");
}
}
void Update ()
{
if(!isControlLable)
{
//在一帧中重置所有的输入,重置输入指令之后所有的方向轴都被设置为0并且所有的按键都被设置为0。
Input.ResetInputAxes();
}
if(Input.GetButtonDown("Jump"))
{
lastJumpButtonTime = Time.time;
}
UpdateSmoothedMovementDirection();
ApplyGravity();
ApplyJumping();
// Calculate actual motion计算实际运动.
Vector3 movement= moveDirection * moveSpeed +new Vector3(0, verticalSpeed, 0) + inAirVelocity;
movement *= Time.deltaTime;
//Move The controller
CharacterController controller = GetComponent<CharacterController>();
collisionFlags = controller.Move(movement);
//Animation sector
if(_animation)
{
if(_characterState==CharaCterSate.Jumping)
{
if(!jumpingReachedApex)
{
_animation[jumpPoseAnimation.name].speed = jumpAnimationSpeed;
_animation[jumpPoseAnimation.name].wrapMode = WrapMode.ClampForever;
_animation.CrossFade(jumpPoseAnimation.name);
}
}
else
{
if(controller.velocity.sqrMagnitude<0.1)
{
_animation.CrossFade(idleAnimation.name);
}
else
{
if(_characterState==CharaCterSate.Running)
{
_animation[runAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0, runMaxAnimationSpeed);
_animation.CrossFade(runAnimation.name);
}
else if(_characterState==CharaCterSate.Trotting)
{
_animation[walkAnimation.name].speed = Mathf.Clamp(controller.velocity.magnitude, 0, trotMaxAnimationSpeed);
_animation.CrossFade(walkAnimation.name);
}
else if(_characterState==CharaCterSate.Walking)
{
_animation[walkAnimation.name].speed=Mathf.Clamp(controller.velocity.magnitude,0,walkMaxAnimationSpeed);
_animation.CrossFade(walkAnimation.name);
}
}
}
}
// Set rotation to the move direction设定旋转移动方向
if(IsGrounded())
{
transform.rotation = Quaternion.LookRotation(moveDirection);
}
else
{
Vector3 xzMove = movement;
xzMove.y = 0;
if(xzMove.sqrMagnitude>0.001)
{
transform.rotation = Quaternion.LookRotation(xzMove);
}
}
// We are in jump mode but just became grounded
if(IsGrounded())
{
lastGroundedTime=Time.time;
inAirVelocity=Vector3.zero;
if(jumping)
{
jumping=false;
SendMessage("DidLand",SendMessageOptions.DontRequireReceiver);
}
}
}
void UpdateSmoothedMovementDirection()
{
Transform cameraTransform = Camera.main.transform;
bool grounded = IsGrounded();
//相对于相机的xz屏幕的前方
Vector3 forward = cameraTransform.TransformDirection(Vector3.forward);
forward.y = 0;
forward = forward.normalized;
//相对于 相机的右
//Always orthogonal to the forward vector总是垂直于前方向量
Vector3 right = new Vector3(forward.z, 0, -forward.x);
var v = Input.GetAxisRaw("Vertical");
var h = Input.GetAxisRaw("Horizontal");
// Are we moving backwards or looking backwards
if (v < -0.2f)
movingBack = true;
else
movingBack = false;
bool wasMoving = isMoving;
isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1;
// Target direction relative to the camera相对于相机的方向
Vector3 targetDirection = h * right + v * forward;
if(grounded)
{
// Lock camera for short period when transitioning moving & standing still
//锁相机时,短时间内转变&站着不动
lockCameraTimer += Time.deltaTime;
if (isMoving != wasMoving)
lockCameraTimer = 0.0f;
// We store speed and direction seperately,
// so that when the character stands still we still have a valid forward direction
//所以当这个角色站着我们仍然有一个有效的前进方向
// moveDirection is always normalized, and we only update it if there is user input.
//moveDirection总是normalized的,我们只在用户输入的时候更新
if(targetDirection!=Vector3.zero)
{
// If we are really slow, just snap to the target direction
//如果我们真的很慢,只是提前到目标方向
if(moveSpeed<walkSpeed * 0.9 && grounded)
{
moveDirection = targetDirection.normalized;
}
// Otherwise smoothly turn towards it
//平稳转向
else
{
moveDirection = Vector3.RotateTowards(moveDirection, targetDirection, rotateSpeed * Mathf.Deg2Rad * Time.deltaTime, 1000);
moveDirection = moveDirection.normalized;
}
}
// Smooth the speed based on the current target direction
//根据当前速度平移速度
float curSmooth = speedSmoothing * Time.deltaTime;
float targetSpeed = Mathf.Min(targetDirection.magnitude, 1.0f);
_characterState = CharaCterSate.Idle;
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
targetSpeed *= runSpeed;
_characterState = CharaCterSate.Running;
}
else if (Time.time - trotAfterSeconds > walkTimeStart)
{
targetSpeed *= trotSpeed;
_characterState = CharaCterSate.Trotting;
}
else
{
targetSpeed *= walkSpeed;
_characterState = CharaCterSate.Walking;
}
moveSpeed = Mathf.Lerp(moveSpeed, targetSpeed, curSmooth);
// Reset walk time start when we slow down
if (moveSpeed < walkSpeed * 0.3)
{
walkTimeStart = Time.time;
}
}
else
{
//lock camera while in air
if (jumping)
lockCameraTimer = 0.0f;
if (isMoving)
inAirVelocity += targetDirection.normalized * Time.deltaTime * inAirControlAcceleration;
}
}
void ApplyJumping()
{
if(lastJumpTime+JumpRepeatTime>Time.time)
return;
if(IsGrounded())
{
// - Only when pressing the button down
// - With a timeout so you can press the button slightly before landing
//超时你可以在着陆时按下按钮
if(canJump && Time.time < lastJumpButtonTime+jumpTimeout)
{
verticalSpeed = CalculateJumpVerticalSpeed(JumpHeight);
SendMessage("DidJump", SendMessageOptions.DontRequireReceiver);
}
}
}
void ApplyGravity()
{
if(isControlLable)
{
// Apply gravity
bool jumpButton = Input.GetButton("Jump");
// When we reach the apex of the jump we send out a message到达跳到顶端时发送消息
if(jumping && !jumpingReachedApex && verticalSpeed<=0.0f)
{
jumpingReachedApex = true;
SendMessage("DidJumpReachApex",SendMessageOptions.DontRequireReceiver);
}
if (IsGrounded())
verticalSpeed = 0;
else
verticalSpeed -= gravity * Time.deltaTime;
}
}
void DidJump()
{
jumping = true;
jumpingReachedApex = false;
lastJumpTime = Time.time;
lastJumpStartHeight = transform.position.y;
lastJumpButtonTime = -10;
_characterState = CharaCterSate.Jumping;
}
float CalculateJumpVerticalSpeed(float targetJumpHeight)
{
// From the jump height and gravity we deduce the upwards
// for the character to reach at the apex.
//speed 从向上跳的高度和重力推出人物向上达到的高度
return Mathf.Sqrt(2 * targetJumpHeight * gravity);
}
bool IsGrounded()
{
return (collisionFlags & CollisionFlags.CollidedBelow) != 0;
}
public void OnControllerColliderHit(ControllerColliderHit hit)
{
if (hit.moveDirection.y > 0.01)
return;
}
public float GetSpeed()
{
return moveSpeed;
}
public bool IsJumping()
{
return jumping;
}
public Vector3 GetDirection()
{
return moveDirection;
}
public bool IsMovingBackwards()
{
return movingBack;
}
public float GetLockCameraTimer()
{
return lockCameraTimer;
}
public bool IsMoving()
{
return Mathf.Abs(Input.GetAxisRaw("Vertical")) + Mathf.Abs(Input.GetAxisRaw("Horizontal")) > 0.5f;
}
public bool HasJumpReachedApex()
{
return jumpingReachedApex;
}
public bool IsGroundWithTimeout()
{
return lastGroundedTime+groundedTimeout>Time.time;
}
public void Reset()
{
gameObject.tag = "Player";
}
}
PS:在那个包里还有第一人称的脚本控制盒相机控制的脚本,如果大家觉得有需要,并且响应强烈的话我考虑下在转换成C#语言。
|