在创建完我们所需要的敌人和防御者以后,我们还要为我们游戏场景添加失败和成功后添加新的Canvas以及播放下一关的场景,我们现在就来实现一下吧
制作游戏成功:
首先创建一个Canvas叫Level Complete Canvas,然后给它添加文字和阴影图片
除此之外我们还要在之前的Main Canvas(之前的,改了个名而已)创建新UI Slider,然后把Handle部分换一张SPrite比如狐狸那张,然后改变background和fill的颜色
制作失败的Canvas:
前面的和成功的Canvas一样,唯一不同的是要创建两个Button,一个是返回一个是重新开始本关
因此我们要创建两个Public方法供Button调用,这里我们创建个空对象就叫LevelController来管理我们的关卡
给它一个同名的脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelController : MonoBehaviour
{
[SerializeField] float waitToLoad = 3f;
[SerializeField] GameObject winLabel;
[SerializeField] GameObject lostLabel;
int numberOfAttackers = 0;
bool levelTimerFinished = false; //判断滑动条的Value是否到1
private void Start()
{
//先把输赢的canvas隐藏
winLabel.SetActive(false);
lostLabel.SetActive(false);
}
public void AttackerSpawn() //给Attacker调用
{
numberOfAttackers++;
}
public void AttackerKilled() //同样给attacker调用
{
numberOfAttackers--;
if (numberOfAttackers <= 0 && levelTimerFinished)
{
StartCoroutine(HandleWinCondition());
}
}
IEnumerator HandleWinCondition() //当关卡成功时候
{
winLabel.SetActive(true);
GetComponent<AudioSource>().Play();
yield return new WaitForSeconds(waitToLoad);
FindObjectOfType<LevelLoader>().LoadNextScene();
}
public void HandleLoseCondition() //当关卡失败后
{
lostLabel.SetActive(true);
Time.timeScale = 0;
}
public void LevelTimerFinished() //滑动条的值为1时候
{
levelTimerFinished = true;
StopSpawners();
}
private void StopSpawners() //停止生产敌人
{
AttackSpawner[] spawnArray = FindObjectsOfType<AttackSpawner>();
foreach (var spawner in spawnArray)
{
spawner.StopSpawning();
}
}
}
然后再挂载到上面
那我们怎么判断关卡是不是失败呢?和植物大战僵尸一样,当进入房子的敌人超过一定数量时游戏直接判负,显示游戏失败。 这里用一个空对象给它一个Trigger
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamageCollider : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D other)
{
FindObjectOfType<Lives>().TakeLife();
}
}
在Main Canvas时我们又创建一个Text
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Lives : MonoBehaviour
{
[SerializeField] int damage = 1;
[SerializeField] int lives = 5;
Text livesText;
private void Start()
{
livesText = GetComponent<Text>();
UpdateDisplay();
}
private void UpdateDisplay()
{
livesText.text = lives.ToString();
}
public void TakeLife()
{
lives -= damage;
UpdateDisplay();
if (lives <= 0)
{
FindObjectOfType<LevelController>().HandleLoseCondition();
//FindObjectOfType<LevelLoader>().LoadYouLose();
}
}
}
之前的LevelLoader已经不需要了,取而代之的是LevelController
制作完以上两点后,我们也可以设置一个让不同路生成不同种类的敌人。用数组的形式在AttackerSpawner上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackSpawner : MonoBehaviour
{
[SerializeField] Attacker[] attackPrefabArray;
[SerializeField] float minSpawnDelay = 1f;
[SerializeField] float maxSpawnDelay = 5f;
bool spawn = true;
IEnumerator Start()
{
while (spawn)
{
yield return new WaitForSeconds(Random.Range(minSpawnDelay, maxSpawnDelay)); //延迟生成Attacker对象
SpawnAttack();
}
}
private void SpawnAttack()
{
var attackerIndex = Random.Range(0, attackPrefabArray.Length);
Spawn(attackPrefabArray[attackerIndex]);
}
public void StopSpawning()
{
spawn = false;
}
private void Spawn(Attacker myAttacker)
{
Attacker newattacker = Instantiate(myAttacker, transform.position, transform.rotation) as Attacker;
newattacker.transform.SetParent(transform); //并把对象转化为我们Spawenr的子对象,简化hierarchy窗口
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attacker : MonoBehaviour
{
GameObject currentTarget;
[Range(0, 5)]
float currentSpeed = 1f;
private void Awake()
{
FindObjectOfType<LevelController>().AttackerSpawn();
}
private void OnDestroy()
{
LevelController level = FindObjectOfType<LevelController>();
if (level != null)
{
level.AttackerKilled();
}
}
void Update()
{
transform.Translate(Vector2.left * currentSpeed * Time.deltaTime);
UpdateAnimationState();
}
private void UpdateAnimationState()
{
if (!currentTarget)
{
GetComponent<Animator>().SetBool("isAttacking", false);
}
}
public void SetMovementSpeed(float speed)
{
currentSpeed = speed;
}
public void Attack(GameObject target)
{
GetComponent<Animator>().SetBool("isAttacking", true); //播放吃植物动画
currentTarget = target;//锁定植物
}
public void StrikeCurrentTarget(float damage)
{
if (!currentTarget) return;
Health health = currentTarget.GetComponent<Health>();
if (health)
{
health.DealDamage(damage);//扣植物的血
}
}
}