<회의 사항>

  1. 오류수정 및 클래스 정리
  2. 추가 구현
  3. 콘솔 꾸미기

 

  • 오류

- 몬스터 랜덤값을 불러왔을 때, 같은 몬스터 동시 타겟팅이 되는 현상.- 장비 착용 시 옵션이 반영되지 않는 현상.- 데미지를 입었을 때, 방어력이 적용되지 않는 현상.

 

몬스터의 경우, 리스트에 들어있는 몬스터를 불러왔을 때 객체를 새로 생성해주지 않아서 그런 것이었다. 생성자를 통해 몬스터 객체를 새로 생성해주었다.

public static List<Monster> RandomMonsters()
{
    List<Monster> monsters = new List<Monster>
    {
        new Monster("변이 쥐", 100, 100, 1, true),
        new Monster("다친 경비 견", 100, 100, 1, true),
        new Monster("실험체 태아", 1, 20, 1, true),
        new Monster("슬라임", 1, 10, 1, true)
    };

    List<Monster> selectedMonsters = new List<Monster>();//load list
    Random random = new Random();//random

    int maxMonsters = random.Next(1, monsters.Count); //반복문에 사용될 몬스터 생성 최댓값 랜덤 생성

    for (int i = 0; i < maxMonsters; i++)
    {
        Monster selectedMonster = monsters[random.Next(monsters.Count)];
        selectedMonsters.Add(new Monster(selectedMonster));
    }
    return selectedMonsters;
}

 

장비 옵션이 적용되지 않는 이유는 스탯창에서 더해주기만 하고 그 값을 전투 때 불러오지 않았다. 방어력 역시 스탯창에 구현만 해두고 값을 가져오지 않아서 그런 것이었다.

 

  • 콘솔 꾸미기
// 윈도우 콘솔창 크기 고정
Console.SetWindowSize(x, y);

// 커서 위치
Console.SetCursorPosition(10,10);

// 커서 없애기
Console.CursorVisible = false;

 

저번에 올렸던 콘솔 배경과 글씨 등의 색을 바꾸는 방법과 콘솔창 크기 고정 및 커서 관련 메서드를 통해 콘솔창을 꾸몄다.

게임 시작화면의 로고가 가운데 오도록 마진값과 텍스트길이로 로고가 가운데 오도록 한 뒤, 로고가 빨강, 노랑, 초록, 파랑, 마젠타 색 순으로 반복하여 변하도록 했다.

 

<마치며>

이번 프로젝트를 진행하며 클래스와 객체에 대한 이해도가 매우 낮다는 사실을 깨달았다.

공부할 때는 분명 이해한 것 같았는데 막상 남에게 설명하려하니 말문이 막혔다. 객체는 알면 알 수록 더 모르겠다. 이 부분은 주말에 좀 더 공부해보아야 할 것 같다.

// 일반 클래스
public class 사람 {
    // ====== 사람 개개인의 특징을 나타낼 수 있는 것 = 상태 = 변수 ======
    public string name;
    public int age;
    public List<아이템> inventory;
    // ==================================================================

    // ====== 사람 만드는 생성자 ======
    // 자...객체가 만들어질거야...매개변수들 초기화해서 준비해...
    public 사람(string name, int age) {
        this.name = name;
        this.age = age;
        inventory = new List<아이템>();
    }
    // ================================


    // ====== 사람이라면 할 수 있는 기능 = 함수 = 메소드 ======
    public void 숨쉬기() {
        // 대충 숨쉬는 코드;
    }
    public void 걷기() {
        // 대충 걷는 코드;
    }
    public void 달리기() {
        // 대충 달리는 코드;
    }
    // ========================================================
}

// 정적 클래스와 객체 생성
public static class 조물주 
{
	// 객체 생성
    public static List<사람> 세계사람목록 = new List<사람>();
    public static 사람 사람만들기(string name, int age) {
        사람 새로운사람 = new 사람(name, age);
        세계사람목록.Add(새로운사람);
        return 새로운사람;
    }
    public static void 사람죽이기(사람 죽일사람) {
        세계사람목록.Remove(죽일사람);
    }
    public static int 세계인구() {
        return 세계사람목록.Count;
    }
    public static void 타노스() {
        for (int i = 0; i < 세계사람목록.Count / 2; i++) {
            사람죽이기(세계사람목록[i]);
        }
    }
}

// 정적 클래스
public static class Game {
    // 게임을 실행하면 여기서부터 실행됩니다.
    public static void Main() {
        조물주.사람만들기("김철수", 20);
        조물주.사람만들기("박개발", 21);
        조물주.사람만들기("이새발", 22);
        Console.WriteLine("세계 인구: " + 조물주.세계인구());          // 콘솔에 "세계 인구: 3" 이 출력됨.
        조물주.사람만들기("박엘리자베스", 10232);
        조물주.타노스();
        Console.WriteLine("세계 인구: " + 조물주.세계인구());          // 콘솔에 "세계 인구: 2" 가 출력됨.

        조물주.세계사람목록[0].숨쉬기();
        조물주.세계사람목록[0].달리기();

    }
}
  1. 클래스는 상태와 동작을 가집니다.
  2. 예제에서는 사람의 개성을 나타내는 name, age, inventory가 상태입니다.
  3. 모든 사람이 공통적으로 가지는 동작인 숨쉬기(), 걷기(), 달리기()가 동작입니다.
  4. 클래스는 그 자체로 값이 되지 않고 이를 바탕으로 만들어진 객체가 실제 값이다.
  5. 예제에서는 조물주의 사람만들기 기능을 통해 네 명의 사람을 만듭니다.
  6. 사람 그 자체는 실존하는 값이 아니고, 김철수, 박개발, 이새발, 박엘리자베스가 실존하는 값입니다.
  7. 조물주는 단 하나만 존재해도 되는, 기능만을 담당하는 클래스 => 정적클래스
  8. Game에서 조물주의 개체를 생성하지는 않습니다. 클래스로부터 바로 함수를 참조할 수 있습니다.
  9. static class 내부에 있는 모든 변수, 함수는 static으로 선언됩니다.

출처 <김세진님> 예시 감사합니다...

 

 

<회의 사항>

1. 게임 기획(스토리, 구현 요소 등)

2. 구상 및 역할 분배

3. 깃 숙지!!!

 

  • Wireframe

  • 역할 분배

스토리 기획 + 상점 및 장비 구현(2인) / 이벤트방 구현(1인) / 전투 구현(2인)

 

<이번 목표>

1. C# 문법에 익숙해지기

2. 역할 분담과 기획

3. 협업 및 소통(팀원들과 친해지기)

 

기존의 개인과제 파일에 던전만 추가하는 형식으로 작업하고, 여유가 된다면 아이템에 대한 여러 기능이나 음악 등을 추가하기로 했다. 팀원 한 명씩 과제 결과물을 확인해본 결과, 내 작업에 덧붙여 작업하기로 했다. 팀프로젝트가 처음이라 깃 사용이 익숙하지 않은 분들이 있어 테스트용 레포지토리에 돌아가며 커밋해보는 시간을 가졌으며, 스토리는 대략적인 컨셉만 짠 뒤, 한 분이 월요일까지 세세하게 짜오기로 했다.

전체적으로 아직 문법에 익숙하지 않아 걱정이 됐지만 최대한 구현할 수 있는대로 해보고 협업하는 것 또한 경험으로 얻어가는 시간이 되었으면 한다.

Nullable 형

null은 아무것도 없다, 즉 참조를 하지 않았다는 뜻이다.

값형은 원래 null을 가질 수 없다. 그러나 값형에 Nullable을 달면 null이라고 하는 값을 사용할 수 있게 된다. 주로 값형 변수가 null인지 아닌지를 구분해야 하는 경우에 사용된다.

// Nullable 형식 변수 선언
int? nullableInt = null;
double? nullableDouble = 3.14;
bool? nullableBool = true;

// 값 할당 및 접근
nullableInt = 10;
int intValue = nullableInt.Value;

// null 값 검사
if (nullableDouble.HasValue)
{
    Console.WriteLine("nullableDouble 값: " + nullableDouble.Value);
}
else
{
    Console.WriteLine("nullableDouble은 null입니다.");
}

// null 병합 연산자 사용
// nullableInt ?? 0과 같이 사용되며, nullableInt가 null이면 0을 반환합니다.
int nonNullableInt = nullableInt ?? 0;
Console.WriteLine("nonNullableInt 값: " + nonNullableInt);

 

 

문자열 빌더(StringBuilder)
  • 문자열 조작

문자열들을 내부적인 버퍼에 넣어 놓고 받아서 조합만 해놨다가 필요할 때 문자열로 만들어준다.

Append(), Insert(), Replace(), Remove() 등 다양한 메서드를 치환할 수 있다.

  • 가변성

내부 버퍼를 사용하여 크기가 동적으로 변할 수 있다. 추가했다 뺐다 바꾸기도 가능하며 추가적인 메모리 할당을 해서 가변적으로 길이를 조절할 수 있다.

  • 효율적인 메모리 관리

주요 메서드

Append: 뒤에 문자열 추가

Insert: 지정한 위치에 문자열 삽입

Remove: 지정한 위치에서 문자열 제거

Replace: 문자열의 일부를 다른 문자열로 대체

Clear: StringBuilder의 내용을 모두 지운다.

StringBuilder sb = new StringBuilder();

// 문자열 추가
sb.Append("Hello");
sb.Append(" ");
sb.Append("World");

// 문자열 삽입
sb.Insert(5, ", ");

// 문자열 치환
sb.Replace("World", "C#");

// 문자열 삭제
sb.Remove(5, 2);

// 완성된 문자열 출력
string result = sb.ToString();
Console.WriteLine(result);

 

 

 예외 처리

예외 처리는 프로그램이 갑작스럽게 종료 되거나 멈추는 상황을 예방하고 안정적으로 유지할 수 있도록 구현해준다.

 

예외 처리 구현

try에 예외가 발생할 수 있는 코드를 작성하고 catch에 예외 처리를 한다.

catch문은 위에서부터 순서대로 실행된다. 다중으로 붙일 수 있어 여러 가지 예외 처리를 할 수 있고, 예외 객체를 받아올 수 있어 예외 처리에 발생한 여러 정보들을 표시할 수 있다.

마지막으로 finally은 예외와 상관 없이 무조건 실행되는 코드이다. 필요하지 않다면 생략 가능하다.

try
{
    // 예외가 발생할 수 있는 코드
}
catch (ExceptionType1 ex)
{
    // ExceptionType1에 해당하는 예외 처리
}
catch (ExceptionType2 ex)
{
    // ExceptionType2에 해당하는 예외 처리
}
finally
{
    // 예외 발생 여부와 상관없이 항상 실행되는 코드
}

 

사용자 정의 예외

사용자가 필요에 따라 자신만의 예외 클래스를 작성할 수 있다.

Exception 클래스를 상속받아 작성한다.

public class NegativeNumberException : Exception
{
    public NegativeNumberException(string message) : base(message)
    {
    }
}
internal class Program
    {
    static void Main(string[] args)
    {
        try
        {
            int number = -10;
            if (number < 0)
            {
                throw new NegativeNumberException("음수는 처리할 수 없습니다.");
            }
        }
        catch (NegativeNumberException ex)
        {
            Console.WriteLine(ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("예외가 발생했습니다: " + ex.Message);
        }
    }
}

 

예외 처리 할 때는 가능한 구체적인 예외 클래스를 사용하는 것이 코드가 안정적이고 예외 처리에 대한 처리가 더욱 정확해진다.

if else문으로 예외를 처리하면 되지 않나 생각이 들었었는데 이 때문에 예외 처리를 사용한다는 것을 깨달았다.

 

 

값형과 참조형

값형(Value Type)

변수에 값을 직접 저장한다. 변수가 독립적으로 데이터를 가지며 어딘가에 할당하거나 대입 해야할 때 값을 복사해서 준다.

예) int, float, double, bool 등

struct MyStruct
{
    public int Value;
}

MyStruct struct1 = new MyStruct();
struct1.Value = 10;

MyStruct struct2 = struct1; // struct2는 struct1의 값 복사

struct2.Value = 20;

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

 

 

참조형(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

 

 

박싱과 언박싱

박싱은 값형을 참조형으로 변환한다. 언박싱은 박싱으로 참조형이었던 값형을 다시 값형으로 돌려놓는다.

 

object 형식은 직,간접적으로 모든 클래스의 최상위 클래스이다.

// 값형
int x = 10;
int y = x;
y = 20;
Console.WriteLine("x: " + x); // 출력 결과: 10
Console.WriteLine("y: " + y); // 출력 결과: 20

// 참조형
int[] arr1 = new int[] { 1, 2, 3 };
int[] arr2 = arr1;
arr2[0] = 4;
Console.WriteLine("arr1[0]: " + arr1[0]); // 출력 결과: 4
Console.WriteLine("arr2[0]: " + arr2[0]); // 출력 결과: 4

// 박싱과 언박싱
int num1 = 10;
object obj = num1; // 박싱
int num2 = (int)obj; // 언박싱
Console.WriteLine("num1: " + num1); // 출력 결과: 10
Console.WriteLine("num2: " + num2); // 출력 결과: 10
List<object> myList = new List<object>();

// 박싱: 값 형식을 참조 형식으로 변환하여 리스트에 추가
int intValue = 10;
myList.Add(intValue); // int를 object로 박싱하여 추가

float floatValue = 3.14f;
myList.Add(floatValue); // float를 object로 박싱하여 추가

// 언박싱: 참조 형식을 값 형식으로 변환하여 사용
int value1 = (int)myList[0]; // object를 int로 언박싱
float value2 = (float)myList[1]; // object를 float로 언박싱

값형을 참조형으로 변환할 때, 값형이 사라지는 것이 아니라 남아있고 값형에 대한 참조형으로 변환되어 새롭게 받아지는 것이다. 그렇기 때문에 너무 많은 박싱과 언박싱이 일어나면 성능이 저하된다.

 

값과 참조형, 박싱과 언박싱은 자주 사용되기 때문에 잘 알아두는 것이 좋을 것 같다.

+ Recent posts