Unity编写雷霆战机流程(二)

xiaoxiao2021-02-28  8

三、功能的具体实现

这里只介绍部分主要功能以及一些核心代码,其他细节可自行设计

1. 开始游戏场景:

开始游戏点击开始按钮,调用Gamanager里的公开方法,运行开始面板的动画(这里用的是DoTween插件)并播放声音即可

public void OnStartGameButton() { startGamePanelTween.DOPlayForward(); clickAudioSource.PlayOneShot(clickClip); }

游戏选项通过Slider的value来控制AudioSource的volume;通过正则表达式来验证玩家名字是否合法;通过QualitySettings.SetQualityLevel(int level)方法来调节画质 查看排行榜我这里建了一个单链表用来表示玩家信息(名字;分数),通过单链表里的排序方法(简单的冒泡排序表)对玩家进行排名,并将排名信息填充到排行榜的预制件单元中。而对于玩家信息的存储则通过PlayerPrefab实现,他存储了string类型字符串,通过对字符串的分割实现了玩家信息的获取。 单链表接口:

public interface IListDS {

void Sort();//排序 void Add(T item1,T item2);//添加信息 bool CheckIsNeedToAdd(T item1,T item2);//检查该信息是否能被添加 Node<T> this[int index] { get; set; }//索引器实现

} 玩家信息存储以及获取:

void CreateRecordBoard() { //PlayerPrefs.SetString("unitData", ""); string strData = PlayerPrefs.GetString("unitData"); if (strData.CompareTo("") == 0)//初始化玩家信息 { strData = "暗夜之鸦,39886;暴躁的恐角龙,8944;士兵76,22000;闪光萨,15886;死亡之翼,6532;YoungForever,9876"; PlayerPrefs.SetString("unitData", strData); } string[] firstSplitStr = strData.Split(';'); for (int i = 0; i < firstSplitStr.Length; i++)//分割字符串 { string[] secodnSplitStr = firstSplitStr[i].Split(','); if (secodnSplitStr[0] == "") { continue; } CreateCell(secodnSplitStr[0], secodnSplitStr[1]);//将玩家信息存储在排行榜的预制件单元里面 } }

退出游戏退出游戏只需一句代码即可

public void OnExitGame() { clickAudioSource.PlayOneShot(clickClip); Application.Quit(); }

2. 第一(三)关场景: 虚拟摇杆控制移动的实现:

[SerializeField] private EasyJoystick joys; void PlayerMove() { if (Gamanager.isOver || Gamanager.isPause)//如果游戏出于暂停状态或结束状态,则不进行移动 { return; } float h = joys.JoystickAxis.x; float v = joys.JoystickAxis.y; if (h < -0.5f)//玩家旋转 { transform.rotation = Quaternion.Euler(begionRotation + new Vector3(0, 45, 0)); } else if (v > 0.5f) { transform.rotation = Quaternion.Euler(begionRotation - new Vector3(0, 45, 0)); } else { transform.rotation = Quaternion.Euler(begionRotation); } rigibid.velocity = new Vector3(h, v, 0) * speed; rigibid.position = new Vector3(Mathf.Clamp(rigibid.position.x, board.minx, board.maxx), Mathf.Clamp(rigibid.position.y, board.miny, board.maxy), 0);//差值函数实现平滑渐变 }

玩家射击(两种子弹的切换):

public void PlayerShoot() { if (timer < shootRate) { timer += Time.deltaTime; } rayBullet.SetActive(false); if (isShoot) { if (tempBulet == TempBullet.Bullet) { if (timer >= shootRate) { playerAudio.PlayOneShot(playerShootAudio); timer = 0; GameObject.Instantiate(bullet, shotPos.position, Quaternion.identity); } } if (tempBulet == TempBullet.Laser) { if (!playerAudio.isPlaying) { playerAudio.PlayOneShot(raySound); } rayBullet.SetActive(true); rayBullet.GetComponent<LineRenderer>().SetPosition(0, rayBullet.transform.position); rayBullet.GetComponent<LineRenderer>().SetPosition(1, rayBullet.transform.position + new Vector3(0, 10, 0)); } } }

敌人的AI以及交互条件相对于外界少了很多,但是敌人有不同种类,不同种类的敌人却又许多共有的行为以及属性,所以这里定义一个抽象类,其他不同种类的敌人继承这个类即可 敌人抽象类:

public enum AwardType { Laser, Bullet, Plane, Life } [System.Serializable] public class Awards//也可以改变Text即可,更简单方便 { public AwardType awardType; public GameObject AwardPre; public float minRange; public float maxRange; } public abstract class Enemy : MonoBehaviour { public Awards[] awards; protected Rigidbody rigidbody; public int blood; public GameObject enemyBoom; public int demage; public float shotRate; [HideInInspector] public float shotTimer = 0; private bool isDied = false; [SerializeField] private AudioClip enemyBoomClip; [SerializeField] private GameObject rayShootHurt; [SerializeField] private GameObject bulletHurt; public void SetDemage(int demage) { //if (isDied) //{ // return; //} blood -= demage; Gamanager.score += demage; if (blood <= 0) { if (transform.name == "Boss(Clone)"&&Gamanager.level!=3) { PlayerPrefs.SetInt("level2",1); Gamanager.isOver = true; } CreateAward(); //GetComponent<AudioSource>().PlayOneShot(enemyBoomClip); //gameObject.SetActive(false); Destroy(GameObject.Instantiate(enemyBoom, transform.position, Quaternion.identity), .5f); //MeshRenderer[] renders = GetComponentsInChildren<MeshRenderer>(); //foreach (var render in renders) //{ // render.enabled = false; //} //isDied = true; Destroy(this.gameObject); return; } if (demage <= 5) { //int index = Random.Range(0, 20); //if (index == 0) //{ Destroy(GameObject.Instantiate(rayShootHurt, transform.position, Quaternion.identity), .5f); //} } else { int index = Random.Range(0, 2); if (index == 0) { Destroy(GameObject.Instantiate(bulletHurt, transform.position, Quaternion.identity), .5f); } } } void CreateAward() { float randomNum = Random.Range(0f, 4f); for (int i = 0; i < awards.Length; i++) { if (randomNum >= awards[i].minRange && randomNum < awards[i].maxRange) { GameObject.Instantiate(awards[i].AwardPre, transform.position, Quaternion.identity); } } } public void DestorySelf() { Destroy(this.gameObject); } protected abstract void Move(); protected abstract void Shoot(); }

UI主要操纵了玩家的移动以及配置一些其他的共有信息,其中进入UI界面时游戏需要暂停,通过让Time.timescale=0即可实现游戏的暂停Time.timescale=1实现游戏的正常运行。 同时游戏管理者在场景切换的时候需要或许并传递场景件公用的信息:

void OnLevelWasLoaded(int level) { PlayerSettings(); } void PlayerSettings() { //声音 volume = PlayerPrefs.GetFloat("volume"); backgroundAudio.volume = volume; volumeSlider.value = volume; //画质 if (PlayerPrefs.GetInt("frame") == 0) { frame = Frame.Fluency; QualitySettings.SetQualityLevel(0); frameToggle.isOn = false; } else if (PlayerPrefs.GetInt("frame") == 1) { frame = Frame.HighQuatility; QualitySettings.SetQualityLevel(5); frameToggle.isOn = true; } //玩家 playerId = PlayerPrefs.GetInt("playerId"); if (playerId == 1) { player1.SetActive(true); } else { player2.SetActive(true); } //昵称 playerName = PlayerPrefs.GetString("playerName"); level = PlayerPrefs.GetInt("enterLevel"); }

* 3. 第二关场景* 对于第二关的处理性相应的简单了许多,这一关主要是游戏的内容的构思。 4. CG场景 这一关主要用来播放CG并跳转到第二关,没有什么额外的操作。 同时附上场景间切换时进度条的显示方法:

using UnityEngine.SceneManagement; [SerializeField] private GameObject loadPanel; [SerializeField] private Slider processBar; public void LoadScene(string str) { StartCoroutine(StartLoadingScene(str)); } IEnumerator StartLoadingScene(string str) { float i = 0; loadPanel.SetActive(true); AsyncOperation acOp = SceneManager.LoadSceneAsync(str);//异步加载场景 acOp.allowSceneActivation = false;//该值为false时即使加载到下一场景也不会立即跳转,知道该值为true while (i<=100) { i++; processBar.value=i/100; yield return new WaitForSeconds(.05f); } acOp.allowSceneActivation=true; loadPanel.SetActive(false); }

四、心得体会 制作这个游戏最大的收获就在于通过自己的构思去设计出了一个完整而有效的框架,正是这个框架才使得自己能把整个游戏制作出来,相较于以前制作游戏时“写到哪想到哪”的方式,确实对我而言有了质的飞跃。同时对UGUI以及一些插件的再次使用也加深了我自己对这些插件以及相应API的印象。

转载请注明原文地址: https://www.6miu.com/read-450318.html

最新回复(0)