Unity3D学习记录 (跑酷小游戏完结)

本日目标:

补充一些动作,添加触发检测
思考如何让游戏一直进行下去
处理一些bug,节约更多资源

项目:

我们为主角添加下铲和倒下的动作,首先来添加动作,定义动作剪辑片段。
在场景中把对应动画片段绑到脚本上去。由于倒下动画只播放一次,随即就切换到结束场景,要添加一个bool变量,来让该动作单次播放。
Unity3D学习记录 (跑酷小游戏完结)_第1张图片

public AnimationClip AmimeBend;
public AnimationClip AmimeFall;
//7.29 SingleCheck
private bool BoolSingleFlag=true;

然后完善动画播放协程:

IEnumerator PlayHeroAnimation()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if (GlobalManager.GlobalState!=GameState.none)
            {
                switch(GlobalManager.ActionType)
                {
                    case HeroActionType.None:
                        break;
                    case HeroActionType.Run:
                        anime.Play(AnimeRun.name);
                        yield return new WaitForSeconds(AnimeRun.length);
                        break;
                    case HeroActionType.Jump:
                        anime.Play(AmimeJump.name);
                        yield return new WaitForSeconds(AmimeJump.length);
                        break;
                    case HeroActionType.Bend:
                        anime.Play(AmimeBend.name);
                        yield return new WaitForSeconds(AmimeBend.length);
                        break;
                    case HeroActionType.Fall:
                        if (BoolSingleFlag==true)
                        {
                            Debug.Log("play fall animation");
                            anime.Play(AmimeFall.name);
                            BoolSingleFlag = false;
                            yield return new WaitForSeconds(AmimeFall.length);     
                        }
                        break;
                }
            }
        }
    }

在对应的碰撞,触发检测上也要添加相应的状态切换代码。

GlobalManager.ActionType = HeroActionType.Fall;

接下来做一个新的障碍物,来让滑铲有实际作用,随便搭一个:
Unity3D学习记录 (跑酷小游戏完结)_第2张图片
我们为其添加碰撞检测,让整个碰撞体覆盖它,只有在滑铲动作时才能通过。很简单,直接上代码:

private void OnTriggerEnter(Collider col)
    {
        if (col.name.Equals(HERONAME))
        {
            if(GlobalManager.ActionType!=HeroActionType.Bend)
            {
                GlobalManager.GlobalState = GameState.GameOver;
                GlobalManager.ActionType = HeroActionType.Fall;
            }
        }
    }

如何让游戏一直进行下去:

目前有两个想法:
1.添加道路,使其组成一个环形
2.像开始菜单那样,让角色不动,场景移动,在适当的时间复位
这两个方法理论上都行得通,先来实现一个环形的道路场景,复制一份桥,将其旋转九十度拼接起来。这样做有一个致命问题,动态生成道具和障碍的方向都是一直向前的,所以转向后需要一个新的脚本,改变一下生成物体的方向。复制一份LevelOneManager脚本,将一些初始化内容删去(关卡状态初始化,音效初始化等),然后修改动态生成道具脚本的位置。
Unity3D学习记录 (跑酷小游戏完结)_第3张图片
新建两个cube作为新的标志点,绑到复制的脚本上。
Unity3D学习记录 (跑酷小游戏完结)_第4张图片
修改方向如下(DynamicCreateProp中):

//旧设置
/* 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;*/
//新设置
        ObjArray[3] = HeroTransform.position.x + 50;
        ObjArray[4] = HeroTransform.position.x + 200;
        ObjArray[5] = REF_Position_left.transform.position.y;
        ObjArray[6] = REF_Position_left.transform.position.z;
        ObjArray[7] = REF_Position_right.transform.position.z;

需要在转向点处添加一个触发器,让SceneManager切换到新的脚本。创建空GameObject命名为TriggerChangeSceneManager,再创建一个脚本用于切换脚本(使用组件的disable属性)

 void Start () {
        //abandon script1 and switch
        GameObject.Find("_LevelOneManager").GetComponent<LevelOne_Manager>().enabled = false;
        GameObject.Find("_LevelOneManager").GetComponent<LevelOne_Manager_North>().enabled = true;
    }

再动态添加该脚本,用之前提到过的AddComponent方法

 private void OnTriggerEnter(Collider other)
   {
       if (gogameobject)
       {
           gogameobject.AddComponent<ChangeSceneManager>();
       }
   }

这样就基本完成了,再添加方法也如上面所说。
由脚本的生命周期可知,前一个脚本开启的协程和回调函数都还会发挥作用,要在SceneManager中手动将其关闭,以节省资源。

StopCoroutine("CheckGameState");
CancelInvoke("DynamicCreateAll");

把一些修改较大的脚本贴上来:
LevelOneManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
//
public class LevelOne_Manager : MonoBehaviour {
    //other
    private AudioSource ASBackgroundMusic;
    private Vector3 HeroOriginalPosition;
    //public varible
    public float GameOverInterval = 2f;
    //
    //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;
    //
    // Use this for initialization
    void Start () {
        //
        //Get Hero's Transform Component
        HeroTransform = GameObject.Find("Hero").GetComponent<Transform>();
        //7.29 GetHeroPosition
        HeroOriginalPosition = HeroTransform.position;
        //check game state every second
        //InvokeRepeating("CheckGameState", 1f, 1f);    Invoke method (abandoned)
        StartCoroutine("CheckGameState");
        //
        //7.27update Dynamic Creating Items
        InvokeRepeating("DynamicCreateAll", 1f, 1f);
        //
        //7.24 added:game state
        GlobalManager.GlobalState = GameState.Preparing;
        //set Global Varible as 0
        GlobalManager.RunningDistance = 0;
        GlobalManager.RedDiamondCount = 0;
        // 
        ASBackgroundMusic = GameObject.Find("_AudioManager/_BackGroundMusic").GetComponent<AudioSource>();
        ASBackgroundMusic.loop = true;
        ASBackgroundMusic.volume = 1f;
        ASBackgroundMusic.Play();
        switch(GlobalManager.GlobalVol)
        {
            case ProjectVolume.none:
                break;
            case ProjectVolume.NoneVolume:
                ASBackgroundMusic.volume = 0f;
                break;
            case ProjectVolume.HalfVolume:
                ASBackgroundMusic.volume = 0.5f;
                break;
            case ProjectVolume.FullVolume:
                ASBackgroundMusic.volume = 1f;
                break;
            default:
                break;
                //
        }
    }
 IEnumerator CheckGameState()
    {
        yield return new WaitForSeconds(1f);
        //continual check
        while (GlobalManager.GlobalState!=GameState.none)
        {
            yield return new WaitForSeconds(1f);
            //running distance
            if (GlobalManager.GlobalState == GameState.Playing)
            {
                GlobalManager.RunningDistance += 2;
            }
            if (GlobalManager.GlobalState == GameState.GameOver)
            {
                yield return new WaitForSeconds(GameOverInterval);
                SceneManager.LoadScene(2);
            }
            //7.29 Check Hero's Position
            if(HeroTransform.position.y<HeroOriginalPosition.y-20)
            {
                GlobalManager.GlobalState = GameState.GameOver;
            }
        }
    }
    //
    //InitiateAllItems (Invoke)
    private void DynamicCreateAll()
    {
        if(GlobalManager.RunningDistance%5==0)
        {
            DynamicCreateProp(GoClonePrefabOriginal, 5, 20);
        }
    }
    //
    //InitiateItems
    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 + 50;
        ObjArray[7] = HeroTransform.position.z + 200;
        //send value
        GoDynamicCloneObject.SendMessage("DynamicCreate", ObjArray,
            SendMessageOptions.DontRequireReceiver);
    //
    }
    //totally Disable this Script
    private void OnDisable()
    {
        StopCoroutine("CheckGameState");
        CancelInvoke("DynamicCreateAll");
    }
    // Update is called once per frame
    void Update () {
  //
 }
}

HeroControl_PC.cs:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//
public class Hero_Control_PC : MonoBehaviour {
   // public GameObject GoHero;
    public float FloRunningSpeed=1f;
    public float FloRotateSpeed = 1f;
    public float HeroJumpPower = 1f;
    //7.28 Animation Clips
    public AnimationClip AnimeRun;
    public AnimationClip AmimeJump;
    public AnimationClip AmimeBend;
    public AnimationClip AmimeFall;
    public AnimationClip AmimeStand;
    private Animation anime;
    private Rigidbody HeroRB;
    //7.29 SingleCheck
    private bool BoolSingleFlag=true;
 // Use this for initialization
 void Start () {
        anime = this.GetComponent<Animation>();
        HeroRB = this.GetComponent<Rigidbody>();
        //7.28 Get Key Input
        StartCoroutine("GetHeroActionInput");
        //play Hero's Animation
        StartCoroutine("PlayHeroAnimation");
 }
 IEnumerator GetHeroActionInput()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if(GlobalManager.GlobalState==GameState.Playing)
            {
                //Default setting:running
                GlobalManager.ActionType = HeroActionType.Run;
                //Turning
                if(Input.GetKey(KeyCode.A))
                {
                    this.transform.Rotate(Vector3.down * FloRotateSpeed);
                }
                else if (Input.GetKey(KeyCode.D))
                {
                    this.transform.Rotate(Vector3.up * FloRotateSpeed);
                }
                //Jumping
                else if(Input.GetKeyDown(KeyCode.Space))
                {
                    HeroRB.AddForce(Vector3.up * HeroJumpPower, ForceMode.Impulse);
                    GlobalManager.ActionType = HeroActionType.Jump;
                    yield return new WaitForSeconds(AmimeJump.length);
                }
                else if(Input.GetKeyDown(KeyCode.S))
                {
                    GlobalManager.ActionType = HeroActionType.Bend;
                    yield return new WaitForSeconds(AmimeBend.length);
                    GlobalManager.ActionType = HeroActionType.Run;
                }
            }
        }
    }
    IEnumerator PlayHeroAnimation()
    {
        yield return new WaitForEndOfFrame();
        while(true)
        {
            yield return new WaitForSeconds(0.01f);
            if (GlobalManager.GlobalState!=GameState.none)
            {
                switch(GlobalManager.ActionType)
                {
                    case HeroActionType.None:
                        break;
                    case HeroActionType.Run:
                        anime.Play(AnimeRun.name);
                        yield return new WaitForSeconds(AnimeRun.length);
                        break;
                    case HeroActionType.Jump:
                        anime.Play(AmimeJump.name);
                        yield return new WaitForSeconds(AmimeJump.length);
                        break;
                    case HeroActionType.Bend:
                        anime.Play(AmimeBend.name);
                        yield return new WaitForSeconds(AmimeBend.length);
                        break;
                    case HeroActionType.Fall:
                        if (BoolSingleFlag==true)
                        {
                            Debug.Log("play fall animation");
                            anime.Play(AmimeFall.name);
                            BoolSingleFlag = false;
                            yield return new WaitForSeconds(AmimeFall.length);     
                        }
                        break;
                }
            }
        }
    }
 // Update is called once per frame
 void Update () {
        ChangeCamera();
        //7.29 prepare
        if(GlobalManager.GlobalState==GameState.Preparing)
        {
            anime.Play(AmimeStand.name);
        }
        //7.24 latest updata
         if(GlobalManager.GlobalState==GameState.Playing)
        {
            this.transform.Translate(Vector3.forward * FloRunningSpeed);
        }
 }
    void ChangeCamera()
    {
        if(Input.GetKey(KeyCode.Alpha1))
        {
            CameraSelect("Cameras", "MainCamera");
        }
        else if(Input.GetKey(KeyCode.Alpha2))
        {
            CameraSelect("Cameras", "Camera_Long");
        }
    }
    void CameraSelect(string CameraTag,string CameraName)
    {
        //Shut down all cameras
        GameObject[] GoCameras = GameObject.FindGameObjectsWithTag(CameraTag);
        foreach(GameObject GoItem in GoCameras)
        {
            GoItem.GetComponent<Camera>().enabled = false;       
        }
        //enable choosed Camera
        GameObject.Find(CameraName).GetComponent<Camera>().enabled = true;
    }
}

项目的需求基本都实现了,收获颇多,动态加载,协程的使用是难点,需要多复习多应用。明天开始学习离散数学,并继续数据结构的复习。过几天来试试写一个Galgame的Demo出来。

你可能感兴趣的:(Unity)