unity制作简单的植物大战僵尸


介绍

简单制作植物大战僵尸游戏。

协程实现各种相机动画
卡片填充方式修改为:“已填充”,实现植物恢复
事件系统实现拖拽植物
植物子弹实现对象池

在这里插入图片描述


掉落阳光

在这里插入图片描述

这段代码是一个Unity游戏中的太阳类(Sun),实现了天上掉落的太阳落下的功能。具体实现如下:

isSkySun:bool 类型的变量,用于标记太阳是否是天上掉落的。
TargetY:float 类型的变量,表示太阳落到的目标位置。
speed:float 类型的变量,表示太阳下落的速度。
InitSkySun 方法:用于初始化天上掉落的太阳,传入太阳的初始位置(x, y)和目标位置 TargetY。
Update 方法:在每一帧更新时,如果太阳是天上掉落的且未被点击(isSkySun 和 isClick 都为 true),则让太阳向下移动。如果太阳到达目标位置 TargetY,则销毁太阳。注意,isClick 变量在该代码中未被定义。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
//太阳 :天上掉落  太阳花掉落
/// </summary>
public class Sun : MonoBehaviour
{
    
    
    //是否是天上掉落的
    private bool isSkySun;

    //天空中的太阳下落的目标点
    private float TargetY;

    //速度
    private float speed = 1;

    private void Start()
    {
    
    
        //初始化
        sunNum = GameObject.Find("SunNum");
    }

    //初始化天上的太阳 初始化开始的位置 及TargetY
    public void InitSkySun(float x, float y, float targetY)
    {
    
    
        //设置初始位置和目标位置
        transform.position = new Vector3(x, y, 0);
        TargetY = targetY;
        isSkySun = true;
    }

    private void Update()
    {
    
    
        if (isSkySun == true)
        {
    
    
            // 天空下落的太阳   过程中 未被点击  
            if (isClick == false)
            {
    
    
                //如果太阳未被点击,则让太阳向下移动
                if (transform.position.y > TargetY)
                {
    
    
                    transform.Translate(new Vector3(0, -1, 0) * Time.deltaTime * speed);
                }
                else
                {
    
    
                    //当太阳到达目标位置时,销毁太阳
                    Destroy(gameObject, 3f);
                }
            }
        }
    }
}

InitSkySun 方法用于初始化天上掉落的太阳,传入太阳的初始位置(x, y)和目标位置 TargetY。Update 方法中,如果太阳未被点击,太阳会一直向下移动,直到到达目标位置 TargetY,然后销毁太阳。


卡片恢复透明度

在这里插入图片描述

在植物卡片上叠加一个灰色卡片,设置透明度。
设置图像类型为“已填充”

在这里插入图片描述


    private void Update()
    {
    
    
        //计时 更新UI
        timer += Time.deltaTime;
        UpdateProgress();
        UpdateBg();

    }

    private void UpdateProgress()
    {
    
    
        //计算相应的百分比
        float per = Mathf.Clamp(timer / coolTime, 0, 1);

        image.fillAmount = 1 - per;
    }

    private void UpdateBg()
    {
    
    
        //1.是否冷却  fillAmount=0
        //2.如果当前拥有的阳光  比消耗的阳光多  

        if (image.fillAmount == 0 && GameMgr.Instance.GetSun() >= costSun)
        {
    
    
            bg.SetActive(false);
        }
        else
        {
    
    
            bg.SetActive(true);
        }
    }

拖拽卡片到方格子上生成植物

在这里插入图片描述

这段代码定义了一个名为"Card"的类,这个类继承了三个接口IBeginDragHandler、IDragHandler、IEndDragHandler。这个类是用于控制游戏中卡牌的行为的,其中包括冷却时间、花费阳光数量、要创建的植物物体、计时器、当前物体等属性。这个类还包括一些方法,如UpdateProgress()、UpdateBg()、ScreenToWorld()、LoadByName()、OnBeginDrag()、OnDrag()和OnEndDrag()等方法,用于更新UI、加载卡牌、生成物体、让物体跟随鼠标、让物体生成在点击到的格子上等。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System;

/// <summary>
///      卡片     
/// </summary>
public class Card : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    
    
    //冷却时间
    public float coolTime = 4;
    //计时器
    private float timer;

    private Image image;

    private GameObject bg;
    //花费的阳光
    public int costSun;

    //要创建的植物物体
    public GameObject plantPrefab;



    //当前的物体
    public GameObject curObj;






    private void Start()
    {
    
    
        //初始化
        costSun = 50;
        timer = 0;
        image = transform.Find("progress").GetComponent<Image>();
        bg = transform.Find("bg").gameObject;

        //string[] cardName = name.Split("_");
        //string resPath = "Prefabs/" + cardName[1];
        //Debug.Log(resPath);
        LoadByName(gameObject.name);
    }


    private void Update()
    {
    
    
        //计时 更新UI
        timer += Time.deltaTime;
        UpdateProgress();
        UpdateBg();

    }


    private void UpdateProgress()
    {
    
    
        //计算相应的百分比
        float per = Mathf.Clamp(timer / coolTime, 0, 1);

        image.fillAmount = 1 - per;
    }


    private void UpdateBg()
    {
    
    
        //1.是否冷却  fillAmount=0
        //2.如果当前拥有的阳光  比消耗的阳光多  

        if (image.fillAmount == 0 && GameMgr.Instance.GetSun() >= costSun)
        {
    
    


            bg.SetActive(false);

        }
        else
        {
    
    
            bg.SetActive(true);
        }
    }


    //屏幕坐标  转  z为0的世界坐标
    public Vector3 ScreenToWorld(Vector3 pos)
    {
    
    

        Vector3 po = Camera.main.ScreenToWorldPoint(pos);

        //确保pos 为0;
        Vector3 final = new Vector3(po.x, po.y, 0);
        return final;

    }
    根据名字 加载 物体
    //public void LoadByName(string name)
    //{
    
    
    //    // 切割字符串
    //    string[] cardName = name.Split("_");
    //    //加载预制体
    //    string resPath = "Prefabs/" + cardName[1];
    //    plantPrefab = Resources.Load<GameObject>(resPath);


    //}

    public void LoadByName(string name)
    {
    
    
        // 切割字符串
    //    string[] cardName = name.Split("_");
        string[] cardName = name.Split('_');


        if (cardName.Length > 1)
        {
    
    
            // 加载预制体
            string resPath = "Prefabs/" + cardName[1];
            plantPrefab = Resources.Load<GameObject>(resPath);
        }
        else
        {
    
    
            // 处理拆分结果不正确的情况
            Debug.LogError("Invalid name format: " + name);
        }
    }





    //生成一个物体  出现在鼠标所对应的世界处坐标
    public void OnBeginDrag(PointerEventData eventData)
    {
    
    
        //  Debug.Log(eventData.position);  
        //  eventData.position  不是  世界坐标    做转换
        // unity 坐标系   1. 世界坐标系   2.屏幕坐标系     3.视口坐标  ***

        //如果黑色背景激活
        if (bg.activeSelf)
        {
    
    
            return;
        }
        curObj = Instantiate(plantPrefab);
        curObj.transform.position = ScreenToWorld(eventData.position);


    }

    //  让物体 跟随 着鼠标
    public void OnDrag(PointerEventData eventData)
    {
    
    
        if (curObj != null)
        {
    
    
            curObj.transform.position = ScreenToWorld(eventData.position);
        }
    }
    //让物体生成在点击到的格子上
    public void OnEndDrag(PointerEventData eventData)
    {
    
    

        //计时器重置
        timer = 0;

        //UI更新  
        GameMgr.Instance.ChangSun(-costSun);

        UIMgr.Instance.ChangeUICount(GameMgr.Instance.GetSun());

        //开始正式种植
        if (curObj == null) {
    
     return; }


        Collider2D[] col = Physics2D.OverlapPointAll(ScreenToWorld(eventData.position));

        // 遍历所有的格子

        for (int i = 0; i < col.Length; i++)
        {
    
    

            //1 是格子

            //2有没有植物
            if (col[i].tag == "Land" && col[i].gameObject.transform.childCount == 0)
            {
    
    

                curObj.transform.position = col[i].transform.position;

                curObj.transform.SetParent(col[i].transform);


                //这里我们生成的是游戏物体  拖动时会调用方法
                //会出现 拖动时发射子弹的bug
                //设置isOnGround 为true


                //以豌豆为例 后续会提取Plant 类
                //Peashooter obj = curObj.GetComponent<Peashooter>();

                //if (obj != null)
                //{
    
    

                //    obj.isOnGround = true;

                //}

                Plant obj = curObj.GetComponent<Plant>();

                if (obj != null)
                {
    
    

                    obj.isOnGround = true;

                }




                curObj = null;
                return;


            }

        }
        // 如果没有合适的格子   curob还在

        if (curObj != null)
        {
    
    
            GameObject.Destroy(curObj);
            curObj = null;
        }







    }
}


僵尸生成器

在这里插入图片描述

僵尸放在一个链表中

Awake(): 在对象被创建时调用的方法,将当前对象赋值给Instance,实现单例模式。
CreateZombine(): 创建僵尸的方法。实例化僵尸预制体,随机选择生成点,设置生成点的父对象、位置和层级。将生成的僵尸添加到存储的僵尸列表中。
AddZombine(Zombine zom): 将僵尸添加到存储的僵尸列表中。
RemoveZombine(Zombine zom): 从存储的僵尸列表中移除僵尸。
CreateStartZombie(): 创建初始僵尸的方法。循环3次,实例化僵尸预制体,随机选择生成点,设置生成点的父对象、位置和层级。将生成的僵尸添加到正在展示的僵尸列表中,将其动画的播放速度设置为0,将其isWalk标志设为false。
DestoryZombieShow(): 销毁正在展示的僵尸的方法。遍历正在展示的僵尸列表,销毁每个僵尸的游戏对象。
Start(): Unity的内置方法,在脚本启用时调用。调用CreateByTime()方法。
CreateByTime(): 按时间创建僵尸的方法。使用协程CreateSomeZombie(),在一定条件下循环生成僵尸。
CreateSomeZombie(): 使用假数据创建一些僵尸的协程方法。在一定条件下循环生成僵尸。根据刷新标志isRefresh的值,生成一定数量的僵尸,并在每次生成之间等待一定的时间间隔。
StopCreateZombie(): 停止创建僵尸的方法。停止所有的协程。
ClearZombie(): 清除所有僵尸的方法。将刷新标志isRefresh设为false,并销毁存储的所有僵尸的游戏对象。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 僵尸管理
/// </summary>
public class ZombieMgr : MonoBehaviour
{
    
    
    public static ZombieMgr Instance;

    public GameObject zombiePrefab;
    //生成点列表
    public Transform[] bornPoint;
    //存储的列表
    public List<Zombine> zombies = new List<Zombine>();

    public List<Zombine> zombineShow = new List<Zombine>();
    private int layerIndex = 0;
    //是否刷新
    public bool isRefresh = false;


    private void Awake()
    {
    
    
        Instance = this;
    }



    public void CreateZombine()
    {
    
    
        //生成物体
        GameObject go = GameObject.Instantiate(zombiePrefab);
        //随机坐标
        int index = Random.Range(0, 5);//0 1 2 3 4
        go.transform.parent = bornPoint[index];
        go.transform.localPosition = Vector3.zero;


        //设置层级
        layerIndex += 1;

        go.GetComponent<SpriteRenderer>().sortingOrder = layerIndex;
        AddZombine(go.GetComponent<Zombine>());
    }

    public void AddZombine(Zombine zom)
    {
    
    
        zombies.Add(zom);
    }

    public void RemoveZombine(Zombine zom)
    {
    
    
        zombies.Remove(zom);
    }


    public void CreateStartZombie()
    {
    
    
        for (int i = 0; i < 3; i++)
        {
    
    
            GameObject go = GameObject.Instantiate(zombiePrefab);
            //随机坐标
            int index = Random.Range(0, 5);//0 1 2 3 4
            go.transform.parent = bornPoint[index];
            go.transform.localPosition = Vector3.zero;

            Zombine zombine = go.GetComponent<Zombine>();
            zombineShow.Add(zombine);
            go.GetComponent<Animator>().speed = 0;

            zombine.isWalk = false;
        }
    }





    public void DestoryZombieShow()
    {
    
    

        for (int i = 0; i < zombineShow.Count; i++)
        {
    
    
            GameObject.Destroy(zombineShow[i].gameObject);
        }
    }

    public void Start()
    {
    
    
        CreateByTime();
    }
    public void CreateByTime()
    {
    
    
        StartCoroutine(CreateSomeZombie());
    }

    //使用假数据
    private IEnumerator CreateSomeZombie()
    {
    
    
        while (layerIndex < 12)
        {
    
    
            //是否在刷
            if (isRefresh == true)
            {
    
    
                int delay = Random.Range(2, 5);
                yield return new WaitForSeconds(delay);

                int randomNum = Random.Range(1, 4);
                for (int i = 0; i < randomNum; i++)
                {
    
    
                    CreateZombine();
                }
            }
            yield return new WaitForSeconds(5);
        }
        yield return new WaitForSeconds(5);
        StartCoroutine(CreateSomeZombie());
    }

    public void StopCreateZombie()
    {
    
    
        StopAllCoroutines();
    }


    public void ClearZombie()
    {
    
    
        isRefresh = false;
        for (int i = 0; i < zombies.Count; i++)
        {
    
    
            GameObject.Destroy(zombies[i].gameObject);

        }
        //zombies.Clear();
    }






}

子弹对象池

在这里插入图片描述

在Awake()函数中,将该脚本实例化为单例对象。

在Start()函数中,创建对象池,即实例化一定数量的子弹对象,将它们添加到对象池中,并将其设为未激活状态。

在GetPoolObject()函数中,从对象池中获取一个子弹对象,如果对象池中存在未被激活的子弹对象,则直接返回该对象,并将其设为激活状态;如果对象池中不存在未被激活的子弹对象,则创建一个新的子弹对象,将其添加到对象池中,并将其设为激活状态,最后返回该对象。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 子弹对象池  仅作为简单讲解  不涉及其他复杂操作   单例
/// </summary>
public class BulletPool : MonoBehaviour
{
    
    

    public static BulletPool Instance;


    //子弹预制体
    public GameObject bulletPrefab;
    //对象池容量
    public int amount = 10;
    // 物品列表
    private List<GameObject> pool = new List<GameObject>();


    private void Awake()
    {
    
    
        Instance = this;
    }


    private void Start()
    {
    
       //创建对象池的物体
        for (int i = 0; i < amount; i++)
        {
    
    
            GameObject go = Instantiate(bulletPrefab);
            //让物体失活
            go.SetActive(false);
            //添加到对象池
            pool.Add(go);

        }
    }

    //从对象池获取物体
    public GameObject GetPoolObject()
    {
    
    
        for (int i = 0; i < pool.Count; i++)
        {
    
    
            //找出未被激活的物体  将其激活  并返回
            if (!pool[i].activeInHierarchy)
            {
    
    
                pool[i].SetActive(true);
                return pool[i];
            }
        }
        //如果没有满足条件的物体  创建物体  添加到池中   激活 并返回
        GameObject go = GameObject.Instantiate(bulletPrefab);
        pool.Add(go);


        go.SetActive(true);
        return go;
    }
}

源码


https://wwez.lanzoul.com/iSCMm0yyk99c


猜你喜欢

转载自blog.csdn.net/qq_20179331/article/details/131064154
今日推荐