ProjectCreep/Assets/TBTK/Scripts/ShootObject.cs

245 lines
7.2 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace TBTK{
public class ShootObject : MonoBehaviour {
#if UNITY_EDITOR
public static bool inspector=false;
#endif
public enum _Type{ Projectile, Beam, Effect, Missile, }
public _Type type;
public delegate void HitCallback();
public HitCallback hitCallback;
[Header("Projectile & Missile")]
public float speed=10;
public float elevation=2; //how elevated the shoot trajectory is (x-axis)
public float swerve=2; //how elevated the shoot trajectory is (y-axis)
public float falloffRange=1; //below this range, the elevation will gradually decrease, try set to match the max range of the tower
private float eta=1; //estimated time to hit target, used to adjust offsetPos during runtime
private float effElevation=1; //actual elevation used in runtime, recalculated based on falloffRange
private float effSwerve=0; //actual elevation used in runtime, recalculated based on falloffRange
//offset to the targetPos for the SO to aim for, adjust in runtime to create a trajectory
//it's always (0, value, 0), and value is consistently droping as the SO approach the target, making the SO aim above the target and drops overtime
private Vector3 offsetPos=Vector3.zero;
[Space(5)] public List<TrailRenderer> trailList=new List<TrailRenderer>();
private Vector3 shootDir;
private Vector3 shootDirP;
[Header("Beam")]
public List<LineRenderer> lines=new List<LineRenderer>();
public float beamDuration=0.5f;
public float startWidth=0.25f;
private List<Vector3> linePos=new List<Vector3>{ Vector3.zero, Vector3.zero };
private Vector3 tgtPos;
[Header("Effect")]
public float effectDuration=0.5f;
public bool attachToShootPoint=false;
[Header("Visual and Audio")]
public VisualObject effectShoot=new VisualObject();
public VisualObject effectHit=new VisualObject();
public AudioClip shootSound;
public AudioClip hitSound;
[Header("Runtime Attribute (For Debugging)")]
public Unit tgtUnit;
public float tgtRadius=0;
public Vector3 targetPos;
//public AttackInfo attackInfo;
public float shootTime;
public Transform shootPoint;
private bool shot=false;
private bool hit=false;
protected GameObject thisObj; //public GameObject GetObj(){ return thisObj; }
protected Transform thisT; //public Transform GetT(){ return thisT; }
public Vector3 GetPos(){ return thisT!=null ? thisT.position : transform.position ; }
public Quaternion GetRot(){ return thisT!=null ? thisT.rotation : transform.rotation ; }
public void Awake(){
thisT=transform;
thisObj=gameObject;
if(type==_Type.Beam){
for(int i=0; i<lines.Count; i++){
if(lines[i]==null){ lines.RemoveAt(i); i-=1; }
}
if(lines.Count==0) Debug.LogWarning("Beam type shoot-object hasn't been assigned any LineRenderer");
}
}
void OnEnable(){
if(trailList==null) return;
for(int i=0; i<trailList.Count; i++) trailList[i].Clear();
}
//called by Unit to fire the shoot-object, all initial calculation for a shot goes here
//~ public void InitShoot(AttackInfo aInfo, Transform shootP=null){
//~ if(aInfo.tgtUnit==null){
//~ ObjectPoolManager.Unspawn(thisObj);
//~ return;
//~ }
//~ shootPoint=shootP;
//~ attackInfo=aInfo;
//~ InitShoot(aInfo.tgtUnit);
//~ if(attachToShootPoint) thisT.parent=shootPoint;
//~ }
public void InitShoot(Node node, HitCallback cb=null, Transform shootP=null, Vector3 offset=default(Vector3)){
tgtRadius=GridManager.GetNodeSize()*0.1f;
targetPos=node.GetPos()+offset;
InitShoot(cb, shootP);
}
public void InitShoot(Unit tUnit, HitCallback cb=null, Transform shootP=null){
tgtUnit=tUnit;
tgtRadius=tgtUnit.GetRadius();
targetPos=tgtUnit.GetTargetPoint();
InitShoot(cb, shootP);
}
public void InitShoot(HitCallback cb=null, Transform shootP=null){
if(attachToShootPoint) thisT.parent=shootPoint;
shot=true; hit=false; shootTime=Time.time;
hitCallback=cb;
if(type==_Type.Projectile || type==_Type.Missile){
shootDir=(targetPos-GetPos()).normalized;
shootDirP=new Vector3(shootDir.z, 0, -shootDir.x);
//estimate the time taken to reach the target (roughly) and calculate the effective elevation based on falloffRange
float dist=Vector3.Distance(GetPos(), targetPos);
eta=dist/speed;
float disMultiplier=Mathf.Clamp((dist-(falloffRange*.5f))/falloffRange, 0, 1);
effElevation=(type==_Type.Projectile ? elevation : Rand.Range(0, elevation))*disMultiplier;
if(type==_Type.Missile){
effSwerve=Rand.Range(0, swerve)*disMultiplier;
effSwerve*=( Rand.value()<0.5f ? 1 : -1 );
}
}
else if(type==_Type.Beam){
if(shootPoint!=null) thisT.parent=shootPoint;
}
else if(type==_Type.Effect){
thisT.LookAt(targetPos);
}
effectShoot.Spawn(GetPos(), GetRot());
AudioManager.PlaySound(shootSound);
}
private void UpdateTargetPos(){
if(tgtUnit!=null) targetPos=tgtUnit.GetTargetPoint();
else tgtRadius=GridManager.GetNodeSize()*0.1f;
}
void Update(){
if(!shot || hit) return;
UpdateTargetPos();
if(type==_Type.Projectile || type==_Type.Missile){
//calculate the offset position based on the shoot time and eta
float t=Mathf.Min((Time.time-shootTime)/eta, 1);
offsetPos=new Vector3(0, (1-t)*effElevation, 0);
if(type==_Type.Missile){
Vector3 os=shootDirP*(1-t)*effSwerve;
offsetPos=new Vector3(os.x, offsetPos.y, os.z);
}
Vector3 dir=(targetPos+offsetPos-GetPos()).normalized;
thisT.LookAt(GetPos()+dir);
float dist=Vector3.Distance(targetPos+offsetPos, GetPos());
if(dist>tgtRadius) thisT.Translate(dir*Mathf.Min(Time.deltaTime*speed, dist), Space.World);
else Hit(GetPos());
}
else if(type==_Type.Beam){
float durRemain=Mathf.Clamp(beamDuration-(Time.time-shootTime), 0, beamDuration);
if(durRemain<=0) Hit(ModifyTargetPosWithTgtRadius());
else{
for(int i=0; i<lines.Count; i++){
linePos[0]=GetPos(); linePos[1]=ModifyTargetPosWithTgtRadius();
lines[i].SetPositions(linePos.ToArray());
lines[i].widthMultiplier = Mathf.Lerp(startWidth, 0, 1-durRemain/beamDuration);
}
}
}
else if(type==_Type.Effect){
if(Time.time-shootTime>effectDuration){
Hit(ModifyTargetPosWithTgtRadius());
}
}
}
void Hit(Vector3 hitPos){
if(hit) return;
hit=true;
effectHit.Spawn(hitPos, Quaternion.identity);
AudioManager.PlaySound(hitSound);
//if(attackInfo!=null && tgtUnit!=null) tgtUnit.ApplyAttack(attackInfo);
if(hitCallback!=null) hitCallback();
ObjectPoolManager.Unspawn(thisObj);
}
Vector3 ModifyTargetPosWithTgtRadius(){
Vector3 dir=(GetPos()-targetPos).normalized;
return targetPos+dir*tgtRadius;
}
public float GetElevationAngle(Vector3 sPos, Vector3 tPos){
if(type!=_Type.Projectile) return 0;
float dist=Vector3.Distance(sPos, tPos);
float elev=elevation*Mathf.Clamp((dist-(falloffRange*.5f))/falloffRange, 0, 1);
return -Mathf.Atan(elev/dist)*Mathf.Rad2Deg;
}
void OnDrawGizmos(){
Gizmos.DrawLine(GetPos(), targetPos);
}
}
}