补充一些动作,添加触发检测
思考如何让游戏一直进行下去
处理一些bug,节约更多资源
我们为主角添加下铲和倒下的动作,首先来添加动作,定义动作剪辑片段。
在场景中把对应动画片段绑到脚本上去。由于倒下动画只播放一次,随即就切换到结束场景,要添加一个bool变量,来让该动作单次播放。
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;
接下来做一个新的障碍物,来让滑铲有实际作用,随便搭一个:
我们为其添加碰撞检测,让整个碰撞体覆盖它,只有在滑铲动作时才能通过。很简单,直接上代码:
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脚本,将一些初始化内容删去(关卡状态初始化,音效初始化等),然后修改动态生成道具脚本的位置。
新建两个cube作为新的标志点,绑到复制的脚本上。
修改方向如下(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出来。