同样的,先看需求
可以看到题目分两个部分,汽车警报响起的前后有着不同的行为,首先我们要完成的是僵尸的随机漫游。
题目要求僵尸在一定半径的圆形范围内做随机运动,直接随机僵尸的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); } }

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; } }