/**********************************************************************************/ // функция стрельбы // /**********************************************************************************/ protected virtual void FireWeapon(Vector2 position, Base.DIREC direction) { GameObject bulletObj = ObjectFactory.GetInstance().CreateGObject(position, direction, BulletType); Bullet bulletCtr = bulletObj.GetComponent <Bullet>(); if (bulletCtr != null) { bulletCtr.Owner = m_ownerID; bulletCtr.OwnerUnitID = m_unitID; } else { // некоторые типы оружия используют других существ в качестве снаряда // в этом случае используем GMovingObject CIGameObject gmo = bulletObj.GetComponent <CIGameObject>(); gmo.Owner = m_ownerID; } m_currentNumOfFiredBullets++; if (m_currentNumOfFiredBullets >= NumberOfBullet) { m_state = WEAPON_STATE.RECHARGE; m_currentRechargeTimer = FireRechargeTime; } }
/**********************************************************************************/ // производим поиск цели // в случае обнаружения, функция сгенерирует // /**********************************************************************************/ protected override void UpdateTarget() { // если ещё не начали никуда двигаться, стрелять тоже не будем // защита от дурака Base.DIREC direction = m_unitWithRadar.MoveDirection; if (direction == Base.DIREC.NO_DIRECTION) { m_targetCheckTimer += m_targetCheckTimerLimit; return; } // получаем список впомогательных точек для поиска List <Point> pointsToCheck = m_cachePoints[(int)direction]; foreach (Point specPoint in pointsToCheck) { Point realPointToCheck = specPoint + m_currentPosition; List <BuildingController> buildingsInPoint = GameObjectMapController.GetInstance().SearchEnemiesBuildingInRadius(realPointToCheck, 0, m_owner); if (buildingsInPoint.Count > 0) { BuildingController targetCtr = buildingsInPoint[Random.Range(0, buildingsInPoint.Count)]; if (RadarUpdate != null) { RadarData data = new RadarData(); data.EnemyDirection.Add(direction); data.DetectedEnemy.Add(targetCtr.gameObject); RadarUpdate(data); } } m_targetCheckTimer += m_targetCheckTimerLimit; } }
/**********************************************************************************/ // запускаем анимацию стрельбы и настраиваем игровые объекты /**********************************************************************************/ public void Fire(Vector2 position, Base.DIREC direction) { m_laserDirection = direction; UpdateCoordinates(); m_lineRenderer.enabled = true; LaserHitPoint.gameObject.SetActive(true); }
/**********************************************************************************/ // перемещает объект в указанном направлении // /**********************************************************************************/ virtual public void MoveGObject(Base.DIREC direction) { Vector2 movement = new Vector2(); switch (direction) { case Base.DIREC.DOWN: movement.y = -1f; break; case Base.DIREC.UP: movement.y = 1f; break; case Base.DIREC.LEFT: movement.x = -1f; break; case Base.DIREC.RIGHT: movement.x = 1f; break; } Vector3 newPosition = m_rb2d.position + movement * speed * speedMultiplier * Time.deltaTime; m_rb2d.MovePosition(newPosition); }
public void ShiftPoint(Base.DIREC direction) { switch (direction) { case Base.DIREC.DOWN: this.y--; break; case Base.DIREC.UP: this.y++; break; case Base.DIREC.LEFT: this.x--; break; case Base.DIREC.RIGHT: this.x++; break; case Base.DIREC.NO_DIRECTION: break; default: Debug.LogError(""); break; } }
/**********************************************************************************/ // функция стрельбы // /**********************************************************************************/ protected override void FireWeapon(Vector2 position, Base.DIREC direction) { for (int shell = 0; shell < ShellsInShot; shell++) { if (m_currentAmmo > 0 && m_currentMagazineAmmo > 0) { CreateBullet(position, direction); m_currentAmmo--; m_currentMagazineAmmo--; } } if (m_currentAmmo <= 0) { m_state = WEAPON_STATE.EMPTY; return; } if (m_currentMagazineAmmo == 0) { m_state = WEAPON_STATE.RECHARGE; m_currentRechargeTimer = FireRechargeTime; } else { m_state = WEAPON_STATE.READY; } }
/**********************************************************************************/ // основная процессинговая функция // обновляем состояния всего вооружения // /**********************************************************************************/ public void Update() { if (!m_isActive) { return; } Base.DIREC directionOfMovement = m_unitWithWeapon.MoveDirection; Vector2 physicalUnitPosition = m_unitWithWeapon.GetGlobalPositionCenter_Unity(); bool weaponFiring = false; foreach (var weapon in m_weapons) { WeaponController wCtr = weapon.Value; wCtr.UpdateWeaponState(physicalUnitPosition, directionOfMovement); // проверяем состояние оружия // в случае, если мы прекратили стрелять, мы должны вновь запустить движение weaponFiring |= wCtr.State == WeaponController.WEAPON_STATE.FIRE; } // стреляли, но прекратили if (weaponFiring == false && m_isFiring == true && ResumeDrive != null) { ResumeDrive(); } }
public override void UpdateWeaponState(Vector2 position, Base.DIREC direction) { if (m_state == WEAPON_STATE.FIRE) { if (HitSoundKey != "") { GameAudioManager.Instance.PlaySound(HitSoundKey); } m_currentNumOfFiredBullets++; if (m_currentNumOfFiredBullets >= NumberOfBullet) { m_state = WEAPON_STATE.RECHARGE; m_currentRechargeTimer = FireRechargeTime; } } else if (m_state == WEAPON_STATE.RECHARGE) { m_currentRechargeTimer -= Time.deltaTime; if (m_currentRechargeTimer <= 0) { m_currentRechargeTimer = 0; m_state = WEAPON_STATE.READY; m_currentNumOfFiredBullets = 0; } } }
/**********************************************************************************/ // перемещает объект к указанной точке // возвращаем true если уже находимся рядом с этой точкой и движение не требуется // /**********************************************************************************/ virtual public bool MoveGObjectToPoint(Point movePoint) { Vector2 unitPoint = GetGlobalPosition_Unity(); Vector2 pointToMove = movePoint.GetUnityPoint(); // проверяем - дошли мы до следующей точки // считаем разницу координат float xCorDiff = pointToMove.x - unitPoint.x; float yCorDiff = pointToMove.y - unitPoint.y; float diffCorr = Mathf.Abs(xCorDiff) + Mathf.Abs(yCorDiff); // рассчитываем точность движения // для скоростных юнитов значение будет увеличено float accurasity = 0.01f; float expectedSpead = speed * speedMultiplier * Time.deltaTime; if (expectedSpead > accurasity) { accurasity += expectedSpead; } if (diffCorr <= accurasity) { // если добрались, окончательно выравниваем объект по точке // и возвращаем true как сигнал об окончании движения MoveGObjectToPosition(pointToMove); return(true); } Base.DIREC direction = Base.DIREC.NO_DIRECTION; // выбираем направление для сближения if (Mathf.Abs(xCorDiff) > Mathf.Abs(yCorDiff)) { if (xCorDiff > 0) { direction = Base.DIREC.RIGHT; } else { direction = Base.DIREC.LEFT; } } else { if (yCorDiff > 0) { direction = Base.DIREC.UP; } else { direction = Base.DIREC.DOWN; } } MoveGObject(direction); return(false); }
/**********************************************************************************/ // устанавливаем направление движения // /**********************************************************************************/ public void SetMovementDirection(Base.DIREC direction) { m_movementDirection = direction; foreach (var animator in m_animators) { animator.SetInteger("direction", (int)direction); } }
/**********************************************************************************/ // вращаем "турель" // /**********************************************************************************/ public void SwitchDirection() { if (m_currentSwitchDirectionTimer <= 0.0f) { Base.DIREC direction = Base.GetRandomDirection(); m_unitToStay.SetMovementDirection(direction); m_currentSwitchDirectionTimer += m_switchDirectionTimerLimit; } }
/**********************************************************************************/ // производим поиск цели // в случае обнаружения произвойдёт RadarUpdate эвент // /**********************************************************************************/ protected override void UpdateTarget() { if (m_targetCheckTimer <= 0) { // если ещё не начали никуда двигаться, стрелять тоже не будем // защита от дурака Base.DIREC direction = m_unitWithRadar.MoveDirection; if (direction == Base.DIREC.NO_DIRECTION) { m_targetCheckTimer += m_targetCheckTimerLimit; return; } // получаем список впомогательных точек для поиска врагов // и проводим поиск целей для стрельбы List <Point> pointsToCheck = m_cachePoints[(int)direction]; foreach (Point specPoint in pointsToCheck) { Point realPointToCheck = specPoint + m_currentPosition; List <CIGameObject> unitsInPoint = GameObjectMapController.GetInstance().SearchEnemiesInRadius(realPointToCheck, m_scaningAreaRadius, m_owner); if (unitsInPoint.Count > 0) { CIGameObject targetCtr = unitsInPoint[Random.Range(0, unitsInPoint.Count)]; if (RadarUpdate != null) { RadarData data = new RadarData(); data.EnemyDirection.Add(direction); data.DetectedEnemy.Add(targetCtr.gameObject); RadarUpdate(data); } } else { // проверяем игроков foreach (CIGameObject plObject in m_players) { // считаем расстояние от точки метания снаряда до игроков // если расстояние меньше m_scaningAreaRadius - вызываем RadarUpdate эвент Point playerPosition = plObject.GetGlobalPosition(); Point positionDiff = playerPosition - realPointToCheck; if (positionDiff.GetSimpleLength() < m_scaningAreaRadius) { RadarData data = new RadarData(); data.EnemyDirection.Add(direction); data.DetectedEnemy.Add(plObject.gameObject); RadarUpdate(data); } } } } m_targetCheckTimer += m_targetCheckTimerLimit; } }
/**********************************************************************************/ // производим поиск цели // в случае обнаружения произвойдёт RadarUpdate эвент // /**********************************************************************************/ protected override void UpdateTarget() { if (m_targetCheckTimer <= 0) { // если ещё не начали никуда двигаться, стрелять тоже не будем // защита от дурака Base.DIREC direction = m_unitWithRadar.MoveDirection; if (direction == Base.DIREC.NO_DIRECTION) { m_targetCheckTimer += m_targetCheckTimerLimit; return; } // получаем список впомогательных точек для поиска врагов // и проводим поиск целей для стрельбы List <Point> pointsToCheck = m_cachePoints[(int)direction]; foreach (Point specPoint in pointsToCheck) { Point realPointToCheck = specPoint + m_currentPosition; List <CIGameObject> unitsInPoint = GameObjectMapController.GetInstance().SearchEnemiesInRadius(realPointToCheck, 0, m_owner); if (unitsInPoint.Count > 0) { CIGameObject targetCtr = unitsInPoint[Random.Range(0, unitsInPoint.Count)]; if (RadarUpdate != null) { RadarData data = new RadarData(); data.EnemyDirection.Add(direction); data.DetectedEnemy.Add(targetCtr.gameObject); RadarUpdate(data); } } else { // проверяем игроков foreach (CIGameObject plObject in m_players) { if (plObject.Owner == (int)m_owner) { continue; } if (plObject.GetGlobalPosition().IsSamePoint(realPointToCheck)) { RadarData data = new RadarData(); data.EnemyDirection.Add(direction); data.DetectedEnemy.Add(plObject.gameObject); RadarUpdate(data); } } } } m_targetCheckTimer += m_targetCheckTimerLimit; } }
/**********************************************************************************/ // функция обработки оружия // если начали стрелять - стреляем пока не выпустим всю очередь // если перезаряжаемся - считаем время до окончания перезарядки // /**********************************************************************************/ public virtual void UpdateWeaponState(Vector2 position, Base.DIREC direction) { if (m_state == WEAPON_STATE.FIRE) { FireWeapon(position, direction); } else if (m_state == WEAPON_STATE.RECHARGE) { RechargeWeapon(); } }
/**********************************************************************************/ // функция создания игровых объектов // создаём новый объект /**********************************************************************************/ protected GameObject GetNewGObjectInstance(Vector2 from, Base.DIREC direction, Base.GO_TYPE objectType, bool useOffset) { GameObject instance = null; GameObject toInstantiate = ObjectLibrary.GetInstance().GetPrefab(objectType); if (toInstantiate == null) { Debug.LogError("ObjectFactory::GetNewGObjectInstance: toInstantiate is null!!!"); return(null); } float OffsetSize = offset; if (!useOffset) { OffsetSize = 0; } Vector2 objectDirection = new Vector2(0f, 0f); switch (direction) { case Base.DIREC.DOWN: instance = Instantiate(toInstantiate, new Vector3(from.x, from.y - OffsetSize, 0f), Quaternion.identity) as GameObject; objectDirection.y = -1f; break; case Base.DIREC.UP: instance = Instantiate(toInstantiate, new Vector3(from.x, from.y + OffsetSize, 0f), Quaternion.identity) as GameObject; objectDirection.y = 1f; break; case Base.DIREC.LEFT: instance = Instantiate(toInstantiate, new Vector3(from.x - OffsetSize, from.y, 0f), Quaternion.identity) as GameObject; objectDirection.x = -1f; break; case Base.DIREC.RIGHT: instance = Instantiate(toInstantiate, new Vector3(from.x + OffsetSize, from.y, 0f), Quaternion.identity) as GameObject; objectDirection.x = 1f; break; } // бонусы у нас недвижимые объекты и контроллер у них соответсвенно свой if (instance.tag != "Bonus") { CIGameObject ctr = instance.GetComponent <CIGameObject>(); ctr.ID = GetUnitID(); ctr.SetDirection(objectDirection); } return(instance); }
/**********************************************************************************/ // функция контроля скорости // снаряд в процессе полёта замедляется // /**********************************************************************************/ private void SpeedManipualation() { if (m_state != BULLET_STATE.BURN) { m_currentFlyTime += Time.deltaTime; int currentStep = (int)Mathf.Floor(m_currentFlyTime / m_timeStep); // проверяем, надо ли обновить скорость и высоту полёта гранаты if (currentStep > m_appliedStep) { if (currentStep >= m_numOfSpeedHeshes) { m_state = BULLET_STATE.BURN; speed = 0.0f; m_animator.SetBool("Burn", true); BurstSystem.Play(); PlayBurstSoundEffect(); BlowUpGrenade(); return; } Point directionVector = new Point((int)m_direction.x, (int)m_direction.y); Base.DIREC direction = directionVector.ToDirection(); switch (direction) { case Base.DIREC.LEFT: case Base.DIREC.RIGHT: speed = m_horisontalSpeed; m_rb2d.position = new Vector2(m_rb2d.position.x, m_rb2d.position.y + m_heightDiff[currentStep]); break; case Base.DIREC.UP: speed = m_speedYHashUp[currentStep]; break; case Base.DIREC.DOWN: speed = m_speedYHashDown[currentStep]; break; } m_appliedStep = currentStep; } } }
/**********************************************************************************/ // создаём поражающий элемент // /**********************************************************************************/ protected virtual void CreateBullet(Vector2 position, Base.DIREC direction) { GameObject bulletObj = ObjectFactory.GetInstance().CreateGObject(position, direction, BulletType); Bullet bulletCtr = bulletObj.GetComponent <Bullet>(); if (bulletCtr != null) { bulletCtr.Owner = m_ownerID; bulletCtr.OwnerUnitID = m_unitID; } else { // некоторые типы оружия используют других существ в качестве снаряда // в этом случае используем GMovingObject CIGameObject gmo = bulletObj.GetComponent <CIGameObject>(); gmo.Owner = m_ownerID; } }
/**********************************************************************************/ // используем указанное оружие // /**********************************************************************************/ private void UseWeapon(WeaponController ctr) { ctr.Fire(); // приостанавливаем движени на стрельбу if (!m_isFiring && ResumeDrive != null) { m_isFiring = true; PauseDrive(); } // если оружие предполагает саморазрушение юнита/объекта после использования - наносим себе ультимативный урон if (ctr.SelfDamaged) { Vector2 physicalUnitPosition = m_unitWithWeapon.GetGlobalPositionCenter_Unity(); Base.DIREC directionOfMovement = m_unitWithWeapon.MoveDirection; ctr.UpdateWeaponState(physicalUnitPosition, directionOfMovement); m_unitWithWeapon.ApplyDamage(new DamageData(100, DamageData.DAMAGE_TYPE.PHYSICAL, m_unitWithWeapon, DamageData.RESPONSE.NOT_EXPECTED)); } }
/**********************************************************************************/ // функция создания игровых объектов // используется, когда надо добавить что-то на игровую сцену /**********************************************************************************/ public GameObject CreateGObject(Vector2 from, Base.DIREC direction, Base.GO_TYPE objectType, bool useOffset = true) { GameObject instance = null; // пробуем получить объект из кеша instance = GetGObjectFromCash(from, direction, objectType, useOffset); // если в кеше ничего нет, создаём новые объект if (instance == null) { instance = GetNewGObjectInstance(from, direction, objectType, useOffset); } if (instance == null) { Debug.Log("CreateGObject:: MAIN ERROR!"); } return(instance); }
/**********************************************************************************/ // строим путь из from в to // !!!!!!! используется ТОЛЬКО при построении системы указателей !!!!!!! // /**********************************************************************************/ private void BuildPath(Point from, Point to) { if (from.IsSamePoint(to)) { Debug.LogError("We cann't build path from 1 same point"); return; } WayNode[,] bluprintOfRoad = new WayNode[m_xSizeOfMap, m_ySizeOfMap]; for (int x = 0; x < m_xSizeOfMap; x++) { for (int y = 0; y < m_ySizeOfMap; y++) { bluprintOfRoad[x, y] = new WayNode(); } } PrepareBluprintOfPath(bluprintOfRoad, from, to, m_blockConnectionData, m_sizeOfBlock * 4); // проходим по результирующему пути задом наперёд и запоминаем направление движения // для системы указателей важно только направление первого шага в пути Base.DIREC nextDirection = Base.DIREC.NO_DIRECTION; Point toProcess = new Point(to); do { WayNode nodeToProcess = bluprintOfRoad[toProcess.x, toProcess.y]; toProcess.ShiftPoint(nodeToProcess.previusRoadDirection); nextDirection = Base.InvertDirection(nodeToProcess.previusRoadDirection); } while (!toProcess.IsSamePoint(from)); // по окончанию обработки nextDirection содержит направление первого шага по достижению точки "to" из "from" // его и сохраняем в таблице int keyToMark = GetSingKey(from.x, from.y); // ключик блока "from" int keyToConnect = GetSingKey(to.x, to.y); // ключик блока "to" m_singMap[keyToMark, keyToConnect] = nextDirection; }
/**********************************************************************************/ // создаём поражающий элемент // /**********************************************************************************/ protected override void CreateBullet(Vector2 position, Base.DIREC direction) { // определяем направления запуска снаряда Point targetPosition = m_target.GetComponent <CIGameObject>().GetGlobalPosition(); Point unitPosition = new Point((int)(position.x / Base.SIZE_OF_CELL), (int)(position.y / Base.SIZE_OF_CELL)); LinkedList <Point> pathToTarget = PathFinder.GetInstance().GetWay(unitPosition, targetPosition); // если объект находится не в той же клетке, что и цель, используем вторую точку пути, так как она находится в направлении движения if (pathToTarget.Count > 1) { pathToTarget.RemoveLast(); Point launcherPoint = pathToTarget.Last.Value; direction = (launcherPoint - unitPosition).ToDirection(); } GameObject bulletObj = ObjectFactory.GetInstance().CreateGObject(position, direction, BulletType); Bullet bulletCtr = bulletObj.GetComponent <Bullet>(); if (bulletCtr != null) { bulletCtr.Owner = m_ownerID; bulletCtr.OwnerUnitID = m_unitID; } else { // некоторые типы оружия используют других существ в качестве снаряда // в этом случае используем GMovingObject CIGameObject gmo = bulletObj.GetComponent <CIGameObject>(); gmo.Owner = m_ownerID; ScarabCtr scarabCtr = bulletObj.GetComponent <ScarabCtr>(); if (scarabCtr != null) { scarabCtr.SetTarget(m_target); } } }
/**********************************************************************************/ // строим дорогу из точки from в точку to // результат записываем в bluprintOfRoadMap // /**********************************************************************************/ void PrepareBluprintOfRoads(BlockDescriptor descriptor, WayNode[,] bluprintOfRoadMap, Point from, Point to, int sizeOfBlock) { // проверки параметров if (from == null || to == null || descriptor == null || bluprintOfRoadMap == null || bluprintOfRoadMap.Length == 0) { Debug.LogError("PrepareBluprintOfRoads: Wrong arguments! " + "descriptor is null? " + (descriptor == null).ToString() + " from is null? " + (from == null).ToString() + " to is null? " + (to == null).ToString() + " bluprintOfRoadMap is null? " + (bluprintOfRoadMap == null).ToString() + " bluprintOfRoadMap.Length " + bluprintOfRoadMap.Length); return; } LinkedList <Point> currentPontsToProcess = new LinkedList <Point>(); LinkedList <Point> nextIterationPontsToProcess = new LinkedList <Point>(); // устанавливаем стартовые параметры алгоритма - начальную точку и её стоимость пути bluprintOfRoadMap[from.x, from.y].wayCost = 0; currentPontsToProcess.AddFirst(from); int possibleClosestDist = Mathf.Abs(from.x - to.x) + Mathf.Abs(from.y - to.y); int nodeClosestDist = possibleClosestDist; bool buildingComplite = false; int maxSteps = sizeOfBlock * 4; while (!buildingComplite && maxSteps > 0) { while (currentPontsToProcess.Count > 0) { // забираем точку для просчёта и работаем с ней Point currentPoint = currentPontsToProcess.First.Value; currentPontsToProcess.RemoveFirst(); int currentWayCost = bluprintOfRoadMap[currentPoint.x, currentPoint.y].wayCost; // просчитываем верхную точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.DOWN, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем нижнюю точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.UP, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем левую точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.RIGHT, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); // просчитываем правую точку CalculatePoint(descriptor, bluprintOfRoadMap, currentPoint, to, Base.DIREC.LEFT, sizeOfBlock, currentWayCost, ref nodeClosestDist, currentPontsToProcess, nextIterationPontsToProcess); } // принимаем решение о дальнейших итерациях WayNode WayNodeFinal = bluprintOfRoadMap[to.x, to.y]; if (WayNodeFinal.wayCost == possibleClosestDist) { buildingComplite = true; } else { // если мы исчерпали возможности для построения маршрута - завершаем проектирование маршрута if (WayNodeFinal.wayCost != WayNode.UNREACHABLE && nextIterationPontsToProcess.Count == 0) { buildingComplite = true; } // в противном случае продолжаем else { // меняем местами коллекции LinkedList <Point> supportPointer = currentPontsToProcess; currentPontsToProcess = nextIterationPontsToProcess; nextIterationPontsToProcess = supportPointer; } } maxSteps--; } if (!buildingComplite) { Debug.LogError("PrepareBluprintOfRoads: We cann't build way"); Logger.CreatePathFinderErrorReport(bluprintOfRoadMap, from, to, descriptor.FreeSpaceMap); } else { // формируем направления дороги по окончанию работы алгоритма "А star!" Point pointToBuild = new Point(to); WayNode nodeForBuilding = null; WayNode finalBuildingNode = bluprintOfRoadMap[from.x, from.y]; Base.DIREC nextDirection = Base.DIREC.NO_DIRECTION; maxSteps = sizeOfBlock * 4; do { // проверяем координыты if (pointToBuild.x < 0 || pointToBuild.x >= sizeOfBlock || pointToBuild.y < 0 || pointToBuild.y >= sizeOfBlock) { continue; } nodeForBuilding = bluprintOfRoadMap[pointToBuild.x, pointToBuild.y]; // добавляем направления, если надо Base.DIREC newDirection = nodeForBuilding.previusRoadDirection; if (!nodeForBuilding.approvedDirections.Contains(newDirection)) { nodeForBuilding.approvedDirections.Add(newDirection); } if (!nodeForBuilding.approvedDirections.Contains(nextDirection) && nextDirection != Base.DIREC.NO_DIRECTION) { nodeForBuilding.approvedDirections.Add(nextDirection); } // устанавливаем цену дороги nodeForBuilding.cellCost = WayNode.ROAD_COST; switch (newDirection) { case Base.DIREC.DOWN: pointToBuild.y--; break; case Base.DIREC.UP: pointToBuild.y++; break; case Base.DIREC.LEFT: pointToBuild.x--; break; case Base.DIREC.RIGHT: pointToBuild.x++; break; default: Debug.LogError("PrepareBluprintOfRoads: wrong direction!"); break; } nextDirection = Base.InvertDirection(newDirection); maxSteps--; } while (nodeForBuilding != finalBuildingNode && maxSteps > 0); } }
/**********************************************************************************/ // функция создания игровых объектов // пытаемся найти объект в кеше /**********************************************************************************/ protected GameObject GetGObjectFromCash(Vector2 from, Base.DIREC direction, Base.GO_TYPE objectType, bool useOffset) { GameObject instance = null; Vector2 objectDirection = new Vector2(0f, 0f); Vector2 newPosition = new Vector2(0f, 0f); // проверяем кеш объектов // если он у нас уже имеется - реиспользуем if (m_objectColection.ContainsKey(objectType)) { LinkedList <GameObject> objectPull = m_objectColection[objectType]; if (objectPull.Count > 0) { instance = objectPull.First.Value; objectPull.RemoveFirst(); float OffsetSize = offset; if (!useOffset) { OffsetSize = 0; } switch (direction) { case Base.DIREC.DOWN: newPosition = new Vector2(from.x, from.y - OffsetSize); objectDirection.y = -1f; break; case Base.DIREC.UP: newPosition = new Vector2(from.x, from.y + OffsetSize); objectDirection.y = 1f; break; case Base.DIREC.LEFT: newPosition = new Vector2(from.x - OffsetSize, from.y); objectDirection.x = -1f; break; case Base.DIREC.RIGHT: newPosition = new Vector2(from.x + OffsetSize, from.y); objectDirection.x = 1f; break; } } } else { // если это первое создание объекта - создаем так же для него LinkedList для последующего хранения m_objectColection[objectType] = new LinkedList <GameObject>(); } // если нашёлся объект - устанавливаем ему направление if (instance != null) { Transform objTransform = instance.GetComponent <Transform>(); objTransform.position = new Vector3(newPosition.x, newPosition.y, 0); if (instance.tag != "Bonus") { CIGameObject ctr = instance.GetComponent <CIGameObject>(); ctr.SetDirection(objectDirection); instance.GetComponent <CIGameObject>().ResetGObject(); } else { BonusCtr ctr = instance.GetComponent <BonusCtr>(); ctr.ResetBonus(); } } return(instance); }
/**********************************************************************************/ // просчитываем конкретную точку построения пути // проверяем её стоимость и на оснеовании проверки вносим коррективы в карту маршрутов // /**********************************************************************************/ void CalculatePoint(BlockDescriptor descriptor, WayNode[,] bluprintOfRoadMap, Point currentPoint, Point to, Base.DIREC prevPointDir, int sizeOfBlock, int currentWayCost, ref int nodeClosestDist, LinkedList <Point> currentPontsToProcess, LinkedList <Point> nextIterationPontsToProcess) { // проверки параметров if (currentPoint == null || to == null || descriptor == null || bluprintOfRoadMap == null || bluprintOfRoadMap.Length == 0) { Debug.LogError("CalculatePoint: Wrong arguments! " + "descriptor is null? " + (descriptor == null).ToString() + " currentPoint is null? " + (currentPoint == null).ToString() + " prevPointDir " + prevPointDir.ToString() + " to is null? " + (to == null).ToString() + " bluprintOfRoadMap is null? " + (bluprintOfRoadMap == null).ToString() + " bluprintOfRoadMap.Length " + bluprintOfRoadMap.Length); return; } int newX = currentPoint.x; int newY = currentPoint.y; // выбираем координаты новой точки в зависимости от положения предыдущей switch (prevPointDir) { case Base.DIREC.DOWN: newY++; break; case Base.DIREC.UP: newY--; break; case Base.DIREC.LEFT: newX++; break; case Base.DIREC.RIGHT: newX--; break; } // если координата удовлетворяет ... if (newX < 0 || newX >= sizeOfBlock || newY < 0 || newY >= sizeOfBlock) { return; } // ... и место свободно для дорог - процессим if (descriptor.FreeWaysMap[newX, newY] == false) { return; } WayNode wayNodeToCheck = bluprintOfRoadMap[newX, newY]; int possibleWayCost = currentWayCost + wayNodeToCheck.cellCost; Point pointToCheck = null; if (wayNodeToCheck.wayCost > possibleWayCost) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; pointToCheck = new Point(newX, newY); } else if (wayNodeToCheck.wayCost == possibleWayCost) { // в случае, если стоимости равны - в половине случаев переключаемся на новый путь для разнообразия if (Random.Range(0, 100) >= 50) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; } } // если мы нашли точку для просчёта - определяемся когда будем её просчитывать // если "квадратное" растояние меньше или равно самого близкого к целевой точке, то будем просчитывать её в первую очередь if (pointToCheck != null) { int dist = Mathf.Abs(pointToCheck.x - to.x) + Mathf.Abs(pointToCheck.y - to.y); if (dist == 0) { nodeClosestDist = 0; return; } else if (dist <= nodeClosestDist) { currentPontsToProcess.AddLast(pointToCheck); nodeClosestDist = dist; } else { nextIterationPontsToProcess.AddLast(pointToCheck); } } }
/**********************************************************************************/ // данное переопределение передаёт контроллеру данные о направлении движении и сохраняет // информацию для дальнейшего использования // /**********************************************************************************/ override public void MoveGObject(Base.DIREC direction) { SetMovementDirection(direction); base.MoveGObject(direction); }
/**********************************************************************************/ // Создаём луч лазера // /**********************************************************************************/ protected override void CreateBullet(Vector2 position, Base.DIREC direction) { m_ctr.Fire(position, direction); }
/**********************************************************************************/ // функция брожения по округе, выбирает рандомный соседний блок и плюхает туда // /**********************************************************************************/ protected void MoveToRandomeBlock() { Point currentPosition = m_unitToDrive.GetGlobalPosition(); // проверяем - надо ли нам искать новую точку для движения bool haveToChouseNewPoint = false; if (m_pointToMove == null) { haveToChouseNewPoint = true; } else if (currentPosition.IsSamePoint(m_pointToMove)) { haveToChouseNewPoint = true; } // выбираем рандомный соседний блок if (haveToChouseNewPoint) { // двигаемся в соседний блок bool blockIsOk = false; Point randomPointInRandomClosestBlock = new Point(); Point pointToCheck = new Point(); // симулируем точку в соседнем блоке while (!blockIsOk) { randomPointInRandomClosestBlock.x = 0; randomPointInRandomClosestBlock.y = 0; Base.DIREC randomDir = Base.GetRandomDirection(); randomPointInRandomClosestBlock.ShiftPoint(randomDir); int sizeOfBlock = MapGenerator.GetInstance().SizeOfBlocks; randomPointInRandomClosestBlock.x *= sizeOfBlock; randomPointInRandomClosestBlock.y *= sizeOfBlock; randomPointInRandomClosestBlock += currentPosition; blockIsOk = MapGenerator.GetInstance().CheckCoordinates(randomPointInRandomClosestBlock.x, randomPointInRandomClosestBlock.y); // если точка действительно существует (а значит и существует блок для этой точки), выбираем рандомную свободную точку в этом блоке if (blockIsOk) { Point blockPosition = new Point(randomPointInRandomClosestBlock.x / sizeOfBlock, randomPointInRandomClosestBlock.y / sizeOfBlock); bool pointIsOk = false; while (!pointIsOk) { int xShift = Random.Range(0, sizeOfBlock); int yShift = Random.Range(0, sizeOfBlock); pointToCheck = new Point(blockPosition.x * sizeOfBlock + xShift, blockPosition.y * sizeOfBlock + yShift); pointIsOk = PathFinder.GetInstance().ValidatePathCell(pointToCheck); } } } LinkedList <Point> path = PathFinder.GetInstance().GetWay(currentPosition, pointToCheck); // сохраняем первую в списке точку, как точку в соседнем блоке, до которой мы будем пытаться добраться m_pointToMove = path.First.Value; } // двигаемся к заданной точке MoveToPoint(m_pointToMove); }
/**********************************************************************************/ // обновляем правила дорог для блока // если этого не сделать, то возможны колизии в соседних блоках, особенно это касается предопределённых блоков // /**********************************************************************************/ public void UpdateRulesForBlock(int x, int y, ROAD_CONNECTION_STATUS[] roadConnections) { // проверки параметров if (x < 0 || y < 0 || roadConnections == null || roadConnections.Length != (int)Base.DIREC.NUM_OF_DIRECTIONS) { Debug.LogError("UpdateRulesForBlock: Wrong arguments! " + "x " + x + " y " + y + "roadConnections is null? " + (roadConnections == null).ToString() + " roadConnections.Length " + roadConnections.Length); return; } // обновляем правила для текущего и соседних блоков for (int i = 0; i < (int)Base.DIREC.NUM_OF_DIRECTIONS; i++) { if (roadConnections[i] != ROAD_CONNECTION_STATUS.BLOCKED) { m_blockRoadRulesDescriptor[x, y].RoadConnections[i] = ROAD_CONNECTION_STATUS.NEEDED; roadConnections[i] = ROAD_CONNECTION_STATUS.NEEDED; } else { m_blockRoadRulesDescriptor[x, y].RoadConnections[i] = ROAD_CONNECTION_STATUS.BLOCKED; } } // проходим по окрестным блокам и обновляем условия // правила должны быть синхронизированы в соседних блоках for (int direction = (int)Base.DIREC.DOWN; direction < (int)Base.DIREC.NUM_OF_DIRECTIONS; direction++) { Point blockToUpdateCor = new Point(x, y); switch (direction) { case (int)Base.DIREC.DOWN: blockToUpdateCor.y--; break; case (int)Base.DIREC.UP: blockToUpdateCor.y++; break; case (int)Base.DIREC.LEFT: blockToUpdateCor.x--; break; case (int)Base.DIREC.RIGHT: blockToUpdateCor.x++; break; default: Debug.LogError("PrepareBluprintOfRoads: wrong direction!"); break; } // проверяем координыты if (blockToUpdateCor.x < 0 || blockToUpdateCor.x >= m_blockRoadRulesDescriptor.GetLength(0) || blockToUpdateCor.y < 0 || blockToUpdateCor.y >= m_blockRoadRulesDescriptor.GetLength(1)) { continue; } BlockDescriptorImitation blockToUpdates = m_blockRoadRulesDescriptor[blockToUpdateCor.x, blockToUpdateCor.y]; Base.DIREC connectionToUpdate = Base.InvertDirection(direction); // синхронизируем blockToUpdates.RoadConnections[(int)connectionToUpdate] = m_blockRoadRulesDescriptor[x, y].RoadConnections[(int)direction]; } }
/**********************************************************************************/ // просчитываем конкретную точку построения пути // проверяем её стоимость и на оснеовании проверки вносим коррективы в карту маршрутов // /**********************************************************************************/ void CalculatePoint(WayNode[,] bluprintOfRoadMap, Point currentPoint, Point to, Base.DIREC prevPointDir, int currentWayCost, ref int nodeClosestDist, LinkedList <Point> currentPontsToProcess, LinkedList <Point> nextIterationPontsToProcess) { int newX = currentPoint.x; int newY = currentPoint.y; // выбираем координаты новой точки в зависимости от положения предыдущей switch (prevPointDir) { case Base.DIREC.DOWN: newY++; break; case Base.DIREC.UP: newY--; break; case Base.DIREC.LEFT: newX++; break; case Base.DIREC.RIGHT: newX--; break; } // если координата удовлетворяет ... if (newX < 0 || newX >= bluprintOfRoadMap.GetLength(0) || newY < 0 || newY >= bluprintOfRoadMap.GetLength(1)) { return; } WayNode wayNodeToCheck = bluprintOfRoadMap[newX, newY]; // формирование цены прохода происходит из стоимости предыдущих клеток + стоимость прохода по клетке + загруженность пути int possibleWayCost = currentWayCost + wayNodeToCheck.cellCost + m_trafficData[newX, newY]; Point pointToCheck = null; if (wayNodeToCheck.wayCost > possibleWayCost) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; pointToCheck = new Point(newX, newY); } else if (wayNodeToCheck.wayCost == possibleWayCost) { // в случае, если стоимости равны - в половине случаев переключаемся на новый путь для разнообразия if (Random.Range(0, 100) >= 50) { wayNodeToCheck.wayCost = possibleWayCost; wayNodeToCheck.previusRoadDirection = prevPointDir; } } // если мы нашли точку для просчёта - определяемся когда будем её просчитывать // если "квадратное" растояние меньше или равно самого близкого к целевой точке, то будем просчитывать её в первую очередь if (pointToCheck != null) { int dist = Mathf.Abs(pointToCheck.x - to.x) + Mathf.Abs(pointToCheck.y - to.y); if (dist == 0) { nodeClosestDist = 0; return; } else if (dist <= nodeClosestDist) { currentPontsToProcess.AddLast(pointToCheck); nodeClosestDist = dist; } else { nextIterationPontsToProcess.AddLast(pointToCheck); } } }
/**********************************************************************************/ // ГЛАВНАЯ ФУНКЦИЯ ПОИСКА ПУТИ ИЗ ОДНОЙ ТОЧКИ ПРОСТРАНСТВА В ДРУГУЮ // оценивает позиции объектов и выбирает оптимальные точки для достижения цели // // ВНИМАНИЕ: для оптимизации при построении пути между удалёнными точками функция будет возвращать путь // до промежуточной точки // /**********************************************************************************/ public LinkedList <Point> GetWay(Point from, Point to) { // определяем в одном или в разных блоках находятся точки Point blockFromPosition = new Point(from.x / m_sizeOfBlock, from.y / m_sizeOfBlock); Point blockToPosition = new Point(to.x / m_sizeOfBlock, to.y / m_sizeOfBlock); Point distanceVector = blockToPosition - blockFromPosition; // если from и to находятся в рамках соседних блоков - строим прямой маршрут if (Mathf.Abs(distanceVector.x) < 2 && Mathf.Abs(distanceVector.y) < 2) { return(BuildSpecificWay(from, to)); } // если в разных - смотрим в каком направлении необходимо осуществить движение и идём в сторону выхода else { MapGenerator mg = MapGenerator.GetInstance(); if (!mg.CheckCoordinates(from) || !mg.CheckCoordinates(to)) { Debug.LogError("Wrong coordinates"); } // выбираем переходный блок для построения пути, но обрезаем длину пути ограничиваясь только // соседними блоками // укорачивание по Х if (distanceVector.x > 1) { distanceVector.x = 1; } else if (distanceVector.x < -1) { distanceVector.x = -1; } // укорачивание по Y if (distanceVector.y > 1) { distanceVector.y = 1; } else if (distanceVector.y < -1) { distanceVector.y = -1; } // определяемся с новой точкой выхода Point exitBlockShift = blockFromPosition + distanceVector; // определяем точку выхода из блока в блок, расположенный ближе к цели int fromKey = GetSingKey(exitBlockShift.x, exitBlockShift.y); int toKey = GetSingKey(blockToPosition.x, blockToPosition.y); if (fromKey < 0 || fromKey >= m_singMap.GetLength(0) || toKey < 0 || toKey >= m_singMap.GetLength(1)) { Debug.LogError("Wrong coordinates"); } Base.DIREC directionToMove = m_singMap[fromKey, toKey]; Point exitPoint = new Point(m_exitBlockPoints[(int)directionToMove]); Point transferPoint = new Point(m_transferBlockPoints[(int)directionToMove]); exitPoint.x += exitBlockShift.x * m_sizeOfBlock; exitPoint.y += exitBlockShift.y * m_sizeOfBlock; transferPoint.x += exitBlockShift.x * m_sizeOfBlock; transferPoint.y += exitBlockShift.y * m_sizeOfBlock; // строим путь до точки выхода LinkedList <Point> way = BuildSpecificWay(from, exitPoint); // добавляем трансферную точку (точка входа в блок, в который будет происходить переход) и возвращаем путь way.AddFirst(transferPoint); return(way); } }