/// <summary> /// フェンスの位置によって、ボールの位置を補正する /// (人の位置を補正する場合にも使用できる。ゴロと同じ扱いのため、isFlyをfalseで指定する) /// </summary> /// <param name="basePoint">ボール位置</param> /// <param name="move">移動するVector</param> /// <param name="isFly">フライか</param> /// <param name="fenseReflect">外野フェンスに当たったか</param> /// <param name="fenseOver">外野フェンスを超えたか</param> /// <returns>補正後の位置</returns> private static MPoint ChangePointByFense(MPoint basePoint, Vector move, bool isFly, out bool fenseReflect, out bool fenseOver) { fenseReflect = false; fenseOver = false; MPoint result = basePoint + move; // 捕逸か後方にファウルした場合、画面外にでないように補正 // (フライの場合は補正せず、呼び出し元で判定) if (!isFly && result.Y < Constants.MinScreenPoint.Y) { return new MPoint(result.X, Constants.MinScreenPoint.Y); } // フェンスの手前であれば何もせずに値を返す // (後方のフェンスをフライで超える場合もそのまま返す) if ((result - Constants.PointHomeBase).Length < Constants.BaseToFenceDistance || result.Y < Constants.MinScreenPoint.Y) { return result; } // 以下、フェンスを超える場合 // フライでフェンス超えた場合はホームラン if (isFly) { fenseOver = true; return result; } // 以下、フェンスを超えるゴロの場合 // フェンスを超える判定結果を設定 fenseReflect = true; // フェンスを超える場合、フェンスまでで止める result = basePoint; do { // 超えるまで少しずつ足す result = result + new Vector(move.X * 0.1, move.Y * 0.1); } while ((result - Constants.PointHomeBase).Length < Constants.BaseToFenceDistance); // 最後に少しだけ超えた分の距離を戻す result = result - new Vector(move.X * 0.1, move.Y * 0.1); // フェンスの手前に補正した座標を返す return result; }
/// <summary> /// 送球開始から完了までの時間を取得 /// </summary> /// <param name="targetBase">送球先ベース</param> /// <param name="memberAbility">送球メンバ能力</param> /// <param name="startPoint">送球開始位置</param> /// <param name="runTargetBase">送球先に自分が走ってベースカバーに行くか</param> /// <returns>送球が届くまでの時間</returns> public static int GetThrowBollReachTime(int targetBase, GameMemberAbility memberAbility, MPoint startPoint, bool runTargetBase) { // 送球速度 double bollSpeed; if (!runTargetBase) { // 通常の送球速度の設定 bollSpeed = memberAbility.Throw * Constants.ThrowBollSpeedOffSet; if (DefensePositionHelper.IsOutFielder(memberAbility.DefensePosition)) { // 外野の場合は送球速度低下 bollSpeed *= Constants.ThrowBollSpeedOffSetByOutFileder; } } else { // ベースカバーが遅れており、自分でベースを踏む場合は自分の走力が送球速度になる bollSpeed = memberAbility.RunSpeed; } // 送球が届くまでの時間(0割を防ぐ意味でも必ず1秒以上かかる) MPoint targetPoint = GameData.GetBasePoint(targetBase); double distance = (targetPoint - startPoint).Length; return Math.Max((int)(Math.Ceiling(distance / bollSpeed)), 1); }
/// <summary> /// フェンスの位置によって、守備メンバの位置を補正する /// </summary> /// <param name="basePoint">元の位置</param> /// <param name="move">移動ベクトル</param> /// <returns>移動後の位置</returns> private static MPoint ChangePointByFense(MPoint basePoint, Vector move) { bool fenseReflect; bool fenseOver; // isFlyをfalseで実行することで、ゴロ扱いし、フェンスを超えないように移動する return ChangePointByFense(basePoint, move, false, out fenseReflect, out fenseOver); }
/// <summary> /// 送球前の回転にかかる時間を取得する /// </summary> /// <param name="position">送球する守備位置</param> /// <param name="targetBase">送球先ベース</param> /// <param name="throwStartPoint">送球開始位置</param> /// <returns>かかる時間</returns> private int GetTurningForThrowTime(DefensePosition position, int targetBase, MPoint throwStartPoint) { GameMemberAbility throwMemberAbility = GameData.GetDefenseMemberAbility(position); if (NeedTurnForThrow(throwMemberAbility, targetBase, throwStartPoint)) { return Constants.TurnForThrowTime; } else { return 0; } }
/// <summary> /// 送球前の回転が必要か判定 /// </summary> /// <param name="throwMemberAbility">送球メンバ能力</param> /// <param name="targetBase">送球先</param> /// <param name="throwStartPoint">送球開始位置</param> /// <returns>余分に時間がかかるか</returns> private bool NeedTurnForThrow(GameMemberAbility throwMemberAbility, int targetBase, MPoint throwStartPoint) { // 回転が必要ない野手であれば不要を返す // (投手と内野のみが対象で、捕手は反対を向いているため回転不要) if (throwMemberAbility.DefensePosition != DefensePosition.Pitcher && throwMemberAbility.DefensePosition != DefensePosition.First && throwMemberAbility.DefensePosition != DefensePosition.Second && throwMemberAbility.DefensePosition != DefensePosition.Third && throwMemberAbility.DefensePosition != DefensePosition.Short) { return false; } // 左投げで一塁送球時に回転が必要 if (throwMemberAbility.GameMember.Player.IsLeftThrow && targetBase == 1) { return true; } // 前にダッシュして後ろ(二塁)に送球する場合は回転が必要 // (二塁へのバントを成功させやすくするための処置のため、バントで二塁送球時に必ず発生する) if (GameData.CurrentBattingBunt && targetBase == 2) { return true; } // 右投げで三塁送球時に回転が必要 // (二塁手/遊撃手/三塁手をむやみに回転させたくないため、投手と一塁手のみ発生する) if (throwMemberAbility.GameMember.Player.IsLeftThrow == false && targetBase == 3 && (throwMemberAbility.DefensePosition == DefensePosition.Pitcher || throwMemberAbility.DefensePosition == DefensePosition.First)) { return true; } // 上記の条件を満たさなければ回転不要 return false; }
/// <summary> /// ランナーの論理位置からUIとしてのマージン位置を算出する /// </summary> /// <param name="point"></param> /// <returns></returns> private Thickness GetRunnerMargin(MPoint point) { // スケールの違いを算出 // (このコントロールでは70がベース間距離) double scaleOffset = 70.0 / Constants.BaseCoord; double left = point.X * scaleOffset + this.Width / 2 - BatterRunner.Width / 2; double top = - point.Y * scaleOffset + 200 + BatterRunner.Height / 2; double right = this.Width - (left + BatterRunner.Width); double bottom = this.Height - (top + BatterRunner.Height); return new Thickness(left, top, right, bottom); }
/// <summary> /// 2点の中間地点に対するベクトルの角度を取得 /// </summary> /// <param name="point1"></param> /// <param name="point2"></param> /// <returns></returns> public static double GetAngle(MPoint point1, MPoint point2) { MPoint interLevel = new MPoint((point1.X + point2.X) / 2, (point1.Y + point2.Y) / 2); return Math.Atan2(interLevel.X, interLevel.Y) * (180 / Math.PI); }
/// <summary> /// 送球開始から完了までの時間を取得 /// </summary> /// <param name="targetBase">送球先ベース</param> /// <param name="throwPosition">送球ポジション</param> /// <param name="startPoint">送球開始位置</param> /// <returns>送球が届くまでの時間</returns> private int GetThrowBollReachTime(int targetBase, DefensePosition throwPosition, MPoint startPoint) { // 送球中の時間 int throwBollReachTime = GetThrowBollReachTime(targetBase, throwPosition, startPoint, false); // 送球中の時間 return throwBollReachTime; }
/// <summary> /// 送球開始から完了までの時間を取得 /// </summary> /// <param name="targetBase">送球先ベース</param> /// <param name="throwPosition">送球ポジション</param> /// <param name="startPoint">送球開始位置</param> /// <param name="runTargetBase">送球先に自分が走ってベースカバーに行くか</param> /// <returns>送球が届くまでの時間</returns> private int GetThrowBollReachTime(int targetBase, DefensePosition throwPosition, MPoint startPoint, bool runTargetBase) { // 送球メンバ能力の取得 GameMemberAbility memberAbility = GameData.GetDefenseMemberAbility(throwPosition); // 送球が届くまでの時間を取得 return GetThrowBollReachTime(targetBase, memberAbility, startPoint, runTargetBase); }
/// <summary> /// 送球を実行する /// (送球先を決定済みで、送球前の回転も完了済みであることを前提とする) /// (ベースカバーを待つ場合は何もしない) /// </summary> private void ExecuteThrow() { // 自分が走って送球先に行くかの変数を定義 bool runToTargetBase = false; // ベースカバーがまだ完了していない場合に待つか自分で走るかを判定 if (FinishedBaseCover(ThrowTargetBase) == false) { // 送球のための回転から送球完了までの時間を取得 // (送球前の回転は実施済みのため、送球開始以降の時間を取得する) int targetBase = ThrowTargetBase; DefensePosition throwPosition = GameData.BollKeepingPosition; MPoint startPoint = GameData.BollPoint; int throwTime = GetThrowBollReachTime(targetBase, throwPosition, startPoint); // ベースカバー完了までの時間を算出 int baseCoverTime = BaseCoverRemainingTime[ThrowTargetBase]; if (baseCoverTime > throwTime) { // ベースカバー完了の方が遅ければ、それが送球完了時間となる // (送球完了時にベースカバー完了するタイミングまで送球開始を待つため) throwTime = baseCoverTime; // 自分でベースまで走った場合と比較してどちらが速いか検証する int throwTimeByRun = GetThrowBollReachTime(targetBase, throwPosition, startPoint, true); if (throwTimeByRun < throwTime) { // 自分でベースまで走った方が早ければ、そのフラグを立てる runToTargetBase = true; // ベースカバーの設定も更新する SetBaseCoverPosition(ThrowTargetBase, GameData.BollKeepingPosition); } else { // 自分で走るよりベースカバーが入るまで待った方が早い場合は何もせず待つ return; } } } // 送球野手 ThrowPosition = GameData.BollKeepingPosition; // 送球開始位置 ThrowStartPoint = GameData.BollPoint; // 送球が届くまでの時間を設定 ThrowReachTime = GetThrowBollReachTime(ThrowTargetBase, GameData.BollKeepingPosition, ThrowStartPoint, runToTargetBase); // 送球先位置 ThrowTargetPoint = GameData.GetBasePoint(ThrowTargetBase); // 守備動作種別の変更 GameData.DefenseTermKind = DefenseTermKind.Throwing; // 進塁停止していたランナーの進塁の再判断 foreach (RunnerStatus runnerStatus in GameData.AllRunnerStatus) { if (runnerStatus.IsStop) { UpdateRunnerStatusForForwardOrBack(runnerStatus); } } // 送球開始のイベント発行 if (ThrowBoll != null) { ThrowBoll(); } }
/// <summary> /// 指定時間後の守備メンバの位置を返す /// </summary> /// <param name="memberAbility">対象メンバ</param> /// <param name="goalPoint">目標位置位置</param> /// <param name="fitureSecond">指定時間</param> /// <returns>指定時間後の守備メンバの位置</returns> private MPoint GetDefenseMovePoint(GameMemberAbility memberAbility, MPoint goalPoint, int fitureSecond) { // 指定時間後の守備メンバの移動距離を取得 double moveDistance = GetDefenseMoveDistance(memberAbility, fitureSecond); // 指定秒数後のメンバ位置を取得 MPoint fitureMemberPoint; { MPoint memberPoint = GameData.GetDefensePoint(memberAbility.DefensePosition); // 目的位置とメンバの位置関係から進行方向のベクトルを取得 Vector memberToGoalVector = (goalPoint - memberPoint); if (memberToGoalVector.Length > moveDistance) { // ベクトルを正規化 double normalizing = memberToGoalVector.Length > 0 ? memberToGoalVector.Length : 1; memberToGoalVector = new Vector(memberToGoalVector.X / normalizing, memberToGoalVector.Y / normalizing); // 正規化したベクトルと移動距離から移動ベクトルを算出 Vector moveVector = new Vector(moveDistance * memberToGoalVector.X, moveDistance * memberToGoalVector.Y); // フェンスによる位置の補正 fitureMemberPoint = ChangePointByFense(memberPoint, moveVector); } else { // 目標位置までの距離が移動可能距離より小さい場合は、目標位置がそのまま最終位置になる fitureMemberPoint = goalPoint; } } return fitureMemberPoint; }
/// <summary> /// 指定したコントロールの位置変更のアニメーションを実行する /// </summary> /// <param name="toPoint"></param> /// <param name="control"></param> private void MoveControl(MPoint toPoint, FrameworkElement control) { Thickness toMargin = UIUtility.GetMargin(toPoint, control); UIUtility.MoveControlByMargin(toMargin, control, TimeSpan.Zero); }
/// <summary> /// 捕球アニメーションの実行 /// </summary> /// <param name="beforeDefensePoint">捕球直前位置</param> public void ExecuteBollCatchAnimation(MPoint beforeDefensePoint) { // 捕球直前位置とボール位置がほとんど変わらなければ何もしない if ((beforeDefensePoint - GameData.BollPoint).Length < UIConstants.LengthOfShowCatchAnime) { return; } // コントロールの初期値設定 CatchAnime.SetGroudPictureMargin(beforeDefensePoint); // フェードアウトアニメーションの実行 UIUtility.FadeOutControl(CatchAnime, TimeSpan.Zero, UIConstants.ShowCatchAnimeDuration); // コントロールの移動はなぜか正常に動作しないため実施せず { // コントロールの移動アニメーションの実行(Canvas方式) //MPoint bollUIPoint = UIUtility.GetControlPoint(GameData.BollPoint); //UIUtility.MoveControlInCanvas(new Point(bollUIPoint.X, bollUIPoint.Y), CatchAnime, new TimeSpan(0, 0, 0, 0, 100), new TimeSpan(0, 0, 0, 0, 500), true, new Point(beforeUIPoint.X, beforeUIPoint.Y)); // コントロールの移動アニメーションの実行(Margin方式) //Thickness toMargin = UIUtility.GetMargin(GameData.BollPoint, CatchAnime); //UIUtility.MoveControlByMargin(toMargin, CatchAnime, new TimeSpan(0, 0, 0, 0, 500), true); } }
/// <summary> /// 指定した守備位置の現在位置を設定する /// </summary> /// <param name="defensePosition">指定する守備</param> /// <param name="point">設定値</param> public void SetDefensePoint(DefensePosition defensePosition, MPoint point) { switch (defensePosition) { case DefensePosition.Pitcher: Defense1Point = point; break; case DefensePosition.Catcher: Defense2Point = point; break; case DefensePosition.First: Defense3Point = point; break; case DefensePosition.Second: Defense4Point = point; break; case DefensePosition.Third: Defense5Point = point; break; case DefensePosition.Short: Defense6Point = point; break; case DefensePosition.Left: Defense7Point = point; break; case DefensePosition.Center: Defense8Point = point; break; case DefensePosition.Right: Defense9Point = point; break; default: throw new Exception("Invalid DefensePosition"); } }
/// <summary> /// 打球速度が減速する場合は減速後の打球速度を返す /// </summary> /// <param name="isFly">フライか</param> /// <param name="passedSecond">経過時間</param> /// <param name="bollPoint">ボール位置</param> /// <param name="bollSpeed">現在の打球速度</param> /// <returns>減速後の打球速度</returns> private static double GetBollSpeed(bool isFly, int passedSecond, MPoint bollPoint, double bollSpeed) { // 打球速度の低下(一定時間経過後か一定距離到達後に減速) if (passedSecond >= Constants.BattedBollAttenuationStartTime || (bollPoint - Constants.PointHomeBase).Length >= Constants.BattedBollAttenuationDistance) { if (isFly) { // フライの場合 return bollSpeed * Constants.BattedBollAttenuationByFly; } else { // ゴロの場合 return bollSpeed * Constants.BattedBollAttenuationByGround; } } // 減速がなければ元の値を返す return bollSpeed; }
/// <summary> /// 送球のための回転開始から送球完了までの時間を取得 /// </summary> /// <param name="targetBase">送球先ベース</param> /// <param name="throwPosition">送球ポジション</param> /// <param name="startPoint">送球開始位置</param> /// <param name="memberAbility">送球メンバ能力</param> /// <returns>かかる時間時間</returns> private int GetThrowBollReachTimeFromTurn(int targetBase, DefensePosition throwPosition, MPoint startPoint, GameMemberAbility memberAbility) { // 送球開始から完了までの時間を取得 int throwTime = GetThrowBollReachTime(targetBase, throwPosition, startPoint); // 送球前の回転が必要な場合は余分に時間を加算 if (NeedTurnForThrow(memberAbility, targetBase, startPoint)) { throwTime += Constants.TurnForThrowTime; } return throwTime; }
/// <summary> /// 指定時間後の守備メンバがボールを捕球できるか /// </summary> /// <param name="memberAbility">対象メンバ</param> /// <param name="bollPoint">指定時間後のボール位置</param> /// <param name="fitureSecond">指定時間</param> /// <returns>捕球できるか</returns> private bool CatchedBoll(GameMemberAbility memberAbility, MPoint bollPoint, int fitureSecond) { // エラー発生後の再判定時、ベースカバーの野手は捕球できない if (IsError && IsBaseCover(memberAbility.GameMember.DefensePosition)) { return false; } // 残り硬直時間を取得 int stiffSecond = GetStiffSecond(memberAbility); if (fitureSecond < stiffSecond) { // 硬直時間中はボールを捕球できない return false; } // メンバとボールの距離 double defenseToBollDistanse = (GameData.GetDefensePoint(memberAbility.DefensePosition) - bollPoint).Length; // 指定時間後の守備メンバの移動距離を取得 double moveDistance = GetDefenseMoveDistance(memberAbility, fitureSecond); // 捕球範囲を取得 double catchArea = GetCatchArea(memberAbility); // 捕球できるかを返す return moveDistance + catchArea >= defenseToBollDistanse; }
/// <summary> /// 送球開始の回転からタッチアウトまでにかかる時間を取得 /// (ベースカバーが遅れて自分でベースまで走る場合も考慮) /// </summary> /// <param name="targetBase">送球先ベース</param> /// <param name="throwPosition">送球ポジション</param> /// <param name="startPoint">送球開始位置</param> /// <param name="memberAbility">送球メンバ能力</param> /// <param name="passedTime">送球開始までに現在時刻からさらに経過している時間(捕球前の予測で使用)</param> /// <returns>かかる時間時間</returns> private int GetThrowTimeFromTurnToTouch(int targetBase, DefensePosition throwPosition, MPoint startPoint, GameMemberAbility memberAbility, int passedTime) { // 送球前の回転から送球完了までの時間の算出 int throwTime = GetThrowBollReachTimeFromTurn(targetBase, throwPosition, startPoint, memberAbility); // ベースカバー完了までの時間を算出(送球開始までにさらに経過している時間は減算) int baseCoverTime = BaseCoverRemainingTime[targetBase] - passedTime; if (baseCoverTime > throwTime) { // ベースカバー完了の方が遅ければ、それが送球完了時間となる // (送球完了時にベースカバー完了するタイミングまで送球開始を待つため) throwTime = baseCoverTime; // 自分でベースまで走った場合と比較してどちらが速いか検証する int throwTimeByRun = GetThrowBollReachTime(targetBase, throwPosition, startPoint, true); if (throwTimeByRun < throwTime) { // 自分でベースまで走った方が早ければ、その時間を送球時間とする throwTime = throwTimeByRun; } } // フォースアウトでも帰塁ランナーのアウトでもなければ // タッチアウト体制への準備時間を加算する if (!IsForceOut(targetBase) && GetBackRunRunner(targetBase) == null) { throwTime += Constants.ReadyingTouchSecond; } // かかる時間を返す return throwTime; }
/// <summary> /// 捕球ハンドラ /// </summary> /// <param name="beforeDefensePoint">捕球直前の守備位置</param> private void m_GameManager_CatchBoll(MPoint beforeDefensePoint) { // 高速試合モードであれば何もしない if (m_GameManager.HighSpeedGameMode) { return; } // 捕球アニメーションの実行 m_DefenseActionView.ExecuteBollCatchAnimation(beforeDefensePoint); SoundManager.PlayEffect(SoundEffectKind.Catch); }
/// <summary> /// 捕球ハンドラ /// </summary> /// <param name="beforeDefensePoint">捕球直前の守備位置</param> private void DefenseActionManger_CatchBoll(MPoint beforeDefensePoint) { if (CatchBoll != null) { CatchBoll(beforeDefensePoint); } }