253 lines
7.8 KiB
C#
253 lines
7.8 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 || !projectilePrefab) 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) return Vector3.zero;
|
|
|
|
var targetAgent = wanderingTarget.GetComponent<Agent>();
|
|
if (targetAgent)
|
|
{
|
|
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)
|
|
{
|
|
SpawnTargetCircle(targetPos, "ShotTarget");
|
|
}
|
|
}
|
|
|
|
private void UpdatePredictedTargetVisual()
|
|
{
|
|
if (wanderingTarget) 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 name1 = "TargetCircle")
|
|
{
|
|
var circle = Instantiate(targetCirclePrefab, position, Quaternion.identity);
|
|
circle.name = name1;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
} |