Unity中SpotLight光照事件的判断

18 篇文章 0 订阅

本文提供了一个基于射线检测的SpotLight光照事件判断的代码,先上一个Debug图大家就明白了。这里写图片描述

接着是Inspector中参数的解释:
这里写图片描述

下面是代码实现:

FlashLight.cs

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


public enum LightEventType { Enter, Stay, Exit }

public class LightEventData
{
    public string name;
    public Action<RaycastHit>[] methods;

    public LightEventData(string na, Action<RaycastHit>[] meths)
    {
        name = na;
        methods = meths;
    }
}

public class RayData
{
    public Vector3 position;
    public Vector3 direction;
    public float distance;

    public RayData(Vector3 dir,Light light)
    {
        position = light.transform.position + direction * distance;
        direction = dir;
        distance = light.range;
    }
    public RayData(float x,float y,Light light)
    {
        float radius = Mathf.Tan(Mathf.Deg2Rad * light.spotAngle / 2) * light.range;

        position =light.transform.position +light.transform.forward 
            * light.range +light.transform.rotation 
            * new Vector3(x,y) * radius;

        direction = Vector3.Normalize(position -light.transform.position);
        distance=Vector3.Distance(position,light.transform.position);
    }
}

public class HitInfoList:List<RaycastHit>
{
    public HitInfoList() { }
    public HitInfoList(HitInfoList copy)
    {
        foreach (var elem in copy)
            this.Add(elem);
    }
    public bool AddElement(RaycastHit info)
    {
        RaycastHit result;
        if (!ExistElement(info,out result))
        {
            this.Add(info);
            return true;
        }
        return false;
    }

    public bool RemoveElement(RaycastHit info)
    {
        RaycastHit result;
        if (ExistElement(info, out result))
        {
            this.Remove(result);
            return true;
        }
        return false;
    }

    public HitInfoList GetSubList(HitInfoList another)
    {
        HitInfoList list = new HitInfoList(this);
        foreach(var elem in another)
        {
            RaycastHit result;
            if (ExistElement(elem, out result))
                list.Remove(result);
        }
        return list;
    }

    public bool ExistElement(RaycastHit info,out RaycastHit result)
    {
        foreach (var elem in this)
        {
            string iName = info.collider.name;
            string eName = elem.collider.name;
            if (iName.Equals(eName))
            {
                result = elem;
                return true;
            }
        }
        result = new RaycastHit();
        return false;
    }
}

public class FlashLight : MonoBehaviour {


    public float dampSpeed=0.5f;
    public float rangeOffset=1f;
    public bool useMultipleRay=true;

    [Range(0.05f,1f)]
    public float sectionOfMultipleRay=0.05f;

    Light light;
    float dampTmp;
    float maxLightDistance;

    static HitInfoList InfoList = new HitInfoList();

    void Start()
    {
        light = GetComponent<Light>();
        maxLightDistance = light.range;
    }

    void Update()
    {
        ControlLightRange();
        DetectObject();
    }

    public void Switch(bool switchOn) { light.enabled = switchOn; }

    void ControlLightRange()
    {
        Ray ray = new Ray(transform.position, transform.forward);
        RaycastHit info;
        float distance;

        if (Physics.Raycast(ray, out info))
        {
            distance = Mathf.Clamp(info.distance + rangeOffset, 0, maxLightDistance);
            Debug.DrawLine(transform.position, info.point);
        }
        else
            distance = maxLightDistance;

        light.range = Mathf.SmoothDamp(light.range, distance, ref dampTmp, dampSpeed);
    }

    void DetectObject()
    {
        HitInfoList list = new HitInfoList();

        foreach (var data in GetRayData())
        {         
            Ray ray = new Ray(transform.position, data.direction);
            RaycastHit info;

            if (Physics.Raycast(ray, out info,data.distance))
                list.AddElement(info);

            Debug.DrawLine(transform.position, data.position, Color.red);
        }

        int operate = (list.Count == InfoList.Count) ? 0
            : ((list.Count > InfoList.Count) ? 1 : -1);

        HandleEvent(list, operate);
    }

    void HandleEvent(HitInfoList current,int operate)
    {

        foreach (var elem in current)
        {
            if (operate != -1 && InfoList.AddElement(elem))
                LightEventListener.EventHandler(elem, LightEventType.Enter);
        }

        foreach(var elem in InfoList.GetSubList(current))
        {
            if (operate != 1 && InfoList.RemoveElement(elem))
                LightEventListener.EventHandler(elem, LightEventType.Exit);
        }

        foreach (var elem in InfoList)
            LightEventListener.EventHandler(elem, LightEventType.Stay);
    }

    List<RayData> GetRayData()
    {
        List<RayData> list = new List<RayData>();

        if (useMultipleRay)
            list.AddRange(GetFullCircleRay(new int[] { 1, -1 },
                new int[] { 1, -1 },sectionOfMultipleRay,light));
        else
            list.Add(new RayData(transform.forward,light));

        return list;
    }

    List<RayData> GetFullCircleRay(int[] xParams, int[] yParams, float section, Light light)
    {
        List<RayData> list = new List<RayData>();

        foreach (var x in xParams)
            foreach (var y in yParams)
                list.AddRange(GetQuarterCircleRay(x, y, section,light));

        return list;
    }

    List<RayData> GetQuarterCircleRay(int xParam, int yParam,float section,Light light)
    {
        float x = 0, y = 0;    
        List<RayData> list = new List<RayData>();

        while ((yParam > 0) ? y <= 1 : y >= -1)
        {
            while (x * x + y * y <= 1)
            {            
                list.Add(new RayData(x,y,light));
                x += xParam*section;
            }
            x = 0;
            y += yParam * section;
        }

        return list;
    }

}

public abstract class LightEventListener : MonoBehaviour
{

    private static List<LightEventData> ListenerList = new List<LightEventData>();

    public static void EventHandler(RaycastHit info, LightEventType type)
    {
        foreach (var listener in ListenerList)
        {
            string lName = listener.name;
            string iName = info.collider.name;
            Action<RaycastHit> Method = listener.methods[(int)type];

            if (lName.Equals(iName) && Method != null)
            {
                Method(info);
            }
        }
    }

    protected abstract void OnLightEnter(RaycastHit info);
    protected abstract void OnLightStay(RaycastHit info);
    protected abstract void OnLightExit(RaycastHit info);

    void OnEnable()
    {
        LightEventData data = new LightEventData(name,
            new Action<RaycastHit>[] { OnLightEnter, OnLightStay, OnLightExit });

        ListenerList.Add(data);
    }

}

Test.cs

using UnityEngine;
using System.Collections;

public class Test:LightEventListener{


    protected override void OnLightEnter(RaycastHit info)
    {
        Debug.Log(info.collider.name + "Enter");
    }

    protected override void OnLightStay(RaycastHit info)
    {
        Debug.Log(info.collider.name + "Stay");
    }

    protected override void OnLightExit(RaycastHit info)
    {
        Debug.Log(info.collider.name + "Exit");
    }

}

最后是代码的分析:
首先需要提出的是代码中使用了较多的遍历,所以对性能可能会有一定的影响。在FlashLight的Update函数中做了两件事,一件是在光照点中心发出一条射线,根据射线检测到的物体的距离对SpotLight的Range进行调节。另一件就是发射事件检测的射线并将结果发送给LightEventListener进行事件的分发。Test.cs继承了Listener,并重写事件代码,使用时将FlashLight.cs添加到SpotLight上,将Test.cs添加到具有Collider的物体上即可。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值