리그오브레전드 등 여러 게임에서 액터가 스킬을쓰거나, 공격을 하면 공격 범위가 나타나 알아차릴 수 있게 합니다. 이 기능을 구현하고 정리하였습니다. built-in pipeline에서만 작동합니다.
작중 씬 내의 골렘의 3가지 공격 패턴에서 각 공격마다 플레이어에게 피해를 입힐 수 있는 공간을 적절한 형태로 나타내어 경고함으로써 플레이어가 직관적으로 공격에 대응할 수 있도록 한다.
해당 기능을 구현하기 위해서는 "프로젝터" 라는 유니티의 기능을 이용했다.
공격 타입에 맞는 프로젝터 기능이 담긴 오브젝트이다.
프로젝터의 머터리얼에는 유형마다 부채꼴, 직선, 원형 형태에 맞는 스프라이트 이미지가 들어가있다.
머터리얼에 사용되는 쉐이더는 유니티의 기본 쉐이더인 Projector/Multiply 쉐이더를 사용한다.
프로젝터의 Ignore Layers에서 불필요한곳에 비추지 않도록 레이어를 설정한다. 해당 씬에서는 Ground(바닥)과 Item(아이템)을 제외한 곳에는 비추지 않도록 하였다.
구현 아이디어?
프로젝터를 이용하여 이를 바닥에 쏘면서 크기를 조절하거나 색상을 변경하는 등 의도에 맞게 스크립트를 작성하여 사용할 수 있다.
1. 부채꼴 형태의 공격 범위
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
//GolemController.cs
...
public class GolemController : MonoBehaviour
{
...
//골렘의 1번 타입(부채꼴 형태) 공격을 시작한다.
private IEnumerator RunAttack1()
{
float runningTime = mDefaultAtt1Duration;
mProjectorAtt1.gameObject.SetActive(true);
mProjectorAtt1.orthographicSize = 1.0f;
while (runningTime > .0f)
{
runningTime -= Time.deltaTime;
mProjectorAtt1.orthographicSize += 3.2f * Time.deltaTime;
yield return null;
}
//공격 범위 주변에 먼지 파티클을 발생시킨다.
for (int i = 0; i < mAtt1DustTimes; ++i)
{
DustParticleCtrl particle = mAttack1Particle.GetFromPool((int)ParticleCode.ATTACK1);
particle.gameObject.SetActive(true);
particle.transform.position = transform.position + transform.forward * i * 2.0f;
}
//공격 범위 각도를 계산하고 지정 거리 이내이면서 각도 이내이면 피격 처리를 한다.
float dotValue = Mathf.Cos(Mathf.Deg2Rad * (mAngleRange * .5f));
Vector3 direction = mPlayerObj.gameObject.transform.position - transform.position;
if (direction.magnitude < mAtt1Distance)
{
if (Vector3.Dot(direction.normalized, transform.forward) > dotValue)
{
mPlayerObj.GetComponent<PlayerController>().HitPlayer(mGolemDamage);
}
}
//골렘이 공격을 마치면 프로젝터를 비활성화하고 기타 변수들을 설정한다.
misAttacking = false;
mProjectorAtt1.gameObject.SetActive(false);
mAnim.SetInteger("AnimState", 0);
mState = State.IDLE;
}
...
}
|
cs |
private IEnumerator RunAttack1()
- 특정 조건이 발생하여 공격타입1을 시작하기 위해 RunAttack1()이라는 코루틴을 실행시킨다.
mProjectorAtt1.gameObject.SetActive(true);
mProjectorAtt1.orthographicSize = 1.0f;
- 부채꼴형태의 프로젝터 오브젝트를 활성화 시키고 정사영 크기를 1로 설정(초기화) 한다.
while (runningTime > .0f)
{
runningTime -= Time.deltaTime;
mProjectorAtt1.orthographicSize += 3.2f * Time.deltaTime;
yield return null;
}
- runningTime동안 프로젝터의 정사영 크기를 증가시킨다.
- 부채꼴형태의 중심으로부터(이미지의 중심)점점 커지면서 보여진다
float dotValue = Mathf.Cos(Mathf.Deg2Rad * (mAngleRange / 2));
Vector3 direction = mPlayerObj.gameObject.transform.position - transform.position;
if (direction.magnitude < mAtt1Distance)
{
if (Vector3.Dot(direction.normalized, transform.forward) > dotValue)
{
mPlayerObj.GetComponent<PlayerController>().HitPlayer(mGolemDamage);
}
}
- dotValue에서는 mAngleRange에 해당하는 공격의 각도에 대한 코사인 값을 저장한다. 씬에서 mAngleRange는 45이다.
- direction에서 플레이어와 공격의 주체(골렘)의 거리를 측정하고 이 값이 mAtt1Distance(피격 거리)보다 가까이 있는경우 다음 검사로 넘어간다.
- 피격 거리보다 가까운 상태에서 각도를 비교하여 참인경우 플레이어에게 피해를 입히는 함수를 실행시킨다.
mProjectorAtt1.gameObject.SetActive(false);
- runningTime이 0보다 작아져 while문을 빠져나오면서 해당 범위에 피해를 입히는 처리를 하고 mProjectorAtt1을 비활성화 시킨다.
2. 직선 형태의 공격 범위
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
//GolemController.cs
...
//골렘의 2번 타입(직선 형태) 공격 시작을 준비한다.
private IEnumerator RunAttack1Watings()
{
float runningTime = mDefaultAtt2Duration;
mProjectorAtt2.gameObject.SetActive(true);
mProjectorAtt2.orthographicSize = 0.01f;
//정사각형의 텍스쳐를 긴 직선 형태로 만들어야하기에 가로세로비율을 크게 조정한다.
//현재 씬에서는 20 : 1 로 조정하여 시작한 상태
mProjectorAtt2.aspectRatio = 20.0f;
//runningTime이 지나면 프로젝터를 종료한다.
while (runningTime > .0f)
{
runningTime -= Time.deltaTime;
mProjectorAtt2.orthographicSize += 1.0f * Time.deltaTime;
mProjectorAtt2.aspectRatio += 10.0f * Time.deltaTime;
yield return null;
}
//골렘이 공격을 마치면 프로젝터를 비활성화하고 기타 변수(공격상태, 애니메이션)들을 설정한다.
mAnim.SetInteger("AnimState", 5);
misAtt_Dash = true;
mState = State.DASH;
}
...
|
cs |
직선 형태의 공격에 사용되는 이미지는 직선 형태가 아닌 정사각형의 이미지이기때문에 이것을 초기에 얇고 긴 형태로 초기화해야한다. orthographicSize와 aspectRatio를 통해 조정한다.
mProjectorAtt2.orthographicSize = 0.01f;
- 매우 얇은 직선 형태에서 이를 확장시키기에 아주 작은 사이즈(0.01f)로 시작을 한다.
mProjectorAtt2.aspectRatio = 20.0f;
- 프로젝터가 호출될때마다 같은 사이즈로 시작해야하기에 초기 값인 20.0f으로 초기화해준다.
- 정사각형의 텍스쳐를 긴 직선 형태로 만들어야하기에 가로세로비율을 크게 조정한다.
while (runningTime > .0f)
{
...
mProjectorAtt2.orthographicSize += 1.0f * Time.deltaTime;
mProjectorAtt2.aspectRatio += 10.0f * Time.deltaTime;
...
}
- aspectRatio를 크게 증가시켜 가로 비율을 크게 증가시켜 길이를 순식간에 늘린다.
- orthographicSize를 통해 정비율로 증가시켜 선의 두께와 길이를 늘린다.
- runningTime이 종료되면 프로젝터는 비활성화되고 다음 기능이 실행된다.
3. 원 형태의 공격 범위
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
//GolemAttack3ProjectorObj.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//골렘의 3번 타입(원 형태) 공격 시작을 준비한다.
public class GolemAttack3ProjectorObj : MonoBehaviour
{
private Projector mProjector;
[SerializeField] private float mInitFovSize;
[SerializeField] private float mDeltaTimeMultiply;
[SerializeField] private float mMaxFovSize;
[SerializeField] private GolemStonePool mStonePool;
[SerializeField] private float mStoneSpawnYPos;
private void Awake()
{
mProjector = GetComponent<Projector>();
}
private void OnEnable()
{
mProjector.fieldOfView = mInitFovSize;
Invoke("SpawnStone", 0.3f);
}
void Update()
{
mProjector.fieldOfView -= Time.deltaTime * mDeltaTimeMultiply;
if(mProjector.fieldOfView < mMaxFovSize)
{
gameObject.SetActive(false);
}
}
//프로젝터 오브젝트 위치에 돌을 떨어뜨리도록 한다.
private void SpawnStone()
{
StoneObj obj = mStonePool.GetFromPool(Random.Range(0,2));
obj.gameObject.SetActive(true);
obj.transform.position = transform.position;
}
}
|
cs |
private Projector mProjector;
- GolemAttack3ProjectorObj 스크립트가 붙은 대상이 가지고있는 프로젝터 컴포넌트를 담는 변수이다.
[SerializeField] private float mInitFovSize;
- 오브젝트가 활성화 되는 시점에 초기화되는 FOV 시야각 크기이다. 작은 원부터 시작하기에 적은 값으로 시작한다.
[SerializeField] private float mDeltaTimeMultiply;
- 오브젝트가 시간이 지남에따라 비춰지는 원의 크기가 증가하는데, DeltaTime에 곱해지는 값이다. 이 속도로 원의 크기가 커지는 정도를 조절한다.
[SerializeField] private float mMaxFovSize;
- 오브젝트가 비활성화 되는 시점의 최대 FOV 시야각 크기이다. 원 형태가 뚜렷하게 보여야 하기에 mInitFovSize보다 크게 설정된다.
[SerializeField] private GolemStonePool mStonePool;
- 씬에서 사용하는 기능으로 돌을 떨어뜨리기 위해 돌을 풀로부터 가져오는 오브젝트 플이다.
[SerializeField] private float mStoneSpawnYPos;
- 씬에서 사용하는 기능으로 돌을 떨어뜨리기 시작할 때 해당 돌의 높이를 초기화하는 변수이다.
private void Awake()
- 자신에게 있는 프로젝터 컴포넌트를 찾는다. Awake에 넣어 비활성화 상태에서도 한번만 실행되어 찾도록 한다.
private void OnEnable()
- 프로젝터의 시야각을 초기 값으로 설정한다.
- 씬에서 공격을 위해 잠시 후 (0.3초) 후 해당 위치에 돌이 떨어지도록 SpawnStone()함수를 실행시킨다.
void Update()
- 프로젝터 오브젝트가 활성화 되면 OnEnable 후에 실행되며 자신의 시야각 크기를 증가시킨다.
- 시야각 크기가 증가되면 비춰지는 원의 크기가 증가되도록 한다.
- 조건문으로 최대 FOV크기를 넘어가면 해당 오브젝트를 비활성화 시킨다.
'unity game modules' 카테고리의 다른 글
[유니티] 안개 효과 (던전 입장 효과) (0) | 2022.07.16 |
---|---|
[유니티] 3인칭 움직이기 (0) | 2022.07.16 |
[유니티] 오브젝트 풀 (Object Pool) (0) | 2022.07.16 |
[유니티] 아이템 사용하기 (0) | 2022.03.05 |
[유니티] 인벤토리 장비 슬롯 (0) | 2022.03.05 |