/// <summary> /// 指定位置までの移動ルートと移動コストを返します /// </summary> /// <returns>The route coordinates and move amount.</returns> /// <param name="from">From.</param> /// <param name="to">To.</param> public List <CoordinateAndValue> CalcurateRouteCoordinatesAndMoveAmount(Main_Cell from, Main_Cell to) { var costs = GetMoveCostToAllCells(from); if (!costs.Any(info => info.coordinate.x == to.X && info.coordinate.y == to.Y)) { throw new ArgumentException(string.Format("x:{0}, y:{1} is not movable.", to.X, to.Y)); } var toCost = costs.First(info => info.coordinate.x == to.X && info.coordinate.y == to.Y); var route = new List <CoordinateAndValue>(); route.Add(toCost); while (true) { var currentCost = route.Last(); var currentCell = cells.First(cell => cell.X == currentCost.coordinate.x && cell.Y == currentCost.coordinate.y); var prevMoveCost = currentCost.value - currentCell.Cost; var previousCost = costs.FirstOrDefault(info => (Mathf.Abs(info.coordinate.x - currentCell.X) + Mathf.Abs(info.coordinate.y - currentCell.Y)) == 1 && info.value == prevMoveCost); if (null == previousCost) { break; } route.Add(previousCost); } route.Reverse(); return(route.ToList()); }
/// <summary> /// 移動経路となるマスを返します /// </summary> /// <returns>The route cells.</returns> /// <param name="startCell">Start cell.</param> /// <param name="moveAmount">Move amount.</param> /// <param name="endCell">End cell.</param> public Main_Cell[] CalculateRouteCells(int x, int y, int moveAmount, Main_Cell endCell) { var startCell = cells.First(c => c.X == x && c.Y == y); var infos = GetRemainingMoveAmountInfos(startCell, moveAmount); if (!infos.Any(info => info.coordinate.x == endCell.X && info.coordinate.y == endCell.Y)) { throw new ArgumentException(string.Format("endCell(x:{0}, y:{1}) is not movable.", endCell.X, endCell.Y)); } var routeCells = new List <Main_Cell>(); routeCells.Add(endCell); while (true) { var currentCellInfo = infos.First(info => info.coordinate.x == routeCells[routeCells.Count - 1].X && info.coordinate.y == routeCells[routeCells.Count - 1].Y); var currentCell = cells.First(cell => cell.X == currentCellInfo.coordinate.x && cell.Y == currentCellInfo.coordinate.y); var previousMoveAmount = currentCellInfo.value + currentCell.Cost; var previousCellInfo = infos.FirstOrDefault(info => (Mathf.Abs(info.coordinate.x - currentCell.X) + Mathf.Abs(info.coordinate.y - currentCell.Y)) == 1 && info.value == previousMoveAmount); if (null == previousCellInfo) { break; } routeCells.Add(cells.First(c => c.X == previousCellInfo.coordinate.x && c.Y == previousCellInfo.coordinate.y)); } routeCells.Reverse(); return(routeCells.ToArray()); }
/// <summary> /// 200725 戻るボタンを押された時、アニメーション無しで移動 /// </summary> /// <param name="cell"></param> public void ReturnTo(Main_Cell beforeCell) { this.transform.position = beforeCell.transform.position; x = beforeCell.X; z = beforeCell.Y; }
public Main_Cell[] GetCellsByDistance(Main_Cell baseCell, int distanceMin, int distanceMax) { return(cells.Where(x => { var distance = Math.Abs(baseCell.X - x.X) + Math.Abs(baseCell.Y - x.Y); return distanceMin <= distance && distance <= distanceMax; }).ToArray()); }
/// <summary> /// 移動経路となるマスを返します /// </summary> /// <returns>The route cells.</returns> /// <param name="startCell">Start cell.</param> /// <param name="moveAmount">Move amount.</param> /// <param name="endCell">End cell.</param> /// //endCellは移動先のセルらしい public Main_Cell[] CalculateRouteCells(int x, int y, int moveAmount, Main_Cell endCell, CalculationMode mode) { //x,yを元に移動開始するセルを取得 var startCell = cells.First(c => c.X == x && c.Y == y); //移動可能となるセルリストがMoveAmountInfo型で返ってくる var moveAbleInfos = GetRemainingMoveAmountInfos(startCell, moveAmount, mode); //移動経路のセルリスト作る List <Main_Cell> routeCells = new List <Main_Cell>(); Debug.Log($"移動経路計算モード = {mode}"); //攻撃モードと探索モードだと検索方法が違う if (mode == CalculationMode.ENEMY_MOVE || mode == CalculationMode.PLAYER_MOVE) { Debug.Log("移動先への最短距離を検索"); //移動可能リストにクリックした移動先のセルが入ってなかったらおかしいのでエラー if (!moveAbleInfos.Any(info => info.coordinate.x == endCell.X && info.coordinate.y == endCell.Y)) { Debug.Log(string.Format("endCell(x:{0}, y:{1}) is not movable.", endCell.X, endCell.Y)); throw new ArgumentException(string.Format("endCell(x:{0}, y:{1}) is not movable.", endCell.X, endCell.Y)); } //まず最終移動先セルを追加 routeCells.Add(endCell); //無限ループ while (true) { //まず最終移動先セルの情報から開始 配列は0スタートでCountは要素数を返すので-1する var currentCellInfo = moveAbleInfos.First(moveAbleInfo => moveAbleInfo.coordinate.x == routeCells[routeCells.Count - 1].X && moveAbleInfo.coordinate.y == routeCells[routeCells.Count - 1].Y); //現在のセルを現在のセル情報(MoveAmountInfo)から取得する var currentCell = cells.First(cell => cell.X == currentCellInfo.coordinate.x && cell.Y == currentCellInfo.coordinate.y); //1つ前のセルの移動力を算出 当然0から始まる var previousMoveAmount = currentCellInfo.amount + currentCell.Cost; //1つ前のセルの中から、移動力が同じ物を探す //ABS : 絶対値を返すので、X方向、Y方向だろうが1なら隣接しているセルとなる var previousCellInfo = moveAbleInfos.FirstOrDefault(moveAbleInfo => (Mathf.Abs(moveAbleInfo.coordinate.x - currentCell.X) + Mathf.Abs(moveAbleInfo.coordinate.y - currentCell.Y)) == 1 && moveAbleInfo.amount == previousMoveAmount); //最終的にはプレイヤーが今居る座標の移動力になり、最大移動力以上のセルは存在しないのでnullとなる if (null == previousCellInfo) { break; } //移動経路リストに最短経路を追加する routeCells.Add(cells.First(c => c.X == previousCellInfo.coordinate.x && c.Y == previousCellInfo.coordinate.y)); } } //最終目的地から追加していってるので順番を逆にする routeCells.Reverse(); //配列で返す return(routeCells.ToArray()); }
/// <summary> /// 戦闘開始前の処理メソッド /// </summary> /// //200814 自分ターンに敵を選択した時、敵AIの攻撃時に呼んで、戦闘ウィンドウを初期化する public void MapOpenBattleView(PlayerModel playerModel, EnemyModel enemyModel, bool isPlayerAttack) { //プレイヤーからの攻撃か否かを設定 this.isPlayerAttack = isPlayerAttack; Unit unit = playerModel.unit; Enemy enemy = enemyModel.enemy; //210219 戦闘に地形効果を追加 //ユニット、敵のセル情報を取得する Main_Cell unitCell = mainMap.Cells.FirstOrDefault(c => c.X == playerModel.x && c.Y == playerModel.z); Main_Cell enemyCell = mainMap.Cells.FirstOrDefault(c => c.X == enemyModel.x && c.Y == enemyModel.y); //通常は有り得ない if (unitCell == null) { Debug.Log($"ERROR:ユニットの座標のセル情報が有りません X:{playerModel.x},Y:{playerModel.z}"); } if (enemyCell == null) { Debug.Log($"ERROR:敵の座標のセル情報が有りません X:{enemyModel.x},Y:{enemyModel.y}"); } //戦闘に使用する数値の計算を実施してUIに表示 var battleParameterDTO = battleCalculator.CalculateBattleParameter(unit, unit.equipWeapon, unitCell, enemy, enemyCell, isPlayerAttack); battleView.GetComponent <BattleView>().UpdateText(battleParameterDTO); //戦闘UI表示 battleView.SetActive(true); //本クラスに戦闘するプレイヤーと敵を設定 BattleInit(unit, enemy); //プレイヤー攻撃時のみ確認ウィンドウを表示してフォーカス if (isPlayerAttack) { //第二引数は攻撃ならtrue battleConfirmWindow.GetComponent <BattleConfirmWindow>().UpdateConfirmtext("戦闘開始して良いですか?", true); battleConfirmWindow.SetActive(true); EventSystem.current.SetSelectedGameObject(null); EventSystem.current.SetSelectedGameObject(battleConfirmWindow.transform.Find("ButtonLayout/StartButton").gameObject); } else { //敵から攻撃された時、武器の回数が減らない場合があるバグ対応 foreach (Item item in unit.carryItem) { if (item.ItemType == ItemType.WEAPON && item.isEquip) { item.weapon = unit.equipWeapon; } } //敵の攻撃時は確認せずに戦闘開始 battleConfirmWindow.SetActive(false); BattleStart(); } }
/// <summary> /// 移動の終了を待つコルーチン /// </summary> /// <returns>The move coroutine.</returns> /// <param name="unit">Unit.</param> /// <param name="cell">Cell.</param> IEnumerator WaitMoveCoroutine(Main_Unit unit, Main_Cell cell) { while (true) { if (cell.Unit == unit) { break; } yield return(new WaitForSeconds(0.1f)); } yield return(new WaitForSeconds(0.5f)); }
/// <summary> /// 指定座標から各マスまで、移動コストいくつで行けるかを計算します /// </summary> /// <returns>The move amount to cells.</returns> /// <param name="from">From.</param> public List <CoordinateAndValue> GetMoveCostToAllCells(Main_Cell from) { var infos = new List <CoordinateAndValue>(); infos.Add(new CoordinateAndValue(from.X, from.Y, 0)); var i = 0; while (true) { var appendInfos = new List <CoordinateAndValue>(); foreach (var calcTargetInfo in infos.Where(info => info.value == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; var aroundCellCoordinates = new Coordinate[] { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), }; // 四方のマスの残移動力を計算 foreach (var aroundCellCoordinate in aroundCellCoordinates) { var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } var remainingMoveAmount = i + targetCell.Cost; appendInfos.Add(new CoordinateAndValue(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); } } infos.AddRange(appendInfos); i++; if (i > infos.Max(x => x.value < 999 ? x.value : 0)) { break; } } return(infos.Where(x => x.value < 999).ToList()); }
/// <summary> /// 210301 周囲に宝箱が存在するかを返す /// </summary> /// <param name="startCell"></param> /// <returns></returns> public Coordinate SearchTreasureBox(Main_Cell startCell) { if (startCell == null) { Debug.Log("warn:セルが存在しません"); //画面端だとセルが無い場合がある return(null); } // 四方のマスの座標配列を作成 var aroundCellCoordinates = new Coordinate[] //上下左右の座標配列 { new Coordinate(startCell.X - 1, startCell.Y), //左 new Coordinate(startCell.X + 1, startCell.Y), //右 new Coordinate(startCell.X, startCell.Y - 1), //上 new Coordinate(startCell.X, startCell.Y + 1), //下 }; // 四方のマスの残移動力を計算 上下左右マスの座標を順に取り出し foreach (Coordinate aroundCellCoordinate in aroundCellCoordinates) { //FirstOrDefaultとは? 現在地から上下左右の座標を元にマップの座標を取得 TreasureModel treasureModel = treasureContainer.GetComponentsInChildren <TreasureModel>().FirstOrDefault( c => c.x == aroundCellCoordinate.x && c.y == aroundCellCoordinate.y); //宝箱が有った時点で座標を返す //もし四方に宝箱が有るような状況なら、配列の先頭の要素、左から開け始める if (treasureModel != null) { //空の場合は無いとみなす if (treasureModel.treasureBox.isEmpty) { continue; } Debug.Log($"宝箱を発見 X:{aroundCellCoordinate.x}, Y:{aroundCellCoordinate.y}"); return(aroundCellCoordinate); } } //周囲に宝箱が無ければnullを返す nullチェック注意 return(null); }
/// <summary> /// 210301カーソルを移動させた時、宝箱が有るかどうか判定 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public bool IsTreasureExist(Main_Cell cell) { if (cell == null) { Debug.Log("warn:セルが存在しません"); //画面端だとセルが無い場合がある return(false); } TreasureModel treasureModel = treasureContainer.GetComponentsInChildren <TreasureModel>().FirstOrDefault( c => c.x == cell.X && c.y == cell.Y); //宝箱が有った時点で座標を返す //もし四方に宝箱が有るような状況なら、配列の先頭の要素、左から開け始める if (treasureModel != null) { return(true); } return(false); }
/// <summary> /// 自軍ターン時の敵の攻撃可能セル確認(紫表示)表示を行う /// 210304 警告処理を軽くする為にメソッドを分けた /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="moveAmount"></param> /// <param name="weapon"></param> /// <param name="isWarning"></param> /// <param name="enemyId"></param> /// <returns></returns> public List <Main_Cell> HighlightEnemyWarnCells(int x, int y, int moveAmount, Weapon weapon, int enemyId) { List <Main_Cell> warnCellList = new List <Main_Cell>(); //200724 渡された座標が最初のセルとなる //FirstはNullが返って来ない物に使うこと var startCell = cells.First(c => c.X == x && c.Y == y); //移動可能なセルを取得してハイライト処理を行う MoveAmountInfo[] moveableInfo = GetRemainingMoveAmountInfos(startCell, moveAmount, CalculationMode.ENEMY_MOVE); //移動可能セルから攻撃対象が存在するか確認を行う処理 foreach (var info in moveableInfo) { //まず移動可能セルをハイライト Main_Cell warnCell = cells.First(c => c.X == info.coordinate.x && c.Y == info.coordinate.y); warnCell.SetIsWarning(enemyId); warnCellList.Add(warnCell); //ここで移動先に既に自分以外の敵がいる場合は、ハイライトはされるが実際は移動して攻撃範囲計算しないよう、対象に含まない //でも、自分自身の座標には移動可能にしないとその場から攻撃が出来ない EnemyModel destinationEnemy = enemyContainer.GetComponentsInChildren <EnemyModel>().FirstOrDefault( c => c.x == info.coordinate.x && c.y == info.coordinate.y); //移動先に敵がいる場合、自分自身以外ならそのセルには移動しない if (destinationEnemy != null) { if (destinationEnemy != ActiveEnemy) { Debug.Log($"既に敵が存在するので移動しないセル X={info.coordinate.x}, Y={info.coordinate.y}"); continue; } } //それぞれの移動可能なセルに対して攻撃可能なセルを確認 List <Main_Cell> attackableCellList = GetWarnAttackableCells(info.coordinate.x, info.coordinate.y, weapon, enemyId); warnCellList.AddRange(attackableCellList); } return(warnCellList); }
/// <summary> /// 210304 敵が攻撃可能な範囲を紫色にする /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="weapon"></param> /// <param name="isWarning"></param> /// <param name="enemyId"></param> /// <returns></returns> private List <Main_Cell> GetWarnAttackableCells(int x, int y, Weapon weapon, int enemyId) { //現在のセルを取得 var startCell = cells.First(c => c.X == x && c.Y == y); List <Main_Cell> attackableCells = new List <Main_Cell>(); //現在のセル攻撃可能範囲 取得 第三引数 : 攻撃時なので、敵のセルを選択可能 foreach (var info in GetAttackRenge(startCell, weapon.range, weapon.isCloseAttack)) { //210219 攻撃可能セルの色を変えてみる Main_Cell attackableCell = cells.First(c => c.X == info.coordinate.x && c.Y == info.coordinate.y); //セルが存在して既にハイライト済みでなければ if (attackableCell != null && attackableCell.IsWarning == false) { attackableCell.SetIsWarning(enemyId); attackableCells.Add(attackableCell); } } //移動可能範囲に攻撃可能なユニットが居ない場合はnullが返ってくる return(attackableCells); }
/// <summary> /// 移動力を元に移動可能範囲の計算を行います /// </summary> /// <returns>The remaining move amount infos.</returns> /// <param name="startCell">Start cell.</param> /// <param name="moveAmount">Move amount.</param> CoordinateAndValue[] GetRemainingMoveAmountInfos(Main_Cell startCell, int moveAmount) { var infos = new List <CoordinateAndValue>(); infos.Add(new CoordinateAndValue(startCell.X, startCell.Y, moveAmount)); for (var i = moveAmount; i >= 0; i--) { var appendInfos = new List <CoordinateAndValue>(); foreach (var calcTargetInfo in infos.Where(info => info.value == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; var aroundCellCoordinates = new Coordinate[] { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), }; // 四方のマスの残移動力を計算 foreach (var aroundCellCoordinate in aroundCellCoordinates) { var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } var remainingMoveAmount = i - targetCell.Cost; appendInfos.Add(new CoordinateAndValue(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); } } infos.AddRange(appendInfos); } // 残移動力が0以上(移動可能)なマスの情報だけを返す return(infos.Where(x => x.value >= 0).ToArray()); }
/// <summary> /// 攻撃可能範囲の計算を行います /// </summary> /// <returns>The remaining move amount infos.</returns> /// <param name="startCell">Start cell.</param> /// <param name="moveAmount">Move amount.</param> CoordinateAndValue[] GetRemainingAccountRangeInfos(Main_Cell startCell, int attackRangeMin, int attackRangeMax) { var infos = new List <CoordinateAndValue>(); infos.Add(new CoordinateAndValue(startCell.X, startCell.Y, attackRangeMax)); for (var i = attackRangeMax; i >= 0; i--) { var appendInfos = new List <CoordinateAndValue>(); foreach (var calcTargetInfo in infos.Where(info => info.value == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; var aroundCellCoordinates = new Coordinate[] { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), }; // 四方のマスの残攻撃範囲を計算 foreach (var aroundCellCoordinate in aroundCellCoordinates) { var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } var remainingMoveAmount = i - 1; appendInfos.Add(new CoordinateAndValue(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); } } infos.AddRange(appendInfos); } // 攻撃範囲内のマスの情報だけを返す return(infos.Where(x => 0 <= x.value && x.value <= (attackRangeMax - attackRangeMin)).ToArray()); }
/// <summary> /// 移動可能なマスをハイライトします /// </summary> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="moveAmount">Move amount.</param> public void HighlightMovableCells(int x, int y, int moveAmount, Weapon equipWeapon, CalculationMode mode) { //まず移動可能なセルのハイライトを消す ResetMovableCells(); //200724 渡された座標が最初のセルとなる //FirstはNullが返って来ない物に使うこと Main_Cell startCell = cells.First(c => c.X == x && c.Y == y); MoveAmountInfo[] moveableInfo = GetRemainingMoveAmountInfos(startCell, moveAmount, mode); foreach (var info in moveableInfo) { //移動可能にしていく trueになるとハイライトが青になる cells.First(c => c.X == info.coordinate.x && c.Y == info.coordinate.y).IsMovable = true; } //210219 先に移動可能か確認ループしてからでないと移動可能かどうかは分からないので2回ループ・・・ foreach (var info in moveableInfo) { //210219 攻撃可能範囲の表示を追加 if (equipWeapon != null) { Main_Cell attackCell = cells.First(c => c.X == info.coordinate.x && c.Y == info.coordinate.y); foreach (var Attackinfo in GetAttackRenge(attackCell, equipWeapon.range, equipWeapon.isCloseAttack)) { //攻撃可能にしていく trueになるとハイライトが赤になる Main_Cell attackableCell = cells.First(c => c.X == Attackinfo.coordinate.x && c.Y == Attackinfo.coordinate.y); if (attackableCell != null) { if (attackableCell.IsMovable == false) { attackableCell.IsAttackable = true; } } } } } }
/// <summary> /// 200802 マップのロードを行う /// </summary> public void LoadMap(Stage stage) { //引数で読み込むファイルを分岐 霊夢ルートステージ1ならSTAGE1mapData MapData mapData = Resources.Load <MapData>($"{stage.chapter.ToString()}mapData"); if (mapData == null) { Debug.Log("ERROR:地形データが存在しませんでした。"); return; } foreach (var cell in cells) { //リスト内の全てのセルを初期化 Destroy(cell.gameObject); } cells = new List <Main_Cell>(); List <CellData> cellDataList = mapData.cells; foreach (CellData cellData in cellDataList) { //座標を入れると勝手に配置、種類を入れると勝手に他のパラメータも入る Main_Cell cell = Instantiate(mainCell); cell.gameObject.SetActive(true); cell.transform.SetParent(transform); cell.SetType(cellData.type); //座標と共に高さを設定 cell.SetCoordinate(cellData.x, cellData.y, cell.Type); cell.IsMovable = false; cells.Add(cell); } //210302 trueにするとセルの種類が表示される ShowSellType(false); Debug.Log($"マップデータを読み込みました。"); }
//編集中の決定ボタン public void EditFireButton() { //セル入力のみボタン押しっぱなし対応 //セル入力モード if (editMode == EditMode.CELL_EDIT) { //200802 セルの種類を変更 Vector3 cursorPos = battleMapManager.GetCursorPos(); int index = mainMap.Cells.IndexOf(mainMap.Cells.FirstOrDefault(c => c.X == cursorPos.x && c.Y == cursorPos.z)); //見つからなければ-1が返ってくるので何もしない if (index == -1) { return; } Main_Cell cell = mainMap.Cells[index]; cell.SetType(inputCellType); //更新^^ mainMap.Cells[index] = cell; } }
/// <summary> /// プレイヤー関連 /// </summary> //210211 プレイヤーの攻撃可能範囲表示 public void HighlightAttacableCells(int x, int y, Weapon weapon) { //まずセルのハイライトを消す ResetMovableCells(); var startCell = cells.First(c => c.X == x && c.Y == y); //攻撃可能範囲取得 第三引数 : 攻撃時なので、敵のセルを選択可能 foreach (var info in GetAttackRenge(startCell, weapon.range, weapon.isCloseAttack)) { //攻撃可能にしていく trueになるとハイライトが赤になる Main_Cell cell = cells.First(c => c.X == info.coordinate.x && c.Y == info.coordinate.y); //210219 武器の時は赤色、そうでない時は緑色にする if (weapon.type != WeaponType.HEAL) { cell.IsAttackable = true; } else { cell.IsHealable = true; } } }
/// <summary> /// ユニットを対象のマスに移動させます /// </summary> /// <param name="cell">Cell.</param> public void MoveTo(Main_Unit unit, Main_Cell cell) { ClearHighlight(); var routeCells = CalculateRouteCells(unit.x, unit.y, unit.moveAmount, cell); var sequence = DOTween.Sequence(); for (var i = 1; i < routeCells.Length; i++) { var routeCell = routeCells[i]; sequence.Append(unit.transform.DOMove(routeCell.transform.position, 0.1f).SetEase(Ease.Linear)); } sequence.OnComplete(() => { unit.x = routeCells[routeCells.Length - 1].X; unit.y = routeCells[routeCells.Length - 1].Y; // 攻撃可能範囲のチェック var isAttackable = HighlightAttackableCells(unit.x, unit.y, unit.attackRangeMin, unit.attackRangeMax); if (!isAttackable) { unit.IsMoved = true; } }); }
/// <summary> /// ユニットを対象のマスに移動させる /// </summary> /// <param name="cell">Cell.</param> public void MoveTo(Main_Cell cell) { //アニメーションを走る動きに変更 animator.SetBool("isMoving", true); //移動ルートのセル配列を取得 var routeCells = map.CalculateRouteCells(x, z, moveAmount, cell, CalculationMode.PLAYER_MOVE); var sequence = DOTween.Sequence(); for (var i = 1; i < routeCells.Length; i++) { //1マスを0.15秒で移動する var routeCell = routeCells[i]; sequence.Append(transform.DOLookAt(routeCell.transform.position, 0.0075f)) .Append(transform.DOMove(routeCell.transform.position, 0.1f).SetEase(Ease.Linear)); } sequence.OnComplete(() => { //プレイヤーモデルの座標を更新 //要素は0スタートなのでLengthから1引いた値となる x = routeCells[routeCells.Length - 1].X; z = routeCells[routeCells.Length - 1].Y; isFocused = false; //モーションを停止に animator.SetBool("isMoving", false); //移動中はフラグで管理する battleMapManager.isMoving = false; //210221 敵の攻撃可能範囲を再計算 map.ReloadHighLightCells(this); // 移動後ウィンドウ表示 battleMapManager.PrepareMapmodeMovedMenu(); }); }
/// <summary> /// 移動力を元に移動可能範囲の計算を行います /// </summary> /// <returns>The remaining move amount infos.</returns> /// <param name="startCell">Start cell.</param> /// <param name="moveAmount">Move amount.</param> MoveAmountInfo[] GetRemainingMoveAmountInfos(Main_Cell startCell, int moveAmount, CalculationMode mode) { //座標と移動可能距離を持つリストを用意する まずは現在位置で最大の移動可能距離 var infos = new List <MoveAmountInfo>(); infos.Add(new MoveAmountInfo(startCell.X, startCell.Y, moveAmount)); //残り移動可能距離を1ずつ減らしていって、0になるまでループまわす for (var i = moveAmount; i >= 0; i--) { //追加していく移動可能距離リスト作成 var appendInfos = new List <MoveAmountInfo>(); //amountはそのセルでの残り移動可能距離 まずは現在位置からスタート foreach (var calcTargetInfo in infos.Where(info => info.amount == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; //TODO ??? var aroundCellCoordinates = new Coordinate[] //上下左右の座標配列 { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), //左 new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), //右 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), //上 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), //下 }; // 四方のマスの残移動力を計算 上下左右マスの座標を順に取り出し foreach (var aroundCellCoordinate in aroundCellCoordinates) { //FirstOrDefaultとは? 現在地から上下左右の座標を元にマップの座標を取得 var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); //nullはマイナス座標などでマップの範囲外とか infoとappendInfosにもう同じ座標のセルが有ればスルー //もうそのセルは移動可能にならないし if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } //移動先に宝箱がある場合、敵、味方共に移動不可 if (IsDestinationTreasureExist(aroundCellCoordinate)) { continue; } if (mode == CalculationMode.PLAYER_MOVE) { //210212 プレイヤー移動時、敵の居るセルは移動不可能にする 攻撃時は選択可能 EnemyModel destinationEnemy = enemyContainer.GetComponentsInChildren <EnemyModel>().FirstOrDefault( c => c.x == aroundCellCoordinate.x && c.y == aroundCellCoordinate.y); if (destinationEnemy != null) { continue; } } else if (mode == CalculationMode.ENEMY_MOVE || mode == CalculationMode.ENEMY_SEARCH) { //同じように、敵はプレイヤーが居るセルには移動出来ない PlayerModel destinationPlayer = unitContainer.GetComponentsInChildren <PlayerModel>().FirstOrDefault( c => c.x == aroundCellCoordinate.x && c.z == aroundCellCoordinate.y); if (destinationPlayer != null) { continue; } } //残りの移動可能距離を取得、対象セルから移動可能コストを引く int remainingMoveAmount; remainingMoveAmount = i - targetCell.Cost; appendInfos.Add(new MoveAmountInfo(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); }//四方のマス取得ループ } //上下左右の座標リストを取得し終わったらinfosにぶちこむ infos.AddRange(appendInfos); } // 残移動力が0以上(移動可能)なマスの情報だけを返す return(infos.Where(x => x.amount >= 0).ToArray()); }
/// <summary> /// 210301 周囲に回復出来るユニットが居るかを返す /// </summary> /// <param name="startCell"></param> /// <returns></returns> public List <PlayerModel> GetHealableUnit(Main_Cell startCell, Unit unit) { if (startCell == null) { Debug.Log("warn:セルが存在しません"); //画面端だとセルが無い場合がある return(null); } List <PlayerModel> healableUnitList = new List <PlayerModel>(); //ユニットの持つ武器で最も長い射程を取得 //TODO これ、射程距離 = 遠攻撃 / 2のリブローが有るのでその内実装する事 int maxRange = 1 - 1; //座標と移動可能距離を持つリストを用意する まずは現在位置で最大の移動可能距離 var infos = new List <MoveAmountInfo>(); infos.Add(new MoveAmountInfo(startCell.X, startCell.Y, maxRange)); //残り移動可能距離を1ずつ減らしていって、0になるまでループまわす for (var i = maxRange; i >= 0; i--) { //追加していく移動可能距離リスト作成 var appendInfos = new List <MoveAmountInfo>(); //amountはそのセルでの残り移動可能距離 まずは現在位置からスタート foreach (var calcTargetInfo in infos.Where(info => info.amount == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; //TODO ??? var aroundCellCoordinates = new Coordinate[] //上下左右の座標配列 { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), //左 new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), //右 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), //上 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), //下 }; // 四方のマスの残移動力を計算 上下左右マスの座標を順に取り出し foreach (var aroundCellCoordinate in aroundCellCoordinates) { //FirstOrDefaultとは? 現在地から上下左右の座標を元にマップの座標を取得 var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); //nullはマイナス座標などでマップの範囲外とか infoとappendInfosにもう同じ座標のセルが有ればスルー //もうそのセルは移動可能にならないし if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } PlayerModel playerModel = unitContainer.GetComponentsInChildren <PlayerModel>().FirstOrDefault( c => c.x == aroundCellCoordinate.x && c.z == aroundCellCoordinate.y); if (i >= maxRange) { //仲間が存在したら if (playerModel != null) { StatusCalculator statusCalc = new StatusCalculator(); int maxHp = playerModel.unit.maxhp + playerModel.unit.job.statusDto.jobHp; maxHp = statusCalc.CalcHpBuff(maxHp, playerModel.unit.job.skills); //回復出来る仲間が居ればリストに入れる if (playerModel.unit.hp < maxHp) { healableUnitList.Add(playerModel); Debug.Log($"回復出来る仲間を発見 X:{aroundCellCoordinate.x}, Y:{aroundCellCoordinate.y}"); } } } //残りの移動可能距離を取得、対象セルから移動可能コストを引く int remainingMoveAmount; remainingMoveAmount = i - 1; appendInfos.Add(new MoveAmountInfo(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); }//四方のマス取得ループ } infos.AddRange(appendInfos); } //ここまで処理が入れば攻撃可能範囲内に敵は存在しない return(healableUnitList); }
/// <summary> /// 210301 周囲に攻撃出来る敵が居るかを返す /// </summary> /// <param name="startCell"></param> /// <returns></returns> public List <EnemyModel> GetAttackableEnemy(Main_Cell startCell, Unit unit) { if (startCell == null) { Debug.Log("warn:セルが存在しません"); //画面端だとセルが無い場合がある return(null); } List <EnemyModel> attackableEnemyList = new List <EnemyModel>(); //ユニットの持つ武器で最も長い射程を取得 int maxRange = 0; foreach (Item item in unit.carryItem) { if (item.ItemType == ItemType.WEAPON) { if (item.weapon.range > maxRange) { maxRange = item.weapon.range - 1; } } } Debug.Log($"最大射程:{maxRange}"); //座標と移動可能距離を持つリストを用意する まずは現在位置で最大の移動可能距離 var infos = new List <MoveAmountInfo>(); infos.Add(new MoveAmountInfo(startCell.X, startCell.Y, maxRange)); //残り移動可能距離を1ずつ減らしていって、0になるまでループまわす for (var i = maxRange; i >= 0; i--) { //追加していく移動可能距離リスト作成 var appendInfos = new List <MoveAmountInfo>(); //amountはそのセルでの残り移動可能距離 まずは現在位置からスタート foreach (var calcTargetInfo in infos.Where(info => info.amount == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; //TODO ??? var aroundCellCoordinates = new Coordinate[] //上下左右の座標配列 { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), //左 new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), //右 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), //上 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), //下 }; // 四方のマスの残移動力を計算 上下左右マスの座標を順に取り出し foreach (var aroundCellCoordinate in aroundCellCoordinates) { //FirstOrDefaultとは? 現在地から上下左右の座標を元にマップの座標を取得 var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); //nullはマイナス座標などでマップの範囲外とか infoとappendInfosにもう同じ座標のセルが有ればスルー //もうそのセルは移動可能にならないし if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } EnemyModel enemyModel = enemyContainer.GetComponentsInChildren <EnemyModel>().FirstOrDefault( c => c.x == aroundCellCoordinate.x && c.y == aroundCellCoordinate.y); if (enemyModel != null) { attackableEnemyList.Add(enemyModel); Debug.Log($"敵を発見 X:{aroundCellCoordinate.x}, Y:{aroundCellCoordinate.y}"); } //残りの移動可能距離を取得、対象セルから移動可能コストを引く int remainingMoveAmount; remainingMoveAmount = i - 1; appendInfos.Add(new MoveAmountInfo(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingMoveAmount)); }//四方のマス取得ループ } infos.AddRange(appendInfos); } return(attackableEnemyList); }
/// <summary> /// ユニットを対象のマスに移動させます /// </summary> /// <param name="cell">Cell.</param> public void MoveTo(Main_Cell cell, bool isAttack) { int moveAmount = enemy.job.move; //210215 ボスはステータス画面に移動力は表示するが、移動しない設定を追加 if (enemyAIPattern == EnemyAIPattern.BOSS) { moveAmount = 0; } //210212 ここで移動中にする battleMapManager.isMoving = true; //移動可能セルを消す map.ResetMovableCells(); map.ResetAttackableCells(); //アニメーションを走る動きに変更 animator.SetBool("isMoving", true); //移動ルートのセル配列を取得 Main_Cell[] routeCells; routeCells = map.CalculateRouteCells(x, y, moveAmount, cell, CalculationMode.ENEMY_MOVE); var sequence = DOTween.Sequence(); for (var i = 1; i < routeCells.Length; i++) { //1マスを0.15秒で移動する var routeCell = routeCells[i]; sequence.Append(transform.DOLookAt(routeCell.transform.position, 0.0075f)) .Append(transform.DOMove(routeCell.transform.position, 0.1f).SetEase(Ease.Linear)); } sequence.OnComplete(() => { //要素は0スタートなのでLengthから1引いた値となる x = routeCells[routeCells.Length - 1].X; y = routeCells[routeCells.Length - 1].Y; isFocused = false; Debug.Log($"移動後の敵の座標 : x={x} , y = {y}"); //モーションを停止に animator.SetBool("isMoving", false); if (isAttack) { //攻撃可能なので攻撃実施 enemyAIManager.enemyAIPhase = EnemyAIPhase.ATTACK; Debug.Log("enemyAIPhase : " + enemyAIManager.enemyAIPhase); } else { //攻撃可能ではないので待機 isMoved = true; SetBeforeAction(false); Debug.Log($"待機 : {enemy.name}"); enemyAIManager.enemyAIPhase = EnemyAIPhase.GET_ENEMY; Debug.Log("enemyAIPhase : " + enemyAIManager.enemyAIPhase); } //移動中ではないのでフラグを戻しておく enemyAIManager.isEnemyMove = false; battleMapManager.isMoving = false; }); }
//210214 最もプレイヤーに近いセルを返す public Main_Cell SearchPlayerNearestCell(int x, int y, int moveAmount, PlayerModel[] players) { //探索モード //探索モードでは向かう対象 = 移動可能ではなく、移動力が最大 = 敵の現在いる座標という事にはならないので、 //まず、プレイヤーに一番近いセルを移動可能距離の中から見つめてそこを行き先にする Debug.Log("プレイヤーへの最短距離を検索"); MoveAmountInfo nearestInfo = null; int nearestDistance = 0; //x,yを元に移動開始するセルを取得 var startCell = cells.First(c => c.X == x && c.Y == y); //移動可能となるセルリストがMoveAmountInfo型で返ってくる var moveAbleInfos = GetRemainingMoveAmountInfos(startCell, moveAmount, CalculationMode.ENEMY_SEARCH); foreach (PlayerModel player in players) { //プレイヤーの座標を取得 Main_Cell playerCell = Cells.FirstOrDefault(c => c.X == player.x && c.Y == player.z); Debug.Log($"探索したプレイヤー{player.unit.name} x= {player.x} , y= {player.z}"); foreach (MoveAmountInfo moveAbleInfo in moveAbleInfos) { //最初の1ループは存在しないので、まず設定 if (nearestInfo == null) { //既に自分以外の敵が居れば除外 if (isDestinationEnemyExist(moveAbleInfo)) { continue; } nearestInfo = moveAbleInfo; nearestDistance = (Mathf.Abs(playerCell.X - moveAbleInfo.coordinate.x) + Mathf.Abs(playerCell.Y - moveAbleInfo.coordinate.y)); } else { //2回目以降のループ //攻撃対象への距離 少ない程優先する int distance = (Mathf.Abs(playerCell.X - moveAbleInfo.coordinate.x) + Mathf.Abs(playerCell.Y - moveAbleInfo.coordinate.y)); Debug.Log($"座標 :X={moveAbleInfo.coordinate.x}, Y={moveAbleInfo.coordinate.y},距離={distance}"); //更にプレイヤーに近いセルが有れば最短セルを更新 if (distance < nearestDistance) { //移動先に敵がいるか判定 if (isDestinationEnemyExist(moveAbleInfo)) { continue; } nearestInfo = moveAbleInfo; Debug.Log($"最もプレイヤーに近いセル更新 :X={nearestInfo.coordinate.x}, Y={nearestInfo.coordinate.y}, 距離={nearestDistance}"); nearestDistance = distance; } } } } Main_Cell nearestCell = cells.First(c => c.X == nearestInfo.coordinate.x && c.Y == nearestInfo.coordinate.y); Debug.Log($"最もプレイヤーに近いセル :X={nearestInfo.coordinate.x}, Y={nearestInfo.coordinate.y}, 距離={nearestDistance}"); return(nearestCell); }
//敵ユニットの移動 private void MoveTo() { //既に移動中の場合は処理が入らない if (isEnemyMove) { return; } isEnemyMove = true; Main_Cell destCell; //攻撃可能か否かで行き先セルを分岐 if (isAttackable) { //行き先の座標をセルに変換 destCell = mainMap.Cells.FirstOrDefault(c => c.X == DestinationAndAttackDto.x && c.Y == DestinationAndAttackDto.y); //事前に確認しているので通常セルが無い事は有り得ない if (destCell == null) { Debug.Log($"Error : 移動可能なセルが有りません : X = {DestinationAndAttackDto.x} , Y = {DestinationAndAttackDto.y}"); } //移動先 = 現在いる場所で攻撃する場合は移動を行わないで攻撃へ if (destCell.X == mainMap.ActiveEnemy.x && destCell.Y == mainMap.ActiveEnemy.y) { isEnemyMove = false; //移動範囲、攻撃範囲消す mainMap.ResetMovableCells(); mainMap.ResetAttackableCells(); enemyAIPhase = EnemyAIPhase.ATTACK; Debug.Log("enemyAIPhase : " + enemyAIPhase); return; } //カーソル移動を実行してからユニット移動 battleMapCursor.transform.DOMove(new Vector3( destCell.transform.position.x, destCell.transform.position.y + 0.5f, destCell.transform.position.z), 0.3f).SetEase(Ease.OutSine).OnComplete(() => { deltaTime = 0; //敵ユニットの移動実行 第二引数は攻撃か否か mainMap.ActiveEnemy.MoveTo(destCell, true); }); } else { //プレイヤーに攻撃出来ない場合 //反撃タイプ、ボスの敵は攻撃範囲にプレイヤーが居なければ待機する if (mainMap.ActiveEnemy.enemyAIPattern != EnemyAIPattern.SEARCH) { //移動可能セルを消す mainMap.ResetMovableCells(); mainMap.ResetAttackableCells(); //行動済みにして次の敵を取得 mainMap.ActiveEnemy.isMoved = true; mainMap.ActiveEnemy.SetBeforeAction(false); enemyAIPhase = EnemyAIPhase.GET_ENEMY; Debug.Log("enemyAIPhase : " + enemyAIPhase); Debug.Log($"敵のAIパターン : {mainMap.ActiveEnemy.enemyAIPattern}"); isEnemyMove = false; } else { //プレイヤーを探索するAIの場合 //プレイヤーのユニットを検索して取得してセルに変換 PlayerModel[] players = unitContainer.GetComponentsInChildren <PlayerModel>(); if (players == null) { Debug.Log("Error : プレイヤーが存在しません"); } //プレイヤーの座標までの最短経路を取得 Main_Cell playerNearestCell = mainMap.ActiveEnemy.Search(players); //カーソル移動を実行してからユニット移動 battleMapCursor.transform.DOMove(new Vector3( playerNearestCell.transform.position.x, playerNearestCell.transform.position.y + 0.5f, playerNearestCell.transform.position.z), 0.3f).SetEase(Ease.OutSine).OnComplete(() => { deltaTime = 0; //敵ユニットの移動実行 第二引数は攻撃か否か mainMap.ActiveEnemy.MoveTo(playerNearestCell, false); }); } } }
//210214 移動距離計算と攻撃範囲計算を分割 //攻撃可能な座標を返す MoveAmountInfo[] GetAttackRenge(Main_Cell startCell, int attackRange, bool isCloseAttack) { //Debug.Log($"射程距離:{attackRange}, 近距離攻撃:{isCloseAttack}"); //座標と移動可能距離を持つリストを用意する まずは現在位置で最大の移動可能距離 var infos = new List <MoveAmountInfo>(); infos.Add(new MoveAmountInfo(startCell.X, startCell.Y, attackRange)); //残り移動可能距離を1ずつ減らしていって、0になるまでループまわす for (var i = attackRange; i >= 0; i--) { //追加していく移動可能距離リスト作成 List <MoveAmountInfo> appendInfos = new List <MoveAmountInfo>(); //amountはそのセルでの残り移動可能距離 まずは現在位置からスタート foreach (var calcTargetInfo in infos.Where(info => info.amount == i)) { // 四方のマスの座標配列を作成 var calcTargetCoordinate = calcTargetInfo.coordinate; //TODO ??? var aroundCellCoordinates = new Coordinate[] //上下左右の座標配列 { new Coordinate(calcTargetCoordinate.x - 1, calcTargetCoordinate.y), //左 new Coordinate(calcTargetCoordinate.x + 1, calcTargetCoordinate.y), //右 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y - 1), //上 new Coordinate(calcTargetCoordinate.x, calcTargetCoordinate.y + 1), //下 }; // 四方のマスの残移動力を計算 上下左右マスの座標を順に取り出し foreach (var aroundCellCoordinate in aroundCellCoordinates) { //FirstOrDefaultとは? 現在地から上下左右の座標を元にマップの座標を取得 var targetCell = cells.FirstOrDefault(c => c.X == aroundCellCoordinate.x && c.Y == aroundCellCoordinate.y); //nullはマイナス座標などでマップの範囲外とか infoとappendInfosにもう同じ座標のセルが有ればスルー //もうそのセルは移動可能にならないし if (null == targetCell || infos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y) || appendInfos.Any(info => info.coordinate.x == aroundCellCoordinate.x && info.coordinate.y == aroundCellCoordinate.y)) { // マップに存在しない、または既に計算済みの座標はスルー continue; } //残りの攻撃可能範囲を取得 int remainingRangeAmount; //プレイヤー、敵の攻撃範囲表示モードでは、移動コストは存在しないので常に1減る remainingRangeAmount = i - 1; appendInfos.Add(new MoveAmountInfo(aroundCellCoordinate.x, aroundCellCoordinate.y, remainingRangeAmount)); }//四方のマス取得ループ } //上下左右の座標リストを取得し終わったらinfosにぶちこむ infos.AddRange(appendInfos); } if (!isCloseAttack) { //210211 攻撃範囲表示の時、近接武器でなければ自分のすぐ隣のセルは返さない //moveAmount-1は自分の一番近いセルを除くということ return(infos.Where(x => x.amount >= 0 && x.amount < attackRange - 1).ToArray()); } else { //近接攻撃可能 残射程が0以上(攻撃可能)なマスの情報だけを返す かつ自分自身は含まない return(infos.Where(x => x.amount >= 0 && x.amount < attackRange).ToArray()); } }
public CellData(Main_Cell mainCell) { this.x = mainCell.X; this.y = mainCell.Y; this.type = mainCell.Type; }
/// <summary> /// ユニット、敵、武器を受け取るとダメージ、命中率などを計算してDTOに詰めて返してくれる /// </summary> /// <param name="unit">ユニット名</param> /// <param name="unitWeapon">ユニットの武器</param> /// <param name="unitCell">ユニットの地形</param> /// <param name="enemy">敵</param> /// <param name="enemyCell">敵の地形</param> /// <param name="isPlayerAttack">プレイヤーからの攻撃か</param> /// <returns></returns> public BattleParameterDTO CalculateBattleParameter(Unit unit, Weapon unitWeapon, Main_Cell unitCell, Enemy enemy, Main_Cell enemyCell, bool isPlayerAttack) { //戻り値を詰めるクラス BattleParameterDTO battleParameterDTO = new BattleParameterDTO(); //職業ステータス補正が入ったクラス JobStatusDto jobstatusDto = unit.job.statusDto; //スキル「狂化」判定 ステータスに反映するので最初に判定 bool isUnitBerserk = false; bool isEnemyBerserk = false; bool isCloseAttack; //近接攻撃フラグ ///ユニットの計算 //210218 ステータスと職業による基礎値の計算 他に装備品補正、バフ、デバフ、スキル判定が必要 int unitMaxHp = unit.maxhp + jobstatusDto.jobHp; int unitLatk = unit.latk + jobstatusDto.jobLatk; int unitCatk = unit.catk + jobstatusDto.jobCatk; int unitDex = unit.dex + jobstatusDto.jobDex; int unitAgi = unit.agi + jobstatusDto.jobAgi; int unitLuk = unit.luk + jobstatusDto.jobLuk; int unitLdef = unit.ldef + jobstatusDto.jobLdef; int unitCdef = unit.cdef + jobstatusDto.jobCdef; //210218 バフを反映する処理を追加 StatusDto buffedStatus = new StatusDto(unitMaxHp, unitLatk, unitCatk, unitAgi, unitDex, unitLdef, unitCdef, unitLuk); StatusCalculator statusCalculator = new StatusCalculator(); //スキル「狂化」判定 if (unit.hp != statusCalculator.CalcHpBuff(unitMaxHp, unit.job.skills) && unit.job.skills.Contains(Skill.狂化)) { isUnitBerserk = true; } //武器、装飾品、スキルのバフ反映 最大値を上回らないようにして返される buffedStatus = statusCalculator.GetBuffedStatus(buffedStatus, unit.name, unit.equipWeapon, unit.equipAccessory, unit.job.skills, isUnitBerserk); //値を更新 unitMaxHp = buffedStatus.hp; unitLatk = buffedStatus.latk; unitCatk = buffedStatus.catk; unitDex = buffedStatus.dex; this.unitDex = buffedStatus.dex; unitAgi = buffedStatus.agi; //スキル発動計算の為DTOにもセット unitLuk = buffedStatus.luk; this.unitLuk = buffedStatus.luk; unitLdef = buffedStatus.ldef; unitCdef = buffedStatus.cdef; ///敵の計算 //210227 敵のステータスも上級職補正等は職業から取得する用に変更 JobStatusDto enemyJobStatusDto = enemy.job.statusDto; //210218 ステータスと職業による基礎値の計算 他に装備品補正、バフ、デバフ、スキル判定が必要 int enemyMaxHp = enemy.maxhp + enemyJobStatusDto.jobHp; int enemyLatk = enemy.latk + enemyJobStatusDto.jobLatk; int enemyCatk = enemy.catk + enemyJobStatusDto.jobCatk; int enemyDex = enemy.dex + enemyJobStatusDto.jobDex; int enemyAgi = enemy.agi + enemyJobStatusDto.jobAgi; int enemyLuk = enemy.luk + enemyJobStatusDto.jobLuk; int enemyLdef = enemy.ldef + enemyJobStatusDto.jobLdef; int enemyCdef = enemy.cdef + enemyJobStatusDto.jobCdef; //210218 敵のバフを反映する処理 StatusDto enemyBuffedStatus = new StatusDto(enemyMaxHp, enemyLatk, enemyCatk, enemyAgi, enemyDex, enemyLdef, enemyCdef, enemyLuk); //スキル「狂化」判定 if (enemy.hp != statusCalculator.CalcHpBuff(enemyMaxHp, enemy.job.skills) && enemy.job.skills.Contains(Skill.狂化)) { isEnemyBerserk = true; } //武器、装飾品、スキルのバフ反映 最大値を上回らないようにして返される enemyBuffedStatus = statusCalculator.GetBuffedStatus(enemyBuffedStatus, enemy.name, enemy.equipWeapon, enemy.equipAccessory, enemy.job.skills, isEnemyBerserk); //値を更新 enemyMaxHp = enemyBuffedStatus.hp; enemyLatk = enemyBuffedStatus.latk; enemyCatk = enemyBuffedStatus.catk; enemyDex = enemyBuffedStatus.dex; this.enemyDex = enemyBuffedStatus.dex; enemyAgi = enemyBuffedStatus.agi; //スキル発動計算の為DTOにもセット enemyLuk = enemyBuffedStatus.luk; this.enemyLuk = enemyBuffedStatus.luk; enemyLdef = enemyBuffedStatus.ldef; enemyCdef = enemyBuffedStatus.cdef; //210226 自分から攻撃した場合、ダメージが上がるスキル //これはステータス画面には表示しないのでstatusCalculator不使用 //プレイヤーから攻撃した時 if (isPlayerAttack) { if (unit.job.skills.Contains(Skill.飛燕の一撃)) { unitAgi += 5; Debug.Log($"{unit.name}スキル{Skill.飛燕の一撃} 速さ+5"); } if (unit.job.skills.Contains(Skill.金剛の一撃)) { unitCdef += 10; Debug.Log($"{unit.name}スキル{Skill.金剛の一撃} 近防-10"); } if (unit.job.skills.Contains(Skill.鬼神の一撃)) { unitCatk += 6; Debug.Log($"{unit.name}スキル{Skill.鬼神の一撃} 近攻+6"); } if (unit.job.skills.Contains(Skill.魔女の一撃)) { unitLatk += 6; Debug.Log($"{unit.name}スキル{Skill.魔女の一撃} 遠攻+6"); } } else { //敵の〇〇の一撃判定 if (enemy.job.skills.Contains(Skill.飛燕の一撃)) { enemyAgi += 5; Debug.Log($"{unit.name}スキル{Skill.飛燕の一撃} 速さ+5"); } if (enemy.job.skills.Contains(Skill.金剛の一撃)) { enemyCdef += 10; Debug.Log($"{unit.name}スキル{Skill.金剛の一撃} 近防-10"); } if (enemy.job.skills.Contains(Skill.鬼神の一撃)) { enemyCatk += 6; Debug.Log($"{unit.name}スキル{Skill.鬼神の一撃} 近攻+6"); } if (enemy.job.skills.Contains(Skill.魔女の一撃)) { enemyLatk += 6; Debug.Log($"{unit.name}スキル{Skill.魔女の一撃} 遠攻+6"); } } //慧眼の一撃(命中率上がるやつ)はまた別の場所で実装 //210219 地形効果を味方、敵のセルから取得 int unitTerrainHit; int unitTerrainDef; //通常は有り得ないが、セルが無ければ0 if (unitCell == null) { Debug.Log("ERROR:ユニットの居るセルの情報が有りません"); unitTerrainHit = 0; unitTerrainDef = 0; } else { unitTerrainHit = unitCell.AvoidRate; unitTerrainDef = unitCell.Defence; } int enemyTerrainHit; int enemyTerrainDef; if (enemyCell == null) { Debug.Log("ERROR:敵の居るセル情報が有りません"); enemyTerrainHit = 0; enemyTerrainDef = 0; } else { enemyTerrainHit = enemyCell.AvoidRate; enemyTerrainDef = enemyCell.Defence; } //スキル判定 目隠し 攻撃を受けた時、回避+20%、守備+2 bool isUnitBlind = false; bool isEnemyBlind = false; if (!isPlayerAttack && unit.job.skills.Contains(Skill.目隠し)) { Debug.Log($"{unit.name} スキル :{Skill.目隠し.ToString()}, {Skill.目隠し.GetStringValue()}"); isEnemyBlind = true; } if (isPlayerAttack && enemy.job.skills.Contains(Skill.目隠し)) { Debug.Log($"{enemy.name} スキル :{Skill.目隠し.ToString()}, {Skill.目隠し.GetStringValue()}"); isUnitBlind = true; } //210220 攻撃可否を確認 //座標の絶対値の合計が距離で、その値が武器の射程距離以下なら反撃可能となる //射程距離が1の場合、武器が近距離攻撃可能の場合は反撃可能となる //距離を取得 int distance = Mathf.Abs(unitCell.X - enemyCell.X) + Mathf.Abs(unitCell.Y - enemyCell.Y); Debug.Log($"ユニットと敵との距離:{distance}"); Debug.Log($"ユニット:X={unitCell.X},Y={unitCell.Y} 敵:X={enemyCell.X},Y={enemyCell.Y}"); //近距離攻撃か遠距離か判定 isCloseAttack = (distance == 1) ? true : false; Debug.Log($"近距離攻撃か:{isCloseAttack}"); //武器の射程距離が距離よりも少ない場合は攻撃不可 if (unitWeapon.range < distance) { isUnitAttackable = false; } else if (distance == 1 && !unitWeapon.isCloseAttack) { //武器の射程距離内だが、射程が1で近距離攻撃可能ではない場合 //(射程2以上の武器は、近距離攻撃フラグが無い場合は近距離攻撃出来ない) isUnitAttackable = false; } else { isUnitAttackable = true; } //天狗のカメラは反撃不可 if (enemy.equipWeapon.name == "天狗のカメラ" && !isPlayerAttack) { isUnitAttackable = false; } battleParameterDTO.isUnitAttackable = isUnitAttackable; Debug.Log($"ユニットの攻撃可否:{battleParameterDTO.isUnitAttackable}"); //敵の攻撃可否判定 プレイヤーと同じ if (enemy.equipWeapon.range < distance) { isEnemyAttackable = false; } else if (distance == 1 && !enemy.equipWeapon.isCloseAttack) { isEnemyAttackable = false; } else { isEnemyAttackable = true; } if (unit.equipWeapon.name == "天狗のカメラ" && isPlayerAttack) { //ユニットの武器が天狗のカメラ、かつ文ちゃんから攻撃した場合 isEnemyAttackable = false; } battleParameterDTO.isEnemyAttackable = isEnemyAttackable; Debug.Log($"敵の攻撃可否:{battleParameterDTO.isEnemyAttackable}"); ///攻撃力計算 //TODO 武器を持っていない状態で攻撃される場合を考慮する int unitWeaponAttack = unitWeapon.attack; //NONE以外の場合は何かしら特効対象が有るということ //かつ武器の特効対象と敵の種族が一致した場合は特効 if (unitWeapon.slayer != RaceType.NONE && unitWeapon.slayer == enemy.race) { //武器威力を3倍にして特効フラグを立てる 特効時は文字の色が変わる unitWeaponAttack *= 3; battleParameterDTO.isUnitAttackSlayer = true; } //210301 攻撃力計算 遠距離、近距離の概念を反映 int unitRefAttack = isCloseAttack ? unitCatk : unitLatk; int enemydef = isCloseAttack ? enemyCdef : enemyLdef; //力+武器威力*特効なら3倍 - 敵防御 - 地形効果 unitAttack = (unitRefAttack + unitWeaponAttack) - enemydef - enemyTerrainDef; //スキル目隠し 敵の回避+20、守備+2 if (isUnitBlind) { unitAttack -= 2; } //スキル「密室の守り」自分が地形効果0以外の場所で戦闘時、受けるダメージ-3 if ((enemyTerrainHit != 0 || enemyTerrainDef != 0) && enemy.job.skills.Contains(Skill.密室の守り)) { unitAttack -= 3; Debug.Log($"{enemy.name}: スキル{Skill.密室の守り} 効果{Skill.密室の守り.GetStringValue()}"); } //スキル「死線」 敵に与えるダメージ+10、敵から受けるダメージ+10 if (unit.job.skills.Contains(Skill.死線)) { //ユニットが持っていれば攻撃力アップ unitAttack += 10; Debug.Log($"{unit.name}: スキル{Skill.死線} 効果{Skill.死線.GetStringValue()}"); } if (enemy.job.skills.Contains(Skill.死線)) { //敵が持っていれば受けるダメージアップ unitAttack += 10; Debug.Log($"{enemy.name}: スキル{Skill.死線} 効果{Skill.死線.GetStringValue()}"); } //0~100の間に訂正 unitAttack = CorrectParameter(unitAttack); battleParameterDTO.unitAttack = unitAttack; //敵の攻撃力計算 プレイヤーと同じ //TODO 敵は武器を持っていない可能性が有るけどまだ考慮してない if (enemy.equipWeapon != null) { //敵も特効の計算 int enemyWeaponAttack = enemy.equipWeapon.attack; if (enemy.equipWeapon.slayer != RaceType.NONE && enemy.equipWeapon.slayer == unit.race) { enemyWeaponAttack *= 3; battleParameterDTO.isEnemyAttackSlayer = true; } int enemyRefAttack = isCloseAttack ? enemyCatk : enemyLatk; int unitdef = isCloseAttack ? unitCdef : unitLdef; enemyAttack = (enemyRefAttack + enemyWeaponAttack) - unitdef - unitTerrainDef; //スキル目隠し 敵の回避+20、守備+2 if (isEnemyBlind) { enemyAttack -= 2; } //スキル「密室の守り」自分が地形効果0以外の場所で戦闘時、受けるダメージ-3 if ((unitTerrainHit != 0 || unitTerrainDef != 0) && unit.job.skills.Contains(Skill.密室の守り)) { enemyAttack -= 3; Debug.Log($"{unit.name}: スキル{Skill.密室の守り} 効果{Skill.密室の守り.GetStringValue()}"); } //スキル「死線」 if (unit.job.skills.Contains(Skill.死線)) { enemyAttack += 10; Debug.Log($"{unit.name}: スキル{Skill.死線} 効果{Skill.死線.GetStringValue()}"); } if (enemy.job.skills.Contains(Skill.死線)) { enemyAttack += 10; Debug.Log($"{enemy.name}: スキル{Skill.死線} 効果{Skill.死線.GetStringValue()}"); } battleParameterDTO.enemyAttack = CorrectParameter(enemyAttack); } ///命中率計算 //物理命中率=物理命中-敵物理回避-地形効果 //3すくみの乗っていない命中率計算 まだマイナス、100%以上になってもよい unitHitRate = (unitDex + unitWeapon.hitRate) - enemyAgi - enemyTerrainHit; //敵が武器を持っていれば、敵の命中率計算 if (enemy.equipWeapon != null) { enemyHitRate = (enemyDex + enemy.equipWeapon.hitRate) - unitAgi - unitTerrainHit; } else { //無ければUI表示は「-」となり参照されることは無いが一旦0 enemyHitRate = 0; } //210226 スキル「一撃離脱反映」自分から攻撃時、回避+30 //プレイヤーの一撃離脱反映 if (isPlayerAttack && unit.job.skills.Contains(Skill.一撃離脱)) { enemyHitRate -= 30; Debug.Log($"{unit.name}: スキル{Skill.一撃離脱} 回避+30"); } else if (!isPlayerAttack && enemy.job.skills.Contains(Skill.一撃離脱)) { //敵の一撃離脱反映 unitHitRate -= 30; Debug.Log($"{enemy.name}: スキル{Skill.一撃離脱} 回避+30"); } //スキル「慧眼の一撃」判定 自分から攻撃時命中+40% if (isPlayerAttack && unit.job.skills.Contains(Skill.慧眼の一撃)) { unitHitRate += 40; Debug.Log($"{unit.name}: スキル{Skill.慧眼の一撃} 命中+40"); } else if (!isPlayerAttack && enemy.job.skills.Contains(Skill.慧眼の一撃)) { enemyHitRate += 40; Debug.Log($"{enemy.name}: スキル{Skill.慧眼の一撃} 命中+40"); } //スキル「自信満々」HPが100%の時命中・回避+15 if (unitMaxHp == unit.hp) { if (unit.job.skills.Contains(Skill.自信満々)) { enemyHitRate -= 15; unitHitRate += 15; Debug.Log($"{unit.name}: スキル{Skill.自信満々} 命中・回避+15"); } if (unit.job.skills.Contains(Skill.完璧主義)) { enemyHitRate -= 15; unitHitRate += 15; Debug.Log($"{unit.name}: スキル{Skill.完璧主義} 命中・回避+15"); } } if (enemyMaxHp == enemy.hp) { if (enemy.job.skills.Contains(Skill.自信満々)) { enemyHitRate += 15; unitHitRate -= 15; Debug.Log($"{unit.name}: スキル{Skill.自信満々} 命中・回避+15"); } if (unit.job.skills.Contains(Skill.完璧主義)) { enemyHitRate += 15; unitHitRate -= 15; Debug.Log($"{enemy.name}: スキル{Skill.完璧主義} 命中・回避+15"); } } //スキル「狂気の瞳」 無条件で回避+20 if (unit.job.skills.Contains(Skill.狂気の瞳)) { enemyHitRate -= 20; Debug.Log($"{unit.name}: スキル{Skill.狂気の瞳} 効果{Skill.狂気の瞳.GetStringValue()}"); } if (enemy.job.skills.Contains(Skill.狂気の瞳)) { unitHitRate -= 20; Debug.Log($"{enemy.name}: スキル{Skill.狂気の瞳} 効果{Skill.狂気の瞳.GetStringValue()}"); } //スキル「狂化」 命中率 - 10% if (isUnitBerserk) { unitHitRate -= 10; Debug.Log($"{unit.name}: スキル{Skill.狂化} 効果{Skill.狂化.GetStringValue()}"); } if (isEnemyBerserk) { enemyHitRate -= 10; Debug.Log($"{enemy.name}: スキル{Skill.狂化} 効果{Skill.狂化.GetStringValue()}"); } //スキル「目隠し」判定 敵の回避+20、守備+2 if (isUnitBlind) { unitHitRate -= 20; } else if (isEnemyBlind) { enemyHitRate -= 20; } //戻り値に計算した値を設定 battleParameterDTO.unitHitRate = unitHitRate; battleParameterDTO.enemyHitRate = enemyHitRate; battleParameterDTO.unitWeapon = unitWeapon; battleParameterDTO.enemyWeapon = enemy.equipWeapon; //3すくみ補正を含めた攻撃力、命中率の補正 battleParameterDTO.affinity = BattleWeaponAffinity.EQUAL; if (enemy.equipWeapon != null) { battleParameterDTO = getWeaponAffinity(battleParameterDTO); } ///必殺率計算 ///必殺率=(技+幸運)/2+武器の必殺 - 敵の幸運 unitCriticalRate = (unitDex + unitLuk) / 2 + unitWeapon.criticalRate - enemyLuk; //210226 スキル「蒐集家」反映 if (SkillUtil.isCollector(unit)) { unitCriticalRate += 10; } //210227 スキル「必殺」 if (unit.job.skills.Contains(Skill.必殺)) { unitCriticalRate += 15; Debug.Log($"{unit.name}: スキル{Skill.必殺} 効果{Skill.必殺.GetStringValue()}"); } //スキル「狂化」 if (isUnitBerserk) { unitCriticalRate += 20; Debug.Log($"{unit.name}: スキル{Skill.狂化} 効果{Skill.狂化.GetStringValue()}"); } unitCriticalRate = CorrectParameter(unitCriticalRate); battleParameterDTO.unitCriticalRate = unitCriticalRate; if (enemy.equipWeapon != null) { enemyCriticalRate = (enemyDex + enemyLuk) / 2 + enemy.equipWeapon.criticalRate - unitLuk; //敵の「蒐集家」判定 if (SkillUtil.isCollector(enemy)) { enemyCriticalRate += 10; } //210227 スキル「必殺」 if (enemy.job.skills.Contains(Skill.必殺)) { enemyCriticalRate += 15; Debug.Log($"{enemy.name}: スキル{Skill.必殺} 効果{Skill.必殺.GetStringValue()}"); } if (isEnemyBerserk) { enemyCriticalRate += 20; Debug.Log($"{enemy.name}: スキル{Skill.狂化} 効果{Skill.狂化.GetStringValue()}"); } enemyCriticalRate = CorrectParameter(enemyCriticalRate); battleParameterDTO.enemyCiritcalRate = enemyCriticalRate; } ///追撃の計算 追撃は攻速差4以上 攻速 = 速さ-{武器の重さ-(力/5)} ///勇者武器は今はガン無視で int unitAttackSpeed = unitAgi - unitWeapon.delay; int enemyAttackSpeed = enemyAgi; if (enemy.equipWeapon != null) { enemyAttackSpeed -= enemy.equipWeapon.delay; } unitChaseFlag = false; enemyChaseFlag = false; //ユニットも敵も追撃可能という状況は有り得ないのでelse //210220 追撃不可武器を作成 if ((unitAttackSpeed - enemyAttackSpeed) >= 4 && unitWeapon.isChaseInvalid == false) { unitChaseFlag = true; } else if ((enemyAttackSpeed - unitAttackSpeed) >= 4 && enemy.equipWeapon.isChaseInvalid == false) { enemyChaseFlag = true; } //自分か敵が宵闇の守りを持っていれば互いに追撃不可 if (unit.job.skills.Contains(Skill.宵闇の守り) || enemy.job.skills.Contains(Skill.宵闇の守り)) { Debug.Log($"スキル{Skill.宵闇の守り}発動 : 互いに追撃不可"); unitChaseFlag = false; enemyChaseFlag = false; } //スキル「切り返し」実装 一旦、宵闇の守りより優先度高い if (!isPlayerAttack && unit.job.skills.Contains(Skill.切り返し)) { //敵から攻撃された時 unitChaseFlag = true; Debug.Log($"{unit.name}: スキル{Skill.切り返し} 効果{Skill.切り返し.GetStringValue()}"); } else if (isPlayerAttack && enemy.job.skills.Contains(Skill.切り返し)) { //敵を攻撃した時 enemyChaseFlag = true; Debug.Log($"{enemy.name}: スキル{Skill.切り返し} 効果{Skill.切り返し.GetStringValue()}"); } battleParameterDTO.unitName = unit.name; battleParameterDTO.enemyName = enemy.name; battleParameterDTO.unitWeaponName = unitWeapon.name; //210220 雑に勇者フラグをDTOに入れる //自分から攻撃した場合のみ2回攻撃なので、そうでなければフラグを倒してしまう if (unitWeapon.isYuusha && isPlayerAttack) { battleParameterDTO.isUnitYuusha = true; } else { battleParameterDTO.isUnitYuusha = false; } if (enemy.equipWeapon != null) { battleParameterDTO.enemyWeaponName = enemy.equipWeapon.name; if (enemy.equipWeapon.isYuusha && !isPlayerAttack) { battleParameterDTO.isEnemyYuusha = true; } else { battleParameterDTO.isEnemyYuusha = false; } } battleParameterDTO.unitHp = unit.hp; battleParameterDTO.unitMaxHp = unitMaxHp; battleParameterDTO.enemyHp = enemy.hp; battleParameterDTO.enemyMaxHp = enemyMaxHp; battleParameterDTO.unitChaseFlag = unitChaseFlag; battleParameterDTO.enemyChaseFlag = enemyChaseFlag; Debug.Log("unitAttackSpeed" + unitAttackSpeed.ToString()); Debug.Log("enemyAttackSpeed" + enemyAttackSpeed.ToString()); return(battleParameterDTO); }