플레이에 긴장감을 추가하기위해, 플레이어를 중심으로 여러발의 공격을 발사하여 플레이어에게 위협을 줄 수 있습니다. 하지만 이 때, 즉시 공격을 발사하는 것 보다는 '이곳에 공격을 할 것이다' 라는 경고를 먼저 주며 그 후에 공격을 주면 더욱 직관적이고 플레이어도 예측이 가능해 적절한 대응을 할 수 있습니다. 이런 기능을 구현하여 특정한 상황에 간단하게 기능을 이용할 수 있도록 만들었습니다.
✅ 직선 형태의 공격을 하기위한 특정 조건이 발생하면 공격을 하기위한 스크립트를 실행한다. 라인렌더러를 이용하여 라인을 그리고 그 라인이 모두 그려지면 특정한 프리팹을 인스턴스한다. (현재 글에서는 로켓 프리팹이다.) 자연스러운 연출을 위해 라인렌더러를 바로 사라지게 하지 않고 라인을 시작점부터 끝점까지 다시 지우며 모두 지워지면 해당 게임오브젝트를 제거한다.
✅ 플레이어를 중심으로 산발적으로 직선 형태로 경고가 나온 후 로켓이 발사되는것을 볼 수 있다.
using System.Collections;
using UnityEngine;
/// <summary>
/// 라인렌더러를 이용하여 공격 방향과 위치를 경고하며, 그 라인을 따라 특정 프리팹이 인스턴스되어 공격하는 기능
/// </summary>
[RequireComponent(typeof(LineRenderer))]
public class StraightRocketController : MonoBehaviour
{
public static StraightRocketController NewStraightRocket()
{
GameObject obj = new GameObject();
obj.AddComponent<StraightRocketController>();
return obj.GetComponent<StraightRocketController>();
}
private LineRenderer mLineRenderer; //라인 렌더러
private void Awake()
{
mLineRenderer = GetComponent<LineRenderer>();
mLineRenderer.startWidth = mLineRenderer.endWidth = 0.25f;
mLineRenderer.sharedMaterial = new Material(Shader.Find("Shader Graphs/StraightRocketLine"));
mLineRenderer.sharedMaterial.SetColor("_Color", Color.red);
}
/// <summary>
/// 직선 형태로 공격하는 로켓을 인스턴스하고 초기화한다.
/// </summary>
/// <param name="obj">인스턴스 할 오브젝트</param>
/// <param name="targetPos">바라보며 공격 할 대상의 위치</param>
/// <param name="startDistance">시작시 startPos로부터 얼마나 떨어져서 시작하는지</param>
/// <param name="startRot">시작시 startPos로부터 월드좌표계 기준으로 어느 방향에서 공격을 해오는 각도</param>
/// <param name="drawingTime">targetPos까지 라인이 그려지는데 걸리는 시간</param>
/// <param name="arrivedTime">로켓이 targetPos까지 가는데 걸리는 시간</param>
public void InitStraightRocket(GameObject obj, Vector3 targetPos, float startDistance, Vector3 startRot, float drawingTime, float arrivedTime)
{
//시작 위치
Vector3 startPos = Quaternion.Euler(startRot) * targetPos;
startPos = targetPos + startPos.normalized * startDistance;
//라인 시작점 설정
mLineRenderer.SetPosition(0, startPos);
Debug.Log(startPos);
// //해당 오브젝트를 인스턴스한다.
// mInstansiatedObject = Instantiate(obj, startPos, Quaternion.identity);
//라인이 그려지는 속도
float lineSpeed = (targetPos - startPos).magnitude / drawingTime;
float movementSpeed = (targetPos - startPos).magnitude / arrivedTime;
//라인이 움직이는 속도
//라인 그리기
StartCoroutine(COR_DrawLine(obj, startPos, targetPos, lineSpeed, movementSpeed));
}
private IEnumerator COR_DrawLine(GameObject obj, Vector3 startPos, Vector3 targetPos, float lineSpeed, float movementSpeed)
{
//목적지까지의 이동 거리
float totalDistance = (startPos - targetPos).magnitude;
//도착지 라인의 현재 위치
Vector3 linePos = startPos;
//방향
Vector3 direction = (targetPos - startPos).normalized;
while (true)
{
//라인의 도착지 이동시키기
linePos += direction * lineSpeed * Time.deltaTime;
//라인 도착지 설정
mLineRenderer.SetPosition(1, linePos);
//두배만큼 라인이 그려진경우 로켓 인스턴스
if (totalDistance * 2.0f < (startPos - linePos).magnitude)
{
GameObject instantiatedObj = Instantiate(obj, startPos, Quaternion.identity);
instantiatedObj.GetComponent<StraightRocketObject>().InitRocket(targetPos, movementSpeed);
//현재 라인 위치는 도착지점
Vector3 destPos = startPos;
//새로 시작점 라인의 위치를 조정할 것
linePos = startPos;
totalDistance = (startPos - targetPos).magnitude;
while (true)
{
//라인의 도착지 이동시키기
linePos += direction * lineSpeed * Time.deltaTime;
//라인 도착지 설정
mLineRenderer.SetPosition(0, linePos);
if (totalDistance < (startPos - linePos).magnitude)
{
Destroy(gameObject);
}
yield return null;
}
}
yield return null;
}
}
}
|
cs |
public static StraightRocketController NewStraightRocket()
{
GameObject obj = new GameObject();
obj.AddComponent<StraightRocketController>();
return obj.GetComponent<StraightRocketController>();
}
|
cs |
외부에서 호출할 수 있으며, 하나의 직선 공격을 위한 컨트롤러를 인스턴스하기위한 함수이다. 특정 매니저를 사용할 필요가 없으며, static으로 선언하여 어디서든 사용할 수 있도록 만들었다.
- 런타임 도중 게임 오브젝트를 생성하고, 해당 게임오브젝트에 컨트롤러를 추가하고 해당 컨트롤러를 리턴한다.
public void InitStraightRocket(GameObject obj, Vector3 targetPos, float startDistance, Vector3 startRot, float drawingTime, float arrivedTime)
{
//시작 위치
Vector3 startPos = Quaternion.Euler(startRot) * targetPos;
startPos = targetPos + startPos.normalized * startDistance;
//라인 시작점 설정
mLineRenderer.SetPosition(0, startPos);
Debug.Log(startPos);
// //해당 오브젝트를 인스턴스한다.
// mInstansiatedObject = Instantiate(obj, startPos, Quaternion.identity);
//라인이 그려지는 속도
float lineSpeed = (targetPos - startPos).magnitude / drawingTime;
float movementSpeed = (targetPos - startPos).magnitude / arrivedTime;
//라인이 움직이는 속도
//라인 그리기
StartCoroutine(COR_DrawLine(obj, startPos, targetPos, lineSpeed, movementSpeed));
}
|
cs |
NewStraightRocket()을 한 후, 리턴한 컨트롤러에서 해당 컨트롤러에게 초기화를 시키도록 하는 함수이다. 인스턴스할 때, 상세한 커스터마이징이 가능하다.
매개변수 설명
- Gameobject obj: 라인이 모두 그려졌을 때, 해당 오브젝트를 인스턴스하여 라인을 따라 이동하는 오브젝트 대상.
- Vector3 targetPos: 공격 대상의 위치
- float startDistance: 공격 대상의 위치로부터 얼마나 떨어져서 공격을 시작할지에 대한 거리
- Vector3 startRot: 공격 대상으로부터 월드좌표 기준으로 어느 회전각도에서 공격을 해올지에 대한 각도
- float drawingTime: 시작>대상위치 까지 라인이 그려지는 시간
- float arrivedTime: 시작>대상위치까지 obj가 도달하는 시간
함수 설명
- mLineRenderer에서 0번째 Position을 startPos로 하여 시작점을 설정한다.
- Vector3 연산을 이용하여 lineSpeed, movementSpeed를 계산하여 코루틴의 인자로 넘긴다.
private IEnumerator COR_DrawLine(GameObject obj, Vector3 startPos, Vector3 targetPos, float lineSpeed, float movementSpeed)
{
//목적지까지의 이동 거리
float totalDistance = (startPos - targetPos).magnitude;
//도착지 라인의 현재 위치
Vector3 linePos = startPos;
//방향
Vector3 direction = (targetPos - startPos).normalized;
while (true)
{
//라인의 도착지 이동시키기
linePos += direction * lineSpeed * Time.deltaTime;
//라인 도착지 설정
mLineRenderer.SetPosition(1, linePos);
//두배만큼 라인이 그려진경우 로켓 인스턴스
if (totalDistance * 2.0f < (startPos - linePos).magnitude)
{
GameObject instantiatedObj = Instantiate(obj, startPos, Quaternion.identity);
instantiatedObj.GetComponent<StraightRocketObject>().InitRocket(targetPos, movementSpeed);
//현재 라인 위치는 도착지점
Vector3 destPos = startPos;
//새로 시작점 라인의 위치를 조정할 것
linePos = startPos;
totalDistance = (startPos - targetPos).magnitude;
while (true)
{
//라인의 도착지 이동시키기
linePos += direction * lineSpeed * Time.deltaTime;
//라인 도착지 설정
mLineRenderer.SetPosition(0, linePos);
if (totalDistance < (startPos - linePos).magnitude)
{
Destroy(gameObject);
}
yield return null;
}
}
yield return null;
}
}
|
cs |
라인을 그리고, 실제로 공격을 하는 게임오브젝트를 인스턴스한 후 라인을 지운 후 스스로 Destroy까지 하는 모든 행동을 수행하는 코루틴 함수이다.
- while문이 두개가 있어 라인을 목적지까지 그리는 작업을 한 후, 라인을 시작점으로부터 목적지까지 지우는 작업을 차례대로 수행하여 공격을 마무리 짓도록 한다.
- 벡터 연산을 이용하여 거리를 측정하고, 기준거리보다 현재 그려진 라인의 거리가 더 큰경우 조건을 참으로하도록 하였다.
✅ 기능 이용(함수 실행)예시 (플레이어를 중심을 약간 벗어난 무작위 위치)
StraightRocketController ctrl = StraightRocketController.NewStraightRocket();
ctrl.InitStraightRocket(mStaightRocket, mPlayerController.transform.position + mPlayerController.transform.forward * Random.value * 10f + mPlayerController.transform.up * Random.value * 10f, 50f, new Vector3(Random.Range(0, 360), Random.Range(0, 360), Random.Range(0, 360)), 1.0f, Random.Range(5.0f, 10.0f));
|
cs |
- 외부에서 해당 스크립트를 포함하지 않고, static 함수인 NewStraight함수를 통해 컨트롤러를 생성하고 해당 컨트롤러에 접근하여 초기화를 진행해 공격을 수행하도록 구현하였다.
▶️ 공격 대상을 플레이어의 트랜스폼 위치로 정확히 지정한 예시
StraightRocketController ctrl = StraightRocketController.NewStaightRocket();
ctrl.InitStraightRocket(mStaightRocket, mPlayerController.transform.position, 50f, new Vector3(Random.Range(0, 360), Random.Range(0, 360), Random.Range(0, 360)), 1.0f, Random.Range(5.0f, 10.0f));
|
cs |
'unity game modules' 카테고리의 다른 글
[유니티] NavMesh를 응용하여 '내비게이션(경로 시각화)' 만들기 (0) | 2022.10.22 |
---|---|
[유니티] 파일브라우저(FileExplorer)를 이용하여 로컬 파일 저장/읽기 (SimpleFileBrowser) (0) | 2022.10.22 |
[유니티] 낙하 감지(Fall Detection) 및 낙하 연출 (0) | 2022.09.01 |
[유니티] 자연스러운 점프(JumpLoop) (0) | 2022.08.27 |
[유니티] TTS(Text-To-Speech) 목소리 구현 (0) | 2022.08.05 |