学unity也有一段时间了,做了不少东西,但因为没有老师也不知道自己做得对不对。所以拿出一点东西出来让大家批评指正。随带说下我的版本是unity5.x
废话不多说进入主题,第三人称游戏我想大家肯定都接触过,比如魔兽、剑灵、暗黑破坏神这样的游戏,玩家可以通过屏幕看到游戏角色,通过鼠标键盘的操作进行人物控制和视角的转向。现在我要做的就是制作这样的第三人称的人物控制。
当我们要写作这样的人物控制的时候先要做就是导入一个带动画的角色模型,
。
假设这个模型只有向前奔跑和默认静止两个动画,现在我们要结合animator状态机来进行设置。在Windows菜单下,打开animator状态机。新建一个状态我把它命名为idle&run
然后再在設置參數,speed,speed2分別表示前后和左右两个方向
设置完状态机,然后为默认的状态机添加混合树
关于状态机这里就不细讲了,明眼人都能看出这里混合了前后左右和默认四个状态的混合。
创建脚本,脚本名字为PlayerControl,挂载在人物对象上
接下来代码如下
void Update()
{
float v = Input.GetAxis("Vertical");
_animator.SetFloat("speed", v);
float v2 = Input.GetAxis("Horizontal");
_animator.SetFloat("speed2", v2);
}
通过获取方向键控制动画播放(WASD或↑←→↓)。动画播放是解决了但是按下后会发现人物只是播放了动画,并没有进行移动,而且人物并没有按照想象的方向进行转向。只是播放了美工给的动画,美工只给了一个向前的动画,我也只设置了一种动画,所以模型会按原有的方向播放动画。
所以现在还要加上移动,要怎么移动我觉得应该由制作的游戏的决定。现在我以摄像机为参考坐标,摄像机的前方就是向前同理还有左右,同时还有上左、上右等共计八个方向。声明一个摄像机的变量public Cameramain Crma;
然后在场景里面把摄像机拖到脚本声明的变量上
这里为了后续的扩展我还声明了 人物移动速度speed,和人物重力Gravity,人物跳跃初速度JumpSpeed。
这里我用我SSR级的画图功底说明,前方是哪里
那么向前的向量是这样获取的
Vector3 temppos = gameObject.transform.position -mainCrma.transform.position;
Vector3 Playerforward = new Vector3(temppos.x, 0, temppos.z);
那么玩家向前的代码就这样写
moveDirection = Playerforward .normalized;//向量化模,规范化长度 gameObject.Move(moveDirection * Time.deltaTime);//向前移动
向左呢,我们这里用四元数解决,
Vector3 Playerleft = Quaternion.Euler(0, -90, 0) * Playerforward; //向左的向量
moveDirection = Playerforward .normalized;//向量化模,规范化长度
gameObject.Move(Playerleft * Time.deltaTime);//向左移动
但是这还是不够的,因为美工给我的是只有1个向前方向的模型,上面脚本可以看到位置模型向左移动但是模型方向是向前的,播放的动画也是向前的。所以当我们改变方向的时候需要旋转模型,这个模型怎么旋转呢。没错,向左旋转就是模型以我上面SSR级的画图功底的B向量的方向旋转90度,代码如下
Quaternion rotationA = Quaternion.AngleAxis(90, Vector3.up) * rotationS;//向正左方向旋轉
原理基本就这样,我还加了CharacterController碰撞器,接下来上完整代码
public class PlayerControl : MonoBehaviour
{
public float speed;
private Animator _animator;
public Camera mainCrma;
// private Ray Cameraray, PlayerRay;
// private RaycastHit fromcam, fromplay;
private GameObject gameObj, gameObj2;
private bool speediszero = true, Ispickup = false;
private int keytime1 = 0, keytime2 = 0;
public float jumpSpeed = 5.0F;
public float gravity = 10.0F;
Vector3 moveDirection = Vector3.zero;
// Use this for initialization
void Start()
{
_animator = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{ Vector3 moveDirection = Vector3.zero;
float v = Input.GetAxis("Vertical");
_animator.SetFloat("speed", v);
float v2 = Input.GetAxis("Horizontal");
_animator.SetFloat("speed2", v2);
}
void FixedUpdate()
{
NewgetInput();//设置基本动作
// OtherBehaviour();//设置特殊情况下的一些动作
}
//void OtherBehaviour()//备用
//{
// if (Input.GetMouseButton(0))
// {
// keytime1++;
// // if(keytime1>20){Ispickup=true;}
// }
// if (Input.GetMouseButtonUp(0))
// {
// keytime1 = 0;
// }
// if (keytime1 == 10)//长按鼠标左键事件
// {
// }
//}
void moveDirectionvalue(Vector3 currentposition)//把重力和向量规范化封装在这里
{
CharacterController controller = GetComponent<CharacterController>();
moveDirection = currentposition.normalized;//向量化模,规范化长度
moveDirection.y -= gravity * Time.deltaTime;//Y方向设置重力
controller.Move(moveDirection * Time.deltaTime);
}
void NewgetInput()
{
/*也许下面的变量可以设成枚举,方便扩展*/
Vector3 temppos = gameObject.transform.position - mainCrma.transform.position;
Vector3 Playerforward = new Vector3(temppos.x, 0, temppos.z); //获取向前的向量 ↑
Vector3 Playerright = Quaternion.Euler(0, 90, 0) * Playerforward; //→
Vector3 Playerleft = Quaternion.Euler(0, -90, 0) * Playerforward; //←
Vector3 Playerbackward = Quaternion.Euler(0, 180, 0) * Playerforward;//↓
Vector3 Playerupleft = Quaternion.Euler(0, -45, 0) * Playerforward;//↖
Vector3 Playerbackleft = Quaternion.Euler(0, -135, 0) * Playerforward;//↙
Vector3 Playerupright = Quaternion.Euler(0, 45, 0) * Playerforward;//↗
Vector3 Playerbackright = Quaternion.Euler(0, 135, 0) * Playerforward;//↘
Quaternion rotationW = Quaternion.LookRotation(Playerforward);//向正前方旋轉↑
Quaternion rotationDW = Quaternion.AngleAxis(45, Vector3.up) * rotationW;//向右前方向旋轉↗
Quaternion rotationD = Quaternion.AngleAxis(90, Vector3.up) * rotationW;//向正右方向旋轉→
Quaternion rotationAW = Quaternion.AngleAxis(-45, Vector3.up) * rotationW;//向左前方向旋轉↖
Quaternion rotationS = Quaternion.LookRotation(-Playerforward);//向正后方旋轉↓
Quaternion rotationDS = Quaternion.AngleAxis(-45, Vector3.up) * rotationS;//向右后方向旋轉↙
Quaternion rotationA = Quaternion.AngleAxis(90, Vector3.up) * rotationS;//向正左方向旋轉←
Quaternion rotationAS = Quaternion.AngleAxis(45, Vector3.up) * rotationS;//向左后方向旋轉↘
float distance= mainCrma.GetComponent<CameraControl>().distance;//获取摄像机的距离
//if (controller.isGrounded)//在地面
//{
if (Input.GetKey(KeyCode.W)||Input.GetKey(KeyCode.UpArrow))
{
gameObject.transform.rotation = rotationW;
moveDirectionvalue(Playerforward);
}
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.RightArrow))
{
gameObject.transform.rotation = rotationA;//改变角色模型的方向
// transform.position += Time.deltaTime * speed * Playerleft.normalized;//角色移动的方向移动
moveDirectionvalue(Playerleft);
// mainCrma.transform.position += Time.deltaTime * speed * Playerleft.normalized;
}
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
gameObject.transform.rotation = rotationS;
//transform.position += Time.deltaTime * speed * Playerbackward.normalized;
moveDirectionvalue(Playerbackward);
// mainCrma.transform.position += Time.deltaTime * speed * Playerbackward.normalized;
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
gameObject.transform.rotation = rotationD;
// transform.position += Time.deltaTime * speed * Playerright.normalized;
moveDirectionvalue(Playerright);
// mainCrma.transform.position += Time.deltaTime * speed * Playerright.normalized;
}
if ((Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.A)) || Input.GetKey(KeyCode.UpArrow)&&Input.GetKey(KeyCode.LeftArrow))
{
gameObject.transform.rotation = rotationAW;
//transform.position += Time.deltaTime * speed * Playerupleft.normalized;
moveDirectionvalue(Playerupleft);
//mainCrma.transform.position += Time.deltaTime * speed * Playerupleft.normalized;
}
if (Input.GetKey(KeyCode.W) && Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.UpArrow) && Input.GetKey(KeyCode.RightArrow))
{
gameObject.transform.rotation = rotationDW;
//transform.position += Time.deltaTime * speed * Playerupright.normalized;
moveDirectionvalue(Playerupright);
// mainCrma.transform.position += Time.deltaTime * speed * Playerupright.normalized;
}
if (Input.GetKey(KeyCode.A) && Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow) && Input.GetKey(KeyCode.LeftArrow))
{
gameObject.transform.rotation = rotationAS;
//transform.position += Time.deltaTime * speed * Playerbackleft.normalized;
moveDirectionvalue(Playerbackleft);
//mainCrma.transform.position += Time.deltaTime * speed * Playerbackleft.normalized;
}
if (Input.GetKey(KeyCode.D) && Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.RightArrow) && Input.GetKey(KeyCode.DownArrow))
{
gameObject.transform.rotation = rotationDS;
// transform.position += Time.deltaTime * speed * Playerbackright.normalized;
moveDirectionvalue(Playerbackright);
// mainCrma.transform.position += Time.deltaTime * speed * Playerbackright.normalized;
}
// }
if (Input.GetMouseButtonDown(0))
{
_animator.SetTrigger("lefthand");
keytime1 = 0;
Ispickup = true;
}
if (Input.GetKeyDown(KeyCode.F))
{
_animator.SetTrigger("headattack");
}
if (Input.GetKeyDown(KeyCode.Space))
{
_animator.SetTrigger("jump");
/*取消了根动作,需要添加位移*/
}
if (Input.GetKeyDown(KeyCode.Q))
{
_animator.SetTrigger("leftkick");
}
if (Input.GetKeyDown(KeyCode.E))
{
_animator.SetTrigger("rightkick");
}
}
}
总结和注意事项
1、为了熟练四元数,使用了四元数其实因为玩家只在XZ方向上进行操作,不会产生万向锁,所以也可以不用四元数
2、为了方便管理和扩展应使用状态机,但是这里只是实现了八个方向移动,所以我省略了这部分
3、方向向量的计算可能存在重复计算,另外设置成枚举或结构体可以使代码更美观
4、speed需要外部赋值初始默认值是0,还要加上CharacterController
5、这个人物脚本是以摄像机为参照物,所以会进行很多向量运算。如果要做的游戏是2.5D或者摄像机不能自由操作的游戏,可以直接使用世界坐标系,减少不必要的计算。