[240119]TIL

응징·2024년 1월 19일
0

TIL

목록 보기
19/36
post-thumbnail

개인공부

  • 알고리즘 코드카타
using System;
using System.Text;

public class Solution {
    public string solution(string s) {
        StringBuilder answer = new StringBuilder();
        int middleIndex = s.Length / 2;

        if(s.Length % 2 == 0)
        {
            answer.Append(s[middleIndex - 1]);
            answer.Append(s[middleIndex]);
        }
        else
        {
            answer.Append(s[middleIndex]);
        }

        return answer.ToString();
    }
}

StringBuilder는 문자열을 효율적으로 편집하기 위한 클래스로, 여러 개의 문자열을 하나로 결합하거나, 문자열을 추가하거나 수정할 때 사용

결론적으로 문자열을 이용하려고 할때 사용하기 유용하다. 대표적인 메소드는 아래와 같다

  • Append

    StringBuilder sb = new StringBuilder();
    sb.Append("Hello");
    sb.Append(" ");
    sb.Append("World");
    string result = sb.ToString(); // "Hello World"

    Append는 문자열을 끝에 추가하는 데 사용된다.

  • Insert

    StringBuilder sb = new StringBuilder("Hello");
    sb.Insert(5, " Beautiful"); // "Hello Beautiful"

특정 위치에 문자열을 삽입

  • Remove

    StringBuilder sb = new StringBuilder("Hello Beautiful");
    sb.Remove(6, 9); // "Hello"

    특정 위치에서부터 일정 길이의 문자열을 제거

  • Replace

    StringBuilder sb = new StringBuilder("Hello World");
    sb.Replace("World", "Universe"); // "Hello Universe"

특정 문자열을 다른 문자열로 대체

TopDownShooting 게임

주요 로직

마우스 방향에 따라 스크립트 반전

private void RotateArm(Vector2 direction)
    {
        float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

        armRenderer.flipY = Mathf.Abs(rotZ) > 90f;
        characterRenderer.flipX = armRenderer.flipY;
        armPivot.rotation = Quaternion.Euler(0, 0, rotZ);
    }
  • Vector2 direction은 마우스 백터 값을 가지고 오는 파라미터

float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

direction)의 아크탄젠트를 계산하고 이것을 라디안에서 각도로(90도,180 등) 변환

armRenderer.flipY = Mathf.Abs(rotZ) > 90f;

Mathf.Abs는 값의 절대값을 반환한다.
rotZ가 90도 보다 클 경우 flipY를 true로 하여 스프라이트를 뒤집는다.

CreateAssetMenu로 Datas생성

[CreateAssetMenu]
깔끔하게 데이터를 정리 수정하기 용이 해보인다.

  • CreateAssetMenu로 Datas생성
[CreateAssetMenu(fileName = "DefaultAttackData", menuName = "TopDownCharacterController/Attacks/Default", order = 0)]
public class AttackSO : ScriptableObject
{
    [Header("Attack Info")]
    public float size;
    public float delay;
    public float power;
    public float speed;
    public LayerMask target;

    [Header("Knock Back Info")]
    public bool isOnKnockback;
    public float knockbackPower;
    public float knockbackTime;
}
[CreateAssetMenu(fileName = "RangedAttackData", menuName = "TopDownCharacterController/Attacks/Ranged", order = 1)]
public class RangedAttackData : AttackSO
{
    [Header("Ranged Attack Data")]
    public string bulletNameTag;
    public float duration;
    public float spread;
    public int numberofProjectilesPerShot;
    public float multipleProjectilesAngel;
    public Color projectileColor; 
}
  • Datas를 포함한 클래스 생성
[Serializable]
public class CharacterStats
{
    public StatsChangeType statsChangeType;  
    [Range(1, 100)] public int maxHealth;
    [Range(1f, 20f)] public float speed;
    public AttackSO attackSO;
}
  • 해당 컴포넌트에서 데이터 받아오기
public CharacterStats CurrentStates { get; private set; }  

CreateAssetMenu로 생성한 Datas는 특별한 방식으로 가지고 와야 한다.

[CreateAssetMenu(fileName = "RangedAttackData", menuName = "TopDownCharacterController/Attacks/Ranged", order = 1)]

가지고 와야하는 위치는 CreateAssetMenu 생성할때 작성하는 듯.
[CreateAssetMenu("파일명",menuName = "생성 경로/ /", ?]

마지막 파라미터는 모르겠음

화살마다 일정한 각도 주기

private void OnShoot(AttackSO attackSO)
    {
        RangedAttackData rangedAttackData = attackSO as RangedAttackData;  //attackSO 하위인 RangedAttackData로 변환
        float projectilesAngleSpace = rangedAttackData.multipleProjectilesAngel;
        int numberOfProjectilesPerShot = rangedAttackData.numberofProjectilesPerShot;

        float minAngle = -(numberOfProjectilesPerShot / 2f) * 
            projectilesAngleSpace + 0.5f * rangedAttackData.multipleProjectilesAngel;


        for (int i = 0; i < numberOfProjectilesPerShot; i++)
        {
            float angle = minAngle + projectilesAngleSpace * i;
            float randomSpread = Random.Range(-rangedAttackData.spread, rangedAttackData.spread);
            angle += randomSpread;
            CreateProjectile(rangedAttackData, angle);
        }
    }

projectilesAngleSpace : 화살끼리 퍼져있는 정도의 값
numberOfProjectilesPerShot : 한번에 발사되는 화살 값

RangedAttackData rangedAttackData = attackSO as RangedAttackData;

attackSO 하위인 RangedAttackData로 변환

float minAngle = -(numberOfProjectilesPerShot / 2f)
projectilesAngleSpace + 0.5f
rangedAttackData.multipleProjectilesAngel;

첫 번째 프로젝타일의 각도를 계산 후 나머지 프로젝타일의 각도는 다중 프로젝타일 사이의 각도 간격과 프로젝타일 수를 고려하여 계산.

즉 화살의 각도는 projectilesAngleSpace 만큼 차이가 나게됨

그리고 for문에서 약간의 랜덤으로 일정한 간격이 안되게끔 함 (이 원리는 다른 함수도 뜯어봐야 해서 아직 모르겠음)

싱글톤

public static ProjectileManager instance;  //싱글톤

private void Awake()
    {
        instance = this;
    }

보통 게임매니저에서 자주 사용함.

  • static 클래스로 만들기
    그렇게 되면 굳이 이 클래스를 생성하지 않아도 여러곳에서 이 클래스에 직접 바로 가능.

  • ProjectileManager instance값을 Awake()로 초기화 하면 Start보다 먼저 우선으로 실행하기 때문에 Start 메소드에서 부터 사용가능

Getcomponent 하위 검사

GetComponentInChildren<//SpriteRenderer>(); //주석은 무시

해당 오브젝트에 컴포넌트 말고도 하위 컴포넌트까지 가져옴

ObjectPool

생성과 소멸이라는 비용이 큰 작업을 최소화하기 위한 로직
빈번하게 생성되고 파괴되는 객체를 여기서 저장하고 재사용함

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class ObjectPool : MonoBehaviour
{
  [System.Serializable]
  public struct Pool
  {
      public string tag;
      public GameObject prefab;  
      public int size;
  }

  public List<Pool> pools;
  public Dictionary<string, Queue<GameObject>> poolDictionary;

  private void Awake()
  {
      poolDictionary = new Dictionary<string, Queue<GameObject>>();  //큐에 넣어둠
      foreach (var pool in pools)
      {
          Queue<GameObject> objectPool = new Queue<GameObject>();
          for (int i = 0; i < pool.size; i++)
          {
              GameObject obj = Instantiate(pool.prefab);
              obj.SetActive(false);
              objectPool.Enqueue(obj); //생성한 오브젝트를 넣기
          }
          poolDictionary.Add(pool.tag, objectPool);
      }
  }

  public GameObject SpawnFromPool(string tag)
  {
      if (!poolDictionary.ContainsKey(tag))
          return null;

      GameObject obj = poolDictionary[tag].Dequeue();//재활용
      poolDictionary[tag].Enqueue(obj);  //재활용한거 다시 넣기

      return obj;
  }
}
 public void ShootBullet(Vector2 startPostiion, Vector2 direction, RangedAttackData attackData)
    {
        GameObject obj = objectPool.SpawnFromPool(attackData.bulletNameTag); //만든 obj 값

        obj.transform.position = startPostiion;  //obj 포지션 이곳으로 재정의
        RangedAttackController attackController = obj.GetComponent<RangedAttackController>();
        attackController.InitializeAttack(direction, attackData, this);

        obj.SetActive(true);
    }
  • Queue<//GameObject> : 게임오브젝트를 저장하는 큐 생성

  • 반복문으로 Pool객체의 프리팹 오브젝트 생성

  • 생성한 오브젝트를 큐에 넣음

  • SpawnFromPool에선 재활용 하는 오브젝트를 리턴함 그리고 재활용한 오브젝트를 다시 큐에 넣음

  • ShootBullet에서 리턴한 재활용 오브젝 포지션을 다시 플레이어 위치로 재정의

&

Shift + F12 : 참조 목록
Ctrl+ F : 검색

아쉽게도 오늘만에 못함 ಥ_ಥ

[아직 남은거]

  • 적 구현
  • 넉백 구현
  • 데미지 피격 구현
  • 파티클 생성

[일요일]

  • 사운드 컨트롤
  • UI 만들기
  • 로직 구현하기
  • 스텟 계산하기
  • 아이템
  • 로직 강화하기
  • 개인과제 만들기 시작

[월요일]

  • 이어서
profile
Unity 개발 위주로 정리합니다

0개의 댓글