/// <summary>
        /// Обнаружение столкновения с коллайдером лузы
        /// </summary>
        /// <param name="parBall">Шар</param>
        /// <param name="parPocket">Луза</param>
        /// <returns>True если есть столкновение</returns>
        private bool IsPocketCollision(BallModelData parBall, Pocket parPocket)
        {
            SpVector3 ballDistanceVector = parPocket.Center - parBall.Center;
            double    centersDistance    = ballDistanceVector.Length();

            return(centersDistance <= parBall.Radius + parPocket.Radius);
        }
        /// <summary>
        /// Обработка столкновения двух шаров
        /// </summary>
        /// <param name="parBall1">Шар 1</param>
        /// <param name="parBall2">Шар 2</param>
        /// <returns>True, если столкновение имело место быть</returns>
        private bool CheckCollisionsWithBalls(BallModelData parBall1, BallModelData parBall2)
        {
            if (DetectCollision(parBall1, parBall2))
            {
                SpVector3 tmpVelocity = ChangeVelocities(parBall1, parBall2);
                parBall2.Velocity = ChangeVelocities(parBall2, parBall1);
                parBall1.Velocity = tmpVelocity;

                double tmpBallSpeed = Math.Max(parBall2.Speed * 0.8, parBall1.Speed * 0.8);

                double intersectionRadius = -(parBall2.Center - parBall1.Center).Length() + parBall2.Radius + parBall1.Radius;

                if (intersectionRadius <= 0)
                {
                    intersectionRadius = 1;
                }

                double posModifier = intersectionRadius / 2;
                parBall1.Center = parBall1.Center + (parBall1.Center - parBall2.Center).Normalize() * (posModifier + 1);
                parBall2.Center = parBall2.Center + (parBall2.Center - parBall1.Center).Normalize() * (posModifier + 1);


                parBall1.Speed = tmpBallSpeed;
                parBall2.Speed = tmpBallSpeed;

                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Обнаружение столкновения между шарами
        /// </summary>
        /// <param name="parBall1">Шар первый</param>
        /// <param name="parBall2">Шар второй</param>
        /// <returns>True если есть столкновение</returns>
        private bool DetectCollision(BallModelData parBall1, BallModelData parBall2)
        {
            SpVector3 ballsDistanceVector = parBall2.Center - parBall1.Center;
            double    centersDistance     = ballsDistanceVector.Length();

            return(centersDistance <= parBall1.Radius + parBall2.Radius);
        }
        /// <summary>
        /// Вспомогательный метод для изменения векторов скоростей столкнувшихся шаров
        /// </summary>
        /// <param name="parBall1">Шар 1</param>
        /// <param name="parBall2">Шар 2</param>
        /// <returns>Новый вектор скорости для Шара 1</returns>
        private SpVector3 ChangeVelocities(BallModelData parBall1, BallModelData parBall2)
        {
            SpVector3 centersVector        = parBall2.Center - parBall1.Center;
            SpVector3 ballOnePerpendicular = centersVector.PerpendicularComponent(parBall1.Velocity);
            SpVector3 ballTwoPerpendicular = centersVector.PerpendicularComponent(parBall2.Velocity);
            SpVector3 ballTwoParallel      = centersVector.ParralelComponent(parBall2.Velocity);

            SpVector3 newBall1Velocity = ballTwoParallel + ballOnePerpendicular;

            return(newBall1Velocity);
        }
        /// <summary>
        /// Проверка, не попал ли шар в лузу
        /// </summary>
        /// <param name="parBall">Проверяемый шар</param>
        /// <returns></returns>
        private bool CheckIfIsPocketed(BallModelData parBall)
        {
            foreach (var fieldPocket in Field.Pockets)
            {
                if (IsPocketCollision(parBall, fieldPocket))
                {
                    return(true);
                }
            }

            return(false);
        }
        /// <summary>
        /// Запуск проверки всех столкновений на всем столе
        /// </summary>
        private void CheckCollisions()
        {
            for (int i = 0; i < BallsInGame.Count; i++)
            {
                BallModelData currentBall = BallsInGame[i];

                if (CheckCollisionsWithBorders(currentBall))
                {
                    ParentGameObject.LinkedAppModel.GetSoundManager()
                    .PlaySfx(EAppSfxAssets.BallCollisionWallSolid, false);
                }

                for (int j = i + 1; j < BallsInGame.Count; j++)
                {
                    if (CheckCollisionsWithBalls(currentBall, BallsInGame[j]))
                    {
                        ParentGameObject.LinkedAppModel.GetSoundManager().PlaySfx(EAppSfxAssets.BallCollision, false);
                    }
                }

                if (CheckIfIsPocketed(currentBall))
                {
                    ParentGameObject.LinkedAppModel.GetSoundManager().PlaySfx(EAppSfxAssets.BallPocketed, false);

                    if (currentBall != PlayerWhiteBall)
                    {
                        BallsPocketed.Add(currentBall);
                        BallsInGame.Remove(currentBall);

                        _flagAtLeastOneBallIsPocketed = true;
                        ActualPlayer.Score           += 100;
                        i--;
                    }
                    else
                    {
                        currentBall.ResetMovement();
                        currentBall.Center      = _levelConfig.PlayerWhiteBall.StartCenterPosition;
                        ActualPlayer.Score     -= 200;
                        ActualPlayer.LifeCount -= 2;
                    }
                }
            }
        }
        /// <summary>
        /// Замена конструктора, процедура инициализации компонента
        /// </summary>
        /// <param name="parEntGameObject">Родительский игровой объект</param>
        /// <param name="parLevelStartConfig">Стартовая конфигурация игрового уровня</param>
        public PocketGameViewProvider Init(GameObject parEntGameObject, LevelStartConfig parLevelStartConfig,
                                           Player parPlayerData)
        {
            base.Init(parEntGameObject, false, true);

            _levelConfig = parLevelStartConfig;

            Field = parLevelStartConfig.LevelGameField;

            BallsPocketed = new List <BallModelData>();
            BallsInGame   = new List <BallModelData>();

            foreach (var ball in parLevelStartConfig.LevelBalls)
            {
                BallsInGame.Add(new BallModelData(ball.BallType)
                {
                    Center = ball.StartCenterPosition
                });
            }

            PlayerWhiteBall = new BallModelData(parLevelStartConfig.PlayerWhiteBall.BallType)
            {
                Center = parLevelStartConfig.PlayerWhiteBall.StartCenterPosition
            };

            BallsInGame.Add(PlayerWhiteBall);

            ActualPlayer = parPlayerData;

            PlayerCurrentAimingAngle = START_PLAYER_ANGLE;
            PlayerChosenShotForce    = POWER_MIN;

            CurrentGameState = EPocketGameState.Init;

            _playersInputManager = parEntGameObject.LinkedAppModel.GetPlayersManager();

            EndLevelResult = ELevelEndResult.None;

            GameReady = true;
            return(this);
        }
        /// <summary>
        /// Перемещение шара
        /// </summary>
        /// <param name="parBall">Шар</param>
        /// <param name="parFixedDeltaTime">Время шага фиксированного обновления в секундах</param>
        internal void MoveBall(BallModelData parBall, double parFixedDeltaTime)
        {
            parBall.Center.X += parBall.Velocity.X * parBall.Speed * parFixedDeltaTime;
            parBall.Center.Y += parBall.Velocity.Y * parBall.Speed * parFixedDeltaTime;

            double modifierDecreasor = 0.3 * parFixedDeltaTime;
            double modifierActual    = 1.00 - modifierDecreasor;

            parBall.Velocity.X = parBall.Velocity.X * modifierActual;
            parBall.Velocity.Y = parBall.Velocity.Y * modifierActual;

            parBall.Speed = parBall.Speed * modifierActual;

            if (Math.Abs(parBall.Velocity.X) < 0.1 && Math.Abs(parBall.Velocity.Y) < 0.1)
            {
                parBall.Velocity.X = 0;
                parBall.Velocity.Y = 0;

                parBall.Speed = 0;
                // ball.IsMoving = false;
            }
        }
        /// <summary>
        /// Проверка столкновения с бортом
        /// </summary>
        /// <param name="parBall">Шар</param>
        /// <returns></returns>
        private bool CheckCollisionsWithBorders(BallModelData parBall)
        {
            foreach (var fieldCollisionLine in Field.CollisionLines)
            {
                if (fieldCollisionLine.CheckCollisionFunc(parBall))
                {
                    //столкновение произошло
                    SpVector3 ballVelocity = parBall.Velocity;

                    parBall.Velocity = 2 * ballVelocity +
                                       2 * fieldCollisionLine.Normal * (-ballVelocity.DotProduct(fieldCollisionLine.Normal)) -
                                       ballVelocity;

                    do
                    {
                        parBall.Center = parBall.Center + (fieldCollisionLine.Normal);
                    } while (fieldCollisionLine.CheckCollisionFunc(parBall));

                    return(true);
                }
            }

            return(false);
        }