实现3d点在屏幕上、屏幕边缘的方向提示

1、首先需要解决两条线段相交的逻辑判定

2、按照方向指示类型的不同,进行不同的判定,以及显示处理

3、按不同类型处理

①需求1:在屏幕边缘,显示屏幕外进攻的敌人的来向

需要敌人点和己方点连线 与 屏幕边缘线段做相交点判定处理

②需求2:在屏幕边缘,提示玩家前进的方向

需要目标点和己方点连线 与 屏幕边缘线段做相交点判定处理

 

一、单个提示标记和线段相交逻辑文件如下(ScreenEdgeTips.cs)

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

public class Line2D
{
    //共线
    public const int COLINE = 0;
    //相交
    public const int CROSS = 1;
    //平行线
    public const int PARALLEL = 2;
    //无相交
    public const int NOT_CROSS = 3;


    private double EPS = 1e-4;

    public Vector3 point1;
    public Vector3 point2;

    private float A;
    private float B;
    private float C;

    public Line2D(Vector3 point1, Vector3 point2)
    {
        this.point1 = point1;
        this.point2 = point2;
        this.calcCoefficient();
    }

    private void calcCoefficient()
    {
        this.A = this.point2.y - this.point1.y;
        this.B = this.point1.x - this.point2.x;
        this.C = this.point2.x * this.point1.y - this.point1.x * this.point2.y;
    }

    private bool checkCross(Vector3 sp1, Vector3 ep1, Vector3 sp2, Vector3 ep2)
    {
    
        if (Math.Max(sp1.x, ep1.x) < Math.Min(sp2.x, ep2.x)){
            return false;
        }

        if (Math.Min(sp1.x, ep1.x) > Math.Max(sp2.x, ep2.x)){
            return false;
        }

        if (Math.Max(sp1.y, ep1.y) < Math.Min(sp2.y, ep2.y)){
            return false;
        }

        if (Math.Min(sp1.y, ep1.y) > Math.Max(sp2.y, ep2.y)){
            return false;
        }
    
        Vector3 vectorA = sp1 - sp2;
        Vector3 vectorB = ep2 - sp2;
        Vector3 vectorC = ep2 - sp2;
        Vector3 vectorD = ep1 - sp2;
        double temp1 = (vectorA.x * vectorB.y - vectorA.y * vectorB.x) * (vectorC.x * vectorD.y - vectorC.y * vectorD.x);

        vectorA = sp2 - sp1;
        vectorB = ep1 - sp1;
        vectorC = ep1 - sp1;
        vectorD = ep2 - sp1;
        double temp2 = (vectorA.x * vectorB.y - vectorA.y * vectorB.x) * (vectorC.x * vectorD.y - vectorC.y * vectorD.x);

        if ((temp1 >= 0) && (temp2 >= 0))
        {
            return true;
        }
 
        return false;
    }

    private bool isDoubleEqualZero(double data)
    {
        if (Math.Abs(data) <= EPS){
            return true;
        }else{
            return false;
        }
    }

    public int Intersection(Line2D otherLine, out Vector3 intersectantPoint)
    {
        intersectantPoint = Vector3.zero;
        if (!checkCross(this.point1, this.point2, otherLine.point1, otherLine.point2))
        {
            return Line2D.NOT_CROSS;
        }
        if (isDoubleEqualZero(this.A * otherLine.B - this.B * otherLine.A))
        {
            if (isDoubleEqualZero((this.A + this.B) * otherLine.C - (otherLine.A + otherLine.B) * this.C))
            {
                return Line2D.COLINE;
            }
            else
            {
                return Line2D.PARALLEL;
            }
        }
        else
        {
            intersectantPoint.x = (otherLine.B * this.C - this.B * otherLine.C) / (otherLine.A * this.B - this.A * otherLine.B);
            intersectantPoint.y = (this.A * otherLine.C - otherLine.A * this.C) / (otherLine.A * this.B - this.A * otherLine.B);
            intersectantPoint.z = 0;
            return Line2D.CROSS;
        }
    }
}


public class ScreenEdgeTips : MonoBehaviour
{
    //UI
    public UILabel distanceLabel;
    public GameObject directContainer;
    public float prefabWidth;

    //cameras
    private Camera mainCamera;
    private Camera uicamera;
    
    //param
    private bool isIdle = false;
    private int type = 0;
    private int fromActorId = 0;
    private int toActorId = 0;
    private Vector3 goalPoint;
    
    private List<Line2D> screenLines;

    public ScreenEdgeTips()
    {
        this.isIdle = true;
    }

    private void InitImp()
    {
        float offsetWidth = prefabWidth / 2;
        float originalPoint = 0 + offsetWidth;
        float correctionWidth = Screen.width - offsetWidth;
        float correctionHeight = Screen.height - offsetWidth;
        Vector3 point1 = new Vector3(offsetWidth, offsetWidth, 0);
        Vector3 point2 = new Vector3(offsetWidth, correctionHeight, 0);
        Vector3 point3 = new Vector3(correctionWidth, correctionHeight, 0);
        Vector3 point4 = new Vector3(correctionWidth, offsetWidth, 0);
        this.screenLines = new List<Line2D>();
        this.screenLines.Add(new Line2D(point1, point2));
        this.screenLines.Add(new Line2D(point2, point3));
        this.screenLines.Add(new Line2D(point3, point4));
        this.screenLines.Add(new Line2D(point4, point1));

        this.mainCamera = Camera.main;
        this.uicamera = NGUITools.FindCameraForLayer(LayersDefine.Ngui);

        this.isIdle = false;
        
    }

    public void Init(int type, int fromActorId, int toActorId)
    {
        this.InitImp();

        this.type = type;
        this.fromActorId = fromActorId;
        this.toActorId = toActorId;

        this.UpdateImp();
    }

    public void Init(int type, int fromActorId, Vector3 goalPoint)
    {
        this.InitImp();

        this.type = type;
        this.fromActorId = fromActorId;
        this.goalPoint = goalPoint;

        this.UpdateImp();
    }

    public bool GetIsIdle()
    {
        return this.isIdle;
    }

    private void SetIsIdle(bool isIdle)
    {
        this.isIdle = isIdle;
        if(isIdle)
        {
            this.type = 0;
            this.fromActorId = 0;
            gameObject.SetActive(false);
        }
    }

    public int GetType()
    {
        return this.type;
    }

    private bool PointIsInScreen(Vector3 pos)
    {
        if (pos.x <= this.screenLines[0].point1.x
            || pos.x >= this.screenLines[1].point2.x
            || pos.y <= this.screenLines[0].point1.y
            || pos.y >= this.screenLines[1].point2.y)
        {
            return false;
        }
        return true;
    }

    private Vector3 WorldToScreenPoint(Vector3 pos)
    {
        if (null != this.mainCamera) 
        {
            return mainCamera.WorldToScreenPoint (pos);
        }
        return Vector3.zero;
    }

    private Vector3 ScreenToUIPoint(Vector3 pos)
    {
        if (null != this.uicamera)
        {
            Vector3 uiWorldPosInUISpace = this.uicamera.ScreenToWorldPoint(pos);
            uiWorldPosInUISpace.z = 0f;
            return uiWorldPosInUISpace;
        }
        return Vector3.zero;
    }

    public void UpdateImp()
    {
        bool isIntersce = false;
        GameObject fromHero = ActorManager.Ins.GetActor(fromActorId);
        GameObject toHero = null;
        if (fromHero != null)
        {
            Vector3 intersecPos = new Vector3();
            Vector3 fromPos = this.WorldToScreenPoint(fromHero.transform.position);
            Vector3 toPos = Vector3.zero;

            if (this.toActorId == 0)
            {
                toPos = this.WorldToScreenPoint(this.goalPoint);
            }
            else
            {
                toHero = ActorManager.Ins.GetActor(toActorId);
                if (toHero != null)
                {
                    toPos = this.WorldToScreenPoint(toHero.transform.position);
                }
                else
                {
                    this.SetIsIdle(true);
                }
            }

            if (toPos != Vector3.zero && this.PointIsInScreen(fromPos))
            {
                Line2D line = new Line2D(fromPos, toPos);
                foreach (Line2D l in this.screenLines)
                {
                    if (line.Intersection(l, out intersecPos) == Line2D.CROSS)
                    {
                        isIntersce = true;
                        break;
                    }
                }

                if (isIntersce)
                {
                    transform.position = this.ScreenToUIPoint(intersecPos);

                    if (directContainer != null)
                    {
                        if (this.type == 1)
                        {
                            float angle = Vector3.Angle(toPos - fromPos, Vector3.right);
                            directContainer.transform.rotation = new Quaternion();
                            directContainer.transform.Rotate(Vector3.forward * angle);
                        }
                        else
                        {
                            float angle = Vector3.Angle(fromPos - toPos, Vector3.right);
                            directContainer.transform.rotation = new Quaternion();
                            directContainer.transform.Rotate(Vector3.back * angle);
                        }
                    }
                    if (this.distanceLabel != null && toHero != null)
                    {
                        this.distanceLabel.text = String.Format("{0}米", Math.Round((toHero.transform.position - fromHero.transform.position).magnitude));
                    }
                }
            }
        }
        else
        {
            this.SetIsIdle(true);
        }
        this.SetIsIdle(false);
        gameObject.SetActive(isIntersce);
    }
}

 二、多个提示的刷新、管理(增删)的逻辑处理文件(ScreenEdgeTipManager.cs

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

public class ScreenEdgeTipManager : MonoBehaviourX
{
    private Dictionary<int, string> prefabNameMap = new Dictionary<int, string>();
    private Dictionary<int, GameObject> prefabMap = new Dictionary<int, GameObject>();
    private ScreenEdgeTips ToGoalCtrl;

    private static ScreenEdgeTipManager instance = null;
    
    public static ScreenEdgeTipManager Ins
    { 
        get
        {
            return instance;  
        } 
    }
    
    ScreenEdgeTipManager()
    {
        instance = this;
    }

    void Start()
    {
        scope.Listen("BattleOver", OnBattleOver);
        scope.Listen("GameShutdown", OnBattleOver);

        prefabNameMap.Add(1, UIDefine.BATTLE_TIPS_GOAL);
        prefabNameMap.Add(2, UIDefine.BATTLE_TIPS_MONSTER);
        prefabNameMap.Add(3, UIDefine.BATTLE_TIPS_RAIDER);
    }

    private void OnBattleOver(object[] args)
    {
        ClearAll();
    }
    
    private void ClearAll()
    {
        gameObject.DestroyAllChildGameObject();
        prefabMap.Clear();

        NGUITools.SetActiveChildren(gameObject, false);
    }

    private GameObject GetPrefab(int type)
    {
        if (!prefabMap.ContainsKey(type))
        {
            GameObject tipsTemplate = null;
            tipsTemplate = (GameObject)ResourceMgr.LoadPrefab(prefabNameMap[type]);
            if (null != tipsTemplate)
            {
                prefabMap.Add(type, tipsTemplate);
            }
        }
        return prefabMap[type];
    }

    private ScreenEdgeTips GetNewTips(int type)
    {
        GameObject tipsTemplate = this.GetPrefab(type);
        GameObject go = GameObject.Instantiate(tipsTemplate) as GameObject;
        go.transform.parent = transform;
        go.layer = LayersDefine.Ngui;
        go.transform.localScale = Vector3.one;
        return go.GetComponent<ScreenEdgeTips>();
    }

    private ScreenEdgeTips GetTipsCtrl(int type)
    {
        if (type == 0)
        {
            return null;
        }
        //根据type复用 或 创建新的提示
        //尝试复用一个已经创建过的UI_BloodUp
        int count = transform.childCount;
        ScreenEdgeTips findTips = null;
        for (int i = 0; i < count; ++i)
        {
            GameObject obj = transform.GetChild(i).gameObject;
            if (!obj.activeSelf)
            {
                ScreenEdgeTips et = obj.GetComponent<ScreenEdgeTips>();
                if (et != null && et != this.ToGoalCtrl && et.GetType() == type && et.GetIsIdle())
                {
                    findTips = et;
                }
                break;
            }
        }
        //不存在时,创建新的tips模板
        return null != findTips ? findTips : this.GetNewTips(type);
    }

    private void _CreateTargetTips(int type, int fromActorId, int toActorId)
    {
        ScreenEdgeTips findTips = this.GetTipsCtrl(type);
        if (findTips != null)
        {
            findTips.Init(type, fromActorId, toActorId);
        }
    }

    private ScreenEdgeTips GetToGoalCtrl()
    {
        this.ToGoalCtrl = this.ToGoalCtrl != null ? this.ToGoalCtrl : this.GetNewTips(1);
        return this.ToGoalCtrl;
    }

    private void _CreatePointTips(int fromActorId, Vector3 goalPoint)
    {
        ScreenEdgeTips findTips = this.GetToGoalCtrl();
        if (findTips != null)
        {
            findTips.Init(1, fromActorId, goalPoint);
        }
    }


    void Update()
    {
        int count = transform.childCount;
        for (int i = 0; i < count; ++i)
        {
            GameObject obj = transform.GetChild(i).gameObject;
            ScreenEdgeTips et = obj.GetComponent<ScreenEdgeTips>();
            if (et != null)
            {
                et.UpdateImp();
            }
        }
    }

    public static void CreateTargetTips(int type, int fromActorId, int toActorId)
    {
        if (instance)
        {
            instance._CreateTargetTips(type, fromActorId, toActorId);
        }
    }

    public static void CreatePointTips(int fromActorId, Vector3 goalPoint)
    {
        if (instance)
        {
            instance._CreatePointTips(fromActorId, goalPoint);
        }
    }
}

 

************************************下面是用到到的基础类,不用也没关系*************************************

 

三、使用到的显示基类(MonoBehaviourX.cs

using System;
using UnityEngine;

public class MonoBehaviourX: MonoBehaviour
{
    protected EventScope scope = null;
    
    protected MonoBehaviourX()
    {
        scope = Eventer.Create();
    }
    
    protected void OnDestroy()
    {
        scope.Destroy();
        scope= null;
    }
}

 四、使用到的事件处理类(EventScope.cs

using System;
using System.Collections.Generic;

public class EventScope
{
    public Dictionary<string, DelegateObjList> eventTable = new Dictionary<string, DelegateObjList>();
    private EventScope parent = null;
    private List<EventScope> childer = new List<EventScope>();
    
    public EventScope(EventScope _parent)
    {
        parent = _parent;
    }
    
    public EventScope CreateChild()
    {
        EventScope scope = new EventScope(this);
        childer.Add(scope);
        return scope;
    }
    
    private void RemoveParentEvents(string name, Delegate deleObject)
    {
        if (parent == null)
        {
            return;
        }
        
        DelegateObjList list;
        if (parent.eventTable.TryGetValue(name, out list))
        {
            list.Remove(deleObject);
        }
        
        parent.RemoveParentEvents(name, deleObject);
    }
    
    public void ClearEvent()
    {
        foreach (var et in eventTable)
        {
            foreach (var _delegate in et.Value.events)
            {
                RemoveParentEvents(et.Key, _delegate);
            }
        }
        
        eventTable.Clear();
    }
    
    public void Destroy()
    {
        ClearEvent();
        
        if (parent != null)
        {
            parent.childer.Remove(this);
        }
    }
    
    public void Listen(string name, CALLBACK handler)
    {
        if (handler == null)
        {
            return;
        }
        DelegateObjList dol;
        if (!eventTable.TryGetValue(name, out dol))
        {
            dol = new DelegateObjList();
            eventTable [name] = dol;
        }
        
        dol.Add(handler);
        
        if (parent != null)
        {
            parent.Listen(name, handler);
        }
    }
}

 五、使用到的事件广播类(Eventer.cs

using System;
using System.Collections.Generic;

public class Eventer
{
    static public EventScope globe = new EventScope(null);
    
    static public EventScope Create()
    {
        return globe.CreateChild();
    }

    public static void Fire(string name, object[] args)
    {
        DelegateObjList dol;
        if (globe.eventTable.TryGetValue(name, out dol))
        {
            dol.Enter();
            int count = dol.events.Count;
            for (int i=0; i<count; ++i)
            {
                CALLBACK callback = dol.events [i] as CALLBACK;
                callback(args);
            }
            dol.Leave();
        }
    }

    public static void Fire(string name)
    {
        object[] args = new object[0] {};
        Fire(name, args);
    }

    //  ---------------- 以下接口提供给Lua调用 ----------------------
    public static void _Fire(string name)
    {
        object[] args = new object[0] {};
        Fire(name, args);
    }

    public static void _Fire(string name, object arg1)
    {
        object[] args = new object[1] { arg1 };
        Fire(name, args);
    }

    public static void _Fire(string name, object arg1, object arg2)
    {
        object[] args = new object[2] { arg1, arg2 };
        Fire(name, args);
    }
    
    public static void _Fire(string name, object arg1, object arg2, object arg3)
    {
        object[] args = new object[3] { arg1, arg2, arg3 };
        Fire(name, args);
    }

    public static void _Fire(string name, object arg1, object arg2, object arg3, object arg4) 
    {
        object[] args = new object[4] { arg1, arg2, arg3, arg4 };
        Fire(name, args);
    }

    public static void _Fire(string name, object arg1, object arg2, object arg3, object arg4, object arg5) 
    {
        object[] args = new object[5] { arg1, arg2, arg3, arg4, arg5 };
        Fire(name, args);
    }

    public static void _Fire(string name, object arg1, object arg2, object arg3, object arg4, object arg5, object arg6) 
    {
        object[] args = new object[6] { arg1, arg2, arg3, arg4, arg5, arg6 };
        Fire(name, args);
    }
}

 六、使用到的回调处理类(DelegateObjList

using System;
using System.Collections.Generic;

public delegate void CALLBACK(object[] args);

public class DelegateObjList
{
    public class DynamicDelegate
    {
        public Delegate callback;
        public bool append;
    }
    
    public List<Delegate> events = new List<Delegate>();
    public List<DynamicDelegate> delayProcesList = null;
    public bool accessEvent = false;
    
    private void AddDynamicDelegate(Delegate dele, bool append)
    {
        if (delayProcesList == null)
        {
            delayProcesList = new List<DynamicDelegate>();
        }
        
        DynamicDelegate dd = new DynamicDelegate();
        dd.append = append;
        dd.callback = dele;
        delayProcesList.Add(dd);
    }
    
    public void Add(Delegate c)
    {
        if (accessEvent)
        {
            AddDynamicDelegate(c, true);
        } else
        {
            events.Add(c);
        }
    }
    
    public void Remove(Delegate c)
    {
        if (accessEvent)
        {
            AddDynamicDelegate(c, false);
        } else
        {
            events.Remove(c);
        }
    }
    
    public void Enter()
    {
        accessEvent = true;
    }
    
    public void Leave()
    {
        accessEvent = false;
        
        if (delayProcesList == null)
        {
            return;
        }
        
        int count = delayProcesList.Count;
        if (count == 0)
        {
            return;
        }
        
        for (int i=0; i<count; ++i)
        {
            var dp = delayProcesList [i];
            if (dp.append)
            {
                events.Add(dp.callback);
            } else
            {
                events.Remove(dp.callback);
            }
        }
        
        delayProcesList.Clear();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值