GameManager
从这一章开始用markDown来编辑。
在场景里生成一个名为GameManager的空物体,挂载上GameManager.cs。
将对应的组件拖入其中,注意除了预制物外,其他组件都必须是场景里的。具体解释看GameManager:
public int m_NumRoundsToWin = 5; //总局数
public float m_StartDelay = 3f; //开始延迟
public float m_EndDelay = 3f; //结束延迟
public CameraControl m_CameraControl; //相机控制脚本
public Text m_MessageText; //提示文本
public GameObject m_TankPrefab; //坦克预制物
public TankManager[] m_Tanks; //TankManager集合
private int m_RoundNumber;
private WaitForSeconds m_StartWait;
private WaitForSeconds m_EndWait;
/* private TankManager m_RoundWinner;
private TankManager m_GameWinner;
*/
在Start()的第一个方法SwanAllTanks(),实例化所有的坦克,并编上号码:
private void SpawnAllTanks()
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].m_Instance =
Instantiate(m_TankPrefab, m_Tanks[i].m_SpawnPoint.position, m_Tanks[i].m_SpawnPoint.rotation) as GameObject;//实例化
m_Tanks[i].m_PlayerNumber = i + 1; //添加编号
m_Tanks[i].Setup();
}
}
第二个方法,在将每个坦克的transform传给cameraControl.cs里的m_Targets,因为这里需要用到camaraControl里的公共变量,但是又不希望可以在组件里改变它,所以之前需要在m_Targets前加上[HideInspector]。
private void SetCameraTargets()
{
Transform[] targets = new Transform[m_Tanks.Length];
for (int i = 0; i < targets.Length; i++)
{
targets[i] = m_Tanks[i].m_Instance.transform;
}
m_CameraControl.m_Targets = targets;
}
完成上述内容后,我们现在来看将所有坦克重置、使用和禁用的方法,这些方法在TankManager.cs里已经实现了,在GameManager.cs中只要遍历一遍就行了
private void ResetAllTanks() //重置
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].Reset();
}
}
private void EnableTankControl() //启用
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].EnableControl();
}
}
private void DisableTankControl() //禁用
{
for (int i = 0; i < m_Tanks.Length; i++)
{
m_Tanks[i].DisableControl();
}
}
接下来我们来执行每一小局的内容,这里用到协程。首先是每一小局开始需要做的工作,3s后开始游戏。
private IEnumerator RoundStarting () {
ResetAllTanks (); //重置
DisableTankControl (); //禁止
m_CameraControl.SetStartPositionAndSize (); //重置摄像机
m_RoundNumber++; //局数+1
m_MessageText.text = "ROUND " + m_RoundNumber; //提示文本
yield return m_StartWait; //等待3s
}
其中的SetStartPositionAndSize ()方法我们要在CameraControl.cs中添加
public void SetStartPositionAndSize () {
FindAveragePosition (); //找到多个坦克的中心点
transform.position = m_DesiredPosition; //调整好相机位置和大小
m_Camera.orthographicSize = FindRequiredSize ();
}
然后是游戏中的协程
private IEnumerator RoundPlaying () {
EnableTankControl (); //启用
m_MessageText.text = "";
while (!OneTankLeft ()) { //没有超过1个留下,则循环结束
yield return null;
}
}
在上面的方法中我们反复去判断是否只剩下不到2辆坦克了,如果是则结束协程,进入后面的内容。其中OneTankLeft()方法如下:
private bool OneTankLeft () {
int numTanksLeft = 0; //定义一个坦克数
for (int i = 0; i < m_Tanks.Length; i++) { //遍历所有的坦克,根据是否显示来判断数量
if (m_Tanks[i].m_Instance.activeSelf)
numTanksLeft++;
}
return numTanksLeft <= 1;
}
接下来是结束的协程:
流程是1.得到小局获胜者 2.获胜者胜利局数+1 3.得到游戏胜利者,显示提示内容
给GameManager.cs添加成员变量
private IEnumerator RoundEnding () {
DisableTankControl ();
m_RoundWinner = null;
m_RoundWinner = GetRoundWinner (); //.得到小局获胜者
if (m_RoundWinner != null) { //获胜者胜利局数+1
m_RoundWinner.m_Wins++;
}
m_GameWinner = GetGameWinner (); // 3.得到游戏胜利者
string message = EndMessage (); //显示提示内容
m_MessageText.text = message;
yield return m_EndWait;
}
获取小局胜利者,用到的方法和OneTankLeft()相同
private TankManager GetRoundWinner () {
for (int i = 0; i < m_Tanks.Length; i++) {
if (m_Tanks[i].m_Instance.activeSelf)
return m_Tanks[i];
}
return null;
}
获取游戏胜利者,通过判断Wins数是否达到胜利数
private TankManager GetGameWinner () {
for (int i = 0; i < m_Tanks.Length; i++) {
if (m_Tanks[i].m_Wins == m_NumRoundsToWin)
return m_Tanks[i];
}
return null;
}
提示信息
提示信息里包含当前小局谁获胜,每个人赢了几局和谁赢得了游戏,(如果有的话)。
private string EndMessage () {
string message = "DRAW!";
if (m_RoundWinner != null)
message = m_RoundWinner.m_ColoredPlayerText + " WINS THE ROUND!";
message += "\n\n\n\n";
for (int i = 0; i < m_Tanks.Length; i++) {
message += m_Tanks[i].m_ColoredPlayerText + ": " + m_Tanks[i].m_Wins + " WINS\n";
}
if (m_GameWinner != null)
message = m_GameWinner.m_ColoredPlayerText + " WINS THE GAME!";
return message;
}
现在游戏前,游戏中,游戏后的协程都完成了,我们把它放在一个总协程里
private IEnumerator GameLoop () {
yield return StartCoroutine (RoundStarting ()); //游戏前
yield return StartCoroutine (RoundPlaying ()); //游戏中
yield return StartCoroutine (RoundEnding ()); //游戏后
if (m_GameWinner != null) {
SceneManager.LoadScene (0); //重新加载场景
} else {
StartCoroutine (GameLoop ());
}
}
再在Start()里开启它
private void Start () {
m_StartWait = new WaitForSeconds (m_StartDelay);
m_EndWait = new WaitForSeconds (m_EndDelay);
SpawnAllTanks ();
SetCameraTargets ();
StartCoroutine (GameLoop ());
}
音效
测试一下呗。至此代码部分都完成,现在我们添加一下音效
新建一个AudioMixer文件夹,在其中新建一个Audio Mixer。重命名为MainMix,双击打开它
确保选中的是这个MainMix,(因为还有一个教程制作好的),在Group下新建三个子类,分别命名为如下所示:
1.将Music的Attenuation 设为-12,并添加一个DuckVolume
2.SFX添加一个Send
3.Driving Attenuation设为-25
4.把Tank的prefab点开,打开第一个audio Source,其中的outPut设为Driving
5.第二个Audio Source 的output设为SFX。
6.同理 Shell
7.TankExplosion的的outPut设为SFX;
8.给场景里的GameManager添加一个Audio Source,做如下设置:
优化
至此所有官方内容都已完成了,测试一下,感觉坦克的移动不是很理想,例如我们希望按下左键和下键坦克能够往左后方移动,实际上它往右后方移动了,因为在代码中“左”是让坦克的的整体朝向向左,而不是轮胎的方向向左。我们打开TankMovement.cs,在Trun()里加上一小段
private void Turn () { // Adjust the rotation of the tank based on the player's input.
if (m_MovementInputValue < 0) { //后退时,左右方向取反
m_TurnInputValue = -m_TurnInputValue;
}
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
Quaternion turnRotation = Quaternion.Euler (0f, turn, 0f);
m_Rigidbody.MoveRotation (m_Rigidbody.rotation * turnRotation);
}
再测试一下就跟我们想要的一样了。Nice。