本人是彻头彻尾的unity小白,打算通过写一个简单的王者荣耀demo来学习unity相关知识。
制作的过程肯定不是最优的处理方案,只是为了使用unity各大系统的功能模块。
本文章主要记录下在学习(制作)过程中遇到的一些坑(其实就是小白不懂的地方)。
【动画】
动画尝试了使用unity的动画控制器,通过UI界面做好一些简单的动画切换逻辑,然后在程序里通过设置的条件变量来控制状态切换。
之前我通过update来检测人物移动到目的地时,把人物从run的状态改成idle状态,发觉他会在原地继续跑一段时间才会切换状态,一开始还以为update里面逻辑没做好,各种调试…… 最后才发觉在动画控制器里默认勾选了【Has Exit Time】导致必须等run动画完全播放结束才能切换动画状态,后来把这个勾选去掉,就正常了。
【寻路】
使用了系统自带的寻路组件NavMeshAgent。在我设置了目的地后,就会把人物的动画切换到run状态,这时我需要知道他到达目的地那刻,把人物动画切换回idle状态。 可我在这寻路组件里好像找不到到达目的地的回调方法…… 最后只能通过不断update检查【hasPath】这个值,当此值从true转false时,代表他到目的地了。
这里也要注意!在【SetDestination】 后,【hasPath】不一定立马就会切换为true的,可能会在下一帧才会变成true。
【获取玩家点击屏幕的对象】
当玩家点击屏幕时,我们需要知道玩家究竟点中屏幕里面的什么。这里我用到了【Physics】通过玩家点击屏幕的坐标发射一条射线到游戏内,返回游戏内经过这条射线的所有交点。
可玩家点击屏幕我们往往只需要知道玩家点击到屏幕显示在最上层的那个单位。而【Physics.RaycastAll】返回的交点集合是无序的!无序的!!!
最后我通过遍历所有和射线有交点的物体,通过点积找出在最前方的代码。
// 这条射线有可能会经过多个交叉点(例如人物,例如花花草草等障碍物),所以保存目标时用数组
RaycastHit[] hits = Physics.RaycastAll(ray, 200);
// 遍历所有交叉点
// 此处说明:由于RaycastHit数组返回的交叉点是【无序】的!!! 此处只需要获取距离屏幕最近的交叉点(就是点击中显示在最前方的物体),所以遍历后通过点积找出在最前方的物体
RaycastHit item = hits[0];
for (int i = 1; i < hits.Length; ++i)
{
float value = Vector3.Dot(item.transform.forward, hits[i].transform.position);
if (value > 0)
{
item = hits[i];
}
}
【人物身上显示的内容(如血条)】
方案1:通过Quaternion.LookRotation方法获取正面朝向摄像头需要的旋转角度
Transform baseTF = transform.parent;
Vector3 relativePos = baseTF.position - Camera.main.transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePos);
baseTF.transform.rotation = rotation;
效果:确实血条会正对着摄像头,可摄像头会跟随人物移动,导致血条也跟着摄像头移动,效果极其古怪(如果摄像头是不动的,可能效果还是可以的)
方案2:在人物身上创建一个canvas,然后创建血条。
此方案制作简单,实现原理也简单,效果还好。 但有个致命的缺点,一个canvas就会执行一次drawcall,如果有几百个小兵,执行几百次drawcall,为了一条血条这样耗性能不值得,也不能接受!
方案3:使用WorldToScreenPoint
只需要把玩家的坐标通过方法WorldToScreenPoint转换成屏幕坐标,就可以轻松的把血条的位置设置到屏幕对应的位置上了。
这也是我最终采取的解决方案。