Explorar el Código

增加九宫格AOI

tanghai hace 4 años
padre
commit
58028b1e80
Se han modificado 39 ficheros con 810 adiciones y 97 borrados
  1. 1 3
      Proto/InnerMessage.proto
  2. 7 4
      Proto/OuterMessage.proto
  3. 179 0
      Server/Hotfix/Demo/AOI/AOIEntitySystem.cs
  4. 40 0
      Server/Hotfix/Demo/AOI/AOIHelper.cs
  5. 184 0
      Server/Hotfix/Demo/AOI/AOIManagerComponentSystem.cs
  6. 10 0
      Server/Hotfix/Demo/AOI/AOISeeCheckHelper.cs
  7. 50 0
      Server/Hotfix/Demo/AOI/CellSystem.cs
  8. 31 0
      Server/Hotfix/Demo/AOI/ChangePosition_NotifyAOI.cs
  9. 2 2
      Server/Hotfix/Demo/C2G_EnterMapHandler.cs
  10. 9 14
      Server/Hotfix/Demo/G2M_CreateUnitHandler.cs
  11. 10 10
      Server/Hotfix/Demo/MessageHelper.cs
  12. 1 0
      Server/Hotfix/Demo/Scene/SceneFactory.cs
  13. 28 0
      Server/Hotfix/Demo/Unit/UnitEnterSightRange_NotifyClient.cs
  14. 23 1
      Server/Hotfix/Demo/Unit/UnitHelper.cs
  15. 20 0
      Server/Hotfix/Demo/Unit/UnitLeaveSightRange_NotifyClient.cs
  16. 37 0
      Server/Model/Demo/AOI/AOIEntity.cs
  17. 7 0
      Server/Model/Demo/AOI/AOIManagerComponent.cs
  18. 16 0
      Server/Model/Demo/AOI/Cell.cs
  19. 16 1
      Server/Model/Demo/EventType.cs
  20. 2 0
      Server/Model/Demo/Unit/Unit.cs
  21. 2 6
      Server/Model/Generate/Message/InnerMessage.cs
  22. 11 6
      Server/Model/Generate/Message/OuterMessage.cs
  23. 17 16
      Server/Model/Generate/Message/OuterOpcode.cs
  24. 4 0
      Server/Model/Server.Model.csproj
  25. 0 1
      Unity/Assets/StreamingAssets/BuildProjectNeedThisFolder.md
  26. 1 1
      Unity/Codes/Hotfix/Demo/AI/AI_Attack.cs
  27. 1 1
      Unity/Codes/Hotfix/Demo/AI/AI_XunLuo.cs
  28. 1 1
      Unity/Codes/Hotfix/Demo/Login/EnterMapHelper.cs
  29. 1 4
      Unity/Codes/Hotfix/Demo/Unit/M2C_CreateUnitsHandler.cs
  30. 17 0
      Unity/Codes/Hotfix/Demo/Unit/M2C_RemoveUnitsHandler.cs
  31. 11 0
      Unity/Codes/Hotfix/Demo/Unit/UnitHelper.cs
  32. 16 0
      Unity/Codes/HotfixView/Demo/Unit/GameObjectComponentSystem.cs
  33. 8 1
      Unity/Codes/Model/Demo/EventType.cs
  34. 2 1
      Unity/Codes/Model/Demo/Unit/Unit.cs
  35. 1 2
      Unity/Codes/Model/Demo/Unit/UnitComponent.cs
  36. 9 0
      Unity/Codes/Model/Demo/Unit/UnitType.cs
  37. 11 6
      Unity/Codes/Model/Generate/Message/OuterMessage.cs
  38. 17 16
      Unity/Codes/Model/Generate/Message/OuterOpcode.cs
  39. 7 0
      Unity/Codes/Model/Module/Numeric/NumericType.cs

+ 1 - 3
Proto/InnerMessage.proto

@@ -165,10 +165,8 @@ message M2G_CreateUnit // IActorResponse
 	int32 Error = 91;
 	string Message = 92;
 
-	// 自己的unit id
-	int64 UnitId = 1;
 	// 所有的unit
-	repeated UnitInfo Units = 2;
+	int64 MyId = 1;
 }
 
 message G2M_SessionDisconnect // IActorLocationMessage

+ 7 - 4
Proto/OuterMessage.proto

@@ -41,10 +41,8 @@ message G2C_EnterMap // IResponse
 	int32 RpcId = 90;
 	int32 Error = 91;
 	string Message = 92;
-	// 自己的unit id
-	int64 UnitId = 1;
-	// 所有的unit
-	repeated UnitInfo Units = 2;
+	// 自己unit
+	int64 MyId = 1;
 }
 
 message UnitInfo
@@ -64,6 +62,11 @@ message M2C_CreateUnits // IActorMessage
 	repeated UnitInfo Units = 2;
 }
 
+message M2C_RemoveUnits // IActorMessage
+{
+	repeated int64 Units = 2;
+}
+
 message C2M_PathfindingResult // IActorLocationMessage
 {
 	int32 RpcId = 90;

+ 179 - 0
Server/Hotfix/Demo/AOI/AOIEntitySystem.cs

@@ -0,0 +1,179 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace ET
+{
+    public static class AOIEntitySystem
+    {
+        [ObjectSystem]
+        public class AwakeSystem: AwakeSystem<AOIEntity, int, Vector3>
+        {
+            public override void Awake(AOIEntity self, int distance, Vector3 pos)
+            {
+                self.ViewDistance = distance;
+                self.Domain.GetComponent<AOIManagerComponent>().Add(self, pos.x, pos.z);
+            }
+        }
+
+        [ObjectSystem]
+        public class DestroySystem: DestroySystem<AOIEntity>
+        {
+            public override void Destroy(AOIEntity self)
+            {
+                self.Domain.GetComponent<AOIManagerComponent>()?.Remove(self);
+                self.ViewDistance = 0;
+                self.SeeUnits.Clear();
+                self.SeePlayers.Clear();
+                self.BeSeePlayers.Clear();
+                self.BeSeeUnits.Clear();
+                self.SubEnterCells.Clear();
+                self.SubLeaveCells.Clear();
+                self.Cell = null;
+            }
+        }
+        
+        // 获取在自己视野中的对象
+        public static Dictionary<long, AOIEntity> GetSeeUnits(this AOIEntity self)
+        {
+            return self.SeeUnits;
+        }
+
+        public static Dictionary<long, AOIEntity> GetBeSeePlayers(this AOIEntity self)
+        {
+            return self.BeSeePlayers;
+        }
+
+        public static Dictionary<long, AOIEntity> GetSeePlayers(this AOIEntity self)
+        {
+            return self.SeePlayers;
+        }
+
+        // cell中的unit进入self的视野
+        public static void SubEnter(this AOIEntity self, Cell cell)
+        {
+            cell.SubsEnterEntities.Add(self.Id, self);
+            foreach (KeyValuePair<long, AOIEntity> kv in cell.AOIUnits)
+            {
+                if (kv.Key == self.Id)
+                {
+                    continue;
+                }
+
+                self.EnterSight(kv.Value);
+            }
+        }
+
+        public static void UnSubEnter(this AOIEntity self, Cell cell)
+        {
+            cell.SubsEnterEntities.Remove(self.Id);
+        }
+
+        public static void SubLeave(this AOIEntity self, Cell cell)
+        {
+            cell.SubsLeaveEntities.Add(self.Id, self);
+        }
+
+        // cell中的unit离开self的视野
+        public static void UnSubLeave(this AOIEntity self, Cell cell)
+        {
+            foreach (KeyValuePair<long, AOIEntity> kv in cell.AOIUnits)
+            {
+                if (kv.Key == self.Id)
+                {
+                    continue;
+                }
+
+                self.LeaveSight(kv.Value);
+            }
+
+            cell.SubsLeaveEntities.Remove(self.Id);
+        }
+
+        // enter进入self视野
+        public static void EnterSight(this AOIEntity self, AOIEntity enter)
+        {
+            // 有可能之前在Enter,后来出了Enter还在LeaveCell,这样仍然没有删除,继续进来Enter,这种情况不需要处理
+            if (self.SeeUnits.ContainsKey(enter.Id))
+            {
+                return;
+            }
+            
+            if (!AOISeeCheckHelper.IsCanSee(self, enter))
+            {
+                return;
+            }
+
+            if (self.Unit.Type == UnitType.Player)
+            {
+                if (enter.Unit.Type == UnitType.Player)
+                {
+                    self.SeeUnits.Add(enter.Id, enter);
+                    enter.BeSeeUnits.Add(self.Id, self);
+                    self.SeePlayers.Add(enter.Id, enter);
+                    enter.BeSeePlayers.Add(self.Id, self);
+                    
+                }
+                else
+                {
+                    self.SeeUnits.Add(enter.Id, enter);
+                    enter.BeSeeUnits.Add(self.Id, self);
+                    enter.BeSeePlayers.Add(self.Id, self);
+                }
+            }
+            else
+            {
+                if (enter.Unit.Type == UnitType.Player)
+                {
+                    self.SeeUnits.Add(enter.Id, enter);
+                    enter.BeSeeUnits.Add(self.Id, self);
+                    self.SeePlayers.Add(enter.Id, enter);
+                }
+                else
+                {
+                    self.SeeUnits.Add(enter.Id, enter);
+                    enter.BeSeeUnits.Add(self.Id, self);
+                }
+            }
+            Game.EventSystem.Publish(new EventType.UnitEnterSightRange() {A = self, B = enter});
+        }
+
+        // leave离开self视野
+        public static void LeaveSight(this AOIEntity self, AOIEntity leave)
+        {
+            if (self.Id == leave.Id)
+            {
+                return;
+            }
+
+            if (!self.SeeUnits.ContainsKey(leave.Id))
+            {
+                return;
+            }
+
+            self.SeeUnits.Remove(leave.Id);
+            if (leave.Unit.Type == UnitType.Player)
+            {
+                self.SeePlayers.Remove(leave.Id);
+            }
+
+            leave.BeSeeUnits.Remove(self.Id);
+            if (self.Unit.Type == UnitType.Player)
+            {
+                leave.BeSeePlayers.Remove(self.Id);
+            }
+
+            Game.EventSystem.Publish(new EventType.UnitLeaveSightRange {A = self, B = leave});
+        }
+
+        /// <summary>
+        /// 是否在Unit视野范围内
+        /// </summary>
+        /// <param name="self"></param>
+        /// <param name="unitId"></param>
+        /// <returns></returns>
+        public static bool IsBeSee(this AOIEntity self, long unitId)
+        {
+            return self.BeSeePlayers.ContainsKey(unitId);
+        }
+    }
+}

+ 40 - 0
Server/Hotfix/Demo/AOI/AOIHelper.cs

@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+
+namespace ET
+{
+    public static class AOIHelper
+    {
+        public static long CreateCellId(int x, int y)
+        {
+            return (long) ((ulong) x << 32) | (uint) y;
+        }
+
+        public static void CalcEnterAndLeaveCell(AOIEntity aoiEntity, int cellX, int cellY, HashSet<long> enterCell, HashSet<long> leaveCell)
+        {
+            enterCell.Clear();
+            leaveCell.Clear();
+            int r = (aoiEntity.ViewDistance - 1) / AOIManagerComponent.CellSize + 1;
+            int leaveR = r;
+            if (aoiEntity.Unit.Type == UnitType.Player)
+            {
+                leaveR += 1;
+            }
+
+            for (int i = cellX - leaveR; i <= cellX + leaveR; ++i)
+            {
+                for (int j = cellY - leaveR; j <= cellY + leaveR; ++j)
+                {
+                    long cellId = CreateCellId(i, j);
+                    leaveCell.Add(cellId);
+
+                    if (i > cellX + r || i < cellX - r || j > cellY + r || j < cellY - r)
+                    {
+                        continue;
+                    }
+
+                    enterCell.Add(cellId);
+                }
+            }
+        }
+    }
+}

+ 184 - 0
Server/Hotfix/Demo/AOI/AOIManagerComponentSystem.cs

@@ -0,0 +1,184 @@
+using System.Collections.Generic;
+
+namespace ET
+{
+    public static class AOIManagerComponentSystem
+    {
+        public static void Add(this AOIManagerComponent self, AOIEntity aoiEntity, float x, float y)
+        {
+            int cellX = (int)(x * 1000) / AOIManagerComponent.CellSize;
+            int cellY = (int)(y * 1000) / AOIManagerComponent.CellSize;
+
+            if (aoiEntity.ViewDistance == 0)
+            {
+                aoiEntity.ViewDistance = 1;
+            }
+
+            AOIHelper.CalcEnterAndLeaveCell(aoiEntity, cellX, cellY, aoiEntity.SubEnterCells, aoiEntity.SubLeaveCells);
+
+            // 遍历EnterCell
+            foreach (long cellId in aoiEntity.SubEnterCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.SubEnter(cell);
+            }
+
+            // 遍历LeaveCell
+            foreach (long cellId in aoiEntity.SubLeaveCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.SubLeave(cell);
+            }
+
+            // 自己加入的Cell
+            Cell selfCell = self.GetCell(AOIHelper.CreateCellId(cellX, cellY));
+            aoiEntity.Cell = selfCell;
+            selfCell.Add(aoiEntity);
+            // 通知订阅该Cell Enter的Unit
+            foreach (KeyValuePair<long, AOIEntity> kv in selfCell.SubsEnterEntities)
+            {
+                kv.Value.EnterSight(aoiEntity);
+            }
+        }
+
+        public static void Remove(this AOIManagerComponent self, AOIEntity aoiEntity)
+        {
+            if (aoiEntity.Cell == null)
+            {
+                return;
+            }
+
+            // 通知订阅该Cell Leave的Unit
+            aoiEntity.Cell.Remove(aoiEntity);
+            foreach (KeyValuePair<long, AOIEntity> kv in aoiEntity.Cell.SubsLeaveEntities)
+            {
+                kv.Value.LeaveSight(aoiEntity);
+            }
+
+            // 通知自己订阅的Enter Cell,清理自己
+            foreach (long cellId in aoiEntity.SubEnterCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.UnSubEnter(cell);
+            }
+
+            foreach (long cellId in aoiEntity.SubLeaveCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.UnSubLeave(cell);
+            }
+
+            // 检查
+            if (aoiEntity.SeeUnits.Count > 1)
+            {
+                Log.Error($"aoiEntity has see units: {aoiEntity.SeeUnits.Count}");
+            }
+
+            if (aoiEntity.BeSeeUnits.Count > 1)
+            {
+                Log.Error($"aoiEntity has beSee units: {aoiEntity.BeSeeUnits.Count}");
+            }
+        }
+
+        private static Cell GetCell(this AOIManagerComponent self, long cellId)
+        {
+            Cell cell = self.GetChild<Cell>(cellId);
+            if (cell == null)
+            {
+                cell = self.AddChildWithId<Cell>(cellId);
+            }
+
+            return cell;
+        }
+
+        public static void Move(AOIEntity aoiEntity, Cell newCell, Cell preCell)
+        {
+            aoiEntity.Cell = newCell;
+            preCell.Remove(aoiEntity);
+            newCell.Add(aoiEntity);
+            // 通知订阅该newCell Enter的Unit
+            foreach (KeyValuePair<long, AOIEntity> kv in newCell.SubsEnterEntities)
+            {
+                if (kv.Value.SubEnterCells.Contains(preCell.Id))
+                {
+                    continue;
+                }
+
+                kv.Value.EnterSight(aoiEntity);
+            }
+
+            // 通知订阅preCell leave的Unit
+            foreach (KeyValuePair<long, AOIEntity> kv in preCell.SubsLeaveEntities)
+            {
+                // 如果新的cell仍然在对方订阅的subleave中
+                if (kv.Value.SubLeaveCells.Contains(newCell.Id))
+                {
+                    continue;
+                }
+
+                kv.Value.LeaveSight(aoiEntity);
+            }
+        }
+
+        public static void Move(this AOIManagerComponent self, AOIEntity aoiEntity, int cellX, int cellY)
+        {
+            long newCellId = AOIHelper.CreateCellId(cellX, cellY);
+            if (aoiEntity.Cell.Id == newCellId) // cell没有变化
+            {
+                return;
+            }
+
+            // 自己加入新的Cell
+            Cell newCell = self.GetCell(newCellId);
+            Move(aoiEntity, newCell, aoiEntity.Cell);
+
+            AOIHelper.CalcEnterAndLeaveCell(aoiEntity, cellX, cellY, aoiEntity.enterHashSet, aoiEntity.leaveHashSet);
+
+            // 算出自己leave新Cell
+            foreach (long cellId in aoiEntity.leaveHashSet)
+            {
+                if (aoiEntity.SubLeaveCells.Contains(cellId))
+                {
+                    continue;
+                }
+
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.SubLeave(cell);
+            }
+
+            // 算出需要通知离开的Cell
+            aoiEntity.SubLeaveCells.ExceptWith(aoiEntity.leaveHashSet);
+            foreach (long cellId in aoiEntity.SubLeaveCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.UnSubLeave(cell);
+            }
+
+            // 这里交换两个HashSet,提高性能
+            ObjectHelper.Swap(ref aoiEntity.SubLeaveCells, ref aoiEntity.leaveHashSet);
+
+            // 算出自己看到的新Cell
+            foreach (long cellId in aoiEntity.enterHashSet)
+            {
+                if (aoiEntity.SubEnterCells.Contains(cellId))
+                {
+                    continue;
+                }
+
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.SubEnter(cell);
+            }
+
+            // 离开的Enter
+            aoiEntity.SubEnterCells.ExceptWith(aoiEntity.enterHashSet);
+            foreach (long cellId in aoiEntity.SubEnterCells)
+            {
+                Cell cell = self.GetCell(cellId);
+                aoiEntity.UnSubEnter(cell);
+            }
+
+            // 这里交换两个HashSet,提高性能
+            ObjectHelper.Swap(ref aoiEntity.SubEnterCells, ref aoiEntity.enterHashSet);
+        }
+    }
+}

+ 10 - 0
Server/Hotfix/Demo/AOI/AOISeeCheckHelper.cs

@@ -0,0 +1,10 @@
+namespace ET
+{
+    public static class AOISeeCheckHelper
+    {
+        public static bool IsCanSee(AOIEntity a, AOIEntity b)
+        {
+            return true;
+        }
+    }
+}

+ 50 - 0
Server/Hotfix/Demo/AOI/CellSystem.cs

@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Text;
+
+namespace ET
+{
+    [ObjectSystem]
+    public class CellDestroySystem: DestroySystem<Cell>
+    {
+        public override void Destroy(Cell self)
+        {
+            self.AOIUnits.Clear();
+
+            self.SubsEnterEntities.Clear();
+
+            self.SubsLeaveEntities.Clear();
+        }
+    }
+
+    public static class CellSystem
+    {
+        public static void Add(this Cell self, AOIEntity aoiEntity)
+        {
+            self.AOIUnits.Add(aoiEntity.Id, aoiEntity);
+        }
+        
+        public static void Remove(this Cell self, AOIEntity aoiEntity)
+        {
+            self.AOIUnits.Remove(aoiEntity.Id);
+        }
+        
+        public static string CellIdToString(this long cellId)
+        {
+            int y = (int) (cellId & 0xffffffff);
+            int x = (int) ((ulong) cellId >> 32);
+            return $"{x}:{y}";
+        }
+
+        public static string CellIdToString(this HashSet<long> cellIds)
+        {
+            StringBuilder sb = new StringBuilder();
+            foreach (long cellId in cellIds)
+            {
+                sb.Append(cellId.CellIdToString());
+                sb.Append(",");
+            }
+
+            return sb.ToString();
+        }
+    }
+}

+ 31 - 0
Server/Hotfix/Demo/AOI/ChangePosition_NotifyAOI.cs

@@ -0,0 +1,31 @@
+using UnityEngine;
+
+namespace ET
+{
+    [Event]
+    public class ChangePosition_NotifyAOI: AEvent<EventType.ChangePosition>
+    {
+        protected override async ETTask Run(EventType.ChangePosition args)
+        {
+            await ETTask.CompletedTask;
+            Vector3 oldPos = args.OldPos;
+            Unit unit = args.Unit;
+            int oldCellX = (int) (oldPos.x * 1000) / AOIManagerComponent.CellSize;
+            int oldCellY = (int) (oldPos.z * 1000) / AOIManagerComponent.CellSize;
+            int newCellX = (int) (unit.Position.x * 1000) / AOIManagerComponent.CellSize;
+            int newCellY = (int) (unit.Position.z * 1000) / AOIManagerComponent.CellSize;
+            if (oldCellX == newCellX && oldCellY == newCellY)
+            {
+                return;
+            }
+
+            AOIEntity aoiEntity = unit.GetComponent<AOIEntity>();
+            if (aoiEntity == null)
+            {
+                return;
+            }
+
+            unit.Domain.GetComponent<AOIManagerComponent>().Move(aoiEntity, newCellX, newCellY);
+        }
+    }
+}

+ 2 - 2
Server/Hotfix/Demo/C2G_EnterMapHandler.cs

@@ -13,8 +13,8 @@ namespace ET
 			long mapInstanceId = StartSceneConfigCategory.Instance.GetBySceneName(session.DomainZone(), "Map").InstanceId;
 			M2G_CreateUnit createUnit = (M2G_CreateUnit)await ActorMessageSenderComponent.Instance.Call(
 				mapInstanceId, new G2M_CreateUnit() { PlayerId = player.Id, GateSessionId = session.InstanceId });
-			player.UnitId = createUnit.UnitId;
-			response.UnitId = createUnit.UnitId;
+			player.UnitId = createUnit.MyId;
+			response.MyId = createUnit.MyId;
 			reply();
 		}
 	}

+ 9 - 14
Server/Hotfix/Demo/G2M_CreateUnitHandler.cs

@@ -10,33 +10,28 @@ namespace ET
 		{
 			UnitComponent unitComponent = scene.GetComponent<UnitComponent>();
 			Unit unit = unitComponent.AddChildWithId<Unit, int>(IdGenerater.Instance.GenerateId(), 1001);
+			unit.Type = UnitType.Player;
 			unit.AddComponent<MoveComponent>();
 			unit.AddComponent<PathfindingComponent, string>("solo");
 			unit.Position = new Vector3(-10, 0, -10);
 			
 			NumericComponent numericComponent = unit.AddComponent<NumericComponent>();
 			numericComponent.Set(NumericType.Speed, 6f); // 速度是6米每秒
+			numericComponent.Set(NumericType.AOI, 15000); // 视野15米
 			
 			unit.AddComponent<MailBoxComponent>();
 			await unit.AddLocation();
 			unit.AddComponent<UnitGateComponent, long>(request.GateSessionId);
 			unitComponent.Add(unit);
-			response.UnitId = unit.Id;
+			// 加入aoi
+			unit.AddComponent<AOIEntity, int, Vector3>(15, unit.Position);
+
+			M2C_CreateUnits m2CCreateUnits = new M2C_CreateUnits();
+			m2CCreateUnits.Units.Add(UnitHelper.CreateUnitInfo(unit));
+			MessageHelper.SendToClient(unit, m2CCreateUnits);
 			
-			// 把自己广播给周围的人
-			M2C_CreateUnits createUnits = new M2C_CreateUnits();
-			createUnits.Units.Add(UnitHelper.CreateUnitInfo(unit));
-			MessageHelper.Broadcast(unit, createUnits);
+			response.MyId = unit.Id;
 			
-			// 把周围的人通知给自己
-			createUnits.Units.Clear();
-			Unit[] units = scene.GetComponent<UnitComponent>().GetAll();
-			foreach (Unit u in units)
-			{
-				createUnits.Units.Add(UnitHelper.CreateUnitInfo(u));
-			}
-			MessageHelper.SendActor(unit.GetComponent<UnitGateComponent>().GateSessionActorId, createUnits);
-
 			reply();
 		}
 	}

+ 10 - 10
Server/Hotfix/Demo/MessageHelper.cs

@@ -1,25 +1,25 @@
 
 
+using System.Collections.Generic;
+
 namespace ET
 {
     public static class MessageHelper
     {
         public static void Broadcast(Unit unit, IActorMessage message)
         {
-            var units = unit.Domain.GetComponent<UnitComponent>().GetAll();
-
-            if (units == null)
-            {
-                return;
-            }
-
-            foreach (Unit u in units)
+            Dictionary<long, AOIEntity> dict = unit.GetBeSeePlayers();
+            foreach (AOIEntity u in dict.Values)
             {
-                UnitGateComponent unitGateComponent = u.GetComponent<UnitGateComponent>();
-                SendActor(unitGateComponent.GateSessionActorId, message);
+                SendToClient(u.Unit, message);
             }
         }
         
+        public static void SendToClient(Unit unit, IActorMessage message)
+        {
+            SendActor(unit.GetComponent<UnitGateComponent>().GateSessionActorId, message);
+        }
+        
         
         /// <summary>
         /// 发送协议给ActorLocation

+ 1 - 0
Server/Hotfix/Demo/Scene/SceneFactory.cs

@@ -31,6 +31,7 @@ namespace ET
                     break;
                 case SceneType.Map:
                     scene.AddComponent<UnitComponent>();
+                    scene.AddComponent<AOIManagerComponent>();
                     break;
                 case SceneType.Location:
                     scene.AddComponent<LocationComponent>();

+ 28 - 0
Server/Hotfix/Demo/Unit/UnitEnterSightRange_NotifyClient.cs

@@ -0,0 +1,28 @@
+namespace ET
+{
+    // 进入视野通知
+    [Event]
+    public class UnitEnterSightRange_NotifyClient: AEvent<EventType.UnitEnterSightRange>
+    {
+        protected override async ETTask Run(EventType.UnitEnterSightRange args)
+        {
+            await ETTask.CompletedTask;
+            AOIEntity a = args.A;
+            AOIEntity b = args.B;
+            if (a.Id == b.Id)
+            {
+                return;
+            }
+
+            Unit ua = a.GetParent<Unit>();
+            if (ua.Type != UnitType.Player)
+            {
+                return;
+            }
+
+            Unit ub = b.GetParent<Unit>();
+
+            UnitHelper.NoticeUnitAdd(ua, ub);
+        }
+    }
+}

+ 23 - 1
Server/Hotfix/Demo/Unit/UnitHelper.cs

@@ -1,4 +1,6 @@
-namespace ET
+using System.Collections.Generic;
+
+namespace ET
 {
     public static class UnitHelper
     {
@@ -20,5 +22,25 @@
 
             return unitInfo;
         }
+        
+        // 获取看见unit的玩家,主要用于广播
+        public static Dictionary<long, AOIEntity> GetBeSeePlayers(this Unit self)
+        {
+            return self.GetComponent<AOIEntity>().GetBeSeePlayers();
+        }
+        
+        public static void NoticeUnitAdd(Unit unit, Unit sendUnit)
+        {
+            M2C_CreateUnits createUnits = new M2C_CreateUnits();
+            createUnits.Units.Add(CreateUnitInfo(sendUnit));
+            MessageHelper.SendToClient(unit, createUnits);
+        }
+        
+        public static void NoticeUnitRemove(Unit unit, Unit sendUnit)
+        {
+            M2C_RemoveUnits removeUnits = new M2C_RemoveUnits();
+            removeUnits.Units.Add(sendUnit.Id);
+            MessageHelper.SendToClient(unit, removeUnits);
+        }
     }
 }

+ 20 - 0
Server/Hotfix/Demo/Unit/UnitLeaveSightRange_NotifyClient.cs

@@ -0,0 +1,20 @@
+namespace ET
+{
+    // 离开视野
+    [Event]
+    public class UnitLeaveSightRange_NotifyClient: AEvent<EventType.UnitLeaveSightRange>
+    {
+        protected override async ETTask Run(EventType.UnitLeaveSightRange args)
+        {
+            await ETTask.CompletedTask;
+            AOIEntity a = args.A;
+            AOIEntity b = args.B;
+            if (a.Unit.Type != UnitType.Player)
+            {
+                return;
+            }
+
+            UnitHelper.NoticeUnitRemove(a.GetParent<Unit>(), b.GetParent<Unit>());
+        }
+    }
+}

+ 37 - 0
Server/Model/Demo/AOI/AOIEntity.cs

@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace ET
+{
+    public class AOIEntity: Entity
+    {
+        public Unit Unit => this.GetParent<Unit>();
+
+        public int ViewDistance;
+
+        public Cell Cell;
+
+        // 观察进入视野的Cell
+        public HashSet<long> SubEnterCells = new HashSet<long>();
+
+        // 观察离开视野的Cell
+        public HashSet<long> SubLeaveCells = new HashSet<long>();
+        
+        // 观察进入视野的Cell
+        public HashSet<long> enterHashSet = new HashSet<long>();
+
+        // 观察离开视野的Cell
+        public HashSet<long> leaveHashSet = new HashSet<long>();
+
+        // 我看的见的Unit
+        public Dictionary<long, AOIEntity> SeeUnits = new Dictionary<long, AOIEntity>();
+        
+        // 看见我的Unit
+        public Dictionary<long, AOIEntity> BeSeeUnits = new Dictionary<long, AOIEntity>();
+        
+        // 我看的见的Player
+        public Dictionary<long, AOIEntity> SeePlayers = new Dictionary<long, AOIEntity>();
+
+        // 看见我的Player单独放一个Dict,用于广播
+        public Dictionary<long, AOIEntity> BeSeePlayers = new Dictionary<long, AOIEntity>();
+    }
+}

+ 7 - 0
Server/Model/Demo/AOI/AOIManagerComponent.cs

@@ -0,0 +1,7 @@
+namespace ET
+{
+    public class AOIManagerComponent: Entity
+    {
+        public static int CellSize = 10 * 1000;
+    }
+}

+ 16 - 0
Server/Model/Demo/AOI/Cell.cs

@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace ET
+{
+    public class Cell: Entity
+    {
+        // 处在这个cell的单位
+        public Dictionary<long, AOIEntity> AOIUnits = new Dictionary<long, AOIEntity>();
+
+        // 订阅了这个Cell的进入事件
+        public Dictionary<long, AOIEntity> SubsEnterEntities = new Dictionary<long, AOIEntity>();
+
+        // 订阅了这个Cell的退出事件
+        public Dictionary<long, AOIEntity> SubsLeaveEntities = new Dictionary<long, AOIEntity>();
+    }
+}

+ 16 - 1
Server/Model/Demo/EventType.cs

@@ -1,4 +1,6 @@
-namespace ET
+using UnityEngine;
+
+namespace ET
 {
 	namespace EventType
 	{
@@ -9,6 +11,7 @@
 		public struct ChangePosition
 		{
 			public Unit Unit;
+			public Vector3 OldPos;
 		}
 
 		public struct ChangeRotation
@@ -25,5 +28,17 @@
 		{
 			public Unit Unit;
 		}
+
+		public struct UnitEnterSightRange
+		{
+			public AOIEntity A;
+			public AOIEntity B;
+		}
+
+		public struct UnitLeaveSightRange
+		{
+			public AOIEntity A;
+			public AOIEntity B;
+		}
 	}
 }

+ 2 - 0
Server/Model/Demo/Unit/Unit.cs

@@ -8,6 +8,8 @@ namespace ET
     {
         public int ConfigId; //配置表id
 
+        public UnitType Type;
+
         [BsonIgnore]
         public UnitConfig Config => UnitConfigCategory.Instance.Get(this.ConfigId);
 

+ 2 - 6
Server/Model/Generate/Message/InnerMessage.cs

@@ -345,13 +345,9 @@ namespace ET
 		[ProtoMember(92)]
 		public string Message { get; set; }
 
-// 自己的unit id
-		[ProtoMember(1)]
-		public long UnitId { get; set; }
-
 // 所有的unit
-		[ProtoMember(2)]
-		public List<UnitInfo> Units = new List<UnitInfo>();
+		[ProtoMember(1)]
+		public long MyId { get; set; }
 
 	}
 

+ 11 - 6
Server/Model/Generate/Message/OuterMessage.cs

@@ -85,13 +85,9 @@ namespace ET
 		[ProtoMember(92)]
 		public string Message { get; set; }
 
-// 自己unit id
+// 自己unit
 		[ProtoMember(1)]
-		public long UnitId { get; set; }
-
-// 所有的unit
-		[ProtoMember(2)]
-		public List<UnitInfo> Units = new List<UnitInfo>();
+		public long MyId { get; set; }
 
 	}
 
@@ -134,6 +130,15 @@ namespace ET
 
 	}
 
+	[Message(OuterOpcode.M2C_RemoveUnits)]
+	[ProtoContract]
+	public partial class M2C_RemoveUnits: Object, IActorMessage
+	{
+		[ProtoMember(2)]
+		public List<long> Units = new List<long>();
+
+	}
+
 	[Message(OuterOpcode.C2M_PathfindingResult)]
 	[ProtoContract]
 	public partial class C2M_PathfindingResult: Object, IActorLocationMessage

+ 17 - 16
Server/Model/Generate/Message/OuterOpcode.cs

@@ -10,21 +10,22 @@ namespace ET
 		 public const ushort G2C_EnterMap = 10007;
 		 public const ushort UnitInfo = 10008;
 		 public const ushort M2C_CreateUnits = 10009;
-		 public const ushort C2M_PathfindingResult = 10010;
-		 public const ushort C2M_Stop = 10011;
-		 public const ushort M2C_PathfindingResult = 10012;
-		 public const ushort M2C_Stop = 10013;
-		 public const ushort C2G_Ping = 10014;
-		 public const ushort G2C_Ping = 10015;
-		 public const ushort G2C_Test = 10016;
-		 public const ushort C2M_Reload = 10017;
-		 public const ushort M2C_Reload = 10018;
-		 public const ushort C2R_Login = 10019;
-		 public const ushort R2C_Login = 10020;
-		 public const ushort C2G_LoginGate = 10021;
-		 public const ushort G2C_LoginGate = 10022;
-		 public const ushort G2C_TestHotfixMessage = 10023;
-		 public const ushort C2M_TestRobotCase = 10024;
-		 public const ushort M2C_TestRobotCase = 10025;
+		 public const ushort M2C_RemoveUnits = 10010;
+		 public const ushort C2M_PathfindingResult = 10011;
+		 public const ushort C2M_Stop = 10012;
+		 public const ushort M2C_PathfindingResult = 10013;
+		 public const ushort M2C_Stop = 10014;
+		 public const ushort C2G_Ping = 10015;
+		 public const ushort G2C_Ping = 10016;
+		 public const ushort G2C_Test = 10017;
+		 public const ushort C2M_Reload = 10018;
+		 public const ushort M2C_Reload = 10019;
+		 public const ushort C2R_Login = 10020;
+		 public const ushort R2C_Login = 10021;
+		 public const ushort C2G_LoginGate = 10022;
+		 public const ushort G2C_LoginGate = 10023;
+		 public const ushort G2C_TestHotfixMessage = 10024;
+		 public const ushort C2M_TestRobotCase = 10025;
+		 public const ushort M2C_TestRobotCase = 10026;
 	}
 }

+ 4 - 0
Server/Model/Server.Model.csproj

@@ -26,6 +26,10 @@
       <Link>Core\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
 
+    <Compile Include="..\..\Unity\Codes\Model\Demo\Unit\UnitType.cs">
+      <Link>Demo\Unit\UnitType.cs</Link>
+    </Compile>
+
     <Compile Include="..\..\Unity\Codes\Model\Module\Recast\**\*.cs">
       <Link>Module\Recast\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>

+ 0 - 1
Unity/Assets/StreamingAssets/BuildProjectNeedThisFolder.md

@@ -1 +0,0 @@
-BuildProjectNeedThisFolder

+ 1 - 1
Unity/Codes/Hotfix/Demo/AI/AI_Attack.cs

@@ -18,7 +18,7 @@ namespace ET
         {
             Scene zoneScene = aiComponent.DomainScene();
 
-            Unit myUnit = zoneScene.GetComponent<UnitComponent>().MyUnit;
+            Unit myUnit = UnitHelper.GetMyUnit(zoneScene);
             if (myUnit == null)
             {
                 return;

+ 1 - 1
Unity/Codes/Hotfix/Demo/AI/AI_XunLuo.cs

@@ -18,7 +18,7 @@ namespace ET
         {
             Scene zoneScene = aiComponent.DomainScene();
 
-            Unit myUnit = zoneScene.GetComponent<UnitComponent>().MyUnit;
+            Unit myUnit = UnitHelper.GetMyUnit(zoneScene);
             if (myUnit == null)
             {
                 return;

+ 1 - 1
Unity/Codes/Hotfix/Demo/Login/EnterMapHelper.cs

@@ -12,7 +12,7 @@ namespace ET
                 G2C_EnterMap g2CEnterMap = await zoneScene.GetComponent<SessionComponent>().Session.Call(new C2G_EnterMap()) as G2C_EnterMap;
 
                 UnitComponent unitComponent = zoneScene.GetComponent<UnitComponent>();
-                unitComponent.MyUnit = unitComponent.Get(g2CEnterMap.UnitId);
+                unitComponent.MyId = g2CEnterMap.MyId;
                 
                 Game.EventSystem.Publish(new EventType.EnterMapFinish() {ZoneScene = zoneScene}).Coroutine();
             }

+ 1 - 4
Unity/Codes/Hotfix/Demo/Unit/M2C_CreateUnitsHandler.cs

@@ -1,7 +1,4 @@
-
-using Vector3 = UnityEngine.Vector3;
-
-namespace ET
+namespace ET
 {
 	[MessageHandler]
 	public class M2C_CreateUnitsHandler : AMHandler<M2C_CreateUnits>

+ 17 - 0
Unity/Codes/Hotfix/Demo/Unit/M2C_RemoveUnitsHandler.cs

@@ -0,0 +1,17 @@
+namespace ET
+{
+	[MessageHandler]
+	public class M2C_RemoveUnitsHandler : AMHandler<M2C_RemoveUnits>
+	{
+		protected override async ETTask Run(Session session, M2C_RemoveUnits message)
+		{	
+			UnitComponent unitComponent = session.Domain.GetComponent<UnitComponent>();
+			foreach (long unitId in message.Units)
+			{
+				unitComponent.Remove(unitId);
+			}
+
+			await ETTask.CompletedTask;
+		}
+	}
+}

+ 11 - 0
Unity/Codes/Hotfix/Demo/Unit/UnitHelper.cs

@@ -0,0 +1,11 @@
+namespace ET
+{
+    public static class UnitHelper
+    {
+        public static Unit GetMyUnit(Scene zoneScene)
+        {
+            UnitComponent unitComponent = zoneScene.GetComponent<UnitComponent>();
+            return unitComponent.Get(unitComponent.MyId);
+        }
+    }
+}

+ 16 - 0
Unity/Codes/HotfixView/Demo/Unit/GameObjectComponentSystem.cs

@@ -0,0 +1,16 @@
+using System;
+
+namespace ET
+{
+    public static class GameObjectComponentSystem
+    {
+        [ObjectSystem]
+        public class DestroySystem: DestroySystem<GameObjectComponent>
+        {
+            public override void Destroy(GameObjectComponent self)
+            {
+                UnityEngine.Object.Destroy(self.GameObject);
+            }
+        }
+    }
+}

+ 8 - 1
Unity/Codes/Model/Demo/EventType.cs

@@ -1,4 +1,6 @@
-namespace ET
+using UnityEngine;
+
+namespace ET
 {
     namespace EventType
     {
@@ -9,6 +11,7 @@
         public struct ChangePosition
         {
             public Unit Unit;
+            public Vector3 OldPos;
         }
 
         public struct ChangeRotation
@@ -71,5 +74,9 @@
         {
             public Unit Unit;
         }
+
+        public struct UnitEnterSightRange
+        {
+        }
     }
 }

+ 2 - 1
Unity/Codes/Model/Demo/Unit/Unit.cs

@@ -18,8 +18,9 @@ namespace ET
             get => this.position;
             set
             {
+                Vector3 oldPos = this.position;
                 this.position = value;
-                Game.EventSystem.Publish(new EventType.ChangePosition() { Unit = this }).Coroutine();
+                Game.EventSystem.Publish(new EventType.ChangePosition() { Unit = this, OldPos = oldPos }).Coroutine();
             }
         }
 

+ 1 - 2
Unity/Codes/Model/Demo/Unit/UnitComponent.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Linq;
 
 namespace ET
 {
@@ -7,6 +6,6 @@ namespace ET
 	public class UnitComponent: Entity
 	{
 		public Dictionary<long, Unit> idUnits = new Dictionary<long, Unit>();
-		public Unit MyUnit;
+		public long MyId;
 	}
 }

+ 9 - 0
Unity/Codes/Model/Demo/Unit/UnitType.cs

@@ -0,0 +1,9 @@
+namespace ET
+{
+    public enum UnitType: byte
+    {
+        Player = 1,
+        Monster = 2,
+        NPC = 3,
+    }
+}

+ 11 - 6
Unity/Codes/Model/Generate/Message/OuterMessage.cs

@@ -85,13 +85,9 @@ namespace ET
 		[ProtoMember(92)]
 		public string Message { get; set; }
 
-// 自己unit id
+// 自己unit
 		[ProtoMember(1)]
-		public long UnitId { get; set; }
-
-// 所有的unit
-		[ProtoMember(2)]
-		public List<UnitInfo> Units = new List<UnitInfo>();
+		public long MyId { get; set; }
 
 	}
 
@@ -134,6 +130,15 @@ namespace ET
 
 	}
 
+	[Message(OuterOpcode.M2C_RemoveUnits)]
+	[ProtoContract]
+	public partial class M2C_RemoveUnits: Object, IActorMessage
+	{
+		[ProtoMember(2)]
+		public List<long> Units = new List<long>();
+
+	}
+
 	[Message(OuterOpcode.C2M_PathfindingResult)]
 	[ProtoContract]
 	public partial class C2M_PathfindingResult: Object, IActorLocationMessage

+ 17 - 16
Unity/Codes/Model/Generate/Message/OuterOpcode.cs

@@ -10,21 +10,22 @@ namespace ET
 		 public const ushort G2C_EnterMap = 10007;
 		 public const ushort UnitInfo = 10008;
 		 public const ushort M2C_CreateUnits = 10009;
-		 public const ushort C2M_PathfindingResult = 10010;
-		 public const ushort C2M_Stop = 10011;
-		 public const ushort M2C_PathfindingResult = 10012;
-		 public const ushort M2C_Stop = 10013;
-		 public const ushort C2G_Ping = 10014;
-		 public const ushort G2C_Ping = 10015;
-		 public const ushort G2C_Test = 10016;
-		 public const ushort C2M_Reload = 10017;
-		 public const ushort M2C_Reload = 10018;
-		 public const ushort C2R_Login = 10019;
-		 public const ushort R2C_Login = 10020;
-		 public const ushort C2G_LoginGate = 10021;
-		 public const ushort G2C_LoginGate = 10022;
-		 public const ushort G2C_TestHotfixMessage = 10023;
-		 public const ushort C2M_TestRobotCase = 10024;
-		 public const ushort M2C_TestRobotCase = 10025;
+		 public const ushort M2C_RemoveUnits = 10010;
+		 public const ushort C2M_PathfindingResult = 10011;
+		 public const ushort C2M_Stop = 10012;
+		 public const ushort M2C_PathfindingResult = 10013;
+		 public const ushort M2C_Stop = 10014;
+		 public const ushort C2G_Ping = 10015;
+		 public const ushort G2C_Ping = 10016;
+		 public const ushort G2C_Test = 10017;
+		 public const ushort C2M_Reload = 10018;
+		 public const ushort M2C_Reload = 10019;
+		 public const ushort C2R_Login = 10020;
+		 public const ushort R2C_Login = 10021;
+		 public const ushort C2G_LoginGate = 10022;
+		 public const ushort G2C_LoginGate = 10023;
+		 public const ushort G2C_TestHotfixMessage = 10024;
+		 public const ushort C2M_TestRobotCase = 10025;
+		 public const ushort M2C_TestRobotCase = 10026;
 	}
 }

+ 7 - 0
Unity/Codes/Model/Module/Numeric/NumericType.cs

@@ -20,5 +20,12 @@
 	    MaxHpPct = MaxHp * 10 + 3,
 	    MaxHpFinalAdd = MaxHp * 10 + 4,
 		MaxHpFinalPct = MaxHp * 10 + 5,
+		
+		AOI = 1003,
+		AOIBase = MaxHp * 10 + 1,
+		AOIAdd = MaxHp * 10 + 2,
+		AOIPct = MaxHp * 10 + 3,
+		AOIFinalAdd = MaxHp * 10 + 4,
+		AOIFinalPct = MaxHp * 10 + 5,
 	}
 }