坦克大战初步总结

65 阅读3分钟

玩家控制管理

1) 实现玩家垂直虚拟轴的控制达到自由移动的目的

2) 基础相机跟随
    相机跟随:声明固定向量距离,利用玩家与相机之间的固定距离关系得出固定距离:
        玩家位置向量 - 相机位置向量 = 固定距离向量;
    在帧更新中不断更新相机位置向量,公式变形:
        相机位置向量 = 玩家位置向量 - 固定距离向量
    这样就可以实现基础的相机跟随,但视角旋转跟随还未实现。
3) 发射炮弹,创建预制体,调整发射点,拿到发射点位置,通过给予刚体.Velocity速度实现炮弹发射,发射方向也设为当前坦克前方方向;
第一人称玩家相机跟随:

image.png

敌人行为管理:

1) 敌人预制体创建
 1)敌人预制体创建:要求随机位置随机朝向
    a.随机位置的管理:
        随机地图范围,但是会出现坦克堆叠生成现象
    解决方案:
        范围为玩家(0,0,0)位置利用Random.range;
        生成位置重叠,利用检测一定范围内是否存在碰撞体(此处用的是球形检测),注意此时地面碰撞也得去除;
        方法是给地图加图层,一般设置为第8层,`~(1<<8)`意思就是排除第8层的检测;主要检测方法是封装布尔函数:
       ** Physics.CheckSphere(Vector3 position,检测半径,~(1<<8))**
        Vector3 Position 一般用do..while()判断生成位置是否重叠,先生成位置,再放入判断函数是否可用
    b. 随机角度生成:
        利用随机函数随机角度:
            int y = Random.Range(0,360);
        因为Instantiate( , ,四元数角度)所以需要将随机的Vector3角度转化为四元数:
        Quaternion targetAngle=Quaternion.Euler(new Vector3(0,y,0));
        

image.png

2) 敌人数量控制:
    定义生成间隔时间internavl,敌人的最大数量maxEnemy,计数坦克器countEnemy,时间计算器timer;
        timer+= Time.deltaTime;  
        if(timer>internavl){
            if(countEnemy<maxEnemy){
                CreatEnemy();
            }
            timer=0;
        }
3)敌人行为控制(基础AI行为):
主题思路: 10米内开火 ,20米内向着玩家移动,20米外自由移动
    开火设计: 1.转向玩家  2.开火
        1) 转向玩家: 先拿到与玩家之间的位置向量,转为四元数再使用四元数旋转插值函数完成相对流畅的转向;
        Vector dir=player.transform.position-tranform.position;
        Quaternion targetAngle=Quaternion.LookRotation(dir);
        tranfrom.rotation=Quaternion.Lerp(transform.position,targetAngle,Time.deltaTime*转向速度);

image.png

        2)开火:若前方范围内是友军,则不开火;
        检测前方是不是友军,这里用的是物理射线的方式;
        先定义射线碰撞器 private RaycastHit hit;
        再判断范围内是否是友军:
        private bool CheckFriend(float 射线范围){
        if(Physics.Raycast(射线发射位置,发射方向为当前前方,out hit ,射线范围长度)){
            if(hit.collider.transform.root.tag=="Enemy"){
            要拿到射线碰撞到的物体,此处带Enemy标签是有空物体创建+多个子对象物体形成的坦克;
            如果直接拿标签是拿不到的比如:hit.collider.tag=="Enemy",是错误的;
            所以要拿到其根对象的标签 : 因为root是Transform里的属性,所以要通过transform.root拿;
            return true;             
            }
        }
        return false;
        }
    

image.png

20米内向玩家移动:
 1)转向玩家  2) 移动
 private void Move()
{
    RotateToPlayer();
    if (!CheckForwardFriend(2f)) {
        transform.position += transform.forward * Time.deltaTime * moveSpeed;
    }
    
20米外自由移动
  1) 移动  2) 每隔三秒调整一次方向移动;
     private void Patrol() {//随机转向移动 
    
    transform.position += transform.forward * Time.deltaTime * moveSpeed;
   tranfrom.rotation=Quaternion.Lerp(transform.position,
           Quaternion.Euler(Vector3.up * y),
           Time.deltaTime*转向速度);

    if (timer > 3f) { 
        y = Random.Range(0, 360);      
        timer = 0;
    }

}
击中敌人,敌人消失;
  我的思路一: 将子弹勾选为触发器挂载脚本,当子弹接触到坦克后,坦克销毁;
  但是当我实现时,我发现拿不到敌人标签,还是前面讲述的问题,碰撞体是空物体下的子物体实现的,要想拿到空物体的标签,必须拿到他的根。
  other.gameobject.transform.root.tag=="Enemy";
  但是新的问题又出现了,我击中后摧毁的确实敌人的零件,击中哪个子物体就摧毁哪个子物体,显然这不是我要的结果;
  于是我又声明了新游戏对象,通过标签直接拿到整个游戏对象,然后摧毁时就摧毁这整个对象;
  虽然效果实现了但还是不够完美,子弹为触发器后,会穿模地板,我取消了重力,但是击中效果还是有不灵敏现象击中两次才摧毁;
  于是我有了第二个想法:
      因为我认为敌人对象勾选触发器后会穿地板模型,所以将子弹设为触发器为我的第一思路;
      当我勾选敌机为触发器时,显然没有穿模,而且拿到碰撞对象也简单的多,子弹不是空对象可以直接拿,然后触发效果也很顺利,没有出现不灵敏现象;
      所以,击中后消失挂载在敌人对象上是最适合的
      难道是因为子弹为触发器时子弹速度过快而导致的穿模吗?