【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版9(附带项目源码)
最终效果
系列导航
文章目录
- 最终效果
- 系列导航
- 前言
- 选择植物
- 简单绘制选择植物面板
- 渲染卡牌数据
- 点击选中和移除卡牌
- 修改UI
- 代码控制
- 开始战斗
- 源码
- 结束语
前言
本节主要实现添加选择植物功能
选择植物
简单绘制选择植物面板
每个卡牌插槽和前面植物卡牌类似,并配置为预制体
渲染卡牌数据
新增ScriptableObject,配置植物列表卡牌数据
[CreateAssetMenu(fileName = "CardData", menuName = "CardData", order = 0)]
public class CardData : ScriptableObject
{// 存储卡牌数据的列表public List<CardItem> cardItemDataList = new List<CardItem>();
}// 表示单个关卡的数据
[System.Serializable]
public class CardItem
{public string name; //名称public float waitTime; //等待时间public int useSun;//需要阳光public GameObject prefab;//预制体public Sprite sprite;//图片
}
配置
新增ChooseCardPanel,渲染植物列表卡牌数据
public class ChooseCardPanel : MonoBehaviour
{public GameObject beforeCardPrefab;public CardData cardData;private void Start() {for (int i = 0; i < cardData.cardItemDataList.Count; i++){GameObject beforeCard = Instantiate(beforeCardPrefab);beforeCard.transform.SetParent(transform, false);beforeCard.name = cardData.cardItemDataList[i].name;beforeCard.GetComponent<Image>().sprite = cardData.cardItemDataList[i].sprite;beforeCard.transform.Find("黑色进度").GetComponent<Image>().fillAmount = 0;beforeCard.transform.Find("黑色底图").gameObject.SetActive(false);Card card = beforeCard.GetComponent<Card>();card.waitTime = cardData.cardItemDataList[i].waitTime;card.useSun = cardData.cardItemDataList[i].useSun;card.prefab = cardData.cardItemDataList[i].prefab;}}
}
配置
运行效果,可以看到卡牌数据都渲染上去了
点击选中和移除卡牌
注意
:这里会使用DOTween实现动画效果,不会用的小伙伴可以查看我之前写的文章:【推荐100个unity插件之2】DoTween动画插件的安装和使用整合(最全)
修改UI
注意修改卡牌容器的顶点位置在左上
卡牌预制体轴心也在左上
选项卡牌面板顶点同样位置在左上
代码控制
这里改动比较大,我直接贴出代码
修改GameManager,新增字段用于判断是否开始游戏
public bool isStart;//是否开始游戏
ChooseCardPanel代码
public class ChooseCardPanel : MonoBehaviour
{public static ChooseCardPanel Instance;public GameObject cardPrefab;//卡牌模板预制体public CardData cardData;//所有卡牌数据public GameObject useCardPanel;//选中的卡牌的对象父类public List<GameObject> useCardList;//选中的卡牌private void Awake(){Instance = this;}private void Start(){//渲染卡牌列表数据for (int i = 0; i < cardData.cardItemDataList.Count; i++){GameObject beforeCard = Instantiate(cardPrefab);beforeCard.transform.SetParent(transform, false);beforeCard.GetComponent<Card>().cardItem = cardData.cardItemDataList[i];}}//添加卡牌public void AddCard(GameObject go){int curIndex = useCardList.Count;if (curIndex >= 8){Debug.Log("已经选中的卡片超过最大数量");return;}useCardList.Add(go);go.transform.SetParent(transform.root);//父级修改为当前对象所在的最顶层父对象,确保卡牌UI在最顶层显示Card card = go.GetComponent<Card>();card.isMoving = true;card.hasUse = true;// DoMove移动到目标位置go.transform.DOMove(useCardPanel.transform.position, 0.8f).OnComplete(() =>{go.transform.SetParent(useCardPanel.transform, false);//移动到其父对象的子物体列表的最前面go.transform.SetAsFirstSibling();card.isMoving = false;});}//移除卡牌public void RemoveCard(GameObject go){useCardList.Remove(go);go.transform.SetParent(transform.root);//父级修改为当前对象所在的最顶层父对象,确保卡牌UI在最顶层显示Card card = go.GetComponent<Card>();card.isMoving = true;card.hasUse = false;go.transform.DOMove(transform.position, 0.8f).OnComplete(() =>{go.transform.SetParent(transform, false);//移动到其父对象的子物体列表的最前面go.transform.SetAsFirstSibling();card.isMoving = false;});}
}
Card代码
public class Card : MonoBehaviour, IDragHandler, IBeginDragHandler, IEndDragHandler, IPointerClickHandler
{[HideInInspector] public CardItem cardItem;//卡牌信息[HideInInspector] public bool hasUse = false;//是否使用[HideInInspector] public bool isMoving = false;//是否正在移动private GameObject darkBg;//黑图private Image image;private GameObject progressBar;//进度对象private float waitTime; //等待时间private int useSun;//需要阳光private GameObject prefab;//预制体public LayerMask layerMask;//检测图层private GameObject thisObject;void Start(){darkBg = transform.Find("黑色底图").gameObject;progressBar = transform.Find("黑色进度").gameObject;image = progressBar.GetComponent<Image>();Init();}private void Init(){if (cardItem == null){Debug.Log("找不到卡牌数据");return;}darkBg.SetActive(false);image.fillAmount = 0;GetComponent<Image>().sprite = cardItem.sprite;gameObject.name = cardItem.name;waitTime = cardItem.waitTime;useSun = cardItem.useSun;prefab = cardItem.prefab;}void Update(){if(!GameManager.Instance.isStart) return;UpdateProgress();UpdateDarkBg();}void UpdateProgress(){image.fillAmount -= 1 / waitTime * Time.deltaTime;}void UpdateDarkBg(){// TODO 且需要阳光数>useSunif (image.fillAmount == 0 && GameManager.Instance.sunSum >= useSun){darkBg.SetActive(false);}else{darkBg.SetActive(true);}}//开始拖拽public void OnBeginDrag(PointerEventData eventData){if(!GameManager.Instance.isStart) return;if (image.fillAmount != 0 || GameManager.Instance.sunSum < useSun) return;thisObject = Instantiate(prefab, transform.position, Quaternion.identity);//关闭运行thisObject.GetComponent<Plants>().isOpen = false;//关闭碰撞thisObject.GetComponent<Collider2D>().enabled = false;//关闭动画thisObject.GetComponent<Animator>().enabled = false;}//拖拽中public void OnDrag(PointerEventData eventData){if (thisObject == null) return;//植物跟随鼠标Vector2 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);thisObject.transform.position = new Vector2(mousePosition.x, mousePosition.y); // 将物体位置设置为鼠标位置}//拖拽结束public void OnEndDrag(PointerEventData eventData){if (thisObject == null) return;Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);Vector2 mousePosition2D = new Vector2(mousePosition.x, mousePosition.y);RaycastHit2D hit = Physics2D.Raycast(mousePosition2D, Vector2.zero, Mathf.Infinity, layerMask);//鼠标“点检测”射线if (hit.collider != null && hit.collider.transform.childCount == 0){thisObject.transform.parent = hit.transform;thisObject.transform.localPosition = Vector2.one;//开始运行thisObject.GetComponent<Plants>().isOpen = true;//开启碰撞thisObject.GetComponent<Collider2D>().enabled = true;//开启动画thisObject.GetComponent<Animator>().enabled = true;thisObject = null;//重置进度progressBar.GetComponent<Image>().fillAmount = 1;//扣除阳光GameManager.Instance.SetSunSum(-useSun);AudioManager.Instance.PlaySFX("种植");}else{Destroy(thisObject);}}//点击事件public void OnPointerClick(PointerEventData eventData){if (isMoving) return;if(GameManager.Instance.isStart) return;if (hasUse){ChooseCardPanel.Instance.RemoveCard(gameObject);}else{ChooseCardPanel.Instance.AddCard(gameObject);}}
}
配置
效果
开始战斗
修改GameManager,定义开始游戏事件,记得去除原来生成阳光和生成僵尸的代码
//开始游戏
public void StartGame(){AudioManager.Instance.PlayMusic("bgm1");isStart = true;//顶部随机位置掉落阳光InvokeRepeating("CreateSunDown", 10, 10);//生成僵尸GenerateZombies.Instance.TableCreateZombie();
}
配置
效果测试,一切正常
源码
源码不出意外的话我会放在最后一节
结束语
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~