Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)

本日目标:

1.使用触发检测完成道具触发的逻辑
2.学习SendMessage方法及相关机制
3.搭好游戏结束结算界面的UI框架
4.实现动态随机生成道具


SendMessage方法:

个人理解,SendMessage提供了一种跨脚本的信息(参数)传递的途径,相当于是给一个物体发送消息,让它完成某个特定的功能(执行一个方法),在以后的开发中应该会非常有用。我们要尽量减少全局变量的定义,仅在非常重要,所有场景都有可能用到的值才将其加入全局变量,其他时候用SendMessage就行了。来看一看官网的API:
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第1张图片

SendMessage三个相关的方法:

SendMessage ("函数名",参数,SendMessageOptions)
这是最基本的方法,仅调用自身的脚本

BroadcastMessage ("函数名",参数,SendMessageOptions)
向下传播,调用自身的和子Object的Script

SendMessageUpwards ("函数名",参数,SendMessageOptions)
会先遍历父对象然后调用自身和父对象的Script

一个实际的例子:

我们先新建一个测试场景,用于测试SendMessage方法。
建一个Object脚本作为测试的函数,三种写法如下。
不带参数的:

public void DisplayNumber()
    {
        Debug.Log("我是被调用的方法");
    }

带一个参数的:

public void DisplayNumber(string str1)
    {
        Debug.Log("我是被调用的方法"+str1);
    }

传一个数组作为参数:

public void DisplayNumber2(System.Object[] ObjArray)
    {
        Debug.Log("number:"+ObjArray[0]);
        Debug.Log("number:"+ObjArray[1]);
        Debug.Log("number:"+ObjArray[2]);
    }

然后再建一个脚本,来写sendMessage方法(此处仅测试最后一个):

public class Test_SendMessage : MonoBehaviour {
    public GameObject invokeobj;
 // Use this for initialization
 void Start () {
        System.Object[] testobj = new System.Object[3];
        testobj[0] = 1;
        testobj[1] = "im a string";
        testobj[2] = 1.23f;
        invokeobj.SendMessage("DisplayNumber2",testobj);
        }
   }

我们可以看一看结果:
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第2张图片


项目进度:

(一)道具的触发检测:

给这颗红宝石来加一个碰撞体Collider(记得勾选IsTrigger,使之能进行触发检测),新建一个Diamond脚本来写碰撞检测。触发检测内容包括:播放一个“ding”的声音,然后让计数器递增(在GlobalManager中定义一个DiamondsCount整形变量)
创建AudioSource用于播放碰到道具时的声音
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第3张图片
在这里插入图片描述
很简单,附上Diamond脚本的内容

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Diamonds : MonoBehaviour {
    public float DiamondRotateSpeed = 1f;
    private const string HeroName = "Hero";
    private AudioSource RedDiamondCollision;
 // Use this for initialization
 	void Start () {
        //rotate(invoke to save more memory)
        InvokeRepeating("RedDiamondRotate", 1f, 0.1f);
        RedDiamondCollision = GameObject.Find("_AudioManager/_AudioEffect").GetComponent<AudioSource>()switch(GlobalManager.GlobalVol)
        {
            case ProjectVolume.none:
                RedDiamondCollision.volume = 0f;
                break;
            case ProjectVolume.NoneVolume:
                RedDiamondCollision.volume = 0f;
                break;
            case ProjectVolume.HalfVolume:
                RedDiamondCollision.volume = 0.5f;
                break;
            case ProjectVolume.FullVolume:
                RedDiamondCollision.volume = 1f;
                break;
        }//应用上这个全局声音设置   
    RedDiamondCollision.loop = false;//
    //count ++
            ++GlobalManager.RedDiamondCount;
            //destroy diamond triggered
            Destroy(this.gameObject);
            Debug.Log("eat diamond!");
        }
    }
    void RedDiamondRotate()
    {
        //RedDiamond Rotation
        this.transform.Rotate(Vector3.down * DiamondRotateSpeed, Space.World);
    }
    // Update is called once per frame
 void Update () {}
 }

(二)游戏结束结算界面

预计在结算界面写奔跑的距离,道具数目,退出按钮,再来一次按钮等
首先是定义部分,挂上所有贴图,并且应用之前定义过的皮肤
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第4张图片

//public
public GUISkin ProjectGUISkin;
public Texture2D TxtBackground;
public Texture2D TxtRedDiamond;
public Texture2D TxtBlueDiamond;
public Texture2D TxtTryAgain;
public Texture2D TxtExit;
//为了测试,把路程设为100
void Start () {
        GlobalManager.RunningDistance = 100;//Used for test!!
        StartCoroutine("DisplayRunningDistance");
        StartCoroutine("DisplayRedDiamond");
 }

创建一个协程来实现数据从0递增到要显示的数据的效果(同样,在GlobalManager中定义好跑步的里程,此处进行调用)。

//Display distance
    IEnumerator DisplayRunningDistance()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.03f);
            if(RunLength>=GlobalManager.RunningDistance-1)
            {
                StopCoroutine("DisplayRunningDistance");
            }
            RunLength++;
        }
    }

然后是道具的结算,代码大同小异,复制过来改一些地方就行了

IEnumerator DisplayRedDiamond()
    {
        yield return new WaitForEndOfFrame();
        while (true)
        {
            yield return new WaitForSeconds(0.03f);
            if (RedDiamondsAmount >= GlobalManager.RedDiamondCount - 1)
            {
                StopCoroutine("DisplayRedDiamond");
            }
            RedDiamondsAmount++;
        }
    }

然后是OnGUI方法中的内容,创建按钮来实现场景转换,都是复习过的内容,就不再赘述了,直接上代码

private void OnGUI()
    {
        GUI.skin = ProjectGUISkin;
        
        //Draw BackGround
        GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), TxtBackground);
        
        //Display Distance
        GUI.Label(new Rect(250,150,250,100), "Distance:" + RunLength);
        
        //Display Diamonds
        //red
        GUI.DrawTexture(new Rect(250, 260, 50, 50), TxtRedDiamond);
        GUI.Label(new Rect(310, 260, 250, 100), ":" + RedDiamondsAmount.ToString());
        
        //Try Again
        if (GUI.Button(new Rect(500, 150, 100, 50),"", ProjectGUISkin.GetStyle("Btn_TryAgain")))
        {
            SceneManager.LoadScene(1);
        }
        
        //Exit
        if (GUI.Button(new Rect(500, 230, 100, 50), "", ProjectGUISkin.GetStyle("Btn_Exit")))
        {
            SceneManager.LoadScene(0);
        }
    }
        

Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第5张图片
一个比较简陋的结算界面就完成了,在关卡界面也加一个GUI,在右上角显示里程和道具数量,打开LevelOneGUI,添加代码。

	//Distance Display
        GUI.Label(new Rect(0, 0, 70, 35), "Distance:" + GlobalManager.RunningDistance.ToString());
        //Diamonds UI Display
        GUI.DrawTexture(new Rect(0, 40, 40, 40), txtRedDiamond);
        GUI.Label(new Rect(45, 40, 50, 30), ":" + GlobalManager.RedDiamondCount.ToString());

(三)动态生成道具

个人觉得动态随机生成应该是整个项目最麻烦的部分了,需要用到随机数的生成还有之前讲到的SendMessage方法,以及回调函数,要传入一个Object数组并且将其解析。新创建一个DynamicCreating脚本,用于实现动态生成。我们首先要分析传入的参数应该有哪些,即生成一个物体需要的数据:物体源对象(一个prefab),生成数量,销毁时间,生成位置(需要一个Vector3,为了实现随机在一个范围中生成,还需要考虑最小值和最大值),把这些思考反映在注释上,之后来一个一个实现。

 //This Script is used for Prefab Clone
    //use instantiate() method
    //parameters:x:xmin->xmax
    //y:const
    //z:zmin->zmax
    //->
    //vec3(x,y,z)
    //
    //object
    //object amount
    //destroy time
    //

写一个DynamicCreate函数,传入一个System.Object[]

 void DynamicCreate(System.Object[] objArray)

这个函数将来要作为SendMessage的目标对象,包含了创建的所有内容。(用Instantiate方法来创建物体,为了避免解析时发生类型错误,做好类型转换)

void DynamicCreate(System.Object[] objArray)
    {
        GameObject ClonePrefab = null;
        int CloneAmount = 0;
        int DestroyTime = 0;
        int x_min = 0;
        int x_max = 0;
        int y = 0;
        int z_min = 0;
        int z_max = 0;
        //parse parameters
        if(objArray!=null)
        {
            if(objArray.Length==8)
            {
                ClonePrefab = (GameObject)objArray[0];
                CloneAmount = System.Convert.ToInt32(objArray[1]);
                DestroyTime= System.Convert.ToInt32(objArray[2]);
                x_min= System.Convert.ToInt32(objArray[3]);
                x_max= System.Convert.ToInt32(objArray[4]);
                y= System.Convert.ToInt32(objArray[5]); 
                z_min= System.Convert.ToInt32(objArray[6]);
                z_max= System.Convert.ToInt32(objArray[7]);
            }
        }
	 //set value
        GameObject GoClone = (GameObject)Instantiate(ClonePrefab);
        GoClone.transform.position = new Vector3(GetRandomNumber(x_min, x_max),
            y, GetRandomNumber(z_min, z_max));
        //confirm destroy time
        Destroy(GoClone, (float)DestroyTime);
    }

这里用到了随机数,还得写一个返回随机数的函数

private int GetRandomNumber(int min,int max)
    {
        return Random.Range(min, max);
    }

然后打开LevelOneManager来添加回调函数,sendMessage方法以及相应的参数传递。先定义对应的GameObject变量。(写好注释,不然会晕)

//used for dynamicCreating
    public GameObject GoClonePrefabOriginal;//prefab PROTOTYPE
    public GameObject GoDynamicCloneObject; //Script Object(Load DynamicCreateScript)
    public GameObject REF_Position_left;    //spwaning start position
    public GameObject REF_Position_right;
    public Transform HeroTransform;

可能会用到人物的位置,所以我们用一个transform来获取角色的transform组件

//Get Hero's Transform Component
   HeroTransform = GameObject.Find("Hero").GetComponent<Transform>();

然后来写SendMessage的函数,我们只需要三个参数:物体原型,创建数量,销毁时间。其他的位置信息我们在场景创建两个cube,摆到合适的位置,相当于两个锚点,确定在两个position之间随机生成道具。
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第6张图片
注意要在场景中把对应的物体GameObject绑到脚本组件上去
Unity3D学习记录(跑酷游戏第三阶段收尾,第四阶段开始)_第7张图片
上代码

private void DynamicCreateProp(GameObject CloneOriginal,int Amount,int DestroyTime)
    {
        //collect parameters
        System.Object[] ObjArray = new System.Object[8];
        ObjArray[0] = CloneOriginal;
        ObjArray[1] = Amount;
        ObjArray[2] = DestroyTime;
        ObjArray[3] = REF_Position_left.transform.position.x;
        ObjArray[4] = REF_Position_right.transform.position.x;
        ObjArray[5] = REF_Position_left.transform.position.y;
        ObjArray[6] = HeroTransform.position.z + 100;
        ObjArray[7] = HeroTransform.position.z + 300;
        //send value
        GoDynamicCloneObject.SendMessage("DynamicCreate", ObjArray,
            SendMessageOptions.DontRequireReceiver);
    }

然后来写生成函数以及回调函数,预计五秒来进行一次生成。

    private void DynamicCreateAll()
    {
        if(GlobalManager.RunningDistance%10==0)
        {
            DynamicCreateProp(GoClonePrefabOriginal, 5, 20);
        }

	//7.27update Dynamic Creating Items
        InvokeRepeating("DynamicCreateAll", 1f, 1f);

差不多了,这样测试一下就有效果了,由于网突然断了导致重新写了一遍,心态有点崩,总结得很不清楚,见谅见谅(反正估计也没几个人看= =

你可能感兴趣的:(Unity)