PlayerCombat.cs (스나이퍼 모드 핵심)
public class PlayerCombat : MonoBehaviour
{
// Sniper Mode 관련 변수
[SerializeField] private LayerMask _layermask;
[SerializeField] private int _maxDistance = 100;
[SerializeField] private Camera _sniperCamera;
[SerializeField] private float _raserTime = 0.3f;
[SerializeField] private float _sniperDamage = 30f;
private LineRenderer _line;
private void Awake()
{
_sniperCamera = Camera.main;
_line = GetComponent<LineRenderer>();
_line.startWidth = 0.01f;
_line.endWidth = 0.01f;
}
void Update()
{
// ... (생략)
if (Input.GetAxis("Fire1") > 0f)
{
if (!_director.SniperMode)
{
// 일반 모드: 총알 생성
GameObject bullet = PoolManager.instance.Get(PoolKey.BULLET);
// ... 위치 설정 및 발사
}
// 스나이퍼 모드: Raycast 발사
if (_director.SniperMode)
{
StartCoroutine(SniperRaser());
}
// ... (생략)
}
}
IEnumerator SniperRaser()
{
_line.enabled = true;
Ray ray = _sniperCamera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, _maxDistance, _layermask))
{
_line.SetPosition(0, _firePosition.position);
_line.SetPosition(1, hit.point);
if (hit.collider.TryGetComponent(out MonsterAI enemy))
{
enemy.TakeDamage(_sniperDamage);
}
// 태그별 이펙트 처리
if (hit.collider.CompareTag("Enemy"))
{
GameObject explosion = PoolManager.instance.Get(PoolKey.EXPLOSION);
explosion.GetComponent<PooledParticle>().RestartAt(hit.transform);
}
else if (hit.collider.CompareTag("Wall"))
{
// 폭발 및 총알 구멍 생성
GameObject bulletHole = PoolManager.instance.Get(PoolKey.BULLETHOLE);
bulletHole.transform.position = hit.point;
bulletHole.transform.forward = hit.normal;
bulletHole.transform.position += hit.normal * 0.01f;
}
}
yield return new WaitForSeconds(_raserTime);
_line.enabled = false;
}
}
SplitEffect.cs (분열 및 재질 유지)
public void Play(GameObject target)
{
for (int i = 0; i < _splitCount; i++)
{
GameObject piece = GameObject.CreatePrimitive(PrimitiveType.Cube);
piece.transform.position = target.transform.position;
piece.transform.localScale = target.transform.localScale * _splitScale;
// 기존 Material 설정으로 어색함 해소
Renderer renderer = piece.GetComponent<Renderer>();
renderer.material = _material;
Rigidbody rb = piece.AddComponent<Rigidbody>();
rb.AddForce(Random.onUnitSphere, ForceMode.Impulse);
Destroy(piece, _splitLifeTime);
}
Destroy(gameObject);
}
Explosion.cs
using UnityEngine;
public class ExplosionEffect : MonoBehaviour, IClickEffect
{
[SerializeField] private ParticleSystem _particle;
private void Explosion(GameObject target)
{
// 폭발 이펙트 생성
ParticleSystem explosion = Instantiate(_particle);
// 이펙트 위치 설정
explosion.transform.position = target.transform.position;
explosion.transform.rotation = target.transform.rotation;
// 타겟 삭제
Destroy(target);
}
public void Play(GameObject target)
{
Explosion(target);
}
}
ChangeColorEffect.cs
using UnityEngine;
public enum Colors
{
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, PURPLE
}
public class ChangeColorEffect : MonoBehaviour, IClickEffect
{
[SerializeField] private Material[] _materials;
[SerializeField] private int _index = 0;
public void Play(GameObject target)
{
ChangeColor(target);
}
private void ChangeColor(GameObject target)
{
// 색 전환
MeshRenderer renderer = target.GetComponent<MeshRenderer>();
renderer.material = _materials[_index];
// 인덱스 전환
_index++;
if (_index >= _materials.Length)
{
_index = 0;
}
}
}
TelleportEffect.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class TelleportEffect : MonoBehaviour, IClickEffect
{
public void Play(GameObject target)
{
Telleport(target);
}
private void Telleport(GameObject target)
{
SceneManager.LoadScene("Lobby");
}
}
LineRenderer를 Awake에서 캐싱하고 기본 너비를 설정하여 초기화했다.Input.GetAxis("Fire1")을 통해 스나이퍼 모드일 때는 총알을 생성하지 않고 Raycast만 쏘도록 예외 처리를 했다.RaycastHit 정보를 이용해 레이저의 시작점(총구)과 끝점(충돌 지점)을 실시간으로 그려준 뒤, 코루틴으로 일정 시간 후 회수했다.SceneManager를 사용하여 클릭 시 로비 씬으로 즉시 이동하는 기능을 추가했다.LineRenderer 코드는 완벽했으나 실제 플레이 시 선이 보이지 않거나 에러가 발생함.Player 오브젝트에 LineRenderer 컴포넌트 자체가 추가되어 있지 않았음.Sprites-Default 재질 할당으로 해결했다.IDamageable 인터페이스를 통해 적, 벽, 장애물 등 클래스가 달라도 동일한 방식으로 데미지를 줄 수 있다.IInteractable을 사용하여 문 열기, 아이템 줍기 등 다양한 이벤트를 하나의 레이캐스트 로직으로 통합 관리할 수 있다.LayerMask를 지정하여 원하는 물체만 감지하거나 무시할 수 있어 성능 최적화에 용이하다.