OneShotOneKill/Assets/Script/InGame/Projectile/Projectile.cs

362 lines
11 KiB
C#
Raw Normal View History

2026-01-13 00:46:52 +00:00
using CodeStage.AntiCheat.ObscuredTypes;
using System.Collections.Generic;
2026-01-11 23:53:38 +00:00
using UnityEngine;
public class Projectile : MonoBehaviour
{
2026-01-12 03:05:09 +00:00
public float m_Speed = 12f;
public float radius = 0.1f;
2026-01-11 23:53:38 +00:00
Vector2 dir;
2026-01-12 03:05:09 +00:00
Vector2 prevPos;
ObscuredInt m_ReflectCount;
2026-01-15 06:34:35 +00:00
float m_LifeTime;
2026-01-13 00:46:52 +00:00
ProjectileData m_ProjectileData;
2026-01-11 23:53:38 +00:00
2026-01-12 03:23:48 +00:00
#region
ProjectileMgr owner;
2026-01-13 00:46:52 +00:00
string prefabKey;
public void SetOwner(ProjectileMgr mgr, string key)
{
owner = mgr;
prefabKey = key;
}
public string GetPoolKey() { return prefabKey; }
void Kill()
{
owner.Return(this);
// 이펙트 표시 필요
}
public ProjectileTableData Get_ProjectileTData() { return m_ProjectileData.m_Data; }
2026-01-12 03:23:48 +00:00
#endregion
2026-01-12 03:05:09 +00:00
public void Set(ProjectileData pd)
2026-01-11 23:53:38 +00:00
{
m_ProjectileData = pd;
2026-01-15 06:34:35 +00:00
m_LifeTime = m_ProjectileData.m_Data.n_ProjectileLife;
2026-01-16 05:45:58 +00:00
if (m_ProjectileData.ActorType == eActor.Mob) m_ProjectileData.Ignore_WallHP = false;
Vector3 pos = pd.tf_Start.position;
Quaternion rot = pd.tf_Start.rotation;
2026-01-14 21:45:26 +00:00
/* =============================
*
* ============================= */
if (pd.PreviousArrow > 0)
{
int count = pd.PreviousArrow + 1;
2026-01-14 21:45:26 +00:00
float spacing = 0.5f;
float offset = (pd.LineIndex - (count - 1) * 0.5f) * spacing;
pos += pd.tf_Start.right * offset;
}
2026-01-14 21:45:26 +00:00
transform.SetPositionAndRotation(pos, rot);
2026-01-14 21:45:26 +00:00
/* =============================
*
* ============================= */
float angle = 0f;
2026-01-14 21:45:26 +00:00
if (pd.DiagonalArrow > 0 && pd.DiagIndex > 0)
{
int n = pd.DiagonalArrow;
// 전체 90도 안에서 분산
float step = 22.5f / (n + 1);
int index = pd.DiagIndex; // 1부터
int tier = (index + 1) / 2; // 1,1,2,2,3,3...
float offset = step * tier;
bool right = (index % 2) == 0;
angle += right ? offset : -offset;
2026-01-14 21:45:26 +00:00
}
transform.rotation = Quaternion.AngleAxis(angle, transform.forward) * transform.rotation;
2026-01-16 05:45:58 +00:00
if (pd.ActorType == eActor.Mob)
transform.eulerAngles += new Vector3(0f, 0f, 180f);
2026-01-11 23:53:38 +00:00
dir = transform.up.normalized;
2026-01-13 00:46:52 +00:00
m_ReflectCount = m_ProjectileData.m_Data.n_AttackBounceLimit;
2026-01-16 05:45:58 +00:00
if (pd.ActorType == eActor.PC)
m_ReflectCount += (int)IngameMgr.Ins.Get_SkillValue(eSkillType.Reflect);
2026-01-15 06:34:35 +00:00
if (m_ProjectileData.m_Data.n_ProjectileID == 3002)
{
transform.eulerAngles = Vector3.zero;
var lv = IngameMgr.Ins.Get_SkillLv(eSkillType.Explosion);
var skillTdata = table_skill.Ins.Get_Data(eSkillType.Explosion);
var scale = skillTdata.f_ExplosionScale + (skillTdata.f_ExplosionScalePerLv * (lv - 1));
transform.localScale = Vector3.one * scale;
m_ReflectCount = 0;
2026-01-15 06:34:35 +00:00
}
m_ReflectCount.RandomizeCryptoKey();
2026-01-11 23:53:38 +00:00
}
void Update()
{
2026-01-15 06:34:35 +00:00
if (m_LifeTime > 0)
{
m_LifeTime -= Time.deltaTime;
if (m_LifeTime <= 0f)
{
Kill();
return;
}
}
2026-01-16 01:51:31 +00:00
if (m_ProjectileData.m_Data.n_ProjectileID == 3002 ||
m_ProjectileData.m_Data.n_ProjectileID == 3301)
return;
2026-01-15 06:34:35 +00:00
2026-01-12 03:05:09 +00:00
prevPos = transform.position;
2026-01-11 23:53:38 +00:00
2026-01-12 03:05:09 +00:00
float moveDist = m_Speed * Time.deltaTime;
transform.position += (Vector3)(dir * moveDist);
}
2026-01-11 23:53:38 +00:00
2026-01-12 03:05:09 +00:00
private void OnTriggerEnter2D(Collider2D collision)
{
2026-01-16 06:21:49 +00:00
if (m_ProjectileData.ActorType == eActor.Mob)
{
if (collision.tag == "Mob" || collision.tag == "MobShield")
return;
}
if (m_ProjectileData.Ignore_WallHP && collision.tag == "Wall_HP")
{
m_ProjectileData.Ignore_WallHP = false;
return;
}
2026-01-13 07:33:06 +00:00
Check_Hit(collision);
2026-01-16 01:51:31 +00:00
if (m_ProjectileData.m_Data.n_ProjectileID == 3002 ||
m_ProjectileData.m_Data.n_ProjectileID == 3301)
return;
2026-01-15 06:34:35 +00:00
2026-01-14 23:18:10 +00:00
if (m_ProjectileData.Pierce > 0)
{
2026-01-14 23:18:10 +00:00
--m_ProjectileData.Pierce;
return;
}
if (m_ReflectCount <= 0)
2026-01-11 23:53:38 +00:00
{
2026-01-12 03:23:48 +00:00
Kill();
2026-01-12 03:05:09 +00:00
return;
2026-01-11 23:53:38 +00:00
}
2026-01-12 03:05:09 +00:00
Vector2 currentPos = transform.position;
Vector2 moveVec = currentPos - prevPos;
float distance = moveVec.magnitude;
Vector2 moveDir = distance > 0 ? moveVec / distance : dir;
// CircleCast로 정확한 충돌 지점(centroid)과 법선 탐색
RaycastHit2D[] hits = Physics2D.CircleCastAll(prevPos, radius, moveDir, distance);
RaycastHit2D hit = new RaycastHit2D();
bool found = false;
foreach (var h in hits)
2026-01-11 23:53:38 +00:00
{
2026-01-12 03:05:09 +00:00
if (h.collider == collision)
{
// 자기 자신(시작 위치에서 걸리는 경우) 제외 로직이 필요할 수 있으나,
// distance > 0 체크 등으로 보완. 여기서는 가장 먼저 닿는 지점을 찾음.
hit = h;
found = true;
break;
}
2026-01-11 23:53:38 +00:00
}
2026-01-12 03:05:09 +00:00
Vector2 normal;
Vector2 targetPos;
2026-01-11 23:53:38 +00:00
2026-01-12 03:05:09 +00:00
if (found)
2026-01-11 23:53:38 +00:00
{
2026-01-12 03:05:09 +00:00
normal = hit.normal;
// CircleCast의 centroid는 충돌 시의 원의 중심 위치입니다.
targetPos = hit.centroid;
}
else
{
// fallback: ClosestPoint
Vector2 closestRef = collision.ClosestPoint(prevPos);
normal = (prevPos - closestRef).normalized;
if (normal == Vector2.zero)
{
normal = (prevPos - (Vector2)collision.bounds.center).normalized;
}
targetPos = closestRef + normal * radius;
2026-01-11 23:53:38 +00:00
}
2026-01-12 03:05:09 +00:00
// 반사
2026-01-11 23:53:38 +00:00
dir = Vector2.Reflect(dir, normal).normalized;
m_ReflectCount--;
m_ReflectCount.RandomizeCryptoKey();
2026-01-11 23:53:38 +00:00
2026-01-12 03:05:09 +00:00
// 회전
2026-01-11 23:53:38 +00:00
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
transform.rotation = Quaternion.Euler(0, 0, angle);
2026-01-12 03:05:09 +00:00
// 위치 보정
transform.position = targetPos;
2026-01-13 07:33:06 +00:00
}
void Check_Hit(Collider2D collision)
{
2026-01-16 05:45:58 +00:00
if (m_ProjectileData.ActorType != eActor.Mob)
2026-01-13 07:33:06 +00:00
{
switch(collision.tag)
2026-01-13 07:33:06 +00:00
{
case "Mob":
2026-01-19 02:53:50 +00:00
m_ReflectCount = 0; m_ReflectCount.RandomizeCryptoKey();
Show_Hit_Effect();
collision.GetComponent<MobActor>().Get_Dmg(m_ProjectileData);
Spawn_LeftRight(collision.transform);
Spawn_UpDown(collision.transform);
Spawn_Bounce(collision.GetComponent<MobActor>());
break;
case "MobShield":
Show_Hit_Effect();
collision.GetComponent<MobShield>().Get_Dmg();
break;
2026-01-13 07:33:06 +00:00
}
}
else
{
if (collision.tag == "Wall_HP")
{
Show_Hit_Effect();
2026-01-13 07:33:06 +00:00
IngameMgr.Ins.Get_Dmg(m_ProjectileData.Dmg);
}
}
2026-01-11 23:53:38 +00:00
}
void Show_Hit_Effect()
{
if (!string.IsNullOrEmpty(m_ProjectileData.m_Data.s_HitEffect))
EffectMgr.Ins.Show_Effect(m_ProjectileData.m_Data.s_HitEffect, transform.position);
}
public void SetDirection(Vector2 direction)
{
dir = direction.normalized;
// 방향에 맞게 회전도 맞춰줌
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg - 90f;
transform.rotation = Quaternion.Euler(0, 0, angle);
}
List<float> GetOffsets(int count, float spacing)
{
List<float> offsets = new List<float>();
float start = -(count - 1) * 0.5f;
for (int i = 0; i < count; i++)
offsets.Add((start + i) * spacing);
return offsets;
}
void SpawnExtra(Vector3 pos, Vector3 dir)
{
var p = ProjectileMgr.Ins.Get(m_ProjectileData.m_Data.s_ProjectilePrefabs);
p.Set(new ProjectileData
{
2026-01-16 05:45:58 +00:00
ActorType = m_ProjectileData.ActorType,
m_Data = m_ProjectileData.m_Data,
tf_Start = transform,
Dmg = m_ProjectileData.Dmg,
Ignore_WallHP = false,
ArrowLeftRight = 0,
ArrowUpDown = 0,
2026-01-14 23:18:10 +00:00
Bounce = m_ProjectileData.Bounce,
Pierce = m_ProjectileData.Pierce,
});
p.transform.position = pos;
p.SetDirection(dir);
}
void Spawn_LeftRight(Transform tf)
{
if (m_ProjectileData.ArrowLeftRight <= 0) return;
Vector3 hitPos = tf.position;
2026-01-14 23:00:10 +00:00
float spacing = 0.5f;
int count = m_ProjectileData.ArrowLeftRight;
var offsets = GetOffsets(count, spacing);
foreach (float offset in offsets)
{
// ▶ 오른쪽
2026-01-14 23:00:10 +00:00
SpawnExtra(hitPos + Vector3.up * (offset + 0.5f), Vector2.right);
// ◀ 왼쪽
2026-01-14 23:00:10 +00:00
SpawnExtra(hitPos + Vector3.up * (offset + 0.5f), Vector2.left);
}
}
void Spawn_UpDown(Transform tf)
{
if (m_ProjectileData.ArrowUpDown <= 0) return;
Vector3 hitPos = tf.position;
2026-01-14 23:12:15 +00:00
float spacing = 0.5f;
int count = m_ProjectileData.ArrowUpDown;
var offsets = GetOffsets(count, spacing);
foreach (float offset in offsets)
{
// ▲ 위
2026-01-14 23:12:15 +00:00
SpawnExtra(hitPos + Vector3.right * offset + Vector3.up * 1.5f, Vector2.up);
// ▼ 아래
2026-01-14 23:12:15 +00:00
SpawnExtra(hitPos + Vector3.right * offset + Vector3.down * 0.5f, Vector2.down);
}
}
void Spawn_Bounce(MobActor mob)
{
if (m_ProjectileData.Bounce <= 0 || mob == null) return;
MobActor target = IngameMgr.Ins.FindNearestMob(mob.transform.position, mob);
2026-01-14 23:00:10 +00:00
if (target == null)
{
Kill();
return;
}
--m_ProjectileData.Bounce;
SpawnExtra(mob.transform.position, target.transform.position - mob.transform.position);
}
}
2026-01-16 05:45:58 +00:00
public enum eActor { PC, Mob, Supporter }
public class ProjectileData
{
public ProjectileTableData m_Data;
public Transform tf_Start;
2026-01-16 05:45:58 +00:00
public eActor ActorType;
ObscuredInt _Lv; public int Lv { get { return _Lv; } set { _Lv = value; _Lv.RandomizeCryptoKey(); } }
2026-01-13 07:33:06 +00:00
ObscuredInt _Dmg; public int Dmg { get { return _Dmg; } set { _Dmg = value; _Dmg.RandomizeCryptoKey(); } }
public bool Ignore_WallHP;
// 발사 계열
public int PreviousArrow;
public int DiagonalArrow;
public int LineIndex;
public int DiagIndex;
// 피격 계열
public int ArrowLeftRight;
public int ArrowUpDown;
public int Bounce;
2026-01-14 23:18:10 +00:00
public int Pierce;
2026-01-11 23:53:38 +00:00
}