Unity NPC在屏幕外随机生成,范围为摄像机边缘

先说明一下,方法比较蠢,打算后面再试试四叉树那些,先说说我目前的思路。

将一个正方形区域想象成一个九宫格,而人物处在中间区域,中间的区域就是摄像机能看到的范围,

九宫格
摄像机能看到的范围坐标Unity也有自带的API可以调用,就是

//此坐标为摄像机的右上角
Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
//此坐标为摄像机的左上角
Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
//此坐标为摄像机的左下角
Camera.main.ScreenToWorldPoint(new Vector3(0,0, 0));
//此坐标为摄像机的右角
Camera.main.ScreenToWorldPoint(new Vector3(Screen.width,0, 0));

这样就知道了中间区域的四个角的位置,剩下八个区域(九宫格除掉中间区域)都可以根据这四个角进行计算。
先定义一个枚举,表明这八个区域-分别从左往右数

public enum AreaType
{
    One = 0,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    eight,
}

再设定一个八个区域内部,就是小各自的左上角和右下角坐标

public struct EnemyCreateArea
{
    public Vector2 leftUpV3;
    public Vector2 rightDownV3;
}

设置一个距离摄像机多少生成的距离参数,和左上角最大区域,右下角最大区域,也就是整个框的大小

public float distance = 3;
//这两个决定了这个九宫格最大的面积
public Vector2 create_LeftUpPos;
public Vector2 create_RightDownPos;

怪物的生成信息

[System.Serializable]
public struct EnemyCreateMessage
{
	//生成的物体
    public GameObject enemyObj;
    //生成间隔
    public float interval;
}

八个区域计算方法,写的很简陋,可以自己重构一下,参数就是引用上面那八个的枚举。因为需要有一个方法先随机从8个区域抽一个。
然后根据switch来判断只计算那个区域的范围,然后返回。

public EnemyCreateArea CalculationArea(AreaType type)
    {
        switch (type)
        {
            case AreaType.One:
                areas[0].leftUpV3 = create_LeftUpPos;
                areas[0].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
                break;
            case AreaType.Two:
                areas[1].leftUpV3.x = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)).x;
                areas[1].leftUpV3.y = create_LeftUpPos.y;
                areas[1].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                break;
            case AreaType.Three:
                areas[2].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[2].leftUpV3.y = create_LeftUpPos.y;
                areas[2].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[2].rightDownV3.x = create_RightDownPos.x;
                break;
            case AreaType.Four:
                areas[3].leftUpV3.x = create_LeftUpPos.x;
                areas[3].leftUpV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)).y;
                areas[3].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0));
                break;
            case AreaType.Five:
                areas[4].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[4].rightDownV3.x = create_RightDownPos.x;
                areas[4].rightDownV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).y;
                break;
            case AreaType.Six:
                areas[5].leftUpV3.x = create_LeftUpPos.x;
                areas[5].leftUpV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).y;
                areas[5].rightDownV3.x = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).x;
                areas[5].rightDownV3.y = create_RightDownPos.y;
                break;
            case AreaType.Seven:
                areas[6].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0));
                areas[6].rightDownV3.x = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0)).x;
                areas[6].rightDownV3.y = create_RightDownPos.y;
                break;
            case AreaType.eight:
                areas[7].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
                areas[7].rightDownV3 = create_RightDownPos;
                break;
            default:
                break;
        }
        return areas[(int)type];
    }

下面是生成的协程方法

 public IEnumerator IE_Create(EnemyCreateMessage message, float interval)
    {
        yield return new WaitForSeconds(interval);
        //从八个格子随机抽取一个区域
        int range_Area = Random.Range(0, 8);
        //九宫格的大小先和摄像机能看见的范围一样大
        create_LeftUpPos = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
        create_RightDownPos= Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
        //然后从中间开始进行扩散,扩散出剩下八个格子的大小
        create_LeftUpPos.x -= distance;
        create_LeftUpPos.y += distance;
        create_RightDownPos.x += distance;
        create_RightDownPos.y -= distance;
        //当前区域=通过上面CalculationArea计算后,返回的最新区域范围。
        currentArea =CalculationArea((AreaType)range_Area);
        //从当前区域的左上角坐标和右下角坐标,进行x之间,y之间的随机抽取。
        float range_PosX = Random.Range(currentArea.leftUpV3.x, currentArea.rightDownV3.x);
        float range_PosY = Random.Range(currentArea.leftUpV3.y, currentArea.rightDownV3.y);
        //调用对象池(没写的直接用Instantiate)
        GameObject enemy = EnemyPool.Instance.GetPoolEnemy(0);
        //生成的NPC位置为上面随机出来的数
        enemy.transform.position = new Vector2(range_PosX, range_PosY);
        //递归,接着下次生成。
        StartCoroutine(IE_Create(message, interval));
    }

可以理解成下图,这个正方形目前代表着,除了中间摄像机区域不能生成,其他区域都可以生成。
请添加图片描述
总代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public struct EnemyCreateMessage
{
    public GameObject enemyObj;
    public float interval;
}

public enum AreaType
{
    One = 0,
    Two,
    Three,
    Four,
    Five,
    Six,
    Seven,
    eight,
}


public struct EnemyCreateArea
{
    public Vector2 leftUpV3;
    public Vector2 rightDownV3;
}

//设计思路
//将主角四周分成8个区域,分别扣除最大区域的X和Y的坐标数,上下左右,左上,左下,右上右下,类似一个九宫格,中间则是主角的位置。

public class CreateEnemy : MonoBehaviour
{
    public EnemyCreateMessage[] messages;
    public EnemyCreateArea[] areas;
    public Vector2 create_LeftUpPos;
    public Vector2 create_RightDownPos;
    private EnemyCreateArea currentArea;
    public float distance = 10;
    
    void Start()
    {
        areas = new EnemyCreateArea[8];
        foreach (var item in messages)
        {
            StartCoroutine(IE_Create(item, item.interval));
        }
    }

    // Update is called once per frame
    void Update()
    {

    }

    public IEnumerator IE_Create(EnemyCreateMessage message, float interval)
    {
        yield return new WaitForSeconds(interval);
        int range_Area = Random.Range(0, 8);
        create_LeftUpPos = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
        create_RightDownPos= Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
        create_LeftUpPos.x -= distance;
        create_LeftUpPos.y += distance;
        create_RightDownPos.x += distance;
        create_RightDownPos.y -= distance;
        currentArea =CalculationArea((AreaType)range_Area);
        float range_PosX = Random.Range(currentArea.leftUpV3.x, currentArea.rightDownV3.x);
        float range_PosY = Random.Range(currentArea.leftUpV3.y, currentArea.rightDownV3.y);
        GameObject enemy = EnemyPool.Instance.GetPoolEnemy(0);
        enemy.transform.position = new Vector2(range_PosX, range_PosY);
        StartCoroutine(IE_Create(message, interval));
    }

    public EnemyCreateArea CalculationArea(AreaType type)
    {
        switch (type)
        {
            case AreaType.One:
                areas[0].leftUpV3 = create_LeftUpPos;
                areas[0].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0));
                break;
            case AreaType.Two:
                areas[1].leftUpV3.x = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)).x;
                areas[1].leftUpV3.y = create_LeftUpPos.y;
                areas[1].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                break;
            case AreaType.Three:
                areas[2].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[2].leftUpV3.y = create_LeftUpPos.y;
                areas[2].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[2].rightDownV3.x = create_RightDownPos.x;
                break;
            case AreaType.Four:
                areas[3].leftUpV3.x = create_LeftUpPos.x;
                areas[3].leftUpV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height, 0)).y;
                areas[3].rightDownV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0));
                break;
            case AreaType.Five:
                areas[4].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
                areas[4].rightDownV3.x = create_RightDownPos.x;
                areas[4].rightDownV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).y;
                break;
            case AreaType.Six:
                areas[5].leftUpV3.x = create_LeftUpPos.x;
                areas[5].leftUpV3.y = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).y;
                areas[5].rightDownV3.x = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).x;
                areas[5].rightDownV3.y = create_RightDownPos.y;
                break;
            case AreaType.Seven:
                areas[6].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0));
                areas[6].rightDownV3.x = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0)).x;
                areas[6].rightDownV3.y = create_RightDownPos.y;
                break;
            case AreaType.eight:
                areas[7].leftUpV3 = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, 0, 0));
                areas[7].rightDownV3 = create_RightDownPos;
                break;
            default:
                break;
        }
        return areas[(int)type];
    }

}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值