Files
ID/Assets/Code/Lab5/BetterProjectileLauncher.cs
2026-03-01 00:29:25 +03:00

253 lines
7.9 KiB
C#

using Code.Base;
using Code.Lab3;
using UnityEngine;
namespace Code.Lab5
{
public class BetterProjectileLauncher : MonoBehaviour
{
[Header("Projectile Settings")]
public GameObject projectilePrefab;
public float launchSpeed = 20f;
public bool useHighTrajectory;
public float fireRate = 1f;
[Header("Target")]
public Wander wanderingTarget;
public float predictionTime = 2f;
public bool useDynamicPrediction = true;
[Header("Visualization")]
public GameObject targetCirclePrefab;
public bool showPredictedPosition = true;
public float circleLifetime = 1f;
[Header("Accuracy")]
[Range(0f, 1f)]
public float accuracy = 0.9f;
public float inaccuracyRadius = 2f;
private float _nextFireTime;
private Transform _predictedTarget;
private GameObject _predictedTargetObject;
private void Start()
{
if (wanderingTarget == null)
{
Debug.LogError("Wandering target not assigned!");
return;
}
if (showPredictedPosition && targetCirclePrefab != null)
{
_predictedTargetObject = new GameObject("PredictedTarget");
_predictedTarget = _predictedTargetObject.transform;
}
}
private void Update()
{
if (wanderingTarget == null) return;
if (Time.time >= _nextFireTime)
{
TryFireAtTarget();
_nextFireTime = Time.time + 1f / fireRate;
}
if (showPredictedPosition && _predictedTarget != null)
{
UpdatePredictedTargetVisual();
}
}
private void TryFireAtTarget()
{
if (wanderingTarget == null || projectilePrefab == null) return;
var targetPos = wanderingTarget.transform.position;
var targetVelocity = GetTargetVelocity();
var predictedPos = PredictTargetPosition(targetPos, targetVelocity);
var aimPosition = ApplyInaccuracy(predictedPos);
if (!Projectile.IsTargetReachable(transform.position, aimPosition, launchSpeed))
{
useHighTrajectory = !useHighTrajectory;
if (!Projectile.IsTargetReachable(transform.position, aimPosition, launchSpeed))
{
return;
}
}
var direction = Projectile.GetFireDirection(
transform.position,
aimPosition,
launchSpeed,
useHighTrajectory
);
if (direction == Vector3.zero) return;
FireProjectile(direction, aimPosition);
}
private Vector3 GetTargetVelocity()
{
if (wanderingTarget == null) return Vector3.zero;
var targetAgent = wanderingTarget.GetComponent<Agent>();
if (targetAgent != null)
{
return targetAgent.velocity;
}
return Vector3.zero;
}
private Vector3 PredictTargetPosition(Vector3 currentPos, Vector3 velocity)
{
if (velocity.magnitude < 0.1f) return currentPos;
float timeToTarget;
if (useDynamicPrediction)
{
var toTarget = currentPos - transform.position;
var distance = toTarget.magnitude;
var closingSpeed = launchSpeed - Vector3.Dot(velocity, toTarget.normalized);
if (closingSpeed > 0)
{
timeToTarget = distance / closingSpeed;
}
else
{
timeToTarget = predictionTime;
}
timeToTarget = Mathf.Min(timeToTarget, predictionTime);
}
else
{
timeToTarget = predictionTime;
}
var predictedPos = currentPos + velocity * timeToTarget;
predictedPos.y = 0.1f;
return predictedPos;
}
private Vector3 ApplyInaccuracy(Vector3 targetPos)
{
if (accuracy >= 1f) return targetPos;
var inaccuracy = (1f - accuracy) * inaccuracyRadius;
var randomOffset = Random.insideUnitCircle * inaccuracy;
var inaccuratePos = targetPos;
inaccuratePos.x += randomOffset.x;
inaccuratePos.z += randomOffset.y;
return inaccuratePos;
}
private void FireProjectile(Vector3 direction, Vector3 targetPos)
{
var projObj = Instantiate(projectilePrefab, transform.position, Quaternion.identity);
var projectile = projObj.GetComponent<Projectile>();
projectile.Set(transform.position, direction, launchSpeed);
if (showPredictedPosition && targetCirclePrefab != null)
{
SpawnTargetCircle(targetPos, "ShotTarget");
}
}
private void UpdatePredictedTargetVisual()
{
if (wanderingTarget == null) return;
var targetPos = wanderingTarget.transform.position;
var targetVelocity = GetTargetVelocity();
var predictedPos = PredictTargetPosition(targetPos, targetVelocity);
_predictedTarget.position = predictedPos;
Debug.DrawLine(transform.position, predictedPos, Color.cyan);
Debug.DrawLine(targetPos, predictedPos, Color.yellow);
}
private void SpawnTargetCircle(Vector3 position, string name = "TargetCircle")
{
var circle = Instantiate(targetCirclePrefab, position, Quaternion.identity);
circle.name = name;
var circlePos = circle.transform.position;
circlePos.y = 0.05f;
circle.transform.position = circlePos;
if (circleLifetime > 0)
{
Destroy(circle, circleLifetime);
}
}
public void SetAccuracy(float newAccuracy)
{
accuracy = Mathf.Clamp01(newAccuracy);
}
public void SetFireRate(float newFireRate)
{
fireRate = Mathf.Max(0.1f, newFireRate);
}
private void OnDrawGizmosSelected()
{
if (wanderingTarget == null) return;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(wanderingTarget.transform.position, 0.5f);
var targetAgent = wanderingTarget.GetComponent<Agent>();
if (targetAgent != null && targetAgent.velocity.magnitude > 0.1f)
{
Gizmos.color = Color.blue;
var velocityVector = targetAgent.velocity.normalized * 2f;
Gizmos.DrawRay(wanderingTarget.transform.position, velocityVector);
}
if (Application.isPlaying)
{
var targetPos = wanderingTarget.transform.position;
var velocity = targetAgent != null ? targetAgent.velocity : Vector3.zero;
var predictedPos = PredictTargetPosition(targetPos, velocity);
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(predictedPos, 0.5f);
Gizmos.DrawLine(transform.position, predictedPos);
}
Gizmos.color = new Color(1f, 0f, 0f, 0.2f);
Gizmos.DrawWireSphere(transform.position, inaccuracyRadius);
}
private void OnDestroy()
{
if (_predictedTargetObject != null)
{
Destroy(_predictedTargetObject);
}
}
}
}