20.开火点逻辑

  1. 20.游戏场景-游戏主逻辑-开火点相关-逻辑处理
    1. 20.1 知识点
      1. 创建开火点对象FireObject脚本继承MonoBehaviour。创建八个空对象作为八宫格的开火点对象。开火点对象的位置应该是要实时更新的,因为分辨率自适应可能造成位置的不同。创建一个表示开火点位置的类型的枚举E_Pos_Type,它有八个值,分别表示在屏幕上的左上、上、右上、左、右、左下、下和右下。在FireObject脚本关联一个开火点位置枚举。并且在场景中创建空对象。
      2. 在 Update() 函数中,首先要更新开火点的位置,目的是分辨率自适应。所以创建更新开火点的位置发明方法UpdatePos。在 UpdatePos() 函数中,根据开火点的类型来更新它在屏幕上的位置,并把屏幕坐标转换为世界坐标。我们应该先打印出玩家对象世界坐标转屏幕坐标的位置。看看玩家坐标所在位置(0,0,0)在屏幕坐标的z轴是多少。这样可以得到摄像机在y=0的屏幕的横截面转换成拼屏幕坐标的z轴的值,比如示例中的351。设置一个三维向量变量作为屏幕位置。在 UpdatePos() 函数中一开始就设置这个屏幕位置的z轴为351。根据所在的开火点位置不同设置好屏幕位置的值。最后把屏幕位置转成世界坐标设置到这个脚本挂载的对象上。我们可以创建一个初始向量用于开火初始方向,可以辅助不同开火组的泛型,根据不同的开火点类型赋值不同的向量。
      3. 创建一些字段帮助我们写当前要发射的开火组的方法。fireInfo 表示当前开火点的数据信息;nowNum 表示当前开火点剩余的子弹数量;nowCD 表示当前开火点的冷却时间;nowDelay 表示当前开火点的延迟时间;nowBulletInfo 表示当前开火点使用的子弹信息;changeAngle 表示散弹时每颗子弹的间隔角度;nowDir 表示用于发射散弹时记录上一次方向。创建ResetFireInfo重置当前要发射的炮台数据方法。定义一个规则,只有当 nowCD 和 nowNum 都为 0 时才认为需要重新获取发射点数据。如果 nowCD 和 nowNum 都不为 0,则直接返回。重置函数会判断组间休息时间。如果 fireInfo 不为 null,则将 nowDelay 减去 Time.deltaTime,减去组间休息的时间。如果 nowDelay 大于 0,则表示还在组间休息中,直接返回。重置函数会从开火数据列表中随机取出一条来按照规则发射炮弹。它首先获取游戏数据管理器中的开火点数据列表,然后从列表中随机选择一个开火点数据,并将其赋值给 fireInfo 变量。获得了随机的开火单条数据。由于不能直接改变数据中的内容,所以函数会使用一开始声明的成员变量来存储数据。它将 fireInfo 中的 num、cd 和 delay 分别赋值给 nowNum、nowCD 和 nowDelay 变量。重置函数会通过发火点数据取出当前要使用的子弹数据信息。它首先将 fireInfo.ids 按照 , 分割成字符串数组,然后分别解析出开始 ID 和结束 ID。然后使用 Random.Range() 函数在开始 ID 和结束 ID 之间随机选择一个子弹 ID,并根据该 ID 从游戏数据管理器中获取子弹信息,赋值给 nowBulletInfo 变量。如果当前开火点类型为散弹,则需要计算每颗子弹的间隔角度。函数会根据开火点位置不同来设置不同的间隔角度。重置函数会在Update中每帧调用。
      4. 创建检测开火UpdateFire函数。用于检测开火。首先,开火函数会判断当前状态是否需要发射子弹。如果 nowCD 和 nowNum 都为 0,则表示不需要发射子弹,直接返回。接下来,开火函数会更新 nowCD 的值。如果 nowCD 大于 0,则表示还在冷却中,直接返回。然后,开火函数会根据开火点数据中定义的类型来发射子弹。如果类型为 1,则表示一个一个地发射子弹朝向玩家。开火函数会动态创建子弹对象,并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowNum 减 1,并重置 nowCD 的值。如果类型为 2,则表示发射散弹。如果 nowCD 为 0,则表示无 CD,一瞬间发射所有的散弹。函数会循环 nowNum 次,每次都动态创建子弹对象并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowCD 和 nowNum 都重置为 0。如果 nowCD 不为 0,则表示每次都会旋转一个角度得到一个新的方向来发射一颗子弹。函数会动态创建子弹对象并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowNum 减 1,并重置 nowCD 的值根据当前nowNum是不是为01重置。这个函数最后再Update中调用。
      5. 在场景中创建一个Main空对象,创建一个Main脚本并挂载。这个脚本是用于动态创建选择好的角色的。
      6. 给各个飞机预制体添加碰撞器后,发现在开始场景中被弹开了。因为开始场景飞机的父物体也有碰撞器,可以把父物体碰撞器移除了。
    2. 20.2 知识点代码
      1. FireObject
      2. Main

20.游戏场景-游戏主逻辑-开火点相关-逻辑处理


20.1 知识点

创建开火点对象FireObject脚本继承MonoBehaviour。创建八个空对象作为八宫格的开火点对象。开火点对象的位置应该是要实时更新的,因为分辨率自适应可能造成位置的不同。创建一个表示开火点位置的类型的枚举E_Pos_Type,它有八个值,分别表示在屏幕上的左上、上、右上、左、右、左下、下和右下。在FireObject脚本关联一个开火点位置枚举。并且在场景中创建空对象。

/// <summary>
/// 表示开火点位置的类型
/// </summary>
public enum E_Pos_Type
{
    TopLeft,
    Top,
    TopRight,
    Left,
    Right,
    BottomLeft,
    Bottom,
    BottomRight,
}

public class FireObject : MonoBehaviour
{
    public E_Pos_Type type;
}

在 Update() 函数中,首先要更新开火点的位置,目的是分辨率自适应。所以创建更新开火点的位置发明方法UpdatePos。在 UpdatePos() 函数中,根据开火点的类型来更新它在屏幕上的位置,并把屏幕坐标转换为世界坐标。我们应该先打印出玩家对象世界坐标转屏幕坐标的位置。看看玩家坐标所在位置(0,0,0)在屏幕坐标的z轴是多少。这样可以得到摄像机在y=0的屏幕的横截面转换成拼屏幕坐标的z轴的值,比如示例中的351。设置一个三维向量变量作为屏幕位置。在 UpdatePos() 函数中一开始就设置这个屏幕位置的z轴为351。根据所在的开火点位置不同设置好屏幕位置的值。最后把屏幕位置转成世界坐标设置到这个脚本挂载的对象上。我们可以创建一个初始向量用于开火初始方向,可以辅助不同开火组的泛型,根据不同的开火点类型赋值不同的向量。

public class FireObject : MonoBehaviour
{
    // 开火点的类型
    public E_Pos_Type type;

    // 表示屏幕上的点
    private Vector3 screenPos; // 表示屏幕中的二维坐标
                               // 初始发射子弹的方向,主要用于作为散弹的初始方向用于计算
    private Vector3 initDir;   // 表示炮管的方向
                               // 这个是用于发射散弹时,记录上一次的方向
    private Vector3 nowDir;    // 表示当前发射子弹的方向
    
    void Update()
    {
        //用于测试得到 玩家转屏幕坐标后 横截面的 z轴值 要注释
        //print(Camera.main.WorldToScreenPoint(PlayerObject.Instance.transform.position));
        //更新位置
        UpdatePos();
    }

    // 根据点的类型来更新它的位置
    private void UpdatePos()
    {
        // 这里设置为和主玩家位置转屏幕坐标后的Z位置一样,目的是让点和玩家所在的横截面是一致的
        screenPos.z = 351;
        switch (type)
        {
            case E_Pos_Type.TopLeft:
                screenPos.x = 0;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Top:
                screenPos.x = Screen.width / 2;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.TopRight:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
            case E_Pos_Type.Left:
                screenPos.x = 0;
                screenPos.y = Screen.height / 2;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Right:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height / 2;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
            case E_Pos_Type.BottomLeft:
                screenPos.x = 0;
                screenPos.y = 0;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Bottom:
                screenPos.x = Screen.width / 2;
                screenPos.y = 0;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.BottomRight:
                screenPos.x = Screen.width;
                screenPos.y = 0;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
        }
        // 把屏幕点转成我们的世界坐标点,得到我们想要的坐标
        this.transform.position = Camera.main.ScreenToWorldPoint(screenPos);
    }
}

创建一些字段帮助我们写当前要发射的开火组的方法。fireInfo 表示当前开火点的数据信息;nowNum 表示当前开火点剩余的子弹数量;nowCD 表示当前开火点的冷却时间;nowDelay 表示当前开火点的延迟时间;nowBulletInfo 表示当前开火点使用的子弹信息;changeAngle 表示散弹时每颗子弹的间隔角度;nowDir 表示用于发射散弹时记录上一次方向。创建ResetFireInfo重置当前要发射的炮台数据方法。定义一个规则,只有当 nowCD 和 nowNum 都为 0 时才认为需要重新获取发射点数据。如果 nowCD 和 nowNum 都不为 0,则直接返回。重置函数会判断组间休息时间。如果 fireInfo 不为 null,则将 nowDelay 减去 Time.deltaTime,减去组间休息的时间。如果 nowDelay 大于 0,则表示还在组间休息中,直接返回。重置函数会从开火数据列表中随机取出一条来按照规则发射炮弹。它首先获取游戏数据管理器中的开火点数据列表,然后从列表中随机选择一个开火点数据,并将其赋值给 fireInfo 变量。获得了随机的开火单条数据。由于不能直接改变数据中的内容,所以函数会使用一开始声明的成员变量来存储数据。它将 fireInfo 中的 num、cd 和 delay 分别赋值给 nowNum、nowCD 和 nowDelay 变量。重置函数会通过发火点数据取出当前要使用的子弹数据信息。它首先将 fireInfo.ids 按照 , 分割成字符串数组,然后分别解析出开始 ID 和结束 ID。然后使用 Random.Range() 函数在开始 ID 和结束 ID 之间随机选择一个子弹 ID,并根据该 ID 从游戏数据管理器中获取子弹信息,赋值给 nowBulletInfo 变量。如果当前开火点类型为散弹,则需要计算每颗子弹的间隔角度。函数会根据开火点位置不同来设置不同的间隔角度。重置函数会在Update中每帧调用。


//当前开火点的数据信息
private FireInfo fireInfo;
private int nowNum;
private float nowCD;
private float nowDelay;
//当前组开火点 使用的子弹信息
private BulletInfo nowBulletInfo;
//散弹时 每颗子弹的间隔角度
private float changeAngle;

//表示屏幕上的点
private Vector3 screenPos;
//初始发射子弹的方向 主要用于作为散弹的初始方向 用于计算
private Vector3 initDir;
//这个是用于发射散弹时 记录上一次的方向的
private Vector3 nowDir;

void Update()
{

    //每一次 都检测 是否需要 重置 开火点 数据
    ResetFireInfo();
    
}

// 重置当前要发射的炮台数据
private void ResetFireInfo()
{
    // 自己定一个规则,只有当 CD 和 Num 都为 0 时才认为需要重新获取我们的发射点数据
    if (nowCD != 0 && nowNum != 0)
        return;

    // 组间休息时间判断
    if (fireInfo != null)
    {
        nowDelay -= Time.deltaTime;
        // 如果还在组间休息中
        if (nowDelay > 0)
            return;
    }

    // 从数据中随机取出一条来按照规则发射炮弹
    List<FireInfo> list = GameDataMgr.Instance.fireData.fireInfoList;
    fireInfo = list[Random.Range(0, list.Count)];

    // 不能直接改变数据中的内容,应该拿变量临时存储下来,不会影响数据本身
    nowNum = fireInfo.num;      // 当前炮塔能够发射的子弹数量
    nowCD = fireInfo.cd;        // 当前炮塔发射每颗子弹的时间间隔
    nowDelay = fireInfo.delay;  // 当前炮塔和同组炮塔的时间间隔

    // 通过发火点数据取出当前要使用的子弹数据信息
    // 得到开始 ID 和结束 ID,用于随机取子弹的信息
    string[] strs = fireInfo.ids.Split(',');
    int beginID = int.Parse(strs[0]);       // 当前规则炮弹 ID 的开始值
    int endID = int.Parse(strs[1]);         // 当前规则炮弹 ID 的结束值
    int randomBulletID = Random.Range(beginID, endID + 1);    // 随机获取该规则下的一种子弹 ID
    nowBulletInfo = GameDataMgr.Instance.bulletData.bulletInfoList[randomBulletID - 1];//得到当前子弹信息

    // 如果是散弹就需要计算我们的间隔角度
    if (fireInfo.type == 2)
    {
        switch (type)
        {
            //这四个点是旋转90度发散弹
            case E_Pos_Type.TopLeft:
            case E_Pos_Type.TopRight:
            case E_Pos_Type.BottomLeft:
            case E_Pos_Type.BottomRight:
                changeAngle = 90f / (nowNum + 1);
                break;

            //这四个点是旋转180度发散弹
            case E_Pos_Type.Top:
            case E_Pos_Type.Left:
            case E_Pos_Type.Right:
            case E_Pos_Type.Bottom:
                changeAngle = 180f / (nowNum + 1);
                break;
        }
    }
}

创建检测开火UpdateFire函数。用于检测开火。首先,开火函数会判断当前状态是否需要发射子弹。如果 nowCD 和 nowNum 都为 0,则表示不需要发射子弹,直接返回。接下来,开火函数会更新 nowCD 的值。如果 nowCD 大于 0,则表示还在冷却中,直接返回。然后,开火函数会根据开火点数据中定义的类型来发射子弹。如果类型为 1,则表示一个一个地发射子弹朝向玩家。开火函数会动态创建子弹对象,并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowNum 减 1,并重置 nowCD 的值。如果类型为 2,则表示发射散弹。如果 nowCD 为 0,则表示无 CD,一瞬间发射所有的散弹。函数会循环 nowNum 次,每次都动态创建子弹对象并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowCD 和 nowNum 都重置为 0。如果 nowCD 不为 0,则表示每次都会旋转一个角度得到一个新的方向来发射一颗子弹。函数会动态创建子弹对象并动态添加子弹脚本。然后将当前的子弹数据传入子弹脚本进行初始化,并设置子弹的位置和朝向。最后,将 nowNum 减 1,并重置 nowCD 的值根据当前nowNum是不是为01重置。这个函数最后再Update中调用。


void Update()
{

    //发射子弹
    UpdateFire();
    
}

// 检测开火
private void UpdateFire()
{
    // 如果当前状态不需要发射子弹,则直接返回
    if (nowCD == 0 && nowNum == 0)
        return;

    // 更新 CD 值
    nowCD -= Time.deltaTime;
    if (nowCD > 0)
        return;

    GameObject bullet;
    BulletObject bulletObj;

    switch (fireInfo.type)
    {
        // 一个一个地发射子弹,朝向玩家
        case 1:
            // 动态创建子弹对象
            bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
            // 动态添加子弹脚本
            bulletObj = bullet.AddComponent<BulletObject>();
            // 把当前的子弹数据传入子弹脚本进行初始化
            bulletObj.InitInfo(nowBulletInfo);

            // 设置子弹的位置和朝向
            bullet.transform.position = this.transform.position;
            bullet.transform.rotation = Quaternion.LookRotation(PlayerObject.Instance.transform.position - this.transform.position);

            // 发射一颗子弹
            --nowNum;
            // 重置 CD 值
            nowCD = nowNum == 0 ? 0 : fireInfo.cd;
            break;

        // 发射散弹
        case 2:
            // 如果无 CD,则一瞬间发射所有散弹
            if (nowCD == 0)
            {
                for (int i = 0; i < nowNum; i++)
                {
                    // 动态创建子弹对象
                    bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                    // 动态添加子弹脚本
                    bulletObj = bullet.AddComponent<BulletObject>();
                    // 把当前的子弹数据传入子弹脚本进行初始化
                    bulletObj.InitInfo(nowBulletInfo);

                    // 设置子弹的位置和朝向
                    bullet.transform.position = this.transform.position;
                    // 每次都会旋转一个角度得到一个新的方向
                    nowDir = Quaternion.AngleAxis(changeAngle * i, Vector3.up) * initDir;
                    bullet.transform.rotation = Quaternion.LookRotation(nowDir);
                }
                // 因为是瞬间创建完所有子弹,所以重置数据
                nowCD = nowNum = 0;
            }
            //有CD一颗一颗的发
            else
            {
                // 动态创建子弹对象
                bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                // 动态添加子弹脚本
                bulletObj = bullet.AddComponent<BulletObject>();
                // 把当前的子弹数据传入子弹脚本进行初始化
                bulletObj.InitInfo(nowBulletInfo);

                // 设置子弹的位置和朝向
                bullet.transform.position = this.transform.position;
                // 每次都会旋转一个角度得到一个新的方向
                nowDir = Quaternion.AngleAxis(changeAngle * (fireInfo.num - nowNum), Vector3.up) * initDir;
                bullet.transform.rotation = Quaternion.LookRotation(nowDir);

                // 发射一颗子弹
                --nowNum;
                // 重置 CD 值 
                nowCD = nowNum == 0 ? 0 : fireInfo.cd;
            }
            break;
    }
}

在场景中创建一个Main空对象,创建一个Main脚本并挂载。这个脚本是用于动态创建选择好的角色的。

public class Main : MonoBehaviour
{
    void Start()
    {
        //根据开始场景中选择的英雄 来动态创建 飞机
        RoleInfo info = GameDataMgr.Instance.GetNowSelHeroInfo();

        //根据数据中的 资源路劲 进行动态创建
        GameObject obj = Instantiate(Resources.Load<GameObject>(info.resName));

        //动态添加脚本
        PlayerObject playerObj = obj.AddComponent<PlayerObject>();

        //根据信息设置值
        playerObj.speed = info.speed * 20;
        playerObj.maxHp = 10;
        playerObj.nowHp = info.hp;
        playerObj.roundSpeed = 20;

        //更新界面上显示的血量
        GamePanel.Instance.ChangeHp(info.hp);
    }
}

给各个飞机预制体添加碰撞器后,发现在开始场景中被弹开了。因为开始场景飞机的父物体也有碰撞器,可以把父物体碰撞器移除了。


20.2 知识点代码

FireObject

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

/// <summary>
/// 表示开火点位置的类型
/// </summary>
public enum E_Pos_Type
{
    TopLeft,
    Top,
    TopRight,

    Left,
    Right,

    BottomLeft,
    Bottom,
    BottomRight,
}

public class FireObject : MonoBehaviour
{
    // 开火点的类型
    public E_Pos_Type type;

    // 当前开火点的数据信息
    private FireInfo fireInfo;  // 当前炮塔的炮弹发射规则
    private int nowNum;         // 当前炮塔还能发射多少颗炮弹
    private float nowCD;        // 当前炮塔还需要等待多长时间才能再次发射炮弹
    private float nowDelay;     // 当前炮塔和同组炮塔之间的时间间隔

    // 当前组开火点使用的子弹信息
    private BulletInfo nowBulletInfo;   // 当前使用的子弹信息

    // 散弹时每颗子弹的间隔角度
    private float changeAngle;  // 散弹模式下炮塔发射子弹的角度变化

    // 表示屏幕上的点
    private Vector3 screenPos; // 表示屏幕中的二维坐标
                               // 初始发射子弹的方向,主要用于作为散弹的初始方向用于计算
    private Vector3 initDir;   // 表示炮管的方向
                               // 这个是用于发射散弹时,记录上一次的方向
    private Vector3 nowDir;    // 表示当前发射子弹的方向
    
    void Update()
    {
        //用于测试得到 玩家转屏幕坐标后 横截面的 z轴值 要注释
        //print(Camera.main.WorldToScreenPoint(PlayerObject.Instance.transform.position));
        //更新位置
        UpdatePos();

        //每一次 都检测 是否需要 重置 开火点 数据
        ResetFireInfo();

        //发射子弹
        UpdateFire();
    }

    // 根据点的类型来更新它的位置
    private void UpdatePos()
    {
        // 这里设置为和主玩家位置转屏幕坐标后的Z位置一样,目的是让点和玩家所在的横截面是一致的
        screenPos.z = 351;
        switch (type)
        {
            case E_Pos_Type.TopLeft:
                screenPos.x = 0;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Top:
                screenPos.x = Screen.width / 2;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.TopRight:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
            case E_Pos_Type.Left:
                screenPos.x = 0;
                screenPos.y = Screen.height / 2;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Right:
                screenPos.x = Screen.width;
                screenPos.y = Screen.height / 2;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
            case E_Pos_Type.BottomLeft:
                screenPos.x = 0;
                screenPos.y = 0;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.Bottom:
                screenPos.x = Screen.width / 2;
                screenPos.y = 0;

                // 初始化炮管方向为向右
                initDir = Vector3.right;
                break;
            case E_Pos_Type.BottomRight:
                screenPos.x = Screen.width;
                screenPos.y = 0;

                // 初始化炮管方向为向左
                initDir = Vector3.left;
                break;
        }
        // 把屏幕点转成我们的世界坐标点,得到我们想要的坐标
        this.transform.position = Camera.main.ScreenToWorldPoint(screenPos);
    }

    // 重置当前要发射的炮台数据
    private void ResetFireInfo()
    {
        // 自己定一个规则,只有当 CD 和 Num 都为 0 时才认为需要重新获取我们的发射点数据
        if (nowCD != 0 && nowNum != 0)
            return;

        // 组间休息时间判断
        if (fireInfo != null)
        {
            nowDelay -= Time.deltaTime;
            // 如果还在组间休息中
            if (nowDelay > 0)
                return;
        }

        // 从数据中随机取出一条来按照规则发射炮弹
        List<FireInfo> list = GameDataMgr.Instance.fireData.fireInfoList;
        fireInfo = list[Random.Range(0, list.Count)];

        // 不能直接改变数据中的内容,应该拿变量临时存储下来,不会影响数据本身
        nowNum = fireInfo.num;      // 当前炮塔能够发射的子弹数量
        nowCD = fireInfo.cd;        // 当前炮塔发射每颗子弹的时间间隔
        nowDelay = fireInfo.delay;  // 当前炮塔和同组炮塔的时间间隔

        // 通过发火点数据取出当前要使用的子弹数据信息
        // 得到开始 ID 和结束 ID,用于随机取子弹的信息
        string[] strs = fireInfo.ids.Split(',');
        int beginID = int.Parse(strs[0]);       // 当前规则炮弹 ID 的开始值
        int endID = int.Parse(strs[1]);         // 当前规则炮弹 ID 的结束值
        int randomBulletID = Random.Range(beginID, endID + 1);    // 随机获取该规则下的一种子弹 ID
        nowBulletInfo = GameDataMgr.Instance.bulletData.bulletInfoList[randomBulletID - 1];//得到当前子弹信息

        // 如果是散弹就需要计算我们的间隔角度
        if (fireInfo.type == 2)
        {
            switch (type)
            {
                //这四个点是旋转90度发散弹
                case E_Pos_Type.TopLeft:
                case E_Pos_Type.TopRight:
                case E_Pos_Type.BottomLeft:
                case E_Pos_Type.BottomRight:
                    changeAngle = 90f / (nowNum + 1);
                    break;

                //这四个点是旋转180度发散弹
                case E_Pos_Type.Top:
                case E_Pos_Type.Left:
                case E_Pos_Type.Right:
                case E_Pos_Type.Bottom:
                    changeAngle = 180f / (nowNum + 1);
                    break;
            }
        }
    }


    // 检测开火
    private void UpdateFire()
    {
        // 如果当前状态不需要发射子弹,则直接返回
        if (nowCD == 0 && nowNum == 0)
            return;

        // 更新 CD 值
        nowCD -= Time.deltaTime;
        if (nowCD > 0)
            return;

        GameObject bullet;
        BulletObject bulletObj;

        switch (fireInfo.type)
        {
            // 一个一个地发射子弹,朝向玩家
            case 1:
                // 动态创建子弹对象
                bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                // 动态添加子弹脚本
                bulletObj = bullet.AddComponent<BulletObject>();
                // 把当前的子弹数据传入子弹脚本进行初始化
                bulletObj.InitInfo(nowBulletInfo);

                // 设置子弹的位置和朝向
                bullet.transform.position = this.transform.position;
                bullet.transform.rotation = Quaternion.LookRotation(PlayerObject.Instance.transform.position - this.transform.position);

                // 发射一颗子弹
                --nowNum;
                // 重置 CD 值
                nowCD = nowNum == 0 ? 0 : fireInfo.cd;
                break;

            // 发射散弹
            case 2:
                // 如果无 CD,则一瞬间发射所有散弹
                if (nowCD == 0)
                {
                    for (int i = 0; i < nowNum; i++)
                    {
                        // 动态创建子弹对象
                        bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                        // 动态添加子弹脚本
                        bulletObj = bullet.AddComponent<BulletObject>();
                        // 把当前的子弹数据传入子弹脚本进行初始化
                        bulletObj.InitInfo(nowBulletInfo);

                        // 设置子弹的位置和朝向
                        bullet.transform.position = this.transform.position;
                        // 每次都会旋转一个角度得到一个新的方向
                        nowDir = Quaternion.AngleAxis(changeAngle * i, Vector3.up) * initDir;
                        bullet.transform.rotation = Quaternion.LookRotation(nowDir);
                    }
                    // 因为是瞬间创建完所有子弹,所以重置数据
                    nowCD = nowNum = 0;
                }
                //有CD一颗一颗的发
                else
                {
                    // 动态创建子弹对象
                    bullet = Instantiate(Resources.Load<GameObject>(nowBulletInfo.resName));
                    // 动态添加子弹脚本
                    bulletObj = bullet.AddComponent<BulletObject>();
                    // 把当前的子弹数据传入子弹脚本进行初始化
                    bulletObj.InitInfo(nowBulletInfo);

                    // 设置子弹的位置和朝向
                    bullet.transform.position = this.transform.position;
                    // 每次都会旋转一个角度得到一个新的方向
                    nowDir = Quaternion.AngleAxis(changeAngle * (fireInfo.num - nowNum), Vector3.up) * initDir;
                    bullet.transform.rotation = Quaternion.LookRotation(nowDir);

                    // 发射一颗子弹
                    --nowNum;
                    // 重置 CD 值 
                    nowCD = nowNum == 0 ? 0 : fireInfo.cd;
                }
                break;
        }
    }

}

Main

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

public class Main : MonoBehaviour
{
    void Start()
    {
        //根据开始场景中选择的英雄 来动态创建 飞机
        RoleInfo info = GameDataMgr.Instance.GetNowSelHeroInfo();

        //根据数据中的 资源路劲 进行动态创建
        GameObject obj = Instantiate(Resources.Load<GameObject>(info.resName));

        //动态添加脚本
        PlayerObject playerObj = obj.AddComponent<PlayerObject>();

        //根据信息设置值
        playerObj.speed = info.speed * 20;
        playerObj.maxHp = 10;
        playerObj.nowHp = info.hp;
        playerObj.roundSpeed = 20;

        //更新界面上显示的血量
        GamePanel.Instance.ChangeHp(info.hp);
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏