1.PlayerSpawner
1.1.정의
- 게임 시작 시 플레이어 스폰 포인트를 랜덤하게 하나 선택하여 그 곳에 플레이어를 배치하는 기능이다.
1.2.기획의도
- 플레이어 랜덤 스폰: 같은 맵에서 반복 플레이 시 조금이라도 지루함을 완화하기
- 장애물 랜덤 배치 기능을 통해 맵 구조를 변경하여 지루함을 완화할 수 있으나 플레이어 랜덤 스폰 기능을 결합하여 익숙하지만 새로운 맵에서 하는 듯한 느낌을 강화 = 기능을 두개 결합하여 지루함 완화 강화
1.3.구성 요소
- 플레이어 스폰 포인트
- 플레이어가 스폰할 수 있는 투명한 위치 오브젝트
- 스폰 포인트를 맵에 배치하여 위치를 준비한다.
- 스폰 포인트는 필요시 개수를 추가, 감소 가능해야함(밸런스 조절용)
- 현재 스폰 포인트는 7개
#맵의 구역마다 스폰포인트를 한 개씩 할당하면 8개인데 한 구역이 작아서 다른 큰 구역과 병합하여
스폰포인트를 7개로 설정함 - 플레이어 오브젝트
- 게임을 플레이하는 플레이어 오브젝트
- 플레이어 스폰 포인트에 배치되는 오브젝트
1.4.기능 명세
1.4.1.플레이어 스폰 포인트 랜덤 선택
- 게임 시작 시 플레이어 스폰 포인트들 중 하나의 스폰 포인트를 선택하는 기능이다.
1.4.2.규칙
- 게임 시작 시 맵에 배치한 플레이어 스폰 포인트들 중 하나를 랜덤하게 선택한다.
- 플레이어 오브젝트의 위치를 선택된 플레이어 스폰 포인트 위치로 변경한다.
1.5.구현 과정
PlayerSpawner 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerSpawner : MonoBehaviour
{
public PlayerStats player;
public Transform[] playerSpawnPoints;
void Start()
{
RandomSelectSpawnPoint();
}
void RandomSelectSpawnPoint()
{
int number = Random.Range(0, playerSpawnPoints.Length);
player.transform.position = playerSpawnPoints[number].transform.position;
}
//매 시작 시 플레이어 스폰 포인트를 랜덤하게 하나 선택
//해당 위치를 플레이어 위치로 변경
}
1.맵에 빈 게임 오브젝트를 생성 > PlayerSpawner로 변경
2.PlayerSpawner에 PlayerSpawner 스크립트를 할당
3.PlayerSpawner 스크립트에서 스폰 포인트 개수를 7개로 변경
4.맵에 배치한 플레이어 스폰 포인트 오브젝트를 할당
2.ItemSpawner
2.1.정의
- 플레이어 주변에 랜덤한 주기로 체력, 마나 포션을 생성하는 기능이다.
2.2.기획의도
- 체력 포션 제공
- 게임 플레이 시 플레이어 스스로 체력 회복하는 기능이 없기 때문에 체력 포션을 제공하여 플레이 지속성을 고려한다. - 마나 포션 제공
- 플레이어의 마나는 초당 1씩 회복 하지만 스킬을 사용하기 위해선 25초 이상 모아야한다.
스킬을 사용하고나면 일반 공격만 사용이 가능하여 전투 패턴이 단조로워지는 문제가 발생한다.
이 때 마나 포션을 제공하여 마나 회복 시간을 단축하여 스킬 사용 기회를 제공한다. - 랜덤 포션 생성과 랜덤 생성 주기
- 항상 동일한 시간에 동일한 포션이 생성되면 플레이어가 예측이 가능해져서 플레이의 난이도가 쉬워진다.
포션 생성 시 랜덤성을 부여하여 적절한 전투 난이도를 유지한다.
- 생성 시간에 약간의 랜덤성을 통해 플레이 시 다양한 상황에 운적 재미를 제공한다.
예) 적들이 몰려오는데 스킬을 이미 사용해서 마나가 없다 > 갑작스런 마나 포션 생성됨 > 마나 회복 후 스킬을
사용하여 적들을 한 방에 처리 > 특정 상황일 때의 운을 통해 재미 경험 가능 - 포션 랜덤 생성 위치
- 플레이어를 중심으로 일정 반경 안에서 랜덤한 위치에 포션을 생성하여 플레이어에게 선택권을 제공한다.
예) 체력이 부족한 상황, 적들 근처에 체력 포션이 스폰 > 위험을 감수하고 먹으러 간다 or 안전하게 도망친다.
- 생성 위치를 예측 불가능하게 하여 쉬운 포션 획득을 방지한다.
2.3.구성요소
2.3.1포션
체력 포션
외형 | -원형 유리병에 담긴 빨간 포션 |
크기 | -플레이어 캐릭터 크기의 반 -약 0.9m -크기가 어느정도 있어야 플레이어가 한 눈에 알아볼 수 있음 |
색상 | -빨간색 |
빛 | -포션 바닥에 포션색과 유사한 붉은빛을 내어 가시성을 높임 |
파티클 | -포션색과 유사한 붉은색 사각형 파티클을 바닥에서 위로 뿌림 -동적인 이펙트를 통해 플레이어에게 확실하게 포션 위치를 알림 |
체력 회복량 | -100 -플레이어 최대 체력의 1/10(포션 생성 주기가 길지 않기 때문에) |
마나 포션
외형 | -원기둥형 유리병에 담긴 푸른 포션 |
크기 | -플레이어 캐릭터 크기의 반 -약 0.9m -크기가 어느정도 있어야 플레이어가 한 눈에 알아볼 수 있음 |
색상 | -푸른색 |
빛 | -포션 바닥에 포션색과 유사한 푸른빛을 내어 가시성을 높임 |
파티클 | -포션색과 유사한 푸른색 사각형 파티클을 바닥에서 위로 뿌림 -동적인 이펙트를 통해 플레이어에게 확실하게 포션 위치를 알림 |
마나 회복량 | -20 -플레이어 최대 마나의 1/5 (마나 회복 기능이 있기 때문에 포션을 먹는다고 무조건 스킬을 사용할 수 없게 함, 포션 섭취 후 스킬 남용 방지, 일반 공격과 스킬 사용 병행 유도) |
2.3.2ItemSpawner 파라미터
이름 | 데이터 타입 | 내용 |
maxDistance | float | -플레이어를 중심으로 아이템이 생성될 반경 -10f |
timeBetSpawn | float | -아이템 생성 시점 -아이템 생성에 필요한 시간이다. |
timeBetSpawnMin | float | -아이템 최소 생성 시점 -아이템 랜덤 생성 주기 계산할 때 사용하는 최소값 -3f |
timeBetSpawnMax | float | -아이템 최대 생성 시점 -아이템 랜덤 생성 주기 계산할 때 사용하는 최대값 -10f |
lastSpawnTime | float | -아이템을 마지막으로 생성 시점 |
itemDestroyTime | float | -생성된 아이템 파괴 제한 시간 -itemDestroyTime초가 지난 후 생성된 아이템을 파괴 -5f |
2.4.기능 명세
2.4.1.기능 구조도
ItemSpawner 기능 구조도
포션 기능 구조도
2.4.2.플로우 차트
ItemSpawner
#Potion 플로우차트는 제외, 기능 자체가 단순해서 규칙으로 충분히 쉽게 이해 가능
2.4.3.세부 기능
2.4.3.1.ItemSpawner
- 아이템 생성 시점 초기화
- 게임 시작 시 아이템 마지막 생성 시점을 0초로 초기화한다.
- 게임 시작 시 아이템 랜덤 생성 주기로 계산한 값을 아이템 생성 시점으로 설정한다. - 아이템 생성 조건
- 현재 시간이 아이템 생성 시점 + 마지막 생성 시점을 지났을 때 아이템을 생성한다. - 아이템 랜덤 생성 주기
- 아이템 생성 시점 = 아이템 생성 최소 ~ 최대 생성 시점 사이의 랜덤한 값 - 아이템 생성
- 아이템 생성 조건을 충족했을 경우 마지막 생성 시점을 현재 시간으로 갱신한다.
- 아이템 생성 시점을 아이템 랜덤 생성 주기로 갱신한다.
- 아이템을 생성 위치에 생성한다. - 아이템 생성 위치
- 플레이어 위치를 기준으로 하는 maxDistance 반경 안의 랜덤한 위치를 생성한다.
- 생성된 위치와 가장 가까운 네비메쉬 위를 아이템 생성 위치로 설정한다.
- 아이템 생성 위치의 높이를 0.7f 높여서 맵의 바닥 위로 살짝 공중에 떠있게 한다.
2.4.3.2.포션
- 회복 기능
- 생성된 체력 포션에 플레이어가 접촉했을 경우 플레이어의 체력을 체력 회복량만큼 회복한다.
- 생성된 마나 포션에 플레이어가 접촉했을 경우 플레이어의 마나를 마나 회복량만큼 회복한다. - 이펙트
- 플레이어에게 포션의 위치와 포션의 종류를 쉽게 알려주기 위한 기능이다. - 회전 기능
- 포션이 제자리에서 느리게 회전하는 기능이다.
- 생성된 포션이 정적으로 있는 것보다 동적인 상태를 보여주는게 더 가시성이 좋아서 회전 기능을 추가함
2.5.구현 과정
ItemSpawner 스크립트
using UnityEngine;
using UnityEngine.AI; // 내비메쉬 관련 코드
// 주기적으로 아이템을 플레이어 근처에 생성하는 스크립트
public class ItemSpawner : MonoBehaviour
{
public GameObject[] items; // 생성할 아이템들
public Transform playerTransform; // 플레이어의 트랜스폼
public float maxDistance = 10f; // 플레이어 위치로부터 아이템이 배치될 최대 반경
public float timeBetSpawnMax = 10f; // 최대 시간 간격
public float timeBetSpawnMin = 3f; // 최소 시간 간격
private float timeBetSpawn; // 생성 간격
private float lastSpawnTime; // 마지막 생성 시점
private void Start()
{
// 생성 간격과 마지막 생성 시점 초기화
timeBetSpawn = Random.Range(timeBetSpawnMin, timeBetSpawnMax);
lastSpawnTime = 0;
}
// 주기적으로 아이템 생성 처리 실행
private void Update() {
// 현재 시점이 마지막 생성 시점에서 생성 주기 이상 지남
// && 플레이어 캐릭터가 존재함
if (Time.time >= lastSpawnTime + timeBetSpawn && playerTransform != null)
{
// 마지막 생성 시간 갱신
lastSpawnTime = Time.time;
// 생성 주기를 랜덤으로 변경
timeBetSpawn = Random.Range(timeBetSpawnMin, timeBetSpawnMax);
// 아이템 생성 실행
Spawn();
}
}
// 실제 아이템 생성 처리
private void Spawn()
{
// 플레이어 근처에서 내비메시 위의 랜덤 위치 가져오기
Vector3 spawnPosition = GetRandomPointOnNavMesh(playerTransform.position, maxDistance);
// 바닥에서 0.7만큼 위로 올리기
spawnPosition += Vector3.up * 0.7f;
// 아이템 중 하나를 무작위로 골라 랜덤 위치에 생성
GameObject selectedItem = items[Random.Range(0, items.Length)];
GameObject item = Instantiate(selectedItem, spawnPosition, Quaternion.identity);
// 생성된 아이템을 5초 뒤에 파괴
Destroy(item, 5f);
}
// 내비메시 위의 랜덤한 위치를 반환하는 메서드
// center를 중심으로 distance 반경 안에서 랜덤한 위치를 찾는다
private Vector3 GetRandomPointOnNavMesh(Vector3 center, float distance)
{
// center를 중심으로 반지름이 maxDistance인 구 안에서의 랜덤한 위치 하나를 저장
// Random.insideUnitSphere는 반지름이 1인 구 안에서의 랜덤한 한 점을 반환하는 프로퍼티
Vector3 randomPos = Random.insideUnitSphere * distance + center;
// 내비메시 샘플링의 결과 정보를 저장하는 변수
NavMeshHit hit;
// maxDistance 반경 안에서, randomPos에 가장 가까운 내비메시 위의 한 점을 찾음
NavMesh.SamplePosition(randomPos, out hit, distance, NavMesh.AllAreas);
// 찾은 점 반환
return hit.position;
}
}
체력 포션 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HealingPotion : MonoBehaviour, IItem
{
public float health = 50;
public void Use(GameObject target)
{
//전달받은 게임 오브젝트로부터 PlayerStats 컴포넌트 가져오기
PlayerStats player = target.GetComponent<PlayerStats>();
//PlayerStats 컴포넌트가 있다면
if(player != null)
{
//체력 회복 실행
player.RestoreHealth(health);
}
//사용되었으므로 자신을 파괴
Destroy(gameObject);
}
}
마나 포션 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ManaPotion : MonoBehaviour, IItem
{
public float mana = 50;
public void Use(GameObject target)
{
//전달받은 게임 오브젝트로부터 PlayerStats 컴포넌트 가져오기
PlayerStats player = target.GetComponent<PlayerStats>();
//PlayerStats 컴포넌트가 있다면
if (player != null)
{
//체력 회복 실행
player.RestoreMana(mana);
}
//사용되었으므로 자신을 파괴
Destroy(gameObject);
}
}
회전 기능 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rotator : MonoBehaviour
{
public float rotationSpeed = 60f;
//해당 오브젝트가 제자리 회전하는 기능
void Update()
{
transform.Rotate(0f, rotationSpeed * Time.deltaTime, 0f);
}
}
포션 충돌 시 포션 먹는 기능 - PlayerStats 스크립트에 추가
//트리거와 충돌 시 - 데드존, 아이템 사용
private void OnTriggerEnter(Collider other)
{
if(other.tag == "Deadzone" && !dead)
{
Die();
Debug.Log("사망");
}
// 아이템과 충돌한 경우 해당 아이템을 사용하는 처리
else if (!dead)
{
//충돌한 상대방으로부터 IItem 컴포넌트 가져오기 시도
IItem item = other.GetComponent<IItem>();
//충돌한 상대방으로부터 IItem 컴포넌트를 가져오는 데 성공했다면
if (item != null)
{
//Use 메서드를 실행하여 아이템 사용
item.Use(gameObject);
//아이템 습득 소리 재생
//playerAudioPlayer.PlayOneShot(itemPickupClip);
}
}
}
ItemSpawner 구현 과정
1.하이어라키 > 빈 게임 오브젝트 생성 > ItemSpawner로 변경
2.ItemSpawner에 ItemSpawner 스크립트 할당
3.ItemSpawner 스크립트에 포션 프리팹, 플레이어 트랜스폼을 할당
4.아이템 생성 반경, 아이템 최소, 최대 생성 시점 설정
포션 구현 과정
1.예전에 구매한 Polygon - Dungeons에 포션 프리팹이 있어서 그것을 사용했다.
2.포션의 가시성을 높이는 빛, 파티클, 회전 기능은 골드 메탈님의 유튜브 영상을 보고 구현했다.
#포션 파티클, 라이트, 회전 기능 참고 링크
www.youtube.com/watch?v=u2DLOay5oO8
3.ObstacleSpawner
3.1.정의
- 게임 시작 시 랜덤한 수의 장애물을 배치하는 기능이다.
3.2.기획의도
- 맵의 통로에 장애물을 배치하여 동일한 맵에서 반복 플레이를 해도 지루하지 않고 익숙하면서 새로운 느낌을 주
#절차적 맵 생성으로 게임을 시작할 때마다 새로운 구조의 맵을 구현하고자 했으나 기술적인 문제로 실패하여
어떻게 하면 동일한 맵에서 반복 플레이를 해도 지루하지 않을까 고민하여 생각한 기능이다.
3.3.구성 요소
- 장애물 배치 포인트
- 맵의 특정 통로에 장애물을 배치하는 포인트
- 장애물 배치 포인트 기본값: 비활성화 - 장애물
- 플레이어, 적의 이동을 제한하는 오브젝트
3.4.기능 명세
3.4.1.장애물 배치 개수 랜덤 선택
- 게임 시작 시 장애물을 배치할 개수를 랜덤 선택하는 기능이다.
- 장애물 배치 개수는 0 ~ 전체 장애물 배치 포인트 개수에서 랜덤한 수를 선택한다.
3.4.2.장애물 배치 포인트 랜덤 선택
- 선택된 장애물 배치 개수만큼 랜덤하게 장애물 배치 포인트를 선택하는 기능이다.
- 장애물 배치 포인트를 선택할 때 중복없이 랜덤 선택한다.
- 선택된 장애물 배치 포인트를 제외하고 나머지 장애물 배치 포인트에서 랜덤 선택한다.
- 장애물 배치 포인트를 모두 선택했을 경우 선택된 장애물 배치 포인트를 활성화하여 장애물을 배치한다.
3.5.구현 과정
Obstacle Spawner 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstacleSpawner : MonoBehaviour
{
private int selectedNum;
public GameObject[] obstaclePoints; //모든 장애물 포인트들
private List<GameObject> allObstacles = new List<GameObject>(); //뽑기할 때 이용할 장애물 포인트들
public List<GameObject> selectedObstacles = new List<GameObject>(); //맵에 배치할 장애물 포인트들
void Start()
{
SaveObstacle();
SelectObstacle();
DeployObstacle();
}
//모든 장애물 포인트를 리스트에 저장, 뽑기 준비
void SaveObstacle()
{
for(int i = 0; i < obstaclePoints.Length; i++)
{
allObstacles.Add(obstaclePoints[i]);
}
}
void SelectObstacle()
{
//설치할 장애물 수 랜덤 선택하기
selectedNum = Random.Range(0, obstaclePoints.Length);
Debug.Log("장애물 배치할 숫자"+selectedNum);
//선택한 숫자만큼 중복 없이 장애물 포인트 선택하기
for( int i = 0; i < selectedNum; i++)
{
int select = Random.Range(0, allObstacles.Count);
Debug.Log("뽑은 번호" + select);
Debug.Log("배치할 장애물 번호"+allObstacles[select].name);
selectedObstacles.Add(allObstacles[select]);
allObstacles.RemoveAt(select); //RemoveAt(int 인덱스) 리스트 해당 인덱스의 데이터를 제거
}
}
//장애물 배치
void DeployObstacle()
{
for(int i = 0; i < selectedObstacles.Count; i++)
{
selectedObstacles[i].SetActive(true);
}
}
}
중복 없는 랜덤 선택 기능
아래의 참고 링크를 보고 배열과 리스트를 이용하여 중복 없는 랜덤 선택 기능을 구현했다.
중복 없는 랜덤 선택 기능 참고 링크
goraniunity2d.blogspot.com/2019/07/blog-post.html?m=1
1.하이어라키 > 빈 게임 오브젝트, ObstacleSpawner로 변경 > ObstacleSpawner 스크립트 할당 > 장애물 배치 포인트 할당
'게임 개발 > 유니티 게임 개발 일지' 카테고리의 다른 글
#19 게임 Title 화면 구현, 오클루전 컬링 적용 (0) | 2020.08.18 |
---|---|
#18 스킬 사용 가능UI, 추락사, 터치 시 콜라이더 무시, 전투 밸런스, 네비 메쉬 문제해결 (0) | 2020.08.12 |
#16 UIManager, GameManager, EnemySpawner 구현 (0) | 2020.08.09 |
#15 맵 구현하기 (0) | 2020.07.29 |
#14 순간이동 스킬 구현(마나 소비, 마나 자동 회복, 스킬재사용대기시간) (0) | 2020.07.24 |
댓글