1.적 객체 해당영역 랜덤 생성
private Vector3 RandomSpawn()
{
int maxAttempt = 3;
int curAttaempt = 0;
Vector3 playerPosition = GameManager.Instance._player.transform.position;
// 콜라이더의 사이즈를 가져오는 bound.size 사용
float range_X = SpawnArea.bounds.size.x;
float range_Z = SpawnArea.bounds.size.z;
Vector3 RandomPostion;
do
{
curAttaempt++;
range_X = UnityEngine.Random.Range((range_X / 2) * -1, range_X / 2);
range_Z = UnityEngine.Random.Range((range_Z / 2) * -1, range_Z / 2);
RandomPostion = new Vector3(range_X, 1f, range_Z);
}
while (curAttaempt < maxAttempt && 3.0f >= Vector3.Distance(RandomPostion, playerPosition));
Vector3 respawnPosition = RandomPostion;
return respawnPosition;
}
해당 코드를 이용하여 소환 좌표를 생성하고 해당 좌표가 플레이어의 위치와 너무 가까우면(3.0f 거리) 안에 생성되는경우
다시 시도하여 원하는 소환좌표를 생성하는 코드이며 총 3번까지 시도 후에도 거리가 가까우면 그 자리에 생성하는 코드이다.(무한 루프가 될 가능성이 있기에 시도횟수 설정)
2.Player Character StatHandler 연동
Player.cs의 Initilaize() 메서드 : 플레이어의 데이터를 초기화하는 메서드
public void Initialize()
{
//Model(UserData) 세팅
if (DataManager.Instance.LoadUserData() == null)
{
//새로하기 , 기본 능력치를 제공
userData = new UserData(DataManager.Instance.UserDB.GetByKey(TestID));
DataManager.Instance.SaveUserData(userData);
}
else
{
//이어하기
userData = new UserData(DataManager.Instance.LoadUserData());
}
statHandler = new StatHandler(StatType.Player);
statHandler.CurrentStat.iD = userData.UID;
statHandler.CurrentStat.health = userData.stat.health;
statHandler.CurrentStat.maxHealth = userData.stat.maxHealth;
statHandler.CurrentStat.atk = userData.stat.atk;
statHandler.CurrentStat.def = userData.stat.def;
statHandler.CurrentStat.moveSpeed = userData.stat.moveSpeed;
statHandler.CurrentStat.atkSpeed = userData.stat.atkSpeed;
statHandler.CurrentStat.reduceDamage = userData.stat.reduceDamage;
statHandler.CurrentStat.critChance = userData.stat.critChance;
statHandler.CurrentStat.critDamage = userData.stat.critDamage;
statHandler.CurrentStat.coolDown = userData.stat.coolDown;
//Controller(FSM 세팅)
playerStateMachine.ChangeState(playerStateMachine.IdleState);
}
StatHandler.cs -> 생성자에 등록된 메서드 PlayerStatConvert()
public static Stat PlayerStatConvert(int key)
{
Stat baseStat = new Stat();
// TODO : 플레이어 기본 스텟 수치 정보
// 현재 Temp
baseStat.iD = 0;
baseStat.health = new BigInteger(10);
baseStat.maxHealth = baseStat.health;
baseStat.atk = new BigInteger(10);
baseStat.def = new BigInteger(5);
baseStat.moveSpeed = 5f;
baseStat.atkSpeed = 5f;
baseStat.reduceDamage = 5f;
baseStat.critChance = 5f;
baseStat.critDamage = 5f;
baseStat.coolDown = 5f;
return baseStat;
}
생성자에서 기본 데이터값을 세팅후 , Player.cs에서 데이터값을 다시 초기화 하여 사용하고있다.
StatHandler는 런타임 중에 플레이어,Enemy 등 객체에서 계속 StatUpdate를 목적으로 만든 클래스이다.
3.투사체 부모클래스 생성
BaseProjectile.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BaseProjectile : MonoBehaviour
{
[SerializeField] protected float speed = 15f; //투사체 속도
[SerializeField] protected float hitOffset = 0f; //투사체 피격 오프셋
[SerializeField] protected bool UseFirePointRotation; //방향성 있는 투사체 각도 조정 bool 변수
[SerializeField] protected Vector3 rotationOffset = new Vector3(0, 0, 0); //투사체 각도 오프셋
[SerializeField] protected GameObject hit; // hit시 발생하는 이펙트 오브젝트
[SerializeField] protected ParticleSystem hitPS;//hit시 발생하는 파티클시스템
[SerializeField] protected GameObject flash; // 투사체의 Flash 효과
[SerializeField] protected Rigidbody rb;
[SerializeField] protected Collider col;
[SerializeField] protected Light lightSourse;
[SerializeField] protected GameObject[] Detached; //해당 투사체에 있는 파티클 컨테이너
[SerializeField] protected ParticleSystem projectilePS; //투사체 발사시 동작하는 파티클 시스템
private bool startChecker = false; //투사체가 현재 사용중인지 체크하는 변수
[SerializeField] protected bool notDestroy = false; //투사체가 파괴됬는지 체크하는 변수
public float knockbackPower;//투사체 넉백 파워
public Vector3 dir; //투사체가 발사되는 방향
public LayerMask TargetLayer; //해당 투사체를 맞추기 위한 타겟 레이어
protected virtual void Start()
{
if (!startChecker)
{
/*lightSourse = GetComponent<Light>();
rb = GetComponent<Rigidbody>();
col = GetComponent<Collider>();
if (hit != null)
hitPS = hit.GetComponent<ParticleSystem>();*/
if (flash != null)
{
flash.transform.parent = null;
}
}
if (notDestroy)
StartCoroutine(DisableTimer(5));
else
Destroy(gameObject, 5);
startChecker = true;
}
protected virtual IEnumerator DisableTimer(float time)
{
yield return new WaitForSeconds(time);
if (gameObject.activeSelf)
gameObject.SetActive(false);
yield break;
}
protected virtual void OnEnable()
{
if (startChecker)
{
if (flash != null)
{
flash.transform.parent = null;
}
if (lightSourse != null)
lightSourse.enabled = true;
col.enabled = true;
rb.constraints = RigidbodyConstraints.None;
}
}
protected virtual void FixedUpdate()
{
if (speed != 0)
{
rb.velocity = dir * speed;
}
}
private void DamageCaculate(GameObject hitObject)
{
ITakeDamageAble damageable = hitObject.GetComponent<ITakeDamageAble>();
//TODO :: 무적시간이 아닐때에도 조건에 추가해야됨
if (damageable != null)
{
damageable.TakeDamage(10);//매직넘버 (플레이어나 Enemy의 Stat값을 받아와서 적용 시켜야됨)
Vector3 directionKnockBack = hitObject.transform.position - transform.position;
damageable.TakeKnockBack(directionKnockBack, knockbackPower);
}
}
private void OnCollisionEnter(Collision collision)
{
if (TargetLayer == ((1 << collision.gameObject.layer) | TargetLayer))
{
Debug.Log($"공격이 {collision.gameObject.name}에 충돌");
DamageCaculate(collision.gameObject);
//Lock all axes movement and rotation
rb.constraints = RigidbodyConstraints.FreezeAll;
//speed = 0;
if (lightSourse != null)
lightSourse.enabled = false;
col.enabled = false;
projectilePS.Stop();
projectilePS.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
ContactPoint contact = collision.contacts[0];
Quaternion rot = Quaternion.FromToRotation(Vector3.up, contact.normal);
Vector3 pos = contact.point + contact.normal * hitOffset;
//Spawn hit effect on collision
if (hit != null)
{
hit.transform.rotation = rot;
hit.transform.position = pos;
if (UseFirePointRotation) { hit.transform.rotation = gameObject.transform.rotation * Quaternion.Euler(0, 180f, 0); }
else if (rotationOffset != Vector3.zero) { hit.transform.rotation = Quaternion.Euler(rotationOffset); }
else { hit.transform.LookAt(contact.point + contact.normal); }
hitPS.Play();
}
//Removing trail from the projectile on cillision enter or smooth removing. Detached elements must have "AutoDestroying script"
foreach (var detachedPrefab in Detached)
{
if (detachedPrefab != null)
{
ParticleSystem detachedPS = detachedPrefab.GetComponent<ParticleSystem>();
detachedPS.Stop();
}
}
if (notDestroy)
StartCoroutine(DisableTimer(hitPS.main.duration));
else
{
gameObject.SetActive(false);
}
ObjectPoolManager.Instance.GetPool("playerProjectile", Utils.POOL_KEY_PLAYERPROJECTILE).GetObject();
}
}
}
Player,Enemy 투사체를 사용하다보니 성질이 비슷하기에 전략패턴을 사용하여 리팩토링 하였다.