【用unity实现100个游戏之7】从零开始制作一个仿杀戮尖塔卡牌回合制游戏

news2024/11/24 7:39:00

文章目录

  • 前言
  • 素材资源
  • 开始
    • 一、UI框架
    • 二、挂载脚本
    • 三、事件监听,用于绑定按钮事件
    • 四、声音管理器
    • 五、excel转txt文本
    • 六、游戏配置
    • 七、用户信息表
    • 八、战斗管理器
    • 九、 敌人管理器
    • 十、玩家血量、能量、防御值、卡牌数
    • 十一、敌人血量 行动显示逻辑
    • 十二、UI提示效果实现
    • 十三、卡牌生成
    • 十四、卡片信息显示
    • 十五、卡牌选中效果
    • 十六、卡牌拖拽
    • 十七、卡片分支(攻击卡、抽卡、防御卡)
    • 十八、防御卡效果
    • 十九、抽卡的卡牌效果
    • 二十、怪物选中效果
    • 二十一、怪物受伤
    • 二十二、攻击卡效果
    • 二十三、曲线效果
    • 二十四、切换敌人回合
    • 二十五、敌人行为逻辑
    • 二十六、游戏胜利or结束逻辑
  • 最终效果
  • 源码
  • 参考
  • 完结

前言

今天来带大家实现一个简单的卡牌回合制游戏

照例先来看看最终效果
在这里插入图片描述

素材资源

百度链接:https://pan.baidu.com/s/1uy8lN9wESsLy7z1YbfsjLw
提取码:4345

开始

一、UI框架

UIManager.cs UI管理器

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

//UI管理器
public class UIManager : MonoBehaviour
{
    //ui管理器
    public static UIManager Instance;
    private Transform canvasTf;//画布的变换组件
    private List<UIBase> uiList;//存储加载过的界面的集合
    private void Awake()
    {
        Instance = this;
        //找世界中的画布
        canvasTf = GameObject.Find("Canvas").transform;
        //初始化集合
        uiList = new List<UIBase>();
    }
    public UIBase ShowUI<T>(string uiName) where T : UIBase
    {
        UIBase ui = Find(uiName);
        if (ui == null)
        {
            //集合中没有 需要从Resources/UI文件夹加载
            GameObject obj = Instantiate(Resources.Load("UI/" + uiName), canvasTf) as GameObject;

            //改名字,默认实例化会加上(clone),所以得重命名
            obj.name = uiName;

            //添加需要的脚本
            ui = obj.AddComponent<T>();

            //添加到集合进行存储
            uiList.Add(ui);
        }
        else
        {
            //显示
            ui.Show();
        }
        return ui;
    }


    //隐藏
    public void HideUI(string uiName)
    {
        UIBase ui = Find(uiName);
        if (ui != null)
        {
            ui.Hide();
        }
    }

    //关闭某个界面
    public void CloseUI(string uiName)
    {
        UIBase ui = Find(uiName);
        if (ui != null)
        {
            uiList.Remove(ui);
            Destroy(ui.gameObject);
        }
    }

    //关闭所有界面
    public void CloseAllUI(){
        for(int i = uiList.Count - 1; i>=0; i--){
            Destroy(uiList[i].gameObject);
        }
        uiList.Clear();//清空合集
    }  


    //从集合中找到名字对应的界面脚本
    public UIBase Find(string uiName)
    {
        for (int i = 0; i < uiList.Count; i++)
        {
            if (uiList[i].name == uiName) return uiList[i];
        }
        return null;
    }
}

UIBase.cs 界面基类

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

//界面基类
public class UIBase : MonoBehaviour
{
    //显示
    public virtual void Show()
    {
        gameObject.SetActive(true);
    }

    //隐藏
    public virtual void Hide()
    {

        gameObject.SetActive(false);
    }
    //关闭界面(销毁)
    public virtual void Close()
    {
        UIManager.Instance.CloseUI(gameObject.name);
    }

}

LoginUI .cs中调用

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

//开始界面(要继承UIBase)
public class LoginUI : UIBase{}

GameApp .cs

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

//游戏入口脚本
public class GameApp : MonoBehaviour
{
    void Start(){
        //显示loginUI创建的脚本名字记得跟预制体物体名字一致
        UIManager.Instance.ShowUI<LoginUI>("LoginUI");
    }
}

二、挂载脚本

在这里插入图片描述
运行效果
在这里插入图片描述

三、事件监听,用于绑定按钮事件

给开始游戏按钮新增事件
在这里插入图片描述

新增UIEventTrigger.cs脚本

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

//事件监听
public class UIEventTrigger : MonoBehaviour, IPointerClickHandler
{
    //这是一个公共的委托,它接受两个参数,一个是被点击的游戏对象,另一个是关于点击事件的数据。
    public Action<GameObject, PointerEventData> onClick;

    //用于获取或添加 UIEventTrigger 组件
    public static UIEventTrigger Get(GameObject obj)
    {
        UIEventTrigger trigger = obj.GetComponent<UIEventTrigger>();
        if (trigger == null)
        {
            trigger = obj.AddComponent<UIEventTrigger>();
        }
        return trigger;
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        //这是 IPointerClickHandler 接口的方法,当 UI 元素被点击时,它将被调用。
        if (onClick != null) onClick(gameObject, eventData);
    }
}

UIBase.cs新增注册事件

//注册事件
public UIEventTrigger Register(string name)
{
    Transform tf = transform.Find(name);
    return UIEventTrigger.Get(tf.gameObject);
}

LoginUI绑定按钮事件

void Awake(){
  //开始游戏
    Register("bg/startBtn").onClick = onStartGameBtn;
}

private void onStartGameBtn(GameObject obj, PointerEventData pData){
    //关闭login界面
    Close();
}

运行效果
在这里插入图片描述

四、声音管理器

AudioManager.cs

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

//声音管理器
public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance;
    private AudioSource bgmSource;//播放bgm的音频
    private void Awake()
    {
        Instance = this;
    }
    //初始化
    public void Init()
    {
        bgmSource = gameObject.AddComponent<AudioSource>();
    }

    //播放bgm
    public void PlayBGM(string name, bool isLoop = true)
    {
        //加载bgm声音剪辑
        AudioClip clip = Resources.Load<AudioClip>("Sounds/BGM/" + name);
        bgmSource.clip = clip;//设置音频
        bgmSource.loop = isLoop;//是否循环
        bgmSource.Play();
    }


    //播放音效
    public void PlaEffect(string name)
    {
        AudioClip clip = Resources.Load<AudioClip>("Sounds/" + name);
        AudioSource.PlayClipAtPoint(clip, transform.position);
    }
}

调用

//游戏入口脚本
public class GameApp : MonoBehaviour
{
    void Start(){
        //初始化声音管理器
        AudioManager.Instance.Init();

        //显示loginUI创建的脚本名字记得跟预制体物体名字一致
        UIManager.Instance.ShowUI<LoginUI>("LoginUI");

        //播放bgm
        AudioManager.Instance.PlayBGM("bgm1");
    }
}

挂载脚本
在这里插入图片描述
效果
在这里插入图片描述

五、excel转txt文本

读取Excel需要用到Excel.dllICSharpCode.SharpZipLib库文件

新建MyEditor.cs放在Editor目录下

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//编辑器的命名空间
using System.IO;//文件流
using Excel;//读取excel
using System.Data;

//编辑器脚本
public static class MyEditor
{
    [MenuItem("我的工具/excel转成txt")]
    public static void ExportExcelToTxt()
    {
        //_Excel文件夹路径
        string assetPath = Application.dataPath + "/_Excel";
        //获得Excel文件夹中的excel文件
        string[] files = Directory.GetFiles(assetPath, "*.xlsx");
        for (int i = 0; i < files.Length; i++)
        {
            files[i] = files[i].Replace('\\', '/');//反斜杠替换成正斜杠
            //通过文件流读取文件
            using (FileStream fs = File.Open(files[i], FileMode.Open, FileAccess.Read))
            {
                //文件流转成excel 对象
                var excelDataReader = ExcelReaderFactory.CreateOpenXmlReader(fs);
                //获得excel数据
                DataSet dataSet = excelDataReader.AsDataSet();
                //读取excel第一张表
                DataTable table = dataSet.Tables[0];
                //将表中内容 读取后 存储到 对应的txt文件
                readTableToTxt(files[i], table);
            }
        }
        //刷新编辑器
        AssetDatabase.Refresh();
    }

    private static void readTableToTxt(string filePath, DataTable table)
    {
        // 获得文件名(不要文件后缀 生成与之名字相同的txt文件)
        string fileName = Path.GetFileNameWithoutExtension(filePath);
        // txt文件存储的路径
        string path = Application.dataPath + "/Resources/Data/" + fileName + ".txt";
        //判断Resources/Data文件夹中是否已经存在对应的txt文件,如果是 则删除
        if (File.Exists(path))
        {
            File.Delete(path);
        }
        // 文件流创建txt文件
        using (FileStream fs = new FileStream(path, FileMode.Create))
        {
            // 文件流转写入流方便写入字符串
            using (StreamWriter sw = new StreamWriter(fs))
            {
                // 遍历table
                for (int row = 0; row < table.Rows.Count; row++)
                {
                    DataRow dataRow = table.Rows[row];
                    string str = "";
                    //遍历列
                    for (int col = 0; col < table.Columns.Count; col++)
                    {
                        string val = dataRow[col].ToString();
                        str = str + val + "\t";//每一项tab分割
                    }

                    //写入
                    sw.Write(str);

                    //如果不是最后一行换行
                    if (row != table.Rows.Count - 1)
                    {
                        sw.WriteLine();
                    }
                }
            }


        }
    }
}

记得在Resources目录下新建Data文件夹用来存放生成的txt文本,点击工具会将_Excel文件夹里的xlsx文件生成为txt保存到/Resources/Data/目录下
在这里插入图片描述
在这里插入图片描述
配置表大概样式
card.xlsx
在这里插入图片描述
cardType.xlsx
在这里插入图片描述
enemy.xlsx
在这里插入图片描述
level.xlsx
在这里插入图片描述

六、游戏配置

游戏配置GameConfigData

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

// 游戏配置表类,每个对象对应一个xt配置表
public class GameConfigData
{
    // 存储配置表中的所有数据
    private List<Dictionary<string, string>> dataDic;
    // 构造函数,参数为字符串
    public GameConfigData(string str)
    {
        // 初始化数据字典
        dataDic = new List<Dictionary<string, string>>();
        // 按换行符切割字符串
        string[] lines = str.Split('\n');
        // 第一行是存储数据的类型
        string[] title = lines[0].Trim().Split('\t');//tab切割
        // 从第三行(下标为2)开始遍历数据,第二行数据是解释说明
        for (int i = 2; i < lines.Length; i++)
        {
            // 创建新的字典存储每行数据
            Dictionary<string, string> dic = new Dictionary<string, string>();
            // 按tab切割每行数据
            string[] tempArr = lines[i].Trim().Split("\t");
            // 将切割后的数据添加到字典中
            for (int j = 0; j < tempArr.Length; j++)
            {
                dic.Add(title[j], tempArr[j]);
            }
            // 将字典添加到数据列表中
            dataDic.Add(dic);
        }
    }

    // 获取所有行的数据
    public List<Dictionary<string, string>> GetLines()
    {
        return dataDic;
    }

    // 根据ID获取一行数据
    public Dictionary<string, string> GetOneById(string id)
    {
        // 遍历数据列表
        for (int i = 0; i < dataDic.Count; i++)
        {
            // 获取当前字典
            Dictionary<string, string> dic = dataDic[i];
            // 如果字典中的ID与参数相同,返回该字典
            if (dic["Id"] == id)
            {
                return dic;
            }
        }
        // 如果没有找到,返回null
        return null;
    }
}

GameConfigManager.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
// 游戏配置管理类
public class GameConfigManager
{
    // 单例模式
    public static GameConfigManager Instance = new GameConfigManager();
    private GameConfigData cardData;//卡牌表
    private GameConfigData enemyData;//敌人表
    private GameConfigData levelData;//关卡表
    private GameConfigData cardTypeData; //卡牌类型
    // 文本资源
    private TextAsset textAsset;

    // 初始化配置文件(txt文件 存储到内存)
    public void Init()
    {
        // 加载卡牌数据
        textAsset = Resources.Load<TextAsset>("Data/card");
        cardData = new GameConfigData(textAsset.text);

        // 加载敌人数据
        textAsset = Resources.Load<TextAsset>("Data/enemy");
        enemyData = new GameConfigData(textAsset.text);

        // 加载关卡数据
        textAsset = Resources.Load<TextAsset>("Data/level");
        levelData = new GameConfigData(textAsset.text);

        //卡牌类型数据
        textAsset = Resources.Load<TextAsset>("Data/cardType");
        cardTypeData = new GameConfigData(textAsset.text);
    }

    // 获取卡牌行数据
    public List<Dictionary<string, string>> GetCardLines()
    {
        return cardData.GetLines();
    }

    // 获取敌人行数据
    public List<Dictionary<string, string>> GetEnemyLines()
    {
        return enemyData.GetLines();
    }

    // 获取关卡行数据
    public List<Dictionary<string, string>> GetLevelLines()
    {
        
        return levelData.GetLines();
    }
    // 根据ID获取卡牌数据
    public Dictionary<string, string> GetCardById(string id)
    {
        return cardData.GetOneById(id);
    }
    // 根据ID获取敌人数据
    public Dictionary<string, string> GetEnemyById(string id)
    {
        return enemyData.GetOneById(id);
    }
    // 根据ID获取关卡数据
    public Dictionary<string, string> GetLevelById(string id)
    {
        return levelData.GetOneById(id);
    }

    //根据ID获取卡牌类型
    public Dictionary<string, string> GetCardTypeById(string id)
    {
        return cardTypeData.GetOneById(id);
    }
}

游戏入口脚本GameApp初始化并调用测试

//初始化配置表
GameConfigManager.Instance.Init();

//测试
string name = GameConfigManager.Instance.GetCardById("1001")["Name"];
print(name);

运行效果
在这里插入图片描述
输出正确
在这里插入图片描述

七、用户信息表

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

//用户信息管理器(拥有的卡牌等信息金币等)
public class RoleManager
{
    public static RoleManager Instance = new RoleManager();
    public List<string> cardList;//存储拥有的卡牌的id
    public void Init()
    {
        cardList = new List<string>();
        //四张攻击卡 四张防御卡 两张效果卡
        cardList.Add("1000");
        cardList.Add("1000");
        cardList.Add("1000");
        cardList.Add("1000");

        cardList.Add("1001");
        cardList.Add("1001");
        cardList.Add("1001");
        cardList.Add("1001");

        cardList.Add("1002");
        cardList.Add("1002");
    }
}

游戏入口脚本GameApp初始化

//初始化用户信息
RoleManager.Instance.Init();

八、战斗管理器

战斗单元

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//战斗单元
public class FightUnit
{
    public virtual void Init() { }//初始化
    public virtual void OnUpdate() { }//每帧调用
}

新增不同战斗状态的脚本控制

# 卡牌战斗初始化
public class FightInit : FightUnit{}

# 玩家回合
public class Fight_PlayerTurn : FightUnit{}

# 敌人回合
public class Fight_EnemyTurn : FightUnit{}

# 胜利
public class Fight_Win : FightUnit{}

# 失败
public class Fight_Fail : FightUnit{}

战斗管理器FightManager

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

//战斗枚举
public enum FightType
{
    None,
    Init,
    Player,//玩家回合
    Enemy,//敌人回合
    Win,
    Fail
}
//战斗管理器
public class FightManager : MonoBehaviour
{

    public static FightManager Instance;
    public FightUnit fightUnit;//战斗单元
    private void Awake()
    {

        Instance = this;
    }
    //切换战斗类型
    public void ChangeType(FightType type)
    {
        switch (type)
        {
            case FightType.None:
                break;
            case FightType.Init:
                fightUnit = new FightInit();
                break;
            case FightType.Player:
                fightUnit = new Fight_PlayerTurn();
                break;
            case FightType.Enemy:
                fightUnit = new Fight_EnemyTurn();
                break;
            case FightType.Win:
                fightUnit = new Fight_Win();
                break;
            case FightType.Fail:
                fightUnit = new Fight_Fail();
                break;
        }
        fightUnit.Init();// 初始化
    }
    private void Update(){
        if(fightUnit != null){
            fightUnit.OnUpdate();
        }
    }

}

挂载脚本
在这里插入图片描述
修改LoginUI ,开始游戏时,进行战斗初始化

private void onStartGameBtn(GameObject obj, PointerEventData pData){
    //关闭login界面
    Close();

    //战斗初始化
    FightManager.Instance.ChangeType(FightType.Init);
}

完善卡牌战斗初始化,切换界面和bgm,记得先新增FightUI脚本代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//卡牌战斗初始化
public class FightInit : FightUnit
{
    public override void Init() {
        //切换bgm
        AudioManager.Instance.PlayBGM("battle");
        //显示战斗界面
        UIManager.Instance.ShowUI<FightUI>("FightUI");
    }
    public override void OnUpdate() { 
        base.OnUpdate();
    }
}

运行效果
在这里插入图片描述

九、 敌人管理器

敌人脚本Enemy

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人脚本
public class Enemy : MonoBehaviour
{
    protected Dictionary<string, string> data;//敌人数据表信息
    public void Init(Dictionary<string, string> data)
    {
        this.data = data;
    }
}

敌人管理器EnemyManeger

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//敌人管理器
public class EnemyManeger
{
    public static EnemyManeger Instance = new EnemyManeger();

    private List<Enemy> enemyList;//存储战斗中的敌人
    //加载敌人资源 id=关卡Id
    public void LoadRes(string id)
    {
        enemyList = new List<Enemy>();
        /* 
         *  Id	Name	EnemyIds	Pos	
         *  Id	关卡名称	敌人Id的数组	所有怪物的位置	
         *  10003	3	10001=10002=10003	3,0,1=0,0,1=-3,0,1	
         */
        //读取关卡表
        Dictionary<string, string> levelData = GameConfigManager.Instance.GetLevelById(id);
        //切割字符串,获取敌人id信息
        string[] enemyIds = levelData["EnemyIds"].Split('=');

        string[] enemyPos = levelData["Pos"].Split('=');// 敌人位置信息
        for (int i = 0; i < enemyIds.Length; i++)
        {
            string enemyId = enemyIds[i];
            string[] posArr = enemyPos[i].Split(',');
            //敌人位置
            float x = float.Parse(posArr[0]);
            float y = float.Parse(posArr[1]);
            float z = float.Parse(posArr[2]);
            // 根据敌人id获得单个敌人信息
            Dictionary<string, string> enemyData = GameConfigManager.Instance.GetEnemyById(enemyId);
            GameObject obj = Object.Instantiate(Resources.Load(enemyData["Model"])) as GameObject;//从资源路径加载对应的敌人

            Enemy enemy = obj.AddComponent<Enemy>();//添加敌人脚本
            enemy.Init(enemyData);//存储敌人信息
            enemyList.Add(enemy);//存储到集合
           
            obj.transform.position = new Vector3(x, y, z);
        }
    }
}

战斗卡牌管理器

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


//战斗卡牌管理器
public class FightCardManager
{
    public static FightCardManager Instance = new FightCardManager();
    public List<string> cardList;//卡堆集合
    public List<string> usedCardList;//弃牌堆
    //初始化
    public void Init()
    {
        cardList = new List<string>();
        usedCardList = new List<string>();
        //定义临时集合
        List<string> tempList = new List<string>();
        //将玩家的卡牌存储到临时集合
        tempList.AddRange(RoleManager.Instance.cardList);
        while (tempList.Count > 0)
        {
            //随机下标
            int tempIndex = Random.Range(0, tempList.Count);
            //添加到卡堆
            cardList.Add(tempList[tempIndex]);
            //临时集合删除
            tempList.RemoveAt(tempIndex);
        }
        Debug.Log(cardList.Count);
    }
}

卡牌战斗初始化FightInit,新增敌人生成,初始化战斗卡牌

//这里读取关卡三的敌人数据做测试,可以自由选择其他的
EnemyManeger.Instance.LoadRes("10003");

//初始化战斗卡牌
FightCardManager.Instance.Init();

运行效果,敌人就被加载出来了
在这里插入图片描述

十、玩家血量、能量、防御值、卡牌数

修改战斗管理器FightManager代码

public int MaxHp;//最大血量
public int CurHp;//当前血量
public int MaxPowerCount;//最大能量(卡牌使用会消耗能量)
public int CurPowerCount;//当前能量
public int DefenseCount;//防御值

public void Init()
{
    MaxHp = 10;
    CurHp = 10;
    MaxPowerCount = 10;
    CurPowerCount = 10;
    DefenseCount = 10;
}

卡牌战斗初始化FightInit脚本,调用初始化战斗数值

//初始化战斗数值
FightManager.Instance.Init();

完善战斗界面FightUI代码

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

//战斗界面
public class FightUI : UIBase
{
    private Text cardCountTxt;//卡牌数量
    private Text noCardCountTxt;//弃牌堆数量
    private Text powerTxt;
    private Text hpTxt;
    private Image hpImg;
    private Text fyTxt;//防御数值

    private void Awake()
    {
        cardCountTxt = transform.Find("hasCard/icon/Text").GetComponent<Text>();
        noCardCountTxt = transform.Find("noCard/icon/Text").GetComponent<Text>();
        powerTxt = transform.Find("mana/Text").GetComponent<Text>();
        hpTxt = transform.Find("hp/moneyTxt").GetComponent<Text>();
        hpImg = transform.Find("hp/fill").GetComponent<Image>();
        fyTxt = transform.Find("hp/fangyu/Text").GetComponent<Text>();
    }

    private void Start()
    {
        UpdateHp();
        UpdatePower();
        UpdateDefense();
        UpdateCardCount();
        UpdateUsedCardCount();
    }

    //更新血量显示
    public void UpdateHp()
    {
        hpTxt.text = FightManager.Instance.CurHp + "/" + FightManager.Instance.MaxHp;
        hpImg.fillAmount = (float)FightManager.Instance.CurHp / (float)FightManager.Instance.MaxHp;
    }

    //更新能量
    public void UpdatePower()
    {
        powerTxt.text = FightManager.Instance.CurPowerCount + "/" + FightManager.Instance.MaxPowerCount;
    }

    //防御更新
    public void UpdateDefense()
    {
        fyTxt.text = FightManager.Instance.DefenseCount.ToString();
    }
    //更新卡堆数量
    public void UpdateCardCount()
    {

        cardCountTxt.text = FightCardManager.Instance.cardList.Count.ToString();
    }
    //更新弃牌堆数量
    public void UpdateUsedCardCount()
    {

        noCardCountTxt.text = FightCardManager.Instance.usedCardList.Count.ToString();
    }
}

运行效果
在这里插入图片描述

十一、敌人血量 行动显示逻辑

UI管理器UIManager新增方法

//创建敌人头部的行动图标物体
public GameObject CreateActionIcon()
{
    GameObject obj = Instantiate(Resources.Load("UI/actionIcon"), canvasTf) as GameObject;
    obj.transform.SetAsFirstSibling();// 设置在父级的第一位
    return obj;
}

//创建敌人底部的血量物体
public GameObject CreateHpItem()
{
    GameObject obj = Instantiate(Resources.Load("UI/HpItem"), canvasTf) as GameObject;
    obj.transform.SetAsFirstSibling();// 设置在父级的第一位
    return obj;
}

完善敌人脚本Enemy

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

public enum ActionType
{
    None,
    Defend,//加防御
    Attack,//攻击
}

//敌人脚本
public class Enemy : MonoBehaviour
{
    protected Dictionary<string, string> data;//敌人数据表信息
    public ActionType type;

    public GameObject hpItemObj;
    public GameObject actionObj;

    //UI相关
    public Transform attackTf;
    public Transform defendTf;
    public Text defendTxt;
    public Text hpTxt;
    public Image hpImg;

    //数值相关
    public int Defend;
    public int Attack;
    public int MaxHp;
    public int CurHp;
    public void Init(Dictionary<string, string> data)
    {
        this.data = data;
    }

    void Start()
    {
        type = ActionType.None;
        hpItemObj = UIManager.Instance.CreateHpItem();
        actionObj = UIManager.Instance.CreateActionIcon();
        attackTf = actionObj.transform.Find("attack");
        defendTf = actionObj.transform.Find("defend");
        defendTxt = hpItemObj.transform.Find("fangyu/Text").GetComponent<Text>();
        hpTxt = hpItemObj.transform.Find("hpTxt").GetComponent<Text>();
        hpImg = hpItemObj.transform.Find("fill").GetComponent<Image>();
        // 设置血条行动力位置
        hpItemObj.transform.position = Camera.main.WorldToScreenPoint(transform.position + Vector3.down * 0.2f);
        actionObj.transform.position = Camera.main.WorldToScreenPoint(transform.Find("head").position);
        SetRandomAction();
        //初始化数值
        Attack = int.Parse(data["Attack"]);
        CurHp = int.Parse(data["Hp"]);
        MaxHp = CurHp;
        Defend = int.Parse(data["Defend"]);

        UpdateHp();
        UpdateDefend();
    }

    //随机一个行动
    void SetRandomAction()
    {
        int ran = Random.Range(1, 3);
        type = (ActionType)ran;
        switch (type)
        {
            case ActionType.None:
                break;
            case ActionType.Defend:
                attackTf.gameObject.SetActive(false);
                defendTf.gameObject.SetActive(true);
                break;
            case ActionType.Attack:
                attackTf.gameObject.SetActive(true);
                defendTf.gameObject.SetActive(false);
                break;
        }
    }

    //更新血量信息
    public void UpdateHp(){
        hpTxt.text = CurHp + "/" + MaxHp;
        hpImg.fillAmount = (float)CurHp / (float)MaxHp;
    }

    //更新防御信息
    public void UpdateDefend(){
        defendTxt.text = Defend.ToString();
    }
}

效果,为了让战斗效果更丰富,加了个背景
在这里插入图片描述

十二、UI提示效果实现

现在卡牌战斗初始化FightInit调用切换玩家回合功能

//切换到玩家回合
FightManager.Instance.ChangeType(FightType.Player);

UI管理器UIManager新增调用提示界面方法,引入DoTween插件,实现提示弹窗的动画效果,不会用DoTween的可以看我之前的另一篇文章:
【unity插件】 DoTween动画插件的安装和使用整合(最全)

//提示界面
public void ShowTip(string msg, Color color, System.Action callback = null)
{
    GameObject obj = Instantiate(Resources.Load("UI/Tips"), canvasTf) as GameObject;
    Text text = obj.transform.Find("bg/Text").GetComponent<Text>();
    text.color = color;
    text.text = msg;
    Tween scale1 = obj.transform.Find("bg").DOScaleY(1, 0.4f);
    Tween scale2 = obj.transform.Find("bg").DOScaleY(0, 0.4f);
    Sequence seq = DOTween.Sequence();
    seq.Append(scale1);
    seq.AppendInterval(0.5f);
    seq.Append(scale2);
    seq.AppendCallback(delegate ()
    {
        if (callback != null) callback();
    });
    MonoBehaviour.Destroy(obj, 2);
}

玩家回合Fight_PlayerTurn调用提示效果

UIManager.Instance.ShowTip("玩家回合", Color.green, delegate(){
    Debug.Log("抽卡");
});

效果
在这里插入图片描述

十三、卡牌生成

战斗卡牌管理器FightCardManager新增两个方法

//是否有卡
public bool HasCard()
{
    return cardList.Count > 0;
}
//抽卡
public string DrawCard()
{
    string id = cardList[cardList.Count - 1];
    cardList.RemoveAt(cardList.Count - 1);
    return id;
}

新增CardItem脚本

public class CardItem : MonoBehaviour
{
    public Dictionary<string, string> data;//卡牌信息

    public void Init(Dictionary<string, string> data)
    {
        this.data = data;
    }
}

战斗界面FightUI修改

//存储卡牌物体的合集
private List<CardItem> cardItemList;

private void Awake()
{
   cardItemList = new List<CardItem>();
}

//创建卡牌物体
public void CreateCardItem(int count)
{
    if (count > FightCardManager.Instance.cardList.Count)
    {
        count = FightCardManager.Instance.cardList.Count;
    }

    for (int i = 0; i < count; i++)
    {
        GameObject obj = Instantiate(Resources.Load("UI/CardItem"), transform) as GameObject;
        obj.GetComponent<RectTransform>().anchoredPosition = new Vector2(-1000, -700);
        var item = obj.AddComponent<CardItem>();
        string cardId = FightCardManager.Instance.DrawCard();
        Dictionary<string, string> data = GameConfigManager.Instance.GetCardById(cardId);
        item.Init(data);
        cardItemList.Add(item);
    }
}

//更新卡牌位置
public void UpdateCardItemPos()
{

    float offset = 800f / cardItemList.Count;
    Vector2 startPos = new Vector2(-cardItemList.Count / 2f * offset + offset * 0.5f, -500);
    for (int i = 0; i < cardItemList.Count; i++)
    {
        cardItemList[i].GetComponent<RectTransform>().DOAnchorPos(startPos, 0.5f);
        startPos.x = startPos.x + offset;
    }
}

UI管理器UIManager新增方法

//获取某个界面的脚本
public T GetUI<T>(string uiName) where T :  UIBase{
    UIBase ui = Find(uiName);
    if(ui != null){
        return ui.GetComponent<T>();
    }
    return null;
}

玩家回合调用抽卡方法

public override void Init() {
 	UIManager.Instance.ShowTip("玩家回合", Color.green, delegate(){
        //抽卡
        Debug.Log("抽卡");
        UIManager.Instance.GetUI<FightUI>("FightUI").CreateCardItem(4);//抽4张
        UIManager.Instance.GetUI<FightUI>("FightUI").UpdateCardItemPos();//更新卡牌位置
    });
}

运行效果
在这里插入图片描述

十四、卡片信息显示

CardItem新增开始方法,渲染卡牌信息

private void Start()
{
    transform.Find("bg").GetComponent<Image>().sprite = Resources.Load<Sprite>(data["BgIcon"]);
    transform.Find("bg/icon").GetComponent<Image>().sprite = Resources.Load<Sprite>(data["Icon"]);
    transform.Find("bg/msgTxt").GetComponent<Text>().text = string.Format(data["Des"], data["Arg0"]);
    transform.Find("bg/nameTxt").GetComponent<Text>().text = data["Name"];
    transform.Find("bg/useTxt").GetComponent<Text>().text = data["Expend"];
    transform.Find("bg/Text").GetComponent<Text>().text = GameConfigManager.Instance.GetCardTypeById(data["Type"])["Name"];
}

效果
在这里插入图片描述

十五、卡牌选中效果

CardItem 新增鼠标事件

public class CardItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
	//鼠标进入
    public void OnPointerEnter(PointerEventData eventData)
    {
        transform.DOScale(1.5f, 0.25f);
        index = transform.GetSiblingIndex();
        transform.SetAsLastSibling();
        transform.Find("bg").GetComponent<Image>().material.SetColor("_lineColor", Color.yellow);
        transform.Find("bg").GetComponent<Image>().material.SetFloat("_lineWidth",10);
    }

    //鼠标离开
    public void OnPointerExit(PointerEventData eventData)
    {
        transform.DOScale(1, 0.25f);
        transform.SetSiblingIndex(index);
        transform.Find("bg").GetComponent<Image>().material.SetColor("_lineColor", Color.black);
        transform.Find("bg").GetComponent<Image>().material.SetFloat("_lineWidth", 1);
    }
    
	private void Start()
    {
       //。。。

        //设置bg背景image的外边框材质
        transform.Find("bg").GetComponent<Image>().material = Instantiate(Resources.Load<Material>("Mats/outline"));
    }
}

运行效果
在这里插入图片描述

十六、卡牌拖拽

CardItem新增拖拽事件

public class CardItem : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
	Vector2 initPos;//拖拽开始时记录卡牌的位置
    //开始拖拽
    public virtual void OnBeginDrag(PointerEventData eventData)
    {
        initPos = transform.GetComponent<RectTransform>().anchoredPosition;
        //播放声音
        AudioManager.Instance.PlaEffect("Cards/draw");
    }

    //拖拽中
    public virtual void OnDrag(PointerEventData eventData)
    {
        Vector2 pos;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
            transform.parent.GetComponent<RectTransform>(),
            eventData.position,
            eventData.pressEventCamera,
            out pos
        ))
        {
            transform.GetComponent<RectTransform>().anchoredPosition = pos;
        }
    }

    //结束拖拽
    public virtual void OnEndDrag(PointerEventData eventData)
    {
        transform.GetComponent<RectTransform>().anchoredPosition = initPos;
        transform.SetSiblingIndex(index);
    }
}

运行效果
在这里插入图片描述

十七、卡片分支(攻击卡、抽卡、防御卡)

修改FightUI创建卡牌物体方法CreateCardItem

// var item = obj.AddComponent<CardItem>();
改为
CardItem item = obj.AddComponent(System.Type.GetType(data["Script"])) as CardItem;

新增各种卡牌脚本

# 无中生有卡(抽卡效果的卡片)
public class AddCard : CardItem {}

# 攻击卡
public class AttackCardItem : CardItem {}

# 防御卡(加护盾效果)
public class DefendCard : CardItem {}

十八、防御卡效果

战斗界面FightUI新增删除卡牌物体方法

//删除卡牌物体
public void RemoveCard(CardItem item)
{
    AudioManager.Instance.PlayEffect("Cards/cardShove");//移除音效
    item.enabled = false;//禁用卡牌逻辑
    //添加到弃牌集合
    FightCardManager.Instance.usedCardList.Add(item.data["Id"]);
    //更新使用后的卡牌数量
    noCardCountTxt.text = FightCardManager.Instance.usedCardList.Count.ToString();
    //从集合中删除
    cardItemList.Remove(item);
    //刷新卡牌位置
    UpdateCardItemPos();
    //卡牌移到弃牌堆效果
    item.GetComponent<RectTransform>().DOAnchorPos(new Vector2(1000, -700), 0.25f);
    item.transform.DOScale(0, 0.25f);
    Destroy(item.gameObject, 1);
}

CardItem新增使用卡牌方法

//尝试使用卡牌
public virtual bool TryUse()
{
    //卡牌需要的费用
    int cost = int.Parse(data["Expend"]);
    if (cost > FightManager.Instance.CurPowerCount)
    {
        //费用不足
        AudioManager.Instance.PlayEffect("Effect/lose");//使用失败音效
        //提示
        UIManager.Instance.ShowTip("费用不足", Color.red);
        return false;
    }
    else
    {
        //减少费用
        FightManager.Instance.CurPowerCount -= cost;
        //刷新费用文本
        UIManager.Instance.GetUI<FightUI>("FightUI").UpdatePower();
        //使用的卡牌删除
        UIManager.Instance.GetUI<FightUI>("FightUI").RemoveCard(this);
        return true;
    }
}

//创建卡牌使用后的特效
public void PlayEffect(Vector3 pos)
{
    GameObject effectobj = Instantiate(Resources.Load(data["Effects"])) as GameObject;
    effectobj.transform.position = pos;
    Destroy(effectobj, 2);
}

防御卡DefendCard调用

public class DefendCard : CardItem
{
    public override void OnEndDrag(PointerEventData eventData)
    {
        if (TryUse() == true)
        {
            //使用效果
            int val = int.Parse(data["Arg0"]);
            //播放使用后的声音(每张卡使用的声音可能不一样)
            AudioManager.Instance.PlayEffect("Effect/healspell");// 这个字段可以配置到表中

            //增加防御力
            FightManager.Instance.DefenseCount += val;
            // 刷新防御文本
            UIManager.Instance.GetUI<FightUI>("FightUI").UpdateDefense();
            Vector3 pos = Camera.main.transform.position;
            pos.y = 0;
            PlayEffect(pos);
        }else{
            base.OnEndDrag(eventData);
        }
    }
}

效果
在这里插入图片描述

十九、抽卡的卡牌效果

public class AddCard : CardItem
{
    public override void OnEndDrag(PointerEventData eventData)
    {
        if (TryUse() == true)
        {
            int val = int.Parse(data["Arg0"]);//抽卡数量
                                              //是否有卡抽
            if (FightCardManager.Instance.HasCard() == true)
            {
                UIManager.Instance.GetUI<FightUI>("FightUI").CreateCardItem(val);
                UIManager.Instance.GetUI<FightUI>("FightUI").UpdateCardItemPos();
                Vector3 pos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 2.5f));
                PlayEffect(pos);
            }
            else
            {
                base.OnEndDrag(eventData);
            }
        }
        else
        {
            base.OnEndDrag(eventData);
        }
    }
}

效果
在这里插入图片描述

二十、怪物选中效果

修改Enemy

SkinnedMeshRenderer _meshrenderer;
void Start()
{
    _meshrenderer = transform.GetComponentInChildren<SkinnedMeshRenderer>();
    
	//。。。
	
	//TODO:测试
    OnSelect();
}

//被攻击卡选中,显示红边
public void OnSelect(){
    _meshrenderer.material.SetColor("_OtlColor", Color.red);
}

//未选中
public void OnUnSelect(){
    _meshrenderer.material.SetColor("_OtlColor", Color.black);
}

效果
在这里插入图片描述

二十一、怪物受伤

敌人管理器EnemyManeger新增移除敌人方法

//移除敌人
public void DeleteEnemy(Enemy enemy){
    enemyList.Remove(enemy);

    //TODO:后续还要做击杀所有怪物的判断
}

敌人脚本Enemy新增受伤方法

public Animator ani;

void Start()
{
	ani = transform.GetComponent<Animator>();
}

//受伤
public void Hit(int val)
{
    //先扣护盾
    if (Defend > val)
    {
        //扣护盾
        Defend -= val;
        //播放受伤
        ani.Play("hit", 0, 0);
    }
    else
    {
        val = val - Defend;
        Defend = 0;
        CurHp -= val;
        if (CurHp <= 0)
        {
            CurHp = 0;
            // 播放死亡
            ani.Play("die");
            //敌人从列表中移除
            EnemyManeger.Instance.DeleteEnemy(this);
            Destroy(gameObject, 1);
            Destroy(actionObj);
            Destroy(hpItemObj);
        }else{
            //受伤
            ani.Play("hit", 0, 0);
        }
    }
    //刷新血量等ui
    UpdateDefend();
    UpdateHp();
}

二十二、攻击卡效果

AttackCardItem 脚本

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;

public class AttackCardItem : CardItem, IPointerDownHandler
{

    //按下
    public void OnPointerDown(PointerEventData eventData)
    {
        //播放声音
        AudioManager.Instance.PlayEffect("Cards/draw");
        //隐藏鼠标
        Cursor.visible = false;
        //关闭所有协同程序
        StopAllCoroutines();
        //启动鼠标操作协同程序
        StartCoroutine(OnMouseDownRight(eventData));

    }
    IEnumerator OnMouseDownRight(PointerEventData pData)
    {
        while (true)
        {
            //如果按下鼠标右键跳出循环
            if (Input.GetMouseButton(1)) break;
            Vector2 pos;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(
                transform.parent.GetComponent<RectTransform>(),
                pData.position,
                pData.pressEventCamera,
                out pos
            ))
            {
                // 进行射线检测是否碰到怪物
                CheckRayToEnemy();
            }

            yield return null;
        }
    }
    Enemy hitEnemy;//射线检测到的敌人脚本
    private void CheckRayToEnemy()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(ray, out hit, 10000, LayerMask.GetMask("Enemy")))
        {
            hitEnemy = hit.transform.GetComponent<Enemy>();
            hitEnemy.OnSelect();//选中
                                //如果按下鼠标左键使用攻击卡
            if (Input.GetMouseButtonDown(0))
            {
                //关闭所有协同程序
                StopAllCoroutines();
                //鼠标显示
                Cursor.visible = true;
                if (TryUse() == true)
                {
                    //播放特效
                    PlayEffect(hitEnemy.transform.position);
                    //打击音效
                    AudioManager.Instance.PlayEffect("Effect/sword");
                    //敌人受伤
                    int val = int.Parse(data["Arg0"]);
                    hitEnemy.Hit(val);
                }
                //敌人未选中
                hitEnemy.OnUnSelect();
                //设置敌人脚本null
                hitEnemy = null;
            }
        }
        else
        {
            //未射到怪物
            if (hitEnemy != null)
            {
                hitEnemy.OnUnSelect();
                hitEnemy = null;
            }

        }

    }
}

运行效果,记得把怪物的图层修改为Enemy
在这里插入图片描述

二十三、曲线效果

LineUI脚本

using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;

public class LineUI : UIBase
{
    //设置开始位置
    public void SetStartPos(Vector2 pos)
    {
        transform.GetChild(0).GetComponent<RectTransform>().anchoredPosition = pos;
    }

    //设置终点位置
    public void SetEndPos(Vector2 pos)
    {

        transform.GetChild(transform.childCount - 1).GetComponent<RectTransform>().anchoredPosition = pos;
        //开始点
        Vector3 startPos = transform.GetChild(0).GetComponent<RectTransform>().anchoredPosition;
        //终点
        Vector3 endPos = pos;
        //中点
        Vector3 midPos = Vector3.zero;
        midPos.y = (startPos.y + endPos.y) * 0.5f;
        midPos.x = startPos.x;
        //计算开始点跟终点的方向
        Vector3 dir = (endPos - startPos).normalized;
        float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;//弧度转角度
        //设置终点角度
        transform.GetChild(transform.childCount - 1).eulerAngles = new Vector3(0, 0, angle - 90);
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
            transform.GetChild(i).GetComponent<RectTransform>().anchoredPosition = GetBezier(startPos, midPos, endPos, i / (float)transform.childCount);
            if (i != transform.childCount - 1)
            {
                dir = (transform.GetChild(i + 1).GetComponent<RectTransform>().anchoredPosition - transform.GetChild(i).GetComponent<RectTransform>().anchoredPosition).normalized;
                angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
                transform.GetChild(i).eulerAngles = new Vector3(0, 0, angle - 90);
            }
        }
    }
    
    //贝塞尔曲线
    public Vector3 GetBezier(Vector3 start, Vector3 mid, Vector3 end, float t)
    {
        return (1f - t) * (1f - t) * start + 2.0f * t * (1.0f - t) * mid + t * t * end;
    }
}

AttackCardItem调用
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

效果
在这里插入图片描述

二十四、切换敌人回合

修改战斗界面FightUI代码,绑定结束事件和删除所有卡牌

private void Awake()
{
     //结束回合按钮,绑定点击事件
     transform.Find("turnBtn").GetComponent<Button>().onClick.AddListener(onChangeTurnBtn);
 }
 
//玩家回合结束,切换到敌人回合
private void onChangeTurnBtn()
{
    //只有玩家回合才能切换
    if (FightManager.Instance.fightUnit is Fight_PlayerTurn)  FightManager.Instance.ChangeType(FightType.Enemy);
}

//删除所有卡牌
public void RemoveAllCards()
{
    for (int i = cardItemList.Count - 1; i > 0; i--)
    {
        RemoveCard(cardItemList[i]);
    }
}

敌人回合Fight_EnemyTurn初始化调用删除所有卡牌和弹窗提示

public override void Init()
{
    //删除所有卡牌
    UIManager.Instance.GetUI<FightUI>("FightUI").RemoveAllCards();
    //显示敌人回合提示
    UIManager.Instance.ShowTip("敌人回合", Color.red, delegate ()
    {
        Debug.Log("执行敌人ai");
    });
}

效果
在这里插入图片描述

二十五、敌人行为逻辑

战斗管理器FightManager新增玩家受击方法

//玩家受击
public void GetPlayerHit(int hit)
{
    //扣护盾
    if (DefenseCount > hit)
    {
        DefenseCount -= hit;
    } else {
        hit = hit - DefenseCount;
        DefenseCount = 0;
        CurHp -= hit;
        if (CurHp <= 0)
        {
            CurHp = 0;
            //切换到游戏失败状态
            ChangeType(FightType.Fail);
        }

    }
    // 更新界面
    UIManager.Instance.GetUI<FightUI>("FightUI").UpdateHp();
    UIManager.Instance.GetUI<FightUI>("FightUI").UpdateDefense();
}

敌人脚本Enemy修改

//隐藏怪物头上的行动标志
public void HideAction()
{
    attackTf.gameObject.SetActive(false);
    defendTf.gameObject.SetActive(false);
}

//执行敌人行动
public IEnumerator DoAction()
{
    HideAction();
    //播放对应的动画(可以配置到excel表这里都默认播放攻击)
    ani.Play("attack");
    //等待某一时间的后执行对应的行为(也可以配置到excel表)
    yield return new WaitForSeconds(0.5f);//这里我写死了
    switch (type)
    {
        case ActionType.None:
            break;
        case ActionType.Defend:
            // 加防御
            Defend += 1;
            UpdateDefend();
            //可以播放对应的特效
            break;
        case ActionType.Attack:
            // 玩家扣血
            FightManager.Instance.GetPlayerHit(Attack);
            //摄像机可以抖一抖
            Camera.main.DOShakePosition(0.1f, 0.2f, 5, 45);
            break;
    }
    //等待动画播放完(这里的时长也可以配置)
    yield return new WaitForSeconds(1);
    //播放待机
    ani.Play("idle");
}

EnemyManeger新增执行活着的怪物的行为

//执行活着的怪物的行为
public IEnumerator DoAllEnemyAction()
{
    for (int i = 0; i < enemyList.Count; i++)
    {
        yield return FightManager.Instance.StartCoroutine(enemyList[i].DoAction());
    }
    // 行动完后更新所有敌人行为
    for (int i = 0; i < enemyList.Count; i++)
    {
        enemyList[i].SetRandomAction();

    }
    // 切换到玩家回合
    FightManager.Instance.ChangeType(FightType.Player);
}

修改Fight_EnemyTurn调用活着的怪物的行为

public override void Init()
{
     //删除所有卡牌
     UIManager.Instance.GetUI<FightUI>("FightUI").RemoveAllCards();
     //显示敌人回合提示
     UIManager.Instance.ShowTip("敌人回合", Color.red, delegate ()
     {
         FightManager.Instance.StartCoroutine(EnemyManeger.Instance.DoAllEnemyAction());
     });
 }

效果
在这里插入图片描述

二十六、游戏胜利or结束逻辑

完善玩家回合Fight_PlayerTurn代码

public override void Init()
{
    UIManager.Instance.ShowTip("玩家回合", Color.green, delegate ()
    {
        //回复行动力
        FightManager.Instance.CurPowerCount = 3;
        UIManager.Instance.GetUI<FightUI>("FightUI").UpdatePower();
        //卡堆己经没有卡重新初始化
        if (FightCardManager.Instance.HasCard() == false)
        {

            FightCardManager.Instance.Init();
            //更新弃卡堆数量
            UIManager.Instance.GetUI<FightUI>("FightUI").UpdateUsedCardCount();
        }
        //抽卡
        Debug.Log("抽卡");
        UIManager.Instance.GetUI<FightUI>("FightUI").CreateCardItem(4);//抽4张
        UIManager.Instance.GetUI<FightUI>("FightUI").UpdateCardItemPos();//更新卡牌位置

        //更新卡牌数
        UIManager.Instance.GetUI<FightUI>("FightUI").UpdateCardCount();
    });
}

游戏失败Fight_Fail代码

public override void Init()
{
    Debug.Log("失败了");
    FightManager.Instance.StopAllCoroutines();
    //显失败界面石到这里的小伙伴可以自已作
}

游戏胜利Fight_Win代码

public override void Init() {
   Debug.Log("游戏胜利");
    //何以显示结算界面预制体有了能看到这里的小伙伴应该可以自己补上了
}

敌人管理器EnemyManeger击杀所有怪物的判断

//移除敌人
public void DeleteEnemy(Enemy enemy)
{
    enemyList.Remove(enemy);

    //击杀所有怪物的判断
    if (enemyList.Count == 0)
    {
        FightManager.Instance.ChangeType(FightType.Win);
    }
}

效果
在这里插入图片描述

最终效果

在这里插入图片描述

源码

要啥源码,给我好好看,好好学

参考

【视频】https://www.bilibili.com/video/BV1eF41177hu/

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,于是最近才开始自习unity。如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我可能也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/960066.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

缓存技术(缓存穿透,缓存雪崩,缓存击穿)

大家好 , 我是苏麟 , 今天聊一聊缓存 . 这里需要一些Redis基础 (可以看相关文章等) 本文章资料来自于 : 黑马程序员 如果想要了解更详细的资料去黑马官网查看 前言:什么是缓存? 缓存,就是数据交换的 缓冲区 (称作Cache [ kʃ ] ),俗称的缓存就是缓冲区内的数据,是存贮数据的…

20230901工作心得:IDEA列操作lambda表达式加强版用法

今天是中小学开学时间&#xff0c;亦是9月的开始&#xff0c;继续努力。 今日收获较大的有四个地方&#xff0c;先说这四点。 1、IDEA列操作 使用场景&#xff1a;需要批量将Excel表格里的数据插入到数据库中&#xff0c;此时需要写大量的insert SQL语句。 比如像这样的&am…

MySQL - 函数

1.1 什么是函数&#xff1f; 要想实现上面的这些效果&#xff0c;就得借助于MySQL当中的内置函数。 函数&#xff1a;是指一段可以直接被另一段程序调用的程序或代码。 MySQL当中内置了很多的函数&#xff0c;根据其操作的数据类型&#xff0c;分为以下四类&#xff1a; 字符…

vue之若依字典的导入使用(不直接使用若依框架,只使用若依字典)

vue之若依字典的导入使用 若依官网&#xff1a;http://ruoyi.vip 若依演示地址&#xff1a;http://vue.ruoyi.vip 若依代码下载&#xff1a;https://gitee.com/y_project/RuoYi-Vue 以上地址是若依的相关地址&#xff0c;可以下载前后端分离版的若依前后端代码。 如果你不想直…

【附安装包】Multisim 14.0安装教程|电子电路

软件下载 软件&#xff1a;Multisim版本&#xff1a;14.0语言&#xff1a;简体中文大小&#xff1a;649.68M安装环境&#xff1a;Win11/Win10/Win8/Win7硬件要求&#xff1a;CPU2.5GHz 内存4G(或更高&#xff09;下载通道①百度网盘丨64位下载链接&#xff1a;https://pan.bai…

深度学习基础篇 第二章: 转置卷积

参考教程&#xff1a; https://arxiv.org/pdf/1603.07285.pdf 文章目录 什么是转置卷积转置卷积的思想一维形式的理解二维形式的理解卷积和转置的关系no pading, unit stridespadding, unit stridesno padding, non-unit stridepadding&#xff0c;non-unit stride pytorch中的…

ububtu部署bind-dns 问题

启动(重启)命令如下&#xff1a; sudo systemctl restart bind9查看状态命令如下&#xff1a; sudo systemctl status bind9停止命令如下&#xff1a; sudo systemctl stop bind9#配置自己的域 要修改一下路径的文件(named.conf.local)&#xff1a; 在这个文件named.conf.loca…

java从入门到起飞(六)——用Socket实现网络通信

文章目录 背景网络编程网络编程三要素 2.DatagramSocket之UDP通信程序2.1 UDP发送数据2.2UDP接收数据2.3 3. Socket之TCP通信程序3.1TCP发送数据3.2TCP接收数据 背景 网络编程 ● 计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线…

开始MySQL之路——MySQL 函数(详细讲解)

MySQL 函数 在MySQL中&#xff0c;为了提高代码重用性和隐藏实现细节&#xff0c;MySQL提高了很多函数。函数可以理解为别人封装好的模块代码。 在MySQL中&#xff0c;函数非常多&#xff0c;主要可以分为以下大类&#xff1a; 聚合函数&#xff1a;这类函数就是用来做简单的…

[深度学习]1. 深度学习知识点汇总

本文记录了我在学习深度学习的过程中遇到过的不懂的知识点&#xff0c;为了方便翻阅&#xff0c;故将其发表于此&#xff0c;随时更新&#xff0c;供大家参考。 深度学习常见知识点 1. 测试精度和训练精度 在深度学习中&#xff0c;测试精度和训练精度是两个重要的指标&#…

408考研-数据结构算法-双链表

双向链表的存储方式 双向链表跟链表差不多&#xff0c;只是它每个结点都含有两个链——一个指向下一结点&#xff0c;另一个指向前一结点。此外&#xff0c;它还能直接访问第一个和最后一个结点。 单链表只能向后操作&#xff0c;不可以向前操作。为了向前、向后操作方便&…

表白墙程序

目录 一、页面代码部分 二、设计程序 二、实现 doPost​编辑 三、实现 doGet 四、前端代码部分 五、使用数据库存储数据 一、页面代码部分 在之前的一篇博客中&#xff0c;已经写过了表白墙的页面代码实现&#xff0c;这里就不再重复了 页面代码如下&#xff1a; <!…

【Vue2】 axios库

网络请求库-axios库 认识Axios库为什么选择Axios库安装Axios axios发送请求常见的配置选项简单请求可以给Axios设置公共的基础配置发送多个请求 axios创建实例为什么要创建axios的实例 axios的拦截器请求拦截器响应拦截器 axios请求封装 认识Axios库 为什么选择Axios库 在游览…

git学习笔记 | 版本管理 - 分支管理

文章目录 git学习笔记Git是什么仓库常见的命令commit 备注规范在文件下设置git忽略文件 .gitignore 版本管理git log | git reflog 查看提交日志/历史版本版本穿梭 git resetgit reset HEAD <file> git checkout -- fileName 丢弃工作区的修改git revertGit恢复之前版本的…

海格里斯HEGERLS高密度料箱式四向穿梭车存储系统有哪些显著优势?

近些年仓储货架向着自动化、智能化发展&#xff0c;因此市面上出现很多不同类型的智能自动化仓储货架。其中&#xff0c;最受企业青睐的便是四向穿梭车货架。四向穿梭车货架根据其载重不同可分为托盘式和料箱式两大类。这两种不同类型的四向穿梭车货架在结构形式和控制方式上基…

Ubuntu20.04下安装google输入法

Ubuntu20.04下安装google输入法 1、添加中文语言支持 打开 系统设置——区域和语言——管理已安装的语言——在“语言”tab下——点击“添加或删除语言” 弹出“已安装语言”窗口&#xff0c;勾选中文&#xff08;简体&#xff09;&#xff0c;点击应用 回到“语言支持”窗…

日本橙皮书数据库—《医疗用医药品质量情报集》

日本橙皮书是一份关于医疗用医药品质量情报的汇总报告&#xff0c;由日本厚生劳动省发布。它主要涵盖了药品的品质再评价信息&#xff0c;特别是针对特定历史阶段的产品&#xff0c;笔者总结信息如下&#xff1a; ①日本橙皮书数据库包含了一系列药品的详细信息&#xff0c;如…

一句话画出动漫效果

链接&#xff1a; AI Comic Factory - a Hugging Face Space by jbilcke-hfDiscover amazing ML apps made by the communityhttps://huggingface.co/spaces/jbilcke-hf/ai-comic-factory 选择类型&#xff1a; Japanese 输入提示词&#xff1a; beauty and school love st…

视频云存储/安防监控/视频AI智能分析网关V3:消防器材缺失检测功能详解

在我们的日常生活中&#xff0c;消防器材是必不可少的&#xff0c;无论是在学校、园区还是工厂里&#xff0c;都需要摆放消防器材&#xff0c;做好防范措施&#xff0c;降低安全隐患。但有时也需防备消防器材被人为挪动甚至破坏&#xff0c;在危急时刻无法发挥作用。TSINGSEE青…