总结一下今天做的面试题(二):随机漫游与汽车爆炸

同样的,先看需求

可以看到题目分两个部分,汽车警报响起的前后有着不同的行为,首先我们要完成的是僵尸的随机漫游。

题目要求僵尸在一定半径的圆形范围内做随机运动,直接随机僵尸的xy坐标是行不通的,不过我们可以采取极坐标的方式随机角度和半径,然后计算僵尸的坐标:

在确定一个坐标后,僵尸就开始移动,直到移动到目标地点后,僵尸随机下一个坐标并开始移动,这里用一个Bool型的moved变量和协程进行控制:

    void Update () {
        if (!moved) {
            StartCoroutine (roam());//if zombie reach the end,start the next roam
        }
    }
    IEnumerator roam(){
        moved = true;
        float _angle = Random.Range (0,360f);
        float _radius = Random.Range (1f, radius);
        Vector3 start_place = m_transform.position;
        Vector3 end_place = new Vector3 (x + _radius * Mathf.Cos (_angle),y,z + _radius * Mathf.Sin (_angle));//get end place with angle and radius
        m_transform.LookAt (end_place);
        float time = (m_transform.position - end_place).magnitude/normal_speed;
        float time0 = 0;
        while (time0 < time) {
            time0 += Time.deltaTime;
            m_rigidbody.MovePosition(m_transform.position+(end_place-start_place).normalized*normal_speed*Time.deltaTime);//move to end place with physical
            yield return null;
            if (alarm)
                break;
        }
        moved = false;
    }

需要注意的是移动的时候不能直接改变僵尸的坐标,而是要让它向着目标地点“向前走”,因为场景中会有其他碰撞体(比如车),直接改变僵尸的坐标的话,遇到这些碰撞虽然暂时不会穿过去,但一旦选定的坐标穿过去了,僵尸也就穿过去了(这句话有点拗口,希望能够理解)

做好僵尸的漫游功能后,在场景中放上车和僵尸的模型,并把相应的脚本绑定这僵尸上:

随后是控制车的血量的脚本,先把血条和点击扣血的时间做出来,这里参考了别人的代码,直接在gui上画了血条。

  
  private Camera camera;
  public
Texture2D blood_red;//pic for red blood public Texture2D blood_black;//pic for lose blood private int hp = 100,alarm_hp=95,click_down=1,max_hp;//the hp of cars void OnMouseDown(){ HP-=click_down; if (hp == alarm_hp) { car_alarm (); } } void OnGUI(){ Vector3 worldPosition = new Vector3 (transform.position.x , transform.position.y + carHeight,transform.position.z);//the positon of blood texture Vector2 position = camera.WorldToScreenPoint (worldPosition); position = new Vector2 (position.x, Screen.height - position.y);//change it to 2D world Vector2 bloodSize = GUI.skin.label.CalcSize (new GUIContent(blood_red));//get the height and width of blood texture int blood_width = blood_red.width * HP/max_hp;//get the width of red blood GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,bloodSize.x,bloodSize.y),blood_black); GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,blood_width,bloodSize.y),blood_red);//draw blood texture }

然后是血量掉到alarm_hp时触发的警报事件,为了方便,这里用一个存在三秒的Button来代替。

由于警报响起必然会触发大量事件,所以这里肯定是采用一个委托

public Action car_alarm;

然后给委托加上相应的事件

car_alarm+=()=>StartCoroutine(show_alarm(alarm_time));

    IEnumerator show_alarm(float time){
        alarm = true;
        yield return new WaitForSeconds (time);
        alarm = false;
    }

OnGUI上加上对应的button

        if (alarm)
        if (GUI.Button (new Rect (Screen.width/2-80, Screen.height/2-30, 160, 60), "警报响了!!")) {
            alarm = false;
        }

警报响了以后,僵尸也应当停止漫游,向汽车跑去,并且开始攻击(由于有两个攻击动画,所以我做了一个随机)

在控制僵尸的脚本上获取这个委托,并注册相应的事件:

car_transform = GameObject.FindGameObjectWithTag ("car").transform;
        car = car_transform.gameObject.GetComponent<cartrick>();
car.car_alarm += Atk_Car;//attack car when car alarm
    void Atk_Car(){
        alarm = true;
        StartCoroutine (GoAndAtk());
    }
    IEnumerator GoAndAtk(){
        Vector3 start_place = m_transform.position;
        Vector3 end_place = car_transform.position;
        m_transform.LookAt (end_place);
        m_animation.Play (clipName[3]);//the zombie change to run
        float time = (m_transform.position - end_place).magnitude/special_speed;
        float time0 = 0;
        while (time0 < time) {
            time0 += Time.deltaTime;
            m_rigidbody.MovePosition(m_transform.position+(end_place-start_place).normalized*special_speed*Time.deltaTime);//move to end place with physical
            yield return null;
        }
        while (true) {
            float i = Random.Range (5f, 7f);//random attack ways
            yield return StartCoroutine (playanimation(clipName[(int)i],1f));
        }
    }
    IEnumerator playanimation(string name,float time){
        m_animation.Play (name);
        yield return new WaitForSeconds(time/2);
        car.HP -= zombie_attack;//down the hp in half of the animator
        yield return new WaitForSeconds(time/2);
    }

之后,类似的,在汽车hp降为0后,提示爆炸信息,汽车爆炸,就不细说了。

效果预览:

本次用到的两个脚本:

using UnityEngine;
using System.Collections;

public class zombie_move : MonoBehaviour {
    Transform m_transform,car_transform;
    cartrick car;
    float radius=3,x,y,z;//the radius and the start place the zombie moves
    float normal_speed=2f,special_speed=3f;//the speed when zombie walk and move to car
    string[] clipName={"idle","crawl","shamble","run","fallBack","attack1","attack2"};
    int zombie_attack=2;//one hit damage to the car
    Animation m_animation;
    bool moved=false,alarm=false;
    Rigidbody m_rigidbody;
    // Use this for initialization
    void Start () {
        m_transform = this.transform;
        m_animation = m_transform.GetComponent<Animation> ();
        car_transform = GameObject.FindGameObjectWithTag ("car").transform;
        car = car_transform.gameObject.GetComponent<cartrick>();
        m_rigidbody=m_transform.GetComponent<Rigidbody> ();
        x = m_transform.position.x;
        y = m_transform.position.y;
        z = m_transform.position.z;
        m_animation.Play (clipName[2]);
        car.car_alarm += Atk_Car;//attack car when car alarm
        car.explosion += () => Destroy (m_transform.gameObject);//destroy the zombie when car explose
    }
    
    // Update is called once per frame
    void Update () {
        if (!moved) {
            StartCoroutine (roam());//if zombie reach the end,start the next roam
        }
    }
    IEnumerator roam(){
        moved = true;
        float _angle = Random.Range (0,360f);
        float _radius = Random.Range (1f, radius);
        Vector3 start_place = m_transform.position;
        Vector3 end_place = new Vector3 (x + _radius * Mathf.Cos (_angle),y,z + _radius * Mathf.Sin (_angle));//get end place with angle and radius
        m_transform.LookAt (end_place);
        float time = (m_transform.position - end_place).magnitude/normal_speed;
        float time0 = 0;
        while (time0 < time) {
            time0 += Time.deltaTime;
            m_rigidbody.MovePosition(m_transform.position+(end_place-start_place).normalized*normal_speed*Time.deltaTime);//move to end place with physical
            yield return null;
            if (alarm)
                break;
        }
        if (!alarm) moved = false;
    }
    IEnumerator GoAndAtk(){
        Vector3 start_place = m_transform.position;
        Vector3 end_place = car_transform.position;
        m_transform.LookAt (end_place);
        m_animation.Play (clipName[3]);//the zombie change to run
        float time = (m_transform.position - end_place).magnitude/special_speed;
        float time0 = 0;
        while (time0 < time) {
            time0 += Time.deltaTime;
            m_rigidbody.MovePosition(m_transform.position+(end_place-start_place).normalized*special_speed*Time.deltaTime);//move to end place with physical
            yield return null;
        }
        while (true) {
            float i = Random.Range (5f, 7f);//random attack ways
            yield return StartCoroutine (playanimation(clipName[(int)i],1f));
        }
    }
    void Atk_Car(){
        alarm = true;
        StartCoroutine (GoAndAtk());
    }
    IEnumerator playanimation(string name,float time){
        m_animation.Play (name);
        yield return new WaitForSeconds(time/2);
        car.HP -= zombie_attack;//down the hp in half of the animator
        yield return new WaitForSeconds(time/2);
    }
}
zombie_move.cs
using UnityEngine;
using System.Collections;
using System;

public class cartrick : MonoBehaviour {
    Transform p_transform;
    private Camera camera;
    float carHeight=2f,alarm_time=3f,explosion_time=3f;
    public Texture2D blood_red;//pic for red blood
    public Texture2D blood_black;//pic for lose blood
    private int hp = 100,alarm_hp=95,click_down=1,max_hp;//the hp of cars
    bool alarm=false,isexplosion=false;
    public int HP{
        get{ return hp;}
        set{ if (value <= 0) {
                value = 0;
                StartCoroutine (take_explosion(explosion_time));
            }
            hp = value;}
    }
    //public delegate void _delegate();
    //public _delegate car_alarm; 
    public Action car_alarm,explosion;
    // Use this for initialization
    void Start () {
        p_transform = this.transform.parent.transform;
        camera = Camera.main;//get the main camera
        car_alarm+=()=>StartCoroutine(show_alarm(alarm_time));
        explosion += () => {
            //StartCoroutine (show_explosion (explosion_time));
            Destroy (p_transform.gameObject);
        };
        max_hp = HP;
    }
    
    // Update is called once per frame
    void Update () {
    
    }
    void OnMouseDown(){
        HP-=click_down;
        if (hp == alarm_hp) {
            car_alarm ();
        }
    }
    void OnGUI(){
        if (alarm)
        if (GUI.Button (new Rect (Screen.width/2-80, Screen.height/2-30, 160, 60), "警报响了!!")) {
            alarm = false;
        }
        if (isexplosion)
        if (GUI.Button (new Rect (Screen.width/2-80, Screen.height/2-30, 160, 60), "车要炸了!")) {
            isexplosion = false;
        }
        Vector3 worldPosition = new Vector3 (transform.position.x , transform.position.y + carHeight,transform.position.z);//the positon of blood texture
        Vector2 position = camera.WorldToScreenPoint (worldPosition);
        position = new Vector2 (position.x, Screen.height - position.y);//change it to 2D world
        Vector2 bloodSize = GUI.skin.label.CalcSize (new GUIContent(blood_red));//get the height and width of blood texture
        int blood_width = blood_red.width * HP/max_hp;//get the width of red blood 
        GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,bloodSize.x,bloodSize.y),blood_black);
        GUI.DrawTexture(new Rect(position.x - (bloodSize.x/2),position.y - bloodSize.y ,blood_width,bloodSize.y),blood_red);//draw blood texture
    }
    IEnumerator show_alarm(float time){
        alarm = true;
        yield return new WaitForSeconds (time);
        alarm = false;
    }
    IEnumerator show_explosion(float time){
        isexplosion = true;
        yield return new WaitForSeconds (time);
    }
    IEnumerator take_explosion(float time){
        isexplosion = true;
        yield return new WaitForSeconds (time);
        explosion ();
        isexplosion = false;
    }
}
cartrick.cs
原文地址:https://www.cnblogs.com/Swallowtail/p/6183053.html