/// <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="parFixedDeltaTime">Время шага фиксированного обновления в секундах</param>
        public override void FixedUpdate(double parFixedDeltaTime)
        {
            base.FixedUpdate(parFixedDeltaTime);

            switch (CurrentGameState)
            {
            case EPocketGameState.Init:
            {
                break;
            }

            case EPocketGameState.Aiming:
            {
                _forceBackwardsDirection = false;

                double currentAimChangeSpeed =
                    _playersInputManager.IsButtonHolding(ActualPlayer.PlayerInput, EGameActionButton.Button_Bumper_Shift)
              ? CHANGE_AIM_ANGLE_SPEED_PRECISE
              : CHANGE_AIM_ANGLE_SPEED_DEFAULT;

                //вверх, влево, вниз, вправо для изменения угла удара
                if (_playersInputManager.IsButtonHolding(ActualPlayer.PlayerInput, EGameActionButton.Dpad_Menu_Up))
                {
                    //наверх
                    if (Math.Abs(PlayerCurrentAimingAngle - AIM_ANGLE_UP) < COMPARISON_TOLERANCE)
                    {
                        // готово
                    }
                    else if (PlayerCurrentAimingAngle > AIM_ANGLE_UP &&
                             PlayerCurrentAimingAngle <= AIM_ANGLE_BOTTOM)
                    {
                        PlayerCurrentAimingAngle -= parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle < AIM_ANGLE_UP)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_UP;
                        }
                    }
                    else if (PlayerCurrentAimingAngle < AIM_ANGLE_UP)
                    {
                        PlayerCurrentAimingAngle += parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle > AIM_ANGLE_UP)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_UP;
                        }
                    }
                    else if (PlayerCurrentAimingAngle > AIM_ANGLE_BOTTOM &&
                             PlayerCurrentAimingAngle <= AIM_ANGLE_CONVERTION_THRESHOLD)
                    {
                        PlayerCurrentAimingAngle = PlayerCurrentAimingAngle + parFixedDeltaTime * currentAimChangeSpeed;
                    }
                }
                else if (_playersInputManager.IsButtonHolding(ActualPlayer.PlayerInput,
                                                              EGameActionButton.Dpad_Menu_Left))
                {
                    //налево
                    if (Math.Abs(PlayerCurrentAimingAngle - AIM_ANGLE_LEFT) < COMPARISON_TOLERANCE)
                    {
                        //готово
                    }
                    else if (PlayerCurrentAimingAngle < AIM_ANGLE_LEFT)
                    {
                        PlayerCurrentAimingAngle += parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle > AIM_ANGLE_LEFT)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_LEFT;
                        }
                    }
                    else if (PlayerCurrentAimingAngle > AIM_ANGLE_LEFT)
                    {
                        PlayerCurrentAimingAngle -= parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle < AIM_ANGLE_LEFT)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_LEFT;
                        }
                    }
                }
                else if (_playersInputManager.IsButtonHolding(ActualPlayer.PlayerInput,
                                                              EGameActionButton.Dpad_Menu_Down))
                {
                    //вниз
                    if (Math.Abs(PlayerCurrentAimingAngle - AIM_ANGLE_BOTTOM) < COMPARISON_TOLERANCE)
                    {
                        //готово
                    }
                    else if (PlayerCurrentAimingAngle < AIM_ANGLE_BOTTOM &&
                             PlayerCurrentAimingAngle >= AIM_ANGLE_UP)
                    {
                        PlayerCurrentAimingAngle += parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle > AIM_ANGLE_BOTTOM)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_BOTTOM;
                        }
                    }
                    else if (PlayerCurrentAimingAngle > AIM_ANGLE_BOTTOM &&
                             PlayerCurrentAimingAngle < AIM_ANGLE_CONVERTION_THRESHOLD)
                    {
                        PlayerCurrentAimingAngle -= parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle < AIM_ANGLE_BOTTOM)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_BOTTOM;
                        }
                    }
                    else if (PlayerCurrentAimingAngle < AIM_ANGLE_UP)
                    {
                        PlayerCurrentAimingAngle -= parFixedDeltaTime * currentAimChangeSpeed;
                    }
                }
                else if (_playersInputManager.IsButtonHolding(ActualPlayer.PlayerInput,
                                                              EGameActionButton.Dpad_Menu_Right))
                {
                    //вправо
                    if (Math.Abs(PlayerCurrentAimingAngle - AIM_ANGLE_RIGHT) < COMPARISON_TOLERANCE)
                    {
                        //готово
                    }
                    else if (PlayerCurrentAimingAngle <= AIM_ANGLE_LEFT)
                    {
                        PlayerCurrentAimingAngle -= parFixedDeltaTime * currentAimChangeSpeed;
                        if (PlayerCurrentAimingAngle >= AIM_ANGLE_BOTTOM)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_RIGHT;
                        }
                    }
                    else if (PlayerCurrentAimingAngle > AIM_ANGLE_LEFT)
                    {
                        PlayerCurrentAimingAngle += parFixedDeltaTime * currentAimChangeSpeed;
                        if (_playerCurrentAimingAngle <= AIM_ANGLE_UP)
                        {
                            PlayerCurrentAimingAngle = AIM_ANGLE_RIGHT;
                        }
                    }
                }
                else if (_playersInputManager.IsActionButtonPressed(ActualPlayer.PlayerInput))
                {
                    PlayerChosenShotForce = POWER_MIN;
                    //теперь начинаем выбирать силу удара
                    CurrentGameState = EPocketGameState.ChooseShotPower;
                }

                break;
            }

            case EPocketGameState.ChooseShotPower:
            {
                if (!_forceBackwardsDirection)
                {
                    if (PlayerChosenShotForce < POWER_MAX)
                    {
                        PlayerChosenShotForce += parFixedDeltaTime * POWER_CHANGE_SPEED;
                        if (PlayerChosenShotForce > POWER_MAX)
                        {
                            PlayerChosenShotForce    = POWER_MAX;
                            _forceBackwardsDirection = !_forceBackwardsDirection;
                        }
                    }
                }
                else
                {
                    PlayerChosenShotForce -= parFixedDeltaTime * POWER_CHANGE_SPEED;
                    if (PlayerChosenShotForce < POWER_MIN)
                    {
                        PlayerChosenShotForce    = POWER_MIN;
                        _forceBackwardsDirection = !_forceBackwardsDirection;
                    }
                }

                if (_playersInputManager.IsActionButtonPressed(ActualPlayer.PlayerInput))
                {
                    //совершаем удар
                    PlayerWhiteBall.Speed    = PlayerChosenShotForce;
                    PlayerWhiteBall.Velocity =
                        Angle.RadiansToVector(Angle.DegreesToRadians(GetAngleApproximated()));
                    _flagAtLeastOneBallIsPocketed = false;
                    ParentGameObject.LinkedAppModel.GetSoundManager().PlaySfx(EAppSfxAssets.BallShotByCue, false);
                    CurrentGameState = EPocketGameState.BallsMovingInProgress;
                }

                break;
            }

            case EPocketGameState.BallsMovingInProgress:
            {
                //подождем пока все завершат свое движение
                if (!UpdateGameField(parFixedDeltaTime))
                {
                    //посмотрим, вдруг мы уже выиграли или проиграли
                    if (!_flagAtLeastOneBallIsPocketed)
                    {
                        //уменьшим счетчик жизней
                        ActualPlayer.LifeCount--;
                    }

                    if (ActualPlayer.LifeCount <= 0)
                    {
                        //мы проиграли...
                        EndLevelResult = ELevelEndResult.Lose;
                        Console.WriteLine("Player lost!");
                    }

                    if (BallsInGame.Count == 1)
                    {
                        if (BallsInGame.First() == PlayerWhiteBall)
                        {
                            //мы выиграли!
                            EndLevelResult = ELevelEndResult.Win;
                            Console.WriteLine("Player completed the level!");
                        }
                        else
                        {
                            throw new ApplicationException("Game Session state machine is broken!");
                        }
                    }

                    if (EndLevelResult != ELevelEndResult.None)
                    {
                        CurrentGameState = EPocketGameState.LevelEnd;
                    }
                    else
                    {
                        CurrentGameState = EPocketGameState.Aiming;
                    }

                    GC.Collect();
                }

                break;
            }

            case EPocketGameState.Paused:
            {
                break;
            }

            case EPocketGameState.LevelEnd:
            {
                GameLevelEnd?.Invoke();
                CurrentGameState = EPocketGameState.Init;
                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }

            if (CurrentGameState != EPocketGameState.Init)
            {
                ViewUpdateSignal(parFixedDeltaTime);
            }
        }
 /// <summary>
 /// Начать игру на уровне
 /// </summary>
 public void StartGameLevel()
 {
     Console.WriteLine("Game level started");
     CurrentGameState = EPocketGameState.Aiming;
     ParentGameObject.LinkedAppModel.GetSoundManager().PlayBgMusic(_levelConfig.LevelBackgroundMusic, true);
 }