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 투사체를 사용하다보니 성질이 비슷하기에 전략패턴을 사용하여 리팩토링 하였다.

 

+ Recent posts