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();
}
}
屏幕边缘提示逻辑
本文介绍了一种用于游戏或应用中屏幕边缘提示的实现方案,包括单个提示标记和线段相交逻辑处理、多个提示的刷新及管理逻辑,并提供了相关的代码实现。
4659

被折叠的 条评论
为什么被折叠?



