Skip to content

Latest commit

 

History

History
168 lines (132 loc) · 6.35 KB

File metadata and controls

168 lines (132 loc) · 6.35 KB

Zone #2

일단 인접한 영역에 있는 player들에게 Broadcasting을 하도록 수정이 끝났다.

몬스터는 정상적으로 걸러서 정보를 전달해주고 있다.

하지만 클라에서 인접한 영역에 있는 오브젝트만 화면에 보이도록 수정이 필요하다.

현재 이렇게 되지 않는 이유는 EnterGame을 했을 때 GameRoom에 있는 모든 Object를 정보를 보내주고 있기 때문이다.

따라서 쉽게 생각하면 EnterGame을 했을 때 인접한 Zone에 있는 Object들의 정보만 보여주면 되는 게 아닐까 싶다.

하지만 한가지 생각을 해야할 것은

내 시야에서(영역에서) 벗어나게 되면 Despawn을 해주는 것과 마찬가지가 된다.

따라서 이렇게 되면 내 시야각에 들어올 때는 Spawn을 해주고 벗어나면 Despawn을 해주는 방식을 사용해야 한다.

MMO와 같이 거대한 대륙에서 모든 플레이어의 정보를 화면에 뿌려줘야하는 것은 말이 안되고

내가 정말로 필요한 player들의 정보만 보여줘야 하기 때문이다.

시야각에 들었을 때 플레이어를 보여주고 벗어났을 때 또 사라지도록 하기 위해서 깔끔하게 구현하기가 생각보다 쉽지가 않다.

또 쉽게 생각해서 몬스터가 스킬을 쓸 때 자신의 주변에 있는 player에게만 broadcasting을 하면 깔끔하게 처리될 수 있지 않을까 싶은데 결국에는 몬스터와 player의 거리에 따라서 몬스터는 안움직이고 있는데 플레이어가 움직여서 플레이어한테 몬스터가 사라지는 패킷을 보내야하는 상황이 발생할 수 있게 된다.

어디까지나 player 시야각 안에서 많은 일들이 일어나야 하는데 이것을 그나마 깔끔하게 처리할 수 있는 방법 중 하나가

player의 시야각을 관리하는 class를 만들어서 계속 추적을 하는 방식이다.

즉, 누가 내 시야각에 들어왔는지를 지속적으로 관리해주는 class를 만들어보자.

내가 현재 있는 위치에서 보통 1초에 한번씩 주변에 있는 object를 저장해놓는 class

using Google.Protobuf;
using Google.Protobuf.Protocol;
using Server.Data;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Server.Game
{
    public class VisionCube
    {
        public Player Owner { get; private set; }
        public HashSet<GameObject> PreviousObject { get; private set; } = new HashSet<GameObject>();

        public VisionCube(Player owner)
        {
            Owner = owner;
        }

        public HashSet<GameObject> GatherObject()
        {
            // 차후 Job 방식으로 처리해주기 위한 null 체크
            if (Owner == null || Owner.Room == null)
                return null;
            HashSet<GameObject> objects = new HashSet<GameObject>();

            // 내 주변에 있는 시야각을 가지고 온다.
            Vector2Int cellPos = Owner.CellPos;
            List<Zone> zones = Owner.Room.GetAdjacentZones(cellPos);
            
            // 인접한 zone에 있는 player들 중에서 자신의 시야각에 있는 player의 정보만 긁어온다.
            foreach (Zone zone in zones)
            {
                foreach (Player player in zone.Players)
                {
                    // 자신의 
                    int dx = player.CellPos.x - cellPos.x;
                    int dy = player.CellPos.y - cellPos.y;
                    // 자신과 대상의 거리가 VisionCells보다 크면 => 범위에서 벗어나면 제외 
                    if (Math.Abs(dx) > GameRoom.VisionCells)
                        continue;
                    if (Math.Abs(dy) > GameRoom.VisionCells)
                        continue;

                    // 자신의 시야각에 있는 대상을 추가
                    objects.Add(player);
                }

                foreach (Monster monster in zone.Monsters)
                {
                    // 자신의 
                    int dx = monster.CellPos.x - cellPos.x;
                    int dy = monster.CellPos.y - cellPos.y;
                    // 자신과 대상의 거리가 VisionCells보다 크면 => 범위에서 벗어나면 제외
                    if (Math.Abs(dx) > GameRoom.VisionCells)
                        continue;
                    if (Math.Abs(dy) > GameRoom.VisionCells)
                        continue;
                    
                    // 자신의 시야각에 있는 대상을 추가
                    objects.Add(monster);
                }

                foreach (Projectile projectile in zone.Projectiles)
                {
                    // 자신의 
                    int dx = projectile.CellPos.x - cellPos.x;
                    int dy = projectile.CellPos.y - cellPos.y;
                    // 자신과 대상의 거리가 VisionCells보다 크면 => 범위에서 벗어나면 제외 
                    if (Math.Abs(dx) > GameRoom.VisionCells)
                        continue;
                    if (Math.Abs(dy) > GameRoom.VisionCells)
                        continue;

                    // 자신의 시야각에 있는 대상을 추가
                    objects.Add(projectile);
                }
            }
            
            return objects;
        }

        // Before & After를 비교해서 
        public void Update()
        {
            if (Owner == null || Owner.Room == null)
                return;
            
            // Update가 되는 시점에 시야각에 있는 모든 object들을 긁어온다.
            HashSet<GameObject> currentObjects = GatherObject();
            
            // 기존엔 없었는데 새로 생긴 object가 있다면 Spawn

            // currentObjects - PreviousObject = 새로 추가된 object
            List<GameObject> added = currentObjects.Except(PreviousObject).ToList();
            if (added.Count > 0)
            {
                S_Spawn spawnPacket = new S_Spawn();
                foreach (GameObject gameObject in added)
                {
                    // MergeFrom을 하는 이유
                    // 참조 값(gameObject.Info)을 곧 바로 넣어주게 되면
                    // 해당 패킷이 곧바로 가는 것이 아니기 때문에 
                    // 참조 값으로 바로 보내주면 에러가 발생할 수 있게 된다.
                    ObjectInfo info = new ObjectInfo();
                    info.MergeFrom(gameObject.Info);
                    spawnPacket.Objects.Add(info);
                }
                Owner.Session.Send(spawnPacket);
            }

            // 기존엔 있었는데 사라진 object는 Despawn

            // PreviousObject - currentObjects = 사라진 object
            List<GameObject> removed = PreviousObject.Except(currentObjects).ToList();
            if (removed.Count > 0)
            {
                S_Despawn despawnPacket = new S_Despawn();
                foreach (GameObject gameObject in removed)
                {
                    despawnPacket.ObjectIds.Add(gameObject.Id);
                }
                Owner.Session.Send(despawnPacket);
            }

            PreviousObject = currentObjects;

            // 0.5뒤 다시 실행
            Owner.Room.PushAfter(500, Update);
        }
    }
}