2023.12.29 / 2024.01.02, 03, 04, 05 오늘의 기록 리마인드

2024. 1. 2. 17:35카테고리 없음

최종프로젝트 전 부족한 실력을 채우기 위해 다시 처음부터 복습을 하고자 한다.

 

1. 조건문과 반복문

 

If 조건문

if(true 조건이 되었을 때 )

{

중괄호 안의 내용이 실행된다.

}

else if(위의 if 조건 외의 다른 조건이 true가 되었을 때)

{

중괄호 안의 내용이 실행

}

else

{

위의 조건문들을 다 제외하고 나머지 조건일 때 실행

}

 

Switch 조건문

 

switch(조건이 되는 변수 / Enum을 사용하면 편리할 것 같다.)

{

 case 1:

실행되는 함수

break;

 

case 2:

실행되는 함수

break;

 

default:

실행되는 함수

break;

}

 

case 1,2,3 에 해당하는 숫자들 대신에 변수들을 넣고 싶다면

enum을 사용하여

namespace ConsoleApp6
{
    internal class Program
    {
        enum Season
        {
            Spring,
            Summer,
            Autumn,
            Winter
        }
        void SeasonIssue(Season stage)
        {
            switch (stage)
            {
                case Season.Spring:
                    Console.WriteLine("봄"); //봄Scene으로 SceneManager.LoadScene("Spring");
                    break;
                case Season.Summer:
                    Console.WriteLine("여름");
                    break;
                    case Season.Autumn:
                    Console.WriteLine("가을");
                    break;
                case Season.Winter:
                    Console.WriteLine("겨울");
                    break;
            }
        }    
    }
}

 

반복문 For, While, Do-While ,Foreach

 

for(int i = 0, i < 10, i++)

{

i가 0부터 9까지 반복할 코드를 작성한다.

}

---------------------------------------------------------

int i = 0

while(i < 10)

{

i가 0에서 9까지 반복할 코드를 작성한다.

i++ 반복을 돌면서 i가 10이 되면 반복을 멈추도록 종료지점을 정확하게 해준다.

}

---------------------------------------------------------

int i = 11;

do
{
// 원래는 11이 10보다 크기 때문에 싱행되면 안됩니다.
    // 하지만 do while 에서는 무조건 한번은 실행됩니다.
    // 11을 한번 출력하고 종료됩니다.
Console.WriteLine(i);  
i++;       
}
while(i <= 10)  

---------------------------------------------------------

static void Main(string[] args)
{
string[] games = new string[3] { "League of Legends", "메이플 스토리", "디아블로" };
foreach (string gameTitle in games)
   {
        // 0번째 반복시 gameTitle : League of Legends
        // 1번째 반복시 gameTitle : 메이플 스토리
        // 2번째 반복시 gameTitle : 디아블로
        Console.WriteLine(gameTitle);
    }
}

실행결과
League of Legends
메이플 스토리
디아블로

 

2. 배열과 컬렉션

Array

string[] game = new game[3]; //Array의 사용형태
game[0] = "Leage of Legends"
game[1] = "메이플 스토리"

Console.WriteLine(game[0]);  // 출력 - Leage of Legends
Console.WriteLine(game[1]);  // 출력 - 메이플 스토리

 

컬렉션

List

List <string> game = new List<string>(); //List의 사용형태

game.Add( "Leage of Legends" ); // 리스트에 데이터 추가

game.Add( "메이플 스토리" );

game.Add( "디아블로" );

game.Remove( "메이플 스토리" ); // 리스트에서 데이터 삭제

foreach(int number in numbers) // 리스트 데이터 출력

{

Console.WriteLine(number);

}

 

    1. Dictionary
    • 딕셔너리(Dictionary)는 키와 값으로 구성된 데이터를 저장
    • 딕셔너리는 중복된 키를 가질 수 없으며, 키와 값의 쌍을 이루어 데이터를 저장
    using System.Collections.Generic;
    
    Dictionary<string, int> scores = new Dictionary<string, int>(); // 빈 딕셔너리 생성
    scores.Add("Alice", 100); // 딕셔너리에 데이터 추가
    scores.Add("Bob", 80);
    scores.Add("Charlie", 90);
    scores.Remove("Bob"); // 딕셔너리에서 데이터 삭제
    
    foreach(KeyValuePair<string, int> pair in scores) // 딕셔너리 데이터 출력
    {
        Console.WriteLine(pair.Key + ": " + pair.Value);
    }
    
    1. Stack
    • Stack은 후입선출(LIFO) 구조를 가진 자료 구조
  • Stack<int> stack1 = new Stack<int>();  // int형 Stack 선언
    
    // Stack에 요소 추가
    stack1.Push(1);
    stack1.Push(2);
    stack1.Push(3);
    
    // Stack에서 요소 가져오기
    int value = stack1.Pop(); // value = 3 (마지막에 추가된 요소)
    
    1. Queue
    • Queue는 선입선출(FIFO) 구조를 가진 자료 구조
  • Queue<int> queue1 = new Queue<int>(); // int형 Queue 선언
    
    // Queue에 요소 추가
    queue1.Enqueue(1);
    queue1.Enqueue(2);
    queue1.Enqueue(3);
    
    // Queue에서 요소 가져오기
    int value = queue1.Dequeue(); // value = 1 (가장 먼저 추가된 요소)
    
      1. HashSet
      • HashSet은 중복되지 않은 요소들로 이루어진 집합
      HashSet<int> set1 = new HashSet<int>();  // int형 HashSet 선언
      
      // HashSet에 요소 추가
      set1.Add(1);
      set1.Add(2);
      set1.Add(3);
      
      // HashSet에서 요소 가져오기
      foreach (int element in set1)
      {
          Console.WriteLine(element);
      }
      

3. 메서드와 구조체

함수를 만들 때는 아래와 같은 형식을 가집니다.

Void - 반환값이 없다.

자료형(int, string, float) - 반환값이 있다.

매개변수 - 함수 내에서 사용되는 변수

// 예시 1: 반환 값이 없는 메서드
public void SayHello()
{
    Console.WriteLine("안녕하세요!");
}

// 예시 2: 매개변수가 있는 메서드
public void GreetPerson(string name)
{
    Console.WriteLine("안녕하세요, " + name + "님!");
}

// 예시 3: 반환 값이 있는 메서드
public int AddNumbers(int a, int b)
{
    int sum = a + b;
    return sum;
}

 

메서드 오버로딩(같은 함수명 다른 매개변수 사용)

void PrintMessage(string message)
{
    Console.WriteLine("Message: " + message);
}

void PrintMessage(int number)
{
    Console.WriteLine("Number: " + number);
}

// 메서드 호출
PrintMessage("Hello, World!");  // 문자열 매개변수를 가진 메서드 호출
PrintMessage(10);  // 정수 매개변수를 가진 메서드 호출

 

구조체란? 여러 개의 데이터를 묶어서 하나의 사용자 정의 형식으로 만들기 위한 방법

함수 내에 자료형을 여러개 사용하고 싶을 때 struct 사용

구조체의 멤버에는 접근할 때 . 연산자를 사용합니다.

 

 

4. 클래스와 객체

클래스 기본 구조

class Person 클래스명 선언
{
    public string Name; 필드에 클래스에서 사용 될 변수를 선언
    public int Age;

    public void PrintInfo() 메서드(함수) 생성
    {
        Console.WriteLine("Name: " + Name);
        Console.WriteLine("Age: " + Age);
    }
}

 

메서드를 호출하기 위해서는 해당 메서드가 속해 있는 클래스의 인스턴스를 생성(붕어빵을 만든다.)
Person p = new Person();
p.Name = "John";
p.Age = 30;
p.PrintInfo(); // 출력: Name: John, Age: 30

  • 구조체는 값 형식이며, 스택에 할당되고 복사될 때 값이 복사됩니다.
  • 클래스는 참조 형식이며, 힙에 할당되고 참조로 전달되므로 성능 측면에서 다소 차이가 있습니다.

 

class Person
{
    public string Name;         // 외부에서 자유롭게 접근 가능
    private int Age;           // 같은 클래스 내부에서만 접근 가능
    protected string Address;  // 같은 클래스 내부와 상속받은 클래스에서만 접근 가능
}

  • 프로퍼티란?
    • 프로퍼티는 클래스 멤버로서, 객체의 필드 값을 읽거나 설정하는데 사용되는 접근자(Accessor) 메서드의 조합입니다.
    • get 접근자는 프로퍼티의 값을 반환하고, set 접근자는 프로퍼티의 값을 설정합니다.

자동 프로퍼티를 사용한 예시

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Person person = new Person();
person.Name = "John";     // 값을 설정
person.Age = 25;          // 값을 설정

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // 값을 읽어 출력

 

 

5. 상속과 다형성(전혀 이해안됌)

상속의 개념

  • 상속은 기존의 클래스(부모 클래스 또는 상위 클래스)를 확장하거나 재사용하여 새로운 클래스(자식 클래스 또는 하위 클래스)를 생성하는 것입니다
// 부모 클래스
public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Eat()
    {
        Console.WriteLine("Animal is eating.");
    }

    public void Sleep()
    {
        Console.WriteLine("Animal is sleeping.");
    }
}

// 자식 클래스
public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("Dog is bark.");
    }
}

public class Cat : Animal
{
    public void Sleep()
    {
        Console.WriteLine("Cat is sleeping.");
    }

    public void Meow()
    {
        Console.WriteLine("Cat is meow.");
    }
}

// 사용 예시
Dog dog = new Dog();
dog.Name = "Bobby";
dog.Age = 3;

dog.Eat();      // Animal is eating.
dog.Sleep();    // Animal is sleeping.
dog.Bark();     // Dog is barking

Cat cat = new Cat();
cat.Name = "KKami";
cat.Age = 10;

cat.Eat();
cat.Sleep();
cat.Meow();

동물이라는 부모클래스를 강아지와 고양이 자식클래스에 상속을 시켜 새로운 함수를 생성한다.

 

무브라는 부모클래스에 사람과 몬스터 자식클래스를 상속시켜 움직임을 구현하는데, 추가로 달리기와 점프 함수를 생성할 수 있는 방법이다.

 

가상(Virtual) 메서드

  • 가상 메서드는 기본적으로 부모 클래스에서 정의되고 자식 클래스에서 재정의할 수 있는 메서드입니다
  • 가상 메서드는 virtual 키워드를 사용하여 선언되며, 자식 클래스에서 필요에 따라 재정의될 수 있습니다.
  • 이를 통해 자식 클래스에서 부모 클래스의 메서드를 변경하거나 확장할 수 있습니다.
public class Unit
{
    public virtual void Move()
    {
        Console.WriteLine("두발로 걷기");
    }

    public void Attack()
    {
        Console.WriteLine("Unit 공격");
    }
}

public class Marine : Unit
{

}

public class Zergling : Unit
{
    public override void Move()
    {
        Console.WriteLine("네발로 걷기");
    }
}

유닛이라는 클래스에 함수는 무브와 어택이 있는데, 이중에서 Move() 함수는 두발로 걷기를 실행하게 되는데, 유닛 클래스를 Zergling 클래스에 상속시켜 virtual로 쓰여진 함수에 override 덮어씌우는 것이다.

// 사용 예시
// #1 참조형태와 실형태가 같을때
Marine marine = new Marine();
marine.Move();
marine.Attack();

Zergling zergling = new Zergling();
zergling.Move();
zergling.Attack();

// #2 참조형태와 실형태가 다를때
List<Unit> list = new List<Unit>();
list.Add(new Marine());
list.Add(new Zergling());

foreach (Unit unit in list)
{
    unit.Move();
}

marine.Move() 는 "두발로 걷기"

zergling.Move() 는 "네발로 걷기"

 

#2 참조형태와 실형태가 다를때...?

Unit 이라는 List를 만들어주고 마린과 저글링을 추가한다.

foreach 반복문을 사용하여 list에서 unit 들을 사용할 것인데, unit.Move() 함수를 실행한다.

고급 문법 및 기능(제너릭 / out ref 키워드)

제너릭

  • 제너릭은 클래스나 메서드를 일반화시켜 다양한 자료형에 대응할 수 있는 기능입니다.
  • 제너릭을 사용하면 코드의 재사용성을 높일 수 있습니다.
  • C#에서는 <T> 형태의 키워드를 이용하여 제너릭을 선언합니다.
  • 제너릭 클래스나 메서드에서 사용할 자료형은 선언 시점이 아닌 사용 시점에 결정됩니다.
  • 제너릭 클래스나 메서드를 사용할 때는 <T> 대신 구체적인 자료형을 넣어줍니다.
// 제너릭 클래스 선언 예시
class Stack<T>
{
    private T[] elements;
    private int top;

    public Stack()
    {
        elements = new T[100];
        top = 0;
    }

    public void Push(T item)
    {
        elements[top++] = item;
    }

    public T Pop()
    {
        return elements[--top];
    }
}

// 제너릭 클래스 사용 예시
Stack<int> intStack = new Stack<int>();
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
Console.WriteLine(intStack.Pop()); // 출력 결과: 3

제너릭 싱글톤 사용법

 

out, ref 키워드

  • out, ref 키워드는 메서드에서 매개변수를 전달할 때 사용합니다.
  • out 키워드는 메서드에서 반환 값을 매개변수로 전달하는 경우에 사용합니다.
  • ref 키워드는 메서드에서 매개변수를 수정하여 원래 값에 영향을 주는 경우에 사용합니다.
  • out, ref 키워드를 사용하면 메서드에서 값을 반환하는 것이 아니라, 매개변수를 이용하여 값을 전달할 수 있습니다.
// out 키워드 사용 예시
void Divide(int a, int b, out int quotient, out int remainder)
{
    quotient = a / b;
    remainder = a % b;
}

int quotient, remainder;
Divide(7, 3, out quotient, out remainder);
Console.WriteLine($"{quotient}, {remainder}"); // 출력 결과: 2, 1

// ref 키워드 사용 예시
void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}"); // 출력 결과: 2, 1
  1. 값의 변경 가능성: ref 매개변수를 사용하면 메서드 내에서 해당 변수의 값을 직접 변경할 수 있습니다. 이는 예기치 않은 동작을 초래할 수 있으므로 주의가 필요합니다.
  2. 성능 이슈: ref 매개변수는 값에 대한 복사 없이 메서드 내에서 직접 접근할 수 있기 때문에 성능상 이점이 있습니다. 그러나 너무 많은 매개변수를 **ref**로 전달하면 코드의 가독성이 떨어지고 유지보수가 어려워질 수 있습니다. 적절한 상황에서 **ref**를 사용하는 것이 좋습니다.
  3. 변수 변경 여부 주의: out 매개변수는 메서드 내에서 반드시 값을 할당해야 합니다. 따라서 out 매개변수를 전달할 때 해당 변수의 이전 값이 유지되지 않으므로 주의해야 합니다.

6. 인터페이스와 열거형(인터페이스 모르겠음)

열거형(Enum)

enum MyEnum
{
    Value1,
    Value2,
    Value3
}

 

열거형 사용

MyEnum myEnum = MyEnum.Value1;

 

열거형 형변환

int intValue = (int)MyEnum.Value1;  // 열거형 값을 정수로 변환
MyEnum enumValue = (MyEnum)intValue;  // 정수를 열거형으로 변환

 

복습 switch문에서 열거형 사용방법

// 월 열거형
public enum Month
{
    January = 1,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}

// 처리하는 함수
static void ProcessMonth(int month)
{
    if (month >= (int)Month.January && month <= (int)Month.December)
    {
        Month selectedMonth = (Month)month;
        Console.WriteLine("선택한 월은 {0}입니다.", selectedMonth);
        // 월에 따른 처리 로직 추가
    }
    else
    {
        Console.WriteLine("올바른 월을 입력해주세요.");
    }
}

// 실행 예제
static void Main()
{
    int userInput = 7; // 사용자 입력 예시
    ProcessMonth(userInput);
}

 

 

7. 예외처리 및 값형과 참조형(예외처리 이해못함)

  • 1) 값형(Value Type)
    • 값형은 변수에 값을 직접 저장합니다.
    • int, float, double, bool 등의 기본 데이터 타입들이 값형에 해당합니다.   

 

struct MyStruct
{
    public int Value; // int 형태의 Value 선언
}

MyStruct struct1 = new MyStruct(); //MyStruct 구조체에 struct1 이라는 객체를 생성
struct1.Value = 10; //struct1 객체에 int 형 Value 값을 10으로 초기화

MyStruct struct2 = struct1; //struct2라는 객체 생성과 동시에 struct1 값을 복사

struct2.Value = 20; //struct2의 객체 값을 20으로 초기화

Console.WriteLine(struct1.Value); // 출력 결과: 10

 

  • 2) 참조형(Reference Type)
  • 참조형은 변수가 데이터에 대한 참조(메모리 주소)를 저장합니다.
  • 클래스, 배열, 인터페이스 등이 참조형에 해당합니다.

class MyClass
{
    public int Value;
}

MyClass obj1 = new MyClass();
obj1.Value = 10;

MyClass obj2 = obj1; // obj2는 obj1과 동일한 객체를 참조

obj2.Value = 20;

Console.WriteLine(obj1.Value); // 출력 결과: 20

8. 델리게이트 람다 및 LINQ

 

9. 알고리즘

 

10. 인풋시스템

New Input System

Input Action: 입력 행동을 정의합니다. 예를 들어 "점프", "공격" 등의 행동을 정의하고, 이러한 행동을 트리거하는 키 또는 버튼을 지정할 수 있습니다.

캐릭터 컨트롤러 스크립트 작성(Action<> 개념활용)

 

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


public class TopDownCharacterController : MonoBehaviour
{
    public event Action<Vector2> OnMoveEvent;  //이동관련 이벤트
    public event Action<Vector2> OnLookEvent;  //카메라 시야 관련 이벤트


    public void CallMoveEvent(Vector2 direction)
    {
        OnMoveEvent?.Invoke(direction);
    }

    public void CallLookEvent(Vector2 direction)
    {
        OnLookEvent?.Invoke(direction);
    }

}

PlayerInputController 스크립트 만들기
 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerInputController : TopDownCharacterController
// TopDownCharacterController를 상속시켜서 플레이어 인풋 컨트롤러 사용
{
    private Camera _camera;
    private void Awake()
    {
        _camera = Camera.main;
    }

    public void OnMove(InputValue value)
    {
        // Debug.Log("OnMove" + value.ToString());
        Vector2 moveInput = value.Get<Vector2>().normalized;
        CallMoveEvent(moveInput);
    }

    public void OnLook(InputValue value) //마우스 커서에 따라서 카메라가 따라가도록
    {
        // Debug.Log("OnLook" + value.ToString());
        Vector2 newAim = value.Get<Vector2>();
        Vector2 worldPos = _camera.ScreenToWorldPoint(newAim); //월드포인트 스크린을 기준으로 에임을 설정
        newAim = (worldPos - (Vector2)transform.position).normalized; //normalized 단위길이를 곱해준다

        if (newAim.magnitude >= .9f)
// Vector 값을 실수로 변환
        {
            CallLookEvent(newAim);
        }
    }

    public void OnFire(InputValue value)
    {
        Debug.Log("OnFire" + value.ToString());
    }
}
 
TopDownMovement 스크립트 만들기
 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TopDownMovement : MonoBehaviour
{
    private TopDownCharacterController _controller;

    private Vector2 _movementDirection = Vector2.zero;
    private Rigidbody2D _rigidbody;

    private void Awake()
    {
        _controller = GetComponent<TopDownCharacterController>();
        _rigidbody = GetComponent<Rigidbody2D>();
    }

    private void Start()
    {
        _controller.OnMoveEvent += Move;
    }

    private void FixedUpdate()
    {
        ApplyMovment(_movementDirection);
    }

    private void Move(Vector2 direction)
    {
        _movementDirection = direction;
    }

    private void ApplyMovment(Vector2 direction)
    {
        direction = direction * 5;

        _rigidbody.velocity = direction;
    }
}
 
 

위의 내용들을 강의를 복습하며 기억나지 않는 부분들을 앞으로 채우고자 한다.

 

튜터님의 " 실전 프로젝트를 임하는 자세" 영상을 보고나서 느낀점이 내가 강의 내용에 대한 이해를 전혀 못하고 있는 상태라는 것이다. 숙련 주차나 심화 주차 강의를 파기보다는 다시 기초로 돌아가서 공부하는 걸 시작하는 것이 코딩에 더욱 도움이 될 것 같다.