/// <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="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> /// Проверка столкновения по модели прямоугольников AABB /// </summary> /// <param name="parBallData">Шар</param> /// <param name="parStart">Стартовая точка линии</param> /// <param name="parEnd">Конечная точка линии</param> /// <returns>True, если столкновение возможно</returns> static bool IsRectCollided(BallModelData parBallData, SpVector3 parStart, SpVector3 parEnd) { if (parBallData.Center.Y - parBallData.Radius > parEnd.Y) { return(false); } if (parBallData.Center.Y + parBallData.Radius < parStart.Y) { return(false); } // } double maxX = Math.Max(parStart.X, parEnd.X); double minX = Math.Min(parStart.X, parEnd.X); if (parBallData.Center.X - parBallData.Radius > maxX) { return(false); } if (parBallData.Center.X + parBallData.Radius < minX) { return(false); } return(true); }
/// <summary> /// Генерирует и возвращает функцию для проверки столкновения для заданной линии /// </summary> /// <param name="parStart">Начало линии</param> /// <param name="parEnd">Конец линии</param> /// <param name="parShouldCheckBottom">Точная проверка столкновения будет являтся пройденной, /// если точка соприкосновения окажется снизу от линии?</param> /// <param name="parNormal">Нормаль</param> /// <returns></returns> static Func <BallModelData, bool> GenFunctionForPocketBorder(SpVector3 parStart, SpVector3 parEnd, bool parShouldCheckBottom, SpVector3 parNormal) { GetEquationKandB(parStart, parEnd, out double k, out double b); Func <BallModelData, bool> retFunc = parData => { if (IsRectCollided(parData, parStart, parEnd)) { SpVector3 pointToCheck = parData.Center + (-parNormal) * parData.Radius; //необходима более точная проверка if (parShouldCheckBottom) { return(pointToCheck.Y < k * pointToCheck.X + b); } else { return(pointToCheck.Y > k * pointToCheck.X + b); } } else { return(false); } }; return(retFunc); }
/// <summary> /// Повернуть точки вокруг опоры /// </summary> /// <param name="parRotationDegreesAngle">Величина поворота в градусах</param> /// <param name="refPoints">Точки для поворота</param> /// <param name="parOrigin">Точка опоры</param> public static void RotateAroundPivot(double parRotationDegreesAngle, ref SpVector3[] refPoints, SpVector3 parOrigin) { double rotationRadians = DegreesToRadians(parRotationDegreesAngle); double s = System.Math.Sin(rotationRadians); double c = System.Math.Cos(rotationRadians); for (var index = 0; index < refPoints.Length; index++) { var refPoint = refPoints[index]; //переместим double x = refPoint.X - parOrigin.X; double y = refPoint.Y - parOrigin.Y; //повернем double xRot = x * c - y * s; double yRot = x * s + y * c; //переместим обратно double xRes = xRot + parOrigin.X; double yRes = yRot + parOrigin.Y; refPoints[index] = new SpVector3(xRes, yRes); } }
/// <summary> /// Получает коэффициенты уравнения прямой y = kx + b /// </summary> /// <param name="parStart">Первая точка</param> /// <param name="parEnd">Вторая точка</param> /// <param name="parEquationK">Коэффициент уравнения k</param> /// <param name="parEquationB">Коэффициент уравнения b</param> static void GetEquationKandB(SpVector3 parStart, SpVector3 parEnd, out double parEquationK, out double parEquationB) { double dY = parEnd.Y - parStart.Y; double dX = parEnd.X - parStart.X; parEquationK = dY / dX; parEquationB = parStart.Y - (parEquationK * parStart.X); }
/// <summary> /// Дополнительный конструктор, генерирующий нормаль и функцию проверки коллизии самостоятельно /// </summary> /// <param name="parStart">Стартовая точка линии</param> /// <param name="parEnd">Конечная точка линии</param> /// <param name="parIsClockwiseNormal">Поиск нормали должен производится по часовой стрелке?</param> /// <param name="parIsCollisionCheckPreciseBottom">Точная проверка столкновения будет являтся пройденной, /// если точка соприкосновения окажется снизу от линии?</param> public CollisionLine(SpVector3 parStart, SpVector3 parEnd, bool parIsClockwiseNormal, bool parIsCollisionCheckPreciseBottom) { Normal = SpVector3.GetNormal2DOnly(parEnd, parStart, parIsClockwiseNormal); CheckCollisionFunc = GenFunctionForPocketBorder(parStart, parEnd, parIsCollisionCheckPreciseBottom, Normal); }
/// <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="parV2">Другой вектор</param> /// <returns></returns> public bool Equal(SpVector3 parV2) { if (_x == parV2.X && _y == parV2.Y && _z == parV2.Z ) //true if all components in one vector is equal to components in another. { return(true); } else { return(false); } }
/// <summary> /// Получить нормаль к линии в двухмерном пространстве /// </summary> /// <param name="parStart">Точка начала линии</param> /// <param name="parEnd">Точка конца линии</param> /// <param name="parIsClockwise">Нормаль нужна по часовой стрелке?</param> /// <returns></returns> public static SpVector3 GetNormal2DOnly(SpVector3 parStart, SpVector3 parEnd, bool parIsClockwise) { SpVector3 resultVector = parEnd - parStart; if (parIsClockwise) { return(new SpVector3(resultVector.Y, -resultVector.X, resultVector.Z).Normalize()); } else { return(new SpVector3(-resultVector.Y, resultVector.X, resultVector.Z).Normalize()); } }
/// <summary> /// Компонент переноса /// </summary> /// <param name="parV2">Второй вектор</param> /// <returns></returns> public SpVector3 ParralelComponent(SpVector3 parV2) { double lengthSquared, dotProduct, scale; lengthSquared = Length() * Length(); dotProduct = DotProduct(parV2); if (lengthSquared != 0) { scale = dotProduct / lengthSquared; } else { return(new SpVector3(0, 0)); } return(new SpVector3(this.Scale(scale))); }
/// <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); }
/// <summary> /// Перекрестное произведение векторов /// </summary> /// <param name="parV2">Второй вектор</param> /// <returns></returns> public SpVector3 CrossProduct(SpVector3 parV2) { return(new SpVector3(_y * parV2.Z - _z * parV2.Y, _z * parV2.X - _x * parV2.Z, _x * parV2.Y - _y * parV2.X)); }
/// <summary> /// Скалярное произведение векторов /// </summary> /// <param name="parV2">Второй вектор</param> /// <returns></returns> public double DotProduct(SpVector3 parV2) { return(_x * parV2.X + _y * parV2.Y + _z * parV2.Z); }
/// <summary> /// Стандартный конструктор /// </summary> /// <param name="parNormal">Нормаль</param> /// <param name="parCheckCollisionFunc">Функция для проверки коллизии с линией</param> public CollisionLine(SpVector3 parNormal, Func <BallModelData, bool> parCheckCollisionFunc) { CheckCollisionFunc = parCheckCollisionFunc; Normal = parNormal; }
/// <summary> /// Стандартный конструктор /// </summary> /// <param name="parCenter">Центр лузы</param> public Pocket(SpVector3 parCenter) { Center = parCenter; }
/// <summary> /// Обновить данные для рендеринга /// </summary> public override void UpdateRenderingData() { if (_modelProviderComponent != null) { if (_modelProviderComponent.GameReady) { //всегда есть задний фон, шары, очки и счетчик жизней if (_currentRenderingBg == null) { _currentRenderingBg = new RenderingData.RenderingData( new RenderingSprite(ActualSubassetsDataLibrary.GetSprite(NORMAL_GAMEFIELD_BG_SPRITE), 0, 0, 0, Color.White, 5)); } if (_ballsRenderingData == null) { _ballsRenderingData = new LinkedList <RenderingData.RenderingData?>(); } _ballsRenderingData.Clear(); foreach (var modelBall in _modelProviderComponent.BallsInGame) { BallGraphicsBind graphicsBind = GameFieldViewConsts.BallGraphicsBinds[modelBall.BallType]; _ballsRenderingData.AddLast(new RenderingData.RenderingData(new RenderingSprite( ActualSubassetsDataLibrary.GetSprite(graphicsBind.StillSpriteKey), GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_X + modelBall.Center.X, GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_Y + (GameFieldViewConsts.FIELD_HEIGHT - modelBall.Center.Y), 0, graphicsBind.BlendColor, 1, 1, 1, EHorizontalAlign.Middle, EVerticalAlign.Middle))); } if (_playerScoreAndLifeCounter == null) { _playerScoreAndLifeCounter = new LinkedList <RenderingData.RenderingData?>(); } _playerScoreAndLifeCounter.Clear(); //очки _playerScoreAndLifeCounter.AddLast(new RenderingData.RenderingData(new RenderingString(1, SCORE_RIGHT_X_OFFSET, SCORE_TOP_Y_OFFSET, _modelProviderComponent.ActualPlayer.Score.ToString(), ActualSubassetsDataLibrary.GetFont(ViewBehaviourConsts.RED_SHADOW_APP_FONT), Color.White, 1, 1, EHorizontalAlign.Right, EVerticalAlign.Top))); //счетчик жизней int line1Lifes = 0; int line2Lifes = 0; int actualPlayerLifesNumber = _modelProviderComponent.ActualPlayer.LifeCount / 2 + _modelProviderComponent.ActualPlayer.LifeCount % 2; bool isLastLifeHalfed = _modelProviderComponent.ActualPlayer.LifeCount % 2 == 1; if (actualPlayerLifesNumber > 2 * LIFES_ON_ONE_LINE) { line1Lifes = LIFES_ON_ONE_LINE; line2Lifes = LIFES_ON_ONE_LINE; } else { if (actualPlayerLifesNumber >= LIFES_ON_ONE_LINE) { line1Lifes = LIFES_ON_ONE_LINE; line2Lifes = actualPlayerLifesNumber - LIFES_ON_ONE_LINE; } else { line1Lifes = actualPlayerLifesNumber; line2Lifes = 0; } } int currentY = LIFECOUNTER_LINE1_START_MIDDLE_Y; int currentX = LIFECOUNTER_LINE1_START_MIDDLE_X; for (int i = 0; i < line1Lifes; i++) { string chosenSpriteKey = LIFE_INDICATOR; if (i == line1Lifes - 1) { if (line2Lifes == 0) { if (isLastLifeHalfed) { chosenSpriteKey = LIFE_INDICATOR_HALF; } } } _playerScoreAndLifeCounter.AddLast(new RenderingData.RenderingData(new RenderingSprite( ActualSubassetsDataLibrary.GetSprite(chosenSpriteKey), currentX, currentY, 0, Color.White, 0, 1, 1, EHorizontalAlign.Middle, EVerticalAlign.Middle))); currentX += LIFECOUNTER_X_ELEM_OFFSET; } currentY += LIFECOUNTER_Y_ELEM_OFFSET; currentX = LIFECOUNTER_LINE1_START_MIDDLE_X; for (int i = 0; i < line2Lifes; i++) { string chosenSpriteKey = LIFE_INDICATOR; if (i == line2Lifes - 1) { if (isLastLifeHalfed) { chosenSpriteKey = LIFE_INDICATOR_HALF; } } _playerScoreAndLifeCounter.AddLast(new RenderingData.RenderingData(new RenderingSprite( ActualSubassetsDataLibrary.GetSprite(chosenSpriteKey), currentX, currentY, 0, Color.White, 0, 1, 1, EHorizontalAlign.Middle, EVerticalAlign.Middle))); currentX += LIFECOUNTER_X_ELEM_OFFSET; } //дальнейший рендеринг зависит от текущего состояния игры if (_playerCueRenderingData == null) { _playerCueRenderingData = new LinkedList <RenderingData.RenderingData?>(); } _playerCueRenderingData.Clear(); if (_modelProviderComponent.CurrentGameState == EPocketGameState.Aiming) { _upperShotChoosePanelOverlay = null; //прицел SpVector3 whiteBallPosOrigin = _modelProviderComponent.PlayerWhiteBall.Center; whiteBallPosOrigin.X += GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_X; whiteBallPosOrigin.Y = GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_Y + (GameFieldViewConsts.FIELD_HEIGHT - whiteBallPosOrigin.Y); double additionalRotOffset = 0; if (_modelProviderComponent.PlayerCurrentAimingAngle > 10 && _modelProviderComponent.PlayerCurrentAimingAngle < 80) { additionalRotOffset = ANGLE_ADJUST_TOLERANCE; } else if (_modelProviderComponent.PlayerCurrentAimingAngle <= 10 && _modelProviderComponent.PlayerCurrentAimingAngle > 0) { additionalRotOffset = CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 0, 10, 0, ANGLE_ADJUST_TOLERANCE); } else if (_modelProviderComponent.PlayerCurrentAimingAngle >= 80 && _modelProviderComponent.PlayerCurrentAimingAngle < 90) { additionalRotOffset = CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 80, 90, ANGLE_ADJUST_TOLERANCE, 0); } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 100 && _modelProviderComponent.PlayerCurrentAimingAngle < 170) { additionalRotOffset = -ANGLE_ADJUST_TOLERANCE; } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 90 && _modelProviderComponent.PlayerCurrentAimingAngle <= 100) { additionalRotOffset = - CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 90, 100, 0, ANGLE_ADJUST_TOLERANCE); } else if (_modelProviderComponent.PlayerCurrentAimingAngle >= 170 && _modelProviderComponent.PlayerCurrentAimingAngle < 180) { additionalRotOffset = - CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 170, 180, ANGLE_ADJUST_TOLERANCE, 0); } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 190 && _modelProviderComponent.PlayerCurrentAimingAngle < 260) { additionalRotOffset = ANGLE_ADJUST_TOLERANCE; } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 180 && _modelProviderComponent.PlayerCurrentAimingAngle <= 190) { additionalRotOffset = CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 180, 190, 0, ANGLE_ADJUST_TOLERANCE); } else if (_modelProviderComponent.PlayerCurrentAimingAngle >= 260 && _modelProviderComponent.PlayerCurrentAimingAngle < 270) { additionalRotOffset = CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 260, 270, ANGLE_ADJUST_TOLERANCE, 0); } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 280 && _modelProviderComponent.PlayerCurrentAimingAngle < 350) { additionalRotOffset = -ANGLE_ADJUST_TOLERANCE; } else if (_modelProviderComponent.PlayerCurrentAimingAngle > 270 && _modelProviderComponent.PlayerCurrentAimingAngle <= 280) { additionalRotOffset = - CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 270, 280, 0, ANGLE_ADJUST_TOLERANCE); } else if (_modelProviderComponent.PlayerCurrentAimingAngle >= 350 && _modelProviderComponent.PlayerCurrentAimingAngle < 360) { additionalRotOffset = - CustomLerp.GetInterpolatedValue(_modelProviderComponent.PlayerCurrentAimingAngle, 350, 360, ANGLE_ADJUST_TOLERANCE, 0); } _playerCueRenderingData.AddLast(new RenderingData.RenderingData(whiteBallPosOrigin.X + 8, whiteBallPosOrigin.Y, 100, 1, Color.Bisque, -1, _modelProviderComponent.GetAngleApproximated() + additionalRotOffset, whiteBallPosOrigin.X, whiteBallPosOrigin.Y)); } else if (_modelProviderComponent.CurrentGameState == EPocketGameState.ChooseShotPower) { _choosingShotAnimationInfobar.CurrentFrame = _modelProviderComponent.GetShotForcePercent() * ANIM_INFOBAR_MAX_FRAME; _choosingShotPlayerCueAnimation.CurrentFrame = _modelProviderComponent.GetShotForcePercent() * ANIM_HANDSHOT_CUE_MAX_FRAME; //панель с информацией о силе удара _upperShotChoosePanelOverlay = new RenderingData.RenderingData(new RenderingSprite( _choosingShotAnimationInfobar.GetCurrentAnimSpriteFrame(), 145, 1, 0, Color.White, -5, 1, 1)); //анимация кия SpVector3 whiteBallPosOrigin = _modelProviderComponent.PlayerWhiteBall.Center; whiteBallPosOrigin.X += GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_X; whiteBallPosOrigin.Y = GameFieldViewConsts.FIELD_MODEL_RENDER_LEFT_TOP_OFFSET_Y + (GameFieldViewConsts.FIELD_HEIGHT - whiteBallPosOrigin.Y); _playerCueRenderingData.AddLast(new RenderingData.RenderingData(new RenderingSprite( _choosingShotPlayerCueAnimation.GetCurrentAnimSpriteFrame(), whiteBallPosOrigin.X + 8, whiteBallPosOrigin.Y + 4, _modelProviderComponent.PlayerCurrentAimingAngle - 180, Color.White, -6, 1, 1, EHorizontalAlign.Left, EVerticalAlign.Middle, whiteBallPosOrigin.X, whiteBallPosOrigin.Y))); } } } }
/// <summary> /// Стандартный конструктор /// </summary> /// <param name="parBallType">Определенный тип шара</param> /// <param name="parStartCenterPosition">Стартовая позиция на столе</param> public BallStartConfig(EBallType parBallType, SpVector3 parStartCenterPosition) { BallType = parBallType; StartCenterPosition = parStartCenterPosition; }
/// <summary> /// Компонент перпендикуляра /// </summary> /// <param name="parV2"></param> /// <returns></returns> public SpVector3 PerpendicularComponent(SpVector3 parV2) { return(new SpVector3(parV2 - this.ParralelComponent(parV2))); }
/// <summary> /// Останавливает шар /// </summary> public void ResetMovement() { Velocity = new SpVector3(0, 0); Speed = 0; }
/// <summary> /// Конструктор, принимающий другой вектор /// </summary> /// <param name="parV">Другой вектор</param> public SpVector3(SpVector3 parV) { _x = parV._x; _y = parV._y; _z = parV._z; }
/// <summary> /// Статический конструктор /// </summary> static GameConsts() { LinkedList <CollisionLine> collisionLines = new LinkedList <CollisionLine>(); //низ, левая сторона SpVector3 lineNormalBottomLeftSide = new SpVector3(0, 1, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncBottomLeftSide = parData => { if (parData.Center.X + parData.Radius < 32 && parData.Center.X - parData.Radius > 122) { return(false); } if (parData.Center.Y - parData.Radius > 20) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalBottomLeftSide, lineCheckFuncBottomLeftSide)); SpVector3 lineNormalBottomRightSide = new SpVector3(0, 1, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncBottomRightSide = parData => { if (parData.Center.X + parData.Radius < 145 && parData.Center.X - parData.Radius > 235) { return(false); } if (parData.Center.Y - parData.Radius > 20) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalBottomRightSide, lineCheckFuncBottomRightSide)); SpVector3 lineNormalTopLeftSide = new SpVector3(0, -1, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncTopLeftSide = parData => { if (parData.Center.X + parData.Radius < 32 && parData.Center.X - parData.Radius > 122) { return(false); } if (parData.Center.Y + parData.Radius < FieldHeight - 20) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalTopLeftSide, lineCheckFuncTopLeftSide)); SpVector3 lineNormalTopRightSide = new SpVector3(0, -1, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncTopRightSide = parData => { if (parData.Center.X + parData.Radius < 145 && parData.Center.X - parData.Radius > 235) { return(false); } if (parData.Center.Y + parData.Radius < FieldHeight - 20) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalTopRightSide, lineCheckFuncTopRightSide)); SpVector3 lineNormalLeftSide = new SpVector3(1, 0, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncLeftSide = parData => { if (parData.Center.X - parData.Radius > 21) { return(false); } if (parData.Center.Y + parData.Radius < 32 && parData.Center.Y - parData.Radius > 128) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalLeftSide, lineCheckFuncLeftSide)); SpVector3 lineNormalRightSide = new SpVector3(-1, 0, 0).Normalize(); Func <BallModelData, bool> lineCheckFuncRightSide = parData => { if (parData.Center.X + parData.Radius < FieldWidth - 21) { return(false); } if (parData.Center.Y + parData.Radius < 32 && parData.Center.Y - parData.Radius > 128) { return(false); } return(true); }; collisionLines.AddLast(new CollisionLine(lineNormalRightSide, lineCheckFuncRightSide)); //низ SpVector3 bottomLeftPocketBottomBorderStart = new SpVector3(27, FieldHeight - 143, 0); SpVector3 bottomLeftPocketBottomBorderEnd = new SpVector3(32, FieldHeight - 138, 0); collisionLines.AddLast(new CollisionLine(bottomLeftPocketBottomBorderStart, bottomLeftPocketBottomBorderEnd, false, true)); SpVector3 bottomLeftPocketTopBorderStart = new SpVector3(16, FieldHeight - 132, 0); SpVector3 bottomLeftPocketTopBorderEnd = new SpVector3(21, FieldHeight - 127, 0); collisionLines.AddLast(new CollisionLine(bottomLeftPocketTopBorderStart, bottomLeftPocketTopBorderEnd, true, false)); SpVector3 bottomMiddlePocketRightBorderStart = new SpVector3(142, FieldHeight - 143, 0); SpVector3 bottomMiddlePocketRightBorderEnd = new SpVector3(145, FieldHeight - 138, 0); collisionLines.AddLast(new CollisionLine(bottomMiddlePocketRightBorderStart, bottomMiddlePocketRightBorderEnd, false, true)); SpVector3 bottomMiddlePocketLeftBorderStart = new SpVector3(125, FieldHeight - 143, 0); SpVector3 bottomMiddlePocketLeftBorderEnd = new SpVector3(122, FieldHeight - 138, 0); collisionLines.AddLast(new CollisionLine(bottomMiddlePocketLeftBorderStart, bottomMiddlePocketLeftBorderEnd, true, true)); SpVector3 bottomRightPocketBottomBorderStart = new SpVector3(240, FieldHeight - 143, 0); SpVector3 bottomRightPocketBottomBorderEnd = new SpVector3(235, FieldHeight - 138, 0); collisionLines.AddLast(new CollisionLine(bottomRightPocketBottomBorderStart, bottomRightPocketBottomBorderEnd, true, true)); SpVector3 bottomRightPocketTopBorderStart = new SpVector3(251, FieldHeight - 132, 0); SpVector3 bottomRightPocketTopBorderEnd = new SpVector3(246, FieldHeight - 127, 0); collisionLines.AddLast(new CollisionLine(bottomRightPocketTopBorderStart, bottomRightPocketTopBorderEnd, false, false)); //верх SpVector3 topLeftPocketBottomBorderStart = new SpVector3(21, FieldHeight - 32, 0); SpVector3 topLeftPocketBottomBorderEnd = new SpVector3(15, FieldHeight - 26, 0); collisionLines.AddLast(new CollisionLine(topLeftPocketBottomBorderStart, topLeftPocketBottomBorderEnd, true, true)); SpVector3 topLeftPocketTopBorderStart = new SpVector3(32, FieldHeight - 21, 0); SpVector3 topLeftPocketTopBorderEnd = new SpVector3(26, FieldHeight - 15, 0); collisionLines.AddLast(new CollisionLine(topLeftPocketTopBorderStart, topLeftPocketTopBorderEnd, false, false)); SpVector3 topMiddlePocketRightBorderStart = new SpVector3(145, FieldHeight - 21, 0); SpVector3 topMiddlePocketRightBorderEnd = new SpVector3(142, FieldHeight - 15, 0); collisionLines.AddLast(new CollisionLine(topMiddlePocketRightBorderStart, topMiddlePocketRightBorderEnd, false, false)); SpVector3 topMiddlePocketLeftBorderStart = new SpVector3(122, FieldHeight - 21, 0); SpVector3 topMiddlePocketLeftBorderEnd = new SpVector3(125, FieldHeight - 15, 0); collisionLines.AddLast(new CollisionLine(topMiddlePocketLeftBorderStart, topMiddlePocketLeftBorderEnd, true, false)); SpVector3 topRightPocketBottomBorderStart = new SpVector3(246, FieldHeight - 32, 0); SpVector3 topRightPocketBottomBorderEnd = new SpVector3(252, FieldHeight - 26, 0); collisionLines.AddLast(new CollisionLine(topRightPocketBottomBorderStart, topRightPocketBottomBorderEnd, false, true)); SpVector3 topRightPocketTopBorderStart = new SpVector3(235, FieldHeight - 21, 0); SpVector3 topRightPocketTopBorderEnd = new SpVector3(241, FieldHeight - 15, 0); collisionLines.AddLast(new CollisionLine(topRightPocketTopBorderStart, topRightPocketTopBorderEnd, true, false)); StandardGameField = new GameField(268, 160, new Pocket[] { new Pocket(new SpVector3(16, 16, 0)), new Pocket(new SpVector3(134, 11, 0)), new Pocket(new SpVector3(252, 16, 0)), new Pocket(new SpVector3(16, 144, 0)), new Pocket(new SpVector3(134, 148, 0)), new Pocket(new SpVector3(252, 144, 0)) }, collisionLines.ToArray()); //добавляем игровые уровни AvailableGameLevels = new List <GameLevel>() { new GameLevel("Los Angeles", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(90, FieldHeight - 80)), new BallStartConfig(EBallType.Blue, new SpVector3(90 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Red, new SpVector3(90 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Purple, new SpVector3(90 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Orange, new SpVector3(90 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80))), new BallStartConfig(EBallType.Green, new SpVector3(90 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 1))) }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel1 )), new GameLevel("Las Vegas", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(100, FieldHeight - 80)), new BallStartConfig(EBallType.Blue, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Red, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Purple, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Orange, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80))), new BallStartConfig(EBallType.Green, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Brown, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Black, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Yellow9Ball, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 4, FieldHeight - 80)) }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel2)), new GameLevel("San Francisco", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(110, FieldHeight - 80)), new BallStartConfig(EBallType.Blue, new SpVector3(110 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Red, new SpVector3(110 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Purple, new SpVector3(110 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Orange, new SpVector3(110 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80))), new BallStartConfig(EBallType.Green, new SpVector3(110 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 1))) }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel3)), new GameLevel("New York", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(80, FieldHeight - 80)), new BallStartConfig(EBallType.Blue, new SpVector3(80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 + DOUBLED_BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Red, new SpVector3(80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Purple, new SpVector3(80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 + DOUBLED_BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Orange, new SpVector3(80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80))), new BallStartConfig(EBallType.Green, new SpVector3(80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 - DOUBLED_BALLS_SETUP_BASE_DISTANCE * 1))) }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel4)), new GameLevel("Atlantic City", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(100, FieldHeight - 80)), new BallStartConfig(EBallType.Blue, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Red, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 1, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE / 2 * 1))), new BallStartConfig(EBallType.Purple, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Orange, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80))), new BallStartConfig(EBallType.Green, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 2, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Brown, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 2))), new BallStartConfig(EBallType.Black, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 + BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Yellow9Ball, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 1))), new BallStartConfig(EBallType.Blue10Ball, new SpVector3(100 - BALLS_SETUP_BASE_DISTANCE * 3, FieldHeight - (80 - BALLS_SETUP_BASE_DISTANCE * 2))), }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel5)), new GameLevel("Test Area", new LevelStartConfig(StandardGameField, new BallStartConfig[] { new BallStartConfig(EBallType.Yellow, new SpVector3(16, 16, 0)), }, new BallStartConfig(EBallType.WhitePlayerBall, StartWhiteBallPlayerPos), EAppMusicAssets.SPLevel1)) }; }