目录
7.创建角色
8.在坦克上面创建血条
9.为游戏对象添加NetworkTransform
10.角色移动
11.摄像机相随
12.炮台移动
测试
7.创建角色
新建一个立方体Player,取消它的外形渲染,然后将位置放在(0,0,0),同时将预制体拉入场景中,成为Player的子物体,同时它的位置也设置为(0,0,0),最后将Mesh Renderer组件移除,这样就看不到网格轮廓 由于坦克物体太大,因此缩放一下 同时将Player的碰撞属性设置一下 因为坦克的一些部件是单独的,而转向过程中需要多个物体同时进行,所以要进行合体8.在坦克上面创建血条
首先创建一个画布,将画布设置世界空间,即可以被摄像机看到 在画布下新建一个text,然后设置相关属性,表示坦克的名字 角色创建完毕后,将该物体保存成预制体,并在场景中删除9.为游戏对象添加NetworkTransform
NetworkTransform是一个可以在客户端同步物体对象位置、旋转和缩放到服务器的组件,添加它时必须要加入NetworkIdentity,之后将信息从服务器同步到其余客户端10.角色移动
在Player物体中添加Player脚本usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingMirror;publicclassPlayer:NetworkBehaviour{privateRigidbodyrb;publicfloatMoveSpeed=8;//水平和垂直的速度privatevoidAwake(){rb=transform.GetComponent<Rigidbody>();}privatevoidFixedUpdate(){if(!isLocalPlayer)return;//如果不是本地角色,就跳过Vector2moveDir;//获取键盘输入的水平和纵向的值,分别是1和-1,代表两个方向,并传给moveDir这个方向变量if(Input.GetAxisRaw("Horizontal")==0&&Input.GetAxisRaw("Vertical")==0){moveDir.x=0;moveDir.y=0;}else{moveDir.x=Input.GetAxisRaw("Horizontal");moveDir.y=Input.GetAxisRaw("Vertical");Move(moveDir);}}voidMove(Vector2direction=default(Vector2)){if(direction!=Vector2.zero)//如果方向变量不是零向量{transform.rotation=Quaternion.LookRotation(newVector3(direction.x,direction.y,0));//调整方向//transform.forward是一个变量,它是根据当前方向计算出的方向变量Vector3movementDir=transform.forward*MoveSpeed*Time.deltaTime;//当前方向的单位向量*速度*时间=当前方向的偏移量rb.MovePosition(rb.position+movementDir);}}}
其中速度*Time.deltaTime保证在不同帧数下角色移动的速度是一样的,也就是与帧数无关。 知识补充: (1)Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关。在性能好的机器上可能fps60,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢,因为Update的执行间隔不一样了。 (2)FixedUpdate是在固定的时间间隔执行,不受游戏帧率的影响。Tick:在处理Rigidbody的时候最好用FixedUpdate。
11.摄像机相随
新建CamFollow脚本,添加到GameScene场景的主摄像机上usingSystem;usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;publicclassCamFollow:MonoBehaviour{publicTransformtarget;publicfloatdistance=10f;publicfloatheight=5f;voidUpdate(){if(!target)return;//没有获得玩家坐标就return//获取当前摄像机的旋转角度,返回一个四元数QuaternioncurrentRotation=Quaternion.Euler(0,transform.eulerAngles.y,0);Vector3pos=target.position;//得到玩家的坐标//四元素*(0,0,1)=当前旋转方向上1单位的距离,再*距离=当前旋转方向上的距离(一个Vector)pos-=currentRotation*Vector3.forward*Mathf.Abs(distance);pos.y=target.position.y+Mathf.Abs(height);//在y方向上偏移一定的高度,相当于将摄像机的x和z轴定死transform.position=pos;//将计算完后的pos赋值给摄像机transform.LookAt(target);//当前摄像机指向玩家//画蛇添足,对游戏效果没有影响,留坑//transform.position=target.position-transform.forward*Mathf.Abs(distance);}}
由于Player是预制体,所以无法得到target的transform,因此需要在Player脚本中写一段代码,表示被创建时,将它的transform信息传给主摄像机的CamFollow脚本的target中即可,就实现了映射。【坑点一】重写OnStartLocalPlayer无效,在Awake函数中有效,添加的代码如下:
Camera.main.GetComponent<CamFollow>().target=transform;
【填坑】不小心把Player中Networkidentity中弄成了强制隐藏,因为界面上会有Mirror的标志,所以设置成隐藏,没想到这里的隐藏是使它失效,应该设置成默认。最后添加以下函数有效
publicoverridevoidOnStartLocalPlayer(){Camera.main.GetComponent<CamFollow>().target=transform;}
如此可以实现摄像机跟随
12.炮台移动
在Player.cs的FixedUpdate函数中添加如下代码:Rayray=Camera.main.ScreenPointToRay(Input.mousePosition);//返回从摄像机到当前鼠标点的一个射线Planeplane=newPlane(Vector3.up,Vector3.up);//新建一个平面,因为玩家对象在(0,0,0),所以参数中的法线和经过的点都是(0,1,0)floatdistance=0f;Vector3hitPos=Vector3.zero;//判断平面和射线是否相交,并将射线起点到交点的距离返回给distanceif(plane.Raycast(ray,outdistance)){hitPos=ray.GetPoint(distance)-transform.position;//获得射线在distance位置的坐标-玩家的坐标=相对玩家的交点坐标}RotateTurret(newVector2(hitPos.x,hitPos.z));//因为炮台转向的是在一个平面上进行的,在unity中是x和z轴
在Player.cs添加如下函数:
voidRotateTurret(Vector2direction=default(Vector2))//防空出错{if(direction==Vector2.zero)return;//因为炮台的z轴要指向射线和平面的交点,所以用LookRotation函数,只需要用到forward参数即可(z轴指向forward),//最后返回一个表示旋转的四元数,并得到它转换后欧拉角中绕y轴旋转的角度intnewRotation=(int)(Quaternion.LookRotation(newVector3(direction.x,0,direction.y)).eulerAngles.y);//因为旋转,实际上是绕y轴旋转的,所以将y轴旋转的角度封装成四元数赋值给炮台的rotation即可turret.rotation=Quaternion.Euler(-90,newRotation,0); //因为炮台本来就旋转了-90,所以这里要不变}
将炮台加入变量中