Stealth游戏流程总结二

下面添加传送门,把门拖到地图,添加normalDoorController的animator,拖动门打开和关闭的动画到animator,连线链接两个动画,设好条件,设好bool参数close,添加sphere collider,勾选id trigger,把这个碰撞器弄成半球状,这样人物靠近半球状区域门自动打开,给门添加door的脚本,

 private int count = 0;
 private Animator anim;
     void Awake()
    {
        anim = GetComponent<Animator>();
     }

     void Start () {
		
	}
	
     void Update () {
        anim.SetBool("close", count <= 0);
	}

     void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player" || other.tag == "Enemy")
        {
            count++;
        }
    }
     void OnTriggerExit(Collider other)
    {
        if (other.tag == "Player" || other.tag == "Enemy")
        {
            count--;
        }
    }

然后添加拿钥匙机器,添加2个box collider,一个用于碰撞一个用于在范围内检测,添加音频,添加SwitchUnit脚本,

  public GameObject laser;
    public Material unLockMat;// 解锁的图标
    public GameObject screen;
     void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player")
        {
            if (Input.GetKeyDown(KeyCode.Z)) { 
            laser.SetActive(false);
            GetComponent<AudioSource>().Play();
            screen.GetComponent<Renderer>().material = unLockMat;
            }
        }
    }
下面添加跟猪脚移动的相机,给main camera添加FellowPlayer脚本,大致的原理是,相机和人物之间有距离差值,计算出差值,人物坐标+差值就等于相机的坐标,由于x轴向上,所以差值为0

   private Vector3 offset;
    private Transform player;

     void Awake()
    {     
        player=GameObject.FindGameObjectWithTag("Player").transform;
        offset = transform.position - player.position;
        offset = new Vector3(0, offset.y, offset.z);   
    }
    void Update () {
        this.transform.position = player.position + offset;
	}
}
这里注意的是写代码时写gameobject自动提示gameobject和Gameobject,选择Gameobject而不是gameobject。还有findGameObjectWithTag这个方法很容易选成 findGameObjectWithTags这样的话接收的是一个数组,不对的


这里有个bug,人物rigidbody的is kinematic不勾选,但是走动时乱晃,这是好像由于还受到了transform的影响,所以勾选constraints锁定这些坐标,这样人物的rigidbody和collider不会穿透到其他物体中

下面解决摄像机的视野问题,人物到墙角时视野变成垂直向下

                       

原理如下:


原本是5点位置观察猪脚,如果有墙挡着人物,5-0连线射线检测发现碰撞物体,那么看4-0连线,直到1-0连线没有碰撞为止

    private Vector3 offset;
    private Transform player;

    void Awake()
    {
    player=GameObject.FindGameObjectWithTag("Player").transform;
        offset = transform.position - player.position;
        offset = new Vector3(0, offset.y, offset.z);   
    }
    void Update () {
        Vector3 beginpos = player.position + offset;    //5点坐标
        Vector3 endpos = player.position + offset.magnitude*Vector3.up;//1点坐标,offset.magnitude
        //为偏移的长度,乘以vector向上的向量等于1点的坐标
        Vector3 pos4 = Vector3.Lerp(beginpos,endpos,0.25f);//4点位置是1和5点取差值除以4得到的
        Vector3 pos3 = Vector3.Lerp(beginpos, endpos, 0.5f);
        Vector3 pos2 = Vector3.Lerp(beginpos, endpos, 0.75f);
        Vector3[] postions = new Vector3[] {beginpos,pos4,pos3,pos2,endpos };
        Vector3 targetPos = postions[0];
        for (int i=0;i<5;i++)
        {
            RaycastHit hitinfo;
            if (Physics.Raycast(postions[i],player.position- postions[i],out hitinfo))
            {
                if (hitinfo.collider.tag!="Player")
                {
                    continue;
                }
                else
                {
                    targetPos = postions[i];
                    break;
                }
            }
            else
            {
                targetPos = postions[i];
                break;
            }
        }
        this.transform.position = targetPos;
      transform.LookAt(player.position);  //最后让相机面向一直朝向人物的位置
	}
虽然通过多点的方式解决了镜头视角问题,但是视角的快速切换在游戏中感觉卡卡的,所以需要平滑处理:

 this.transform.position = Vector3.Lerp(transform.position,targetPos,Time.deltaTime*3);
 transform.LookAt(player.position);  //最后让相机面向一直朝向人物的位置


 下面设置navigation,把地图属性设置为静态,window--->navigation设置好Radius ,height然后bake

添加机器人,添加rigidbody和capsule collider,添加导航Nav Mesh Agent,添加sphere collider,勾选is trigger,范围为10,来判断10m内有没有人
创建animator Controller名为EnemyController,

给animator添加一个locomotion二维混合树。给混合树添加17个动画(包括所有的转向动画和站立动画),在compute position选择speed and angular speed,给混合树添加2个参数speed和angularspeed(转向速度),第一层是运动再添加一层,该层为射击。添加shoot的bool参数用于切换2个层,第二层拖动射击的所有动画,设置好转换条件,


由于射击层独立于移动层,所以创建身体遮罩,创建avatar Mask名为EnemyShootMask,weight改为1,注意blendtree的parameters选择angularspeed和speed

       


创建EnemySight脚本,

 public bool playerInsight = false;
    public float fieldOfView = 110;
    public Vector3 alertPosition = Vector3.zero;
    private Animator playerAnim;
    private UnityEngine.AI.NavMeshAgent agent;
    private SphereCollider collider;
    void Awake()
    {
        playerAnim = GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>();
        agent = this.GetComponent<UnityEngine.AI.NavMeshAgent>();
        collider = this.GetComponent<SphereCollider>();
    }

    private void OnTriggerStay(Collider other)
    {
        if (other.tag=="Player")
        {
            //这里得到机器人的前方,得到机器人和角色的向量,如果前方和向量的夹角小于110/2,
            //那么就认为机器人看到了猪脚
            Vector3 forward = transform.forward;
            Vector3 playerDir = other.transform.position - transform.position;
            float temp=Vector3.Angle(forward,playerDir);
            if (temp<0.5f*fieldOfView)
            {
                playerInsight = true;
                alertPosition = other.transform.position;
            }
            else
            {
                playerInsight = false;
            }
        }

        //如果机器人在附件判断主角是否在快跑状态,就算有墙挡着主角,也可以通过自动导航来检测折线的距离
        //总和是否在sphereCollider范围内,是就保存主角的当前位置
        if(playerAnim.GetCurrentAnimatorStateInfo(0).IsName("locomotion")){
            UnityEngine.AI.NavMeshPath path = new UnityEngine.AI.NavMeshPath();
            if (agent.CalculatePath(other.transform.position,path))
            {
                Vector3[] wayPoints = new Vector3[path.corners.Length+2];
                wayPoints[0] = transform.position;
                wayPoints[wayPoints.Length - 1] = other.transform.position;
                for (int i=0;i<path.corners.Length;i++)
                {
                    wayPoints[i + 1] = path.corners[i];
                }
                float length = 0;
                for (int i=1;i<wayPoints.Length;i++)
                {
                    length += (wayPoints[i] - wayPoints[i - 1]).magnitude;
                }
                if (length<collider.radius)
                {
                    alertPosition = other.transform.position;
                }
            }
        }
    }
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Player")
        {
            playerInsight = false;
        }
    }

在GameController脚本中添加一个方法

 public void SeePlayer(Transform player)
    {
        alermOn = true;
        lastPlayerPosition = player.position;
    }
在EnemySight添加一行代码
if (temp<0.5f*fieldOfView)
            {
                playerInsight = true;
                alertPosition = other.transform.position;
                GameController._instance.SeePlayer(other.transform); //发现主角时会叫其他同伴过来
            }
接下来,定义一个preLastPlayPosition在awake时候来保存取得的player坐标,当有人发现了主角,调用了seePlayer函数,修改了player坐标,在EnemySight脚本中update函数不断检测,当发现坐标修改时候,把坐标给警报坐标

 private Vector3 preLastPlayPosition;
    void Awake()
    {
        playerAnim = GameObject.FindGameObjectWithTag("Player").GetComponent<Animator>();
        agent = this.GetComponent<UnityEngine.AI.NavMeshAgent>();
        collider = this.GetComponent<SphereCollider>();
        preLastPlayPosition = GameController._instance.lastPlayerPosition;
    }

    void Update()
    {
        if (preLastPlayPosition!=GameController._instance.lastPlayerPosition)
        {
            alertPosition = GameController._instance.lastPlayerPosition;
            preLastPlayPosition= GameController._instance.lastPlayerPosition;
        }
    }
运行脚本时候发现一个报空指针错误
 preLastPlayPosition = GameController._instance.lastPlayerPosition;
这是因为该代码在awake函数中,而在gameController中awake有

 _instance = this;
当运行Enemy的awake函数时候还没有触发gameController的awake,所以拿到空指针。解决方法把
 preLastPlayPosition = GameController._instance.lastPlayerPosition;

放到start函数中


下面添加机器人智能行走功能。创建4个waypoint的Gameobject,图标改成小旗子,放到地图中

给机器人创建EnemyMove脚本,声明

public Transform[] wayPoints;
把4个waypoint拖动到脚本属性中

public Transform[] wayPoints;
    public float patrolTime = 3f;
    private float patrolTimer = 0;
    private int index = 0;
    private UnityEngine.AI.NavMeshAgent agent;

     void Awake()
    {
        agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
        agent.destination = wayPoints[index].position;
    }

	void Update () {
        Patrolling();
    }
    //机器人巡逻函数
    private void Patrolling()
    {
        if (agent.remainingDistance<0.5f)
        {
            patrolTimer += Time.deltaTime;
            if (patrolTimer>patrolTime)
            {
                index++;
                Debug.Log(patrolTimer);
                index %= 4;
                Debug.Log(patrolTimer);
                agent.destination = wayPoints[index].position;
                patrolTimer = 0;
            }
        }
    }
如果在代码中添加了agent.stop();那么自动导航会永远停止,不会再运行。

运行游戏,看到机器人会先走到第一个位置,过3秒移动到第二个位置,再到第三位置,第四位置。。。如此循环

但现在问题是机器人像幽灵一样飘过去,没有行走动画

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值