You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
3.9 KiB
C#

using System;
using Fusion;
using UnityEngine;
namespace Projectiles
{
/// <summary>
/// Weapon component that fires a laser beam that damages targets on hit.
/// </summary>
[DefaultExecutionOrder(15)]
public class WeaponBeam : WeaponComponent
{
// PRIVATE MEMBERS
[SerializeField]
private float _damage = 10f;
[SerializeField]
private EHitType _hitType = EHitType.Projectile;
[SerializeField]
private LayerMask _hitMask;
[SerializeField]
private float _maxDistance = 50f;
[SerializeField]
private float _beamRadius = 0.2f;
[SerializeField, Tooltip("Number of raycast rays fired. First is always in center, other are spread around in the radius distance.")]
private int _raycastAmount = 5;
[SerializeField]
private WeaponTrigger _weaponTrigger;
[Header("Beam Visuals")]
[SerializeField]
private GameObject _beamStart;
[SerializeField]
private GameObject _beamEnd;
[SerializeField]
private LineRenderer _beam;
[SerializeField]
private float _beamEndOffset = 0.5f;
[SerializeField]
private bool _updateBeamMaterial;
[SerializeField]
private float _textureScale = 3f;
[SerializeField]
private float _textureScrollSpeed = -8f;
[Header("Camera Effect")]
[SerializeField]
private ShakeSetup _cameraShakePosition;
[SerializeField]
private ShakeSetup _cameraShakeRotation;
[Networked]
private float _beamDistance { get; set; }
// WeaponComponent INTERFACE
public override void Fire()
{
var hit = ProcessBeamHit();
if (hit.Distance > 0f)
{
HitUtility.ProcessHit(Object.InputAuthority, FireTransform.forward, hit, _damage, _hitType);
}
}
// NetworkBehaviour INTERFACE
public override void FixedUpdateNetwork()
{
// Update beam distance only when trigger is firing
if (_weaponTrigger.IsBusy == true)
{
ProcessBeamHit();
}
else
{
_beamDistance = -1f;
}
}
private void LateUpdate()
{
if (Object == null || Object.IsValid == false)
return;
// Beam needs to be updated after camera pivot change
// - after PlayerAgent.LateUpdate
UpdateBeam();
if (_beamDistance > 0f && HasInputAuthority == true)
{
var cameraShake = Context.Camera.ShakeEffect;
cameraShake.Play(_cameraShakePosition, EShakeForce.ReplaceSame);
cameraShake.Play(_cameraShakeRotation, EShakeForce.ReplaceSame);
}
}
// PRIVATE MEMBERS
private LagCompensatedHit ProcessBeamHit()
{
_beamDistance = _maxDistance;
if (ProjectileUtility.CircleCast(Runner, Object.InputAuthority, FireTransform.position, FireTransform.forward, _maxDistance, _beamRadius, _raycastAmount, _hitMask, out LagCompensatedHit hit) == true)
{
_beamDistance = hit.Distance;
return hit;
}
return default;
}
private void UpdateBeam()
{
bool beamActive = _beamDistance > 0f;
_beamStart.SetActiveSafe(beamActive);
_beamEnd.SetActiveSafe(beamActive);
_beam.gameObject.SetActiveSafe(beamActive);
if (beamActive == false)
return;
var startPosition = _beamStart.transform.position;
var targetPosition = FireTransform.position + FireTransform.forward * _beamDistance;
var visualDirection = targetPosition - startPosition;
float visualDistance = visualDirection.magnitude;
visualDirection /= visualDistance; // Normalize
if (_beamEndOffset > 0f)
{
// Adjust target position
visualDistance = visualDistance > _beamEndOffset ? visualDistance - _beamEndOffset : 0f;
targetPosition = startPosition + visualDirection * visualDistance;
}
_beamEnd.transform.SetPositionAndRotation(targetPosition, Quaternion.LookRotation(-visualDirection));
_beam.SetPosition(0, startPosition);
_beam.SetPosition(1, targetPosition);
if (_updateBeamMaterial == true)
{
var beamMaterial = _beam.material;
beamMaterial.mainTextureScale = new Vector2(visualDistance / _textureScale, 1f);
beamMaterial.mainTextureOffset += new Vector2(Time.deltaTime * _textureScrollSpeed, 0f);
}
}
}
}