Я решил добавить систему помощи при прицеливании, которая проверяет, есть ли цели достаточно близко к куда игрок целится. Если это так, прицел корректируется соответствующим образом, чтобы гарантировать попадание в цель.
Чтобы добиться этого, я решил выстрелить тремя лучами. Один находится в том же направлении, в котором стреляют, а два других образуют конус. Затем функция корректирует направление стрельбы в соответствии с кратчайшим лучом, обнаружившим вражескую цель. Эта проверка очень полезна, поскольку после изменения цели текущая цель не изменится, поскольку «прямой» луч всегда будет более коротким (если что-то еще не пройдет перед текущей целью).
Меня это уже немного расстраивает. Я знаю, что рейкасты не очень хороши с точки зрения производительности, и я постоянно снимаю три из них одновременно. Возможно, есть лучший способ добиться того, что мне нужно... но это еще не конец! Видите ли, в игре также используются надувные стены. Они отражают снаряды, и мне нужно принять это во внимание и для помощи прицеливанию.
В этом случае мне нужно, чтобы помощь прицеливания регулировала угол так, чтобы стрельба отскакивала и попадала во врага. В этом случае, когда луч ударяется о упругую стену, он «отражается» и продолжает двигаться, пока не найдет врага или не найдет ничего. Конечно, «отражение» осуществляется путем съемки еще одного рейкаста, а это означает, что количество увеличивается еще больше! Если я создам уровень с множеством упругих стен, игра гарантированно будет иметь большие скачки задержки, а то и вовсе вылетит. Простое решение — просто остановить «отражение», если оно происходит X раз в одном и том же исходном raycast, что я и реализовал до сих пор, но меня это решение не устраивает.
В этом сценарии я по-прежнему снимаю сумасшедшее количество лучей в каждом кадре, и выбор количества отражений «слишком много» может быть утомительным. Может быть, лучи можно было бы «отражать» без необходимости снимать новый для каждого отражения? Может быть, я смогу реализовать эту систему вообще без использования raycasts? Я не уверен. В любом случае, вот код:
Код: Выделить всё
private float AngledRaycastDistance(float angle, Vector2 origin, bool testRaycast, float currentDistance = 0)
{
float totalDistance = currentDistance;
Vector2 dir = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle));
Vector2 currentPoint = origin;
RaycastHit2D hit;
Collider2D lastHit = null;
// This is just for debugging and avoiding infinite loop to gather data
int numberOfReflections = 0;
while (totalDistance < Mathf.Infinity) // && numberOfReflections < 7)
{
numberOfReflections++;
hit = Physics2D.Raycast(currentPoint, dir.normalized, Mathf.Infinity, mask);
if (hit.collider != null)
{
if (hit.collider.tag == "Enemy")
{
totalDistance += hit.distance;
break;
}
else if (hit.collider.tag == "BouncyWall")
{
// For debugging
if(testRaycast)
{
Debug.DrawRay(currentPoint, dir, Color.green);
// EditorApplication.isPaused = true;
}
// This part is necessary otherwise the reflected raycast will keep hitting the
// surface it was just reflected on, instead of keep traveling.
// The first time it gets to this check, it will always go on the "else"
if (lastHit != null && hit.collider == lastHit)
{
currentPoint += dir;
} else
{
currentPoint = hit.point;
Vector2 reflectionDir = Vector2.Reflect(dir, hit.normal);
dir = reflectionDir.normalized;
totalDistance += hit.distance;
lastHit = hit.collider;
Debug.Log("Surface Normal: " + hit.normal);
Debug.Log("Incident Direction: " + dir);
Debug.Log("Reflected Direction: " + reflectionDir);
}
}
else
{
Debug.LogError("It should never come here");
return Mathf.Infinity;
}
}
else
{
return Mathf.Infinity;
}
}
return totalDistance;
}
public void SetStickInput(InputAction.CallbackContext context)
{
// I'm taking the shoot direction directly from the new input system
shootDirection = context.ReadValue();
float angle = Mathf.Atan2(shootDirection.y, shootDirection.x);
// Ignore this
if (_isUsingSnap && shootDirection != Vector2.zero)
{
float snapAngle = 4f * Mathf.Deg2Rad;
float snappedAngle = Mathf.Round(angle / snapAngle) * snapAngle;
Vector2 snappedDirection = new Vector2(Mathf.Cos(snappedAngle), Mathf.Sin(snappedAngle));
shootDirection = snappedDirection;
angle = Mathf.Atan2(shootDirection.y, shootDirection.x);
}
else if(_isUsingLockOn && shootDirection != Vector2.zero)
{
float coneBroadness = 3f;
// Shoot three raycasts. Each bounces from bouncyWalls and stops if it meets an enemy. Returns the total distance traveled
float straightDistance = AngledRaycastDistance(angle, transform.position, true);
float angledUpDistance = AngledRaycastDistance(angle + (coneBroadness * Mathf.Deg2Rad), transform.position, false);
float angledDownDistance = AngledRaycastDistance(angle - (coneBroadness * Mathf.Deg2Rad), transform.position, false);
float smallestValue = Mathf.Min(straightDistance, angledUpDistance, angledDownDistance);
if (Mathf.Abs(angledUpDistance - smallestValue) < 0.0001f)
{
Vector2 lockedDirection = new Vector2(Mathf.Cos(angle + (coneBroadness * Mathf.Deg2Rad)), Mathf.Sin(angle + (coneBroadness * Mathf.Deg2Rad)));
shootDirection = lockedDirection;
}
else if (Mathf.Abs(angledDownDistance - smallestValue) < 0.0001f)
{
Vector2 lockedDirection = new Vector2(Mathf.Cos(angle - (coneBroadness * Mathf.Deg2Rad)), Mathf.Sin(angle - (coneBroadness * Mathf.Deg2Rad)));
shootDirection = lockedDirection;
}
// For debugging
//Debug.DrawRay(transform.position, new Vector2(Mathf.Cos(angle + (coneBroadness * Mathf.Deg2Rad)), Mathf.Sin(angle + (coneBroadness * Mathf.Deg2Rad))) * 10f, Color.yellow, 0.1f);
//Debug.DrawRay(transform.position, new Vector2(Mathf.Cos(angle - (coneBroadness * Mathf.Deg2Rad)), Mathf.Sin(angle - (coneBroadness * Mathf.Deg2Rad))) * 10f, Color.yellow, 0.1f);
//Debug.DrawRay(transform.position, new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * 10f, Color.red, 0.1f);
}
}
Я знаю, что нужно многое решить, но буду признателен за любое предложение!
Заранее всем спасибо за уделенное время!
Подробнее здесь: https://stackoverflow.com/questions/781 ... in-my-game
Мобильная версия