C# 문법 종합반 1주차 

1.C#의 char은 2byte 이다.

C/C++ 에서는 1byte , C#은 단일문자인 char이 유니코드를 포맷하고있기에 2byte로 처리

 

C/C++
1.ASCII(아스키코드) 인코딩 사용 0 - 127까지 값으로 표현 할 수 있는 문자를 저장 가능
2.다국어 지원을 위해 wchar_t 타입을 사용하여 2바이트 이상 문자로 제어 가능

C#
1.UTF-16 인코딩을 사용하여 유니코드 문자를 저장 가능
2.유니코드 문자(UTF-16의 코드유닛)를 저장하기 위한 자료형으로 전 세계 다양한 문자집합 사용가능

 

2.리터럴 상수 : C#에서는 컴파일러에 의해 상수 값으로 처리 되며, 
변수나 상수에 할당되거나 연산에 사용

프로그램에서 직접 사용되는 상수 값  또는 소스코드에 직접 기록되어 있는 값을 의미함

ex) int num = 10  , int 0x10 (16진수 int) , 0b10 (2진수 int) ,3.14(double), 3.14f(float) , 문자열내 따옴표 사용하기 등등...

 

(중요)

3. 변수란 ? : 데이터(숫자,문자)를 저장하고 사용하기 위해 할당받은 공간

int num; //변수를 선언 ->데이터 저장을 위한 '공간'을 할당 받음)
num = 10; // 변수 초기화 (변수에 데이터를 대입,저장함)

 

(중요)

4.코드컨벤션 : 협업시 코드 작성시 지키는 약속

(밑에 예시대로 꼭 할 필요는 없음, 밑에는 대부분 이렇게 많이함)

4-1.식별자 표기법
PascalCase : 클래스 메서드 프로퍼티 , 단어의 첫글자는 대문자 이후에도 단어 첫글자 대문자
ex)ClassName,MethodName,
cameCase : 변수 , 매개변수, 로컬변수 ,단어의 첫글자는 소문자 이후로는 대문자
ex)variableName,paremeterName
대문자 약어 : 전부 대문자를 사용 , ID , HTTP,FTP 
4-2.들여쓰기 : 탭 또는 스페이스 4칸
4-3.중괄호 위치 항상 새로운 줄에 시작
4-4.빈줄 사용 : 관련 없는 코드 사이에는 빈줄을 사용하여 구분
메서드,클래스 등의 블록 사이에는 두줄을 띄어 씀

 

5.형변환 : 명시적 형변환 , 암시적 형변환

5-1.명시적형변환
int num1 = 10;
long num2 = (long)num1;

 

5-2.암시적 형 변환
1.작은 데이터 타입에서 더 큰 데이터 타입으로 대입
byte num1 = 10;
int num2 = num1;
2.리터럴 값
float result = 1;
3.정수형과 부동소수점 연산 수행 : 결과는 부동소수점으로 변환됨
int num1 =10;
float num2 = 3.14f;
float result = num1 + num2;

 

6.입력받은 문자열 Split으로 바로 분리하기

namespace Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.Write("이름을 입력하십시오 : ");
            string name = Console.ReadLine();
            Console.WriteLine("반갑습니다. 휴먼 - {0} ", name);

            Console.Write("두개의 숫자를 입력하십시오 : ");
            string input = Console.ReadLine();
			
            //문자열 입력 받아서 바로 분리해서 배열로 사용하기
            string[] numbers = input.Split(' '); // 문자열을 공백으로 구분하여 배열로 만듬
            int num1 = int.Parse(numbers[0]); //첫번째 값을 정소루 변호나하여 저장
            int num2 = int.Parse(numbers[1]); //두번째 값을 정소루 변호나하여 저장
			//
            
            int sum = num1 + num2;

            Console.WriteLine("{0} + {1} = {2}", num1, num2, sum);
        }
    }
}

 

7.var 키워드 : 변수를 선언시에 변수의 자료형이 컴파일러에 의해 자동으로 결정

사용 이유 :

1.변수 선언시 초기화 하는값의 자료형에 따라 변수의 자료형이 결정될때

2.변수 선언 시점에 자료형을 정확히 알 수 없는 경우

 

8.비트 연산 : 유니티에서 마스크레이어 체크할때 사용되기에 알아둬야함

(플래그 연산이라고도 함)

 

9.String 메서드 확인하기

Split('분리할 단일문자') : 문장을 분리자에 의 분리
IndexOf("검색할 단어") : 문장에서 해당 단어 검색 후 해단 문자열의 index 반환
Replace("검색 단어" , "대체 단어") : 검색한 단어위치를 대체 단어로 변경
문자열 -> 숫자 : int.Parse(string 변수);
숫자->문자열 : 숫자 변수.ToString();
비교1(문자열 값 비교) : str1 == str2 (bool 값 리턴)
비교2(문자열 대소 비교) : Compare(str1,str2) (int 값 리턴)
변수가 0보다 작으면 str1 < str2 / 변수가 0보다 크면 str1 > str2 

 

포멧팅 :
1. 문자열 형식화 : Format("{0} + {1}" ,num1 , num2);
2. 문자열 보간 : string message = $"{num1} + {num2}";

 

위에 메서드를 활용한 예시 코드

namespace Test
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string str1 = "Hello,World";
            string str2 = new string('h', 5);

            string str3 = str1 + " " + str2;
            Console.WriteLine(str3);

            string[] str4 = str1.Split(',');
            Console.WriteLine(str4[0]);
            Console.WriteLine(str4[1]);

            int index = str1.IndexOf("World");
            Console.WriteLine(index); //단어가 문장의 어디에 있는지 index로 반환함

            string newStr = str1.Replace("World", "Universe");
            Console.WriteLine(newStr);
            Console.WriteLine(str1);

            string str5 = "123";
            int num = int.Parse(str5);
            Console.WriteLine(num);

            num += 10;

            Console.WriteLine(num.ToString());
            Console.WriteLine(str1 == str2);
            Console.WriteLine(string.Compare(str1 ,str2)); // str1 - str2
            //대소비교시 아스키코드의 정수값을 가져오기에 소문자가 더욱 크다.

            string name = "John";
            int age = 30;
            string message = string.Format("My Name is {0} and {1}", name, age);
            Console.WriteLine(message);

            message = $"My Name is {name} and {age}";
            Console.WriteLine(message);
        }
    }


}

 

C# 문법 종합반 1주차 과제 제출

더보기
더보기
//1.이름과 나이를 입력 받고 출력하는 코드를 작성하세요
//namespace Test
//{
//    internal class Program
//    {
//        static void Main(string[] args)
//        {
//            Console.Write("이름을 입력 하십시오 : ");
//            string name = Console.ReadLine();
//            Console.Write("나이를 입력 하십시오 : ");
//            string age = Console.ReadLine();

//            Console.WriteLine("이름 : {0} / 나이 : {1} ", name, age);
//        }
//    }
//}

//2.두 수를 입력 받고 사칙연산의 결과를 출력하세요
//namespace Test
//{
//    internal class Program
//    {
//        static void Main(string[] args)
//        {
//            string[] numbers;
//            do
//            {
//                Console.Write("두 수를 입력해 주십시오 : ");
//                numbers = Console.ReadLine().Split(' ');

//                if(numbers.Length != 2)
//                {
//                    Console.WriteLine("\n문제 발생");
//                    Console.WriteLine("1.숫자가 두개 초과 및 미만입니다.");
//                    Console.WriteLine("2.숫자를 스페이스바로 분리하여 주십시오.");
//                    Console.WriteLine("다시 입력해주십시오.\n");
//                }

//            } while (numbers.Length != 2);



//            float num1 = float.Parse(numbers[0]);
//            float num2 = float.Parse(numbers[1]);

//            string result;

//            Console.WriteLine("덧셈 : {0} ", num1 + num2);

//            Console.WriteLine("뺄셈 : {0} ", num1 - num2);

//            result = string.Format("{0:N2}", num1 * num2);
//            Console.WriteLine("곱셈 : {0} ", num1 * num2);

//            result = string.Format("{0:N2}", num1 / num2);
//            Console.WriteLine("나눗셈 : {0} ", num1 / num2);
//        }
//    }
//}

//3.섭씨온도를 화씨온도로 변환하는 프로그램을 만들어주세요
//namespace Test
//{
//    internal class Program
//    {
//        static void Main(string[] args)
//        {
//            Console.Write("섭씨(C) 온도를 입력하십시오 : ");
//            float celsius = float.Parse(Console.ReadLine());

//            float fahrenheit = (celsius * 9 / 5) + 32;

//            string message = $"화씨(F) 온도 : {fahrenheit}";
//            Console.WriteLine(message);
//        }
//    }
//}

//4.BMI 지수를 계산하는 프로그램을 만들어봅시다
//BMI = 체중 / (신장^2)
//namespace Test
//{
//    internal class Program
//    {
//        static void Main(string[] args)
//        {
//            Console.Write("체중을 입력 하십시오 : ");
//            float weight = float.Parse(Console.ReadLine());

//            Console.Write("신장을 입력 하십시오 : ");
//            float height = float.Parse(Console.ReadLine());

//            float BMI = weight / (height * height) * 10000;

//            string result = string.Format("{0:N2}",BMI);
//            Console.Write("당신의 BMI 지수 : {0}", result);
//        }
//    }
//}

https://www.acmicpc.net/problem/15829

 

백준문제에 수식까지 다 알려주며 쉬운 문제를 하나 풀었는데 , 계속 정답체크가 안되서 찾아보았다.

 

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

int main(void)
{
    //a == 1 -> 'a' - 96 
    //int hash = ch - 96;

    int r = 31;
    unsigned long long m = 1234567891;
    unsigned long long sum = 0;

    int StrSize;
    string str;
    cin >> StrSize >> str;
    
    int* num = new int[StrSize];
    for(int i =0; i < StrSize;i++)
    {
        num[i] = str[i] - 96;
    }

    for(int i =0; i < StrSize; i++)
    {
        sum += (num[i] * (unsigned long long)pow(r,i)) % m  ;
    }

    cout << sum % m ;
    delete[] num;
}

제공해준 수식에는 제곱이 있어 그저 cmath 라이브러리의 pow()함수를 사용해서 풀려고 했으나,

문제가 계속 풀리지 않았었다.

 

하지만 원인은 오버플로우였었다. Pow에 타입캐스팅으로 unsigned long long 자료형도 선언했지만 Pow에서 Return하는 자료형은 이미 double이기에 i의 숫자가 49승만 되도 결국 오버플로우 발생한다.

 

그럼 해당 숫자를 받을 자료형이 없는가? 그건 아니다

정수론의 합동식 성질을 이용하는것이다. 

* 합동식이란 ??

합동식 표현 방법 / 모듈러 연산이라고도 한다.

 

합동식의 정수배 성질을 이용하여 문제를 해결한다.

해당 성질이 적용 됨으로
위의 방식도 적용이 된다.

즉, 해당 문제도 정답도 숫자 m을 나눈 나머지값을 원한다. 

pow함수를 쓰는게 아닌 직접 Pow함수를 선언하여 만들고

제곱할때마다 숫자 m (123456791)를 나눠서 나머지값을 구하고 오버플로우 나지 않게 대처하는것이였다.

 

밑에 코드는 위에 설명을 반영하여 고쳐서 제출하였다.

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

unsigned long long Pow(int a , int b)
{
    unsigned long long result = 1;
    for(int i = 0; i < b ; i++)
    {
    	//합동식의 성질이 잘 드러나는 부분
        result *= a;
        result %= 1234567891;
    }

    return result;
}

int main(void)
{
    //a == 1 -> 'a' - 96 
    //int hash = ch - 96;

    int r = 31;
    unsigned long long m = 1234567891;
    unsigned long long sum = 0;

    int StrSize;
    string str;
    cin >> StrSize >> str;
    
    int* num = new int[StrSize];
    for(int i =0; i < StrSize;i++)
    {
        num[i] = str[i] - 96;
    }

    for(int i =0; i < StrSize; i++)
    {
        sum += (num[i] * Pow(r,i));
    }

    cout << sum % m ;
    delete[] num;
}

 


공부해야될것 :: 오늘 유니티 미니프로젝트 발표가 있어 발표를하고 다른조들 작품을 보며 필요한 점을 배웠었다.

파티클을 스크린 뷰에서 표현 하게 할 수 있는것 : UI 의 카메라 렌더링 모드를 바꾸기 (12조)
우리가 흔히 보는 광고창이 스르륵 넘어가는 것을 유니티 컴포넌트로 구현 : 스크롤뷰 컴포넌트 사용하기 ( 6조 )

 

 

 

 
//총알을 180도로 아래 방향으로 총알의 방향벡터를 만드는 로직 
 void MakeCircle()
 {
     Vector3 targetPos = playerTransform.position;
     Vector3 myPos = new Vector3(0f, 1.4f, 0);
     Vector3 vectorToTarget = (targetPos - myPos).normalized;
     int n = 30;

     for(int i = 0; i < n; i ++)
     {
         GameObject MakeCircle = Instantiate(bullet, myPos, Quaternion.identity);
         Vector2 circleVec = new Vector2(Mathf.Cos((Mathf.PI) * i / n ), Mathf.Sin(-(Mathf.PI) * i / n ));
         MakeCircle.GetComponent<Rigidbody2D>().AddForce(circleVec * 30f);
     }
     
 }

삼각함수를 통해서 원하는 각도의 좌표방향을 알수있다.

 

Mathf.Cos()은 x축 방향을 의미, Mathf.Sin()은 y축 방향을 의미한다.

단위 원 일때 (반지름이 1인 원) Cos = 밑변 / 빚면인데 빚면은 반지름을 나타내고 결국 밑변은 x좌표값을 나타낸다.

즉 Cos(degree) = x 좌표를 의미한다. Sin도 이와 같은 의미를 취한다 .

 

Mathf.Cos()의 매개변수는 Radian값을 의미하므로 (Mathf.Pi / 180) * degree 라는 변환식을 사용하거나

DegtoRad 매크로 함수를 사용해셔 변환해서 사용하면된다. 

 

지금 위의 함수는 180도의 범위만 필요하기에  Mathf.Pi만 사용하였다. 

 


게임 개발에서 삼각함수는 뗄래야 땔수 없는 존재지만 오늘 개발중에 살짝 과부하가 와서

기억을 전혀 못했었다.. 공부를 다시 할 필요가 있다. 

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

[System.Serializable]
public struct Stage
{
    public bool isBoss;
    public int stageNum;
    public int boardWidth;
    public int boardHeight;
    public bool isClear;
}

Stage 관리를 하기위해 Stage 구조체를 만들었다. 

 

public class DataManager : MonoBehaviour
{
    [SerializeField] private List<Stage> stages;
    private int curStage;

    public List<Stage> Stages { get { return stages; } }
    public int CurStage { set { curStage = value - 1; } }


    public Stage GetCurStgae()
    {
        return stages[curStage];
    }

    public void SetCurStgaeIsClear()
    {
    	//해당 코드는 문법상 오류 발생
        //stages[curStage].isClear = false;

        //구조체 변수를 새로 선언해서 초기화 해줘야함
        Stage stage = stages[curStage];
        stage.isClear = true;
        stages[curStage] = stage;
    }

Class 내에 Stage 구조체를 이용해 List를 만들어서 List[index]로 변수에 접근했으나 Stage의 변수에 접근하지 못했다. 

분명 List로 만들어서 배열처럼 접근했으나 이런식으로 답변이 나왔다.

 

다른 Stage 구조체를 선언해서 다시 넣어줘서  해결하였다.

 


해당 문제는 기초가 모자라 그런것 같다.. 다시 한번 기초를 다듬어봐야할것 같다.

 

1.Class 내부 변수 이름으로 호출하기

 

Unity에서 대화상자를 구현하고 있는데 Excel을 이용하여 외부데이터를 불러와서 읽어오는 방식을 택했

다.

 

해당 Asset을 사용하여 Excel를 읽었다.

https://github.com/mikito/unity-excel-importer

 

GitHub - mikito/unity-excel-importer

Contribute to mikito/unity-excel-importer development by creating an account on GitHub.

github.com

 

DialogDB.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using UnityEngine;

[ExcelAsset]
public class DialogDB : ScriptableObject
{
	public List<DialogDBEntity> Stage1; // Replace 'EntityType' to an actual type that is serializable.
	public List<DialogDBEntity> Stage2; // Replace 'EntityType' to an actual type that is serializable.
	
    //외부에서 내부 변수를 이름으로 호출 할 수있는 함수
	public object PrintField(string name)
    {
    	// public변수가 아니면 GetField에서 null이 리턴된다
        var result = this.GetType().GetField(name).GetValue(this); 

        return result;
    }
}

 

DialogeSystem.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Text;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;

public class DialogeSystem : MonoBehaviour
{
    [SerializeField] DialogDB dialogDB;

    List<DialogDBEntity> curStageDialoge;

    private void Start()
    {		
        //해당 오브젝트가 활성화시 Stage1의 DialogDB를 호출한다.
        DialogLoad("Stage1");
    }

    public void DialogLoad(string StageName)
    {
    	//매개변수의 StageName을 받아서 dialgoDB Class의 변수를 이름으로 호출한다.
        object curStage = dialogDB.PrintField(StageName);
        //object 자료형으로 호출하였기에 언박싱을 하여 원래의 자료형으로 돌려줘야한다.
        curStageDialoge = (List<DialogDBEntity>)curStage;
    }

}

 

DialogeSystem 에서 DialogeDB의 PrintField() 메서드를 통해서 해당 클래스의 변수를 박싱( List<DialogDBEntity  ->var)해서 받아오고, 받아온 변수를 언박싱(object -> List<DialogDBEntity>)하여 내가 쓸수있게 만들어준다. 

 

GitHub 기초 및 익히기 

GitHub : VCS ( Version Control System) 버전 관리 시스템

Local Repository (로컬 저장소) : 작업한것이 각 사용자의 컴퓨터에 저장됨

Remote Repository (원격 저장소) : 작업한 것이 서버에 저장됨

 

Commit : 작업한 것의 메모, 개인이 작한것이 아직 원격저장소에 push하지 않은 단계

History : Commit의 기록들 , 누가 작업했는지 기록을 알수있다.

 

Branch(가지) : 저장소의 단위 ex) 너 Branch 하나 파서 작업해라 -> 너의 개인 저장소 만들어서 기능구현 해라

Merge(병합) : 각각의 저장소를 상위 저장소로 합치는것 -> 각각 기능구현한 작업물을 하나로 합치는 과정

 

Fetch Origin : 현재 위치한 Branch의 최신 이력 정보를 확인하는 기능

Pull : 저장소에 올라온 작업물을 가져오는것 -> Pull할떄는 상관이없다. 어차피 원격저장소에 다 저장이 되어있기에

Push : 작업한것을 저장소로 보내는 과정 -> <매우 중요> Push 할때는 웬만하면 소통을 하고 ,신중히 Push 할것 

잘못 Push하면 상위 저장소가 날라가게될 수도 있기에 

 

Stash (스태시) : 변경사항을 커밋하기엔 아직 이르거나, 다른 브랜치로 체크아웃(다른 사람 브랜치 들갈때) 유지 하는것

Revert : 원하는 커밋한 내용을 삭제할때 사용되는 기능 , History에 남는다. 

 

 

<GitHub 작업시 중요한점 >


1. Merge 작업하기 전 무조건 확인 해야되는점 

 

2.GitHub 자주 충돌나는 원인 및 대처법

 

이런 경우 오류가 발생한다.

 

충돌이 일어난경우 부모 branch를 수정할것인지 자신의 branch를 수정할것인지 대안을 준다.

웬만하면 자기 branch를 수정하는것이 좋다... 부모것 함부로 수정후 Push하면 대참사가 날수도 있다.

내건 수정하거나 없애고 새로운 branch로 클론을 만들어 작업하는것이 훨씬좋다.

 

3.History에서 해당 시점으로 Branch 생성하기.

Histroy에서 해당 기능을 사용하면 해당 시점으로 Branch를 만들어 다시 작업 할 수있다.

 


추가로 공부해야 될 것

동적 생성 : Scene에 작업할것이 너무 많은경우 사용하는 방법

깃 플로우 전략

깃 컨벤션

유튜브 <골드메탈> 영상 시청 및 따라 만들기 - 3D 쿼터뷰 액션게임


추가 및 수정한 스크립트

Player.cs :: 물리 충돌시 각종 버그를 수정하기위헤 FixedUpdate를 추가

1.오브젝트와 충돌시 회전속력이 발생하는 버그를 없애기위해 FreezeRotation()함수 추가

2.벽을 관통하는 버그가 있어 StopToWall() 함수 추가

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

public class Player : MonoBehaviour
{
    bool isBorder = false; //현재 벽과 충돌했는지 체크 유무

    private void Move()
    {
        //정규화를 하는 이유? : 대각선 이동시 빨라지기 때문에 크기(속도)를 1로 만들어서 속도를 똑같이 만들어줘야한다.
        //쉽게 말하면 방향값이 1로 보정되는 벡터라고 생각하면 된다.
        //왜 빨라짐? : hAxis = 1 , vAxis = 1 인경우 대각성 이동시 피타고라스의 법칙으로 root(1^2+1^2) = root(2) = 1.141... 때문이다.
        moveVec = new Vector3(hAxis, 0, vAxis).normalized;

        //무기 교체시 or 무기 공격시 or 재장전시 움직이지 못하게 설정
        if (isSwap || !isFireReady || isReload) moveVec = Vector3.zero;

        //회피 동작 중인경우 회피 방향벡터가 대입되어 방향전환을 못하게 막기 위한 코드
        if (isDodge) moveVec = dodgeVec;

        //transform 이동은 가끔 물리충돌을 무시하는 경우가 발생 함
        //Rigidbody 컴포넌트에서 CollisonDetection을 Continuos로 변경 
        //Cpu를 더 먹어 최적화는 떨어지지만 정확도는 올라가기 때문 

        //위의 조건과 같이 걸어버리면 회전할시에 moveVec에 영향을 받게 되서 캐릭터가 굳어버릴수 있다.
        if (!isBorder)
        { 
            if (!wDown) transform.position += moveVec * speed * Time.deltaTime;
            else transform.position += moveVec * speed * 0.3f * Time.deltaTime;
        }

        //SetXXXX("애니메이션 이름" , bool 변수) 를 넣어 조건문처럼 사용 할 수 있다.
        anim.SetBool("isRun", moveVec != Vector3.zero);
        anim.SetBool("isWalk", wDown);
    }

    private void Turn()
    {
        //지정된 벡터를 향해서 회전시켜주는 함수 - 3D기능
        //현재 플레이어 위치에 방향벡터를 더해서 우리가 나아가는 쪽으로 오브젝트를 회전한다는 뜻
        //#1.키보드에 의한 회전
        transform.LookAt(transform.position + moveVec);

        //#2.마우스에 의한 회전
        if (fDown)
        {
            //스크린 -> 월드로 Ray를 쏘는 함수
            Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
            RaycastHit rayHit;
            if (Physics.Raycast(ray, out rayHit, 100))
            {
                Vector3 nextVec = rayHit.point - transform.position;
                nextVec.y = 0; //마스크가 없기에 부피가 있는 콜라이더 물체를 클릭시 캐릭터가 빙빙 도는 현상을 막기위해 y =0 추가
                transform.LookAt(transform.position + nextVec);
            }
        }
    }

    void FreezeRotation()
    {
        //rigid.angularVelocity : 물리회전속도
        rigid.angularVelocity = Vector3.zero;
    }

    void StopToWall()
    {
        Debug.DrawRay(transform.position, transform.forward * 5, Color.green);
        //레이어마스크(Wall)과 5 거리 안에 충돌시 isBorder는 true가 될것이다.
        isBorder = Physics.Raycast(transform.position, transform.forward, 5, LayerMask.GetMask("Wall"));
    }

    private void FixedUpdate()
    {
        FreezeRotation();
        StopToWall();
    }


}

 

Item.cs :: 드랍 아이템들의 Player와의 물리충돌을 없애기 위해

1.Floor와 충돌시 Rigidbody를 Kinematic 처리 와 물리충돌을 담당하는 Collider을 비활성화

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

public class Item : MonoBehaviour
{
    Rigidbody rigid;
    SphereCollider sphereCollider;

    //열거형 변수 enum
    public enum Type
    {
        Ammo, Coin, Grenade, Heart, Weapon
    };

    public Type type; //아이템 종류 변수
    public int value; //아이템 값을 저장할 변수

    private void Awake()
    {
        rigid = GetComponent<Rigidbody>();

        //Item 오브젝트에는 2개의 스피어콜라이더가 있음
        //GetComponent 호출시 Inspector 제일 위에있는 컴포넌트를 호출하게됨
        sphereCollider = GetComponent<SphereCollider>();
    }

    void Update()
    {
        //Rotate(오일러 각도) : 오브젝트를 해당 각도(방향벡터)로 회전시키는 함수 
       transform.Rotate(Vector3.up * 10 * Time.deltaTime);
    }

    //물리효과 변경을 스크립트로 수정
    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.CompareTag("Floor"))
        {
            //바닥에 충돌시 키네마틱(물리충돌연산x)로 변경
            rigid.isKinematic = true;
            sphereCollider.enabled = false;

        }
    }
}

수정된 물리연산들 & 유니티 기능 정리

1.자동회전방지

탄피에도 RigidBody와 Collider가 있어 탄피가 쌓이고 Player가 위로 올라가면 자동으로 회전하는 버그가 있었다.

이 버그의 원인은 Player <> 총알탄피 의 물리충돌로 인하여 Player의 Rigidbody에 회전속력이 추가되기에 발생되는 것이다.

 

Player.cs

  void FreezeRotation()
    {
        //rigid.angularVelocity : 물리회전속도
        rigid.angularVelocity = Vector3.zero;
    }

해당 코드를 FixedUpdate()에서 호출하여 회전속도를 강제로 0으로 만들어 처리하였다.

 

2.충돌레이어 설정

오브젝트의 레이어를 설정하여 충돌을 원하는데로 컨트롤 할 수 있다.

위 그림과 같이 탄피(BulletCase)와 플레이어 총알(PlayerBullet)의 레이어를 설정하고 충돌 관계를 설정하여 

충돌을 방지했다. 

 

1,2 번으로 문제점을 해결한 결과

1.탄피가 겹치지 않게 설정 완료 , 2.Player가 탄피위에 올라가도 충돌 발생 X

 

3.벽 관통 방지

해당 프로젝트에서 캐릭터가 벽을 관통하는 버그가 있었다. 실제로 3D프로젝트를 진행하면 자주 겪는 버그다.

이 버그를 대처하기 위해서는 RayCast기능을 활용하였다.

Unity Editer에서 확인하기 편하게 해당 코드를 적어준다.

해당 코드는 인게임에서는 보이지 않고 에디터에서만 보이는 레이저다.

    void StopToWall()
    {
        Debug.DrawRay(transform.position, transform.forward * 5, Color.green);
        //레이어마스크(Wall)과 5 거리 안에 충돌시 isBorder는 true가 될것이다.
        isBorder = Physics.Raycast(transform.position, transform.forward, 5, LayerMask.GetMask("Wall"));
    }

isBorder Bool변수를 만들어 레이저가 벽에 닿은 경우 true가 되게 만든뒤 

private void Move()
    {

        //무기 교체시 or 무기 공격시 or 재장전시 움직이지 못하게 설정
        if (isSwap || !isFireReady || isReload) moveVec = Vector3.zero;

        //위의 조건과 같이 걸어버리면 회전할시에 moveVec에 영향을 받게 되서 캐릭터가 굳어버릴수 있다.
        if (!isBorder)
        { 
            if (!wDown) transform.position += moveVec * speed * Time.deltaTime;
            else transform.position += moveVec * speed * 0.3f * Time.deltaTime;
        }

        //SetXXXX("애니메이션 이름" , bool 변수) 를 넣어 조건문처럼 사용 할 수 있다.
        anim.SetBool("isRun", moveVec != Vector3.zero);
        anim.SetBool("isWalk", wDown);
    }

Move()함수에 추가하여 transform에 영향을 안주게 막아버렸다. 이러면 애니메이션은 계속 재생하기에

플레이어가 키를 눌렀다는것을 인지함과 동시에 벽 관통 버그를 원천 봉쇄해버렸다.

 

4.아이템 물리 총돌 제거

물리 충돌을 담당하는 콜라이더와 Player가 충돌시 문제가 발생한다.

충돌시 아이템의 회전속도가 지멋대로 바뀌며 Player는 해당 아이템에 올라 탈 수 있다.

Item.cs

  private void Awake()
    {
        rigid = GetComponent<Rigidbody>();

        //Item 오브젝트에는 2개의 스피어콜라이더가 있음
        //GetComponent 호출시 Inspector 제일 위에있는 컴포넌트를 호출하게됨
        sphereCollider = GetComponent<SphereCollider>();
    }
    
    
    //물리효과 변경을 스크립트로 수정
    private void OnCollisionEnter(Collision collision)
    {
        if(collision.gameObject.CompareTag("Floor"))
        {
            //바닥에 충돌시 키네마틱(물리충돌연산x)로 변경
            rigid.isKinematic = true;
            sphereCollider.enabled = false;

        }
    }


Floor와 충돌할때, Rigid를 키네마틱 , 물리충돌 담당하는 콜라이더를 비활성화 시켜 Player와의 충돌을 방지하였다.


 

 

 

오늘로 사전캠프가 종료되고 9월9일(월)부터 본 캠프가 시작된다.

유튜브 <골드메탈> 영상 시청 및 따라 만들기 - 3D 쿼터뷰 액션게임


추가 및 수정한 스크립트

Player.cs :: 총을 추가해서 해당 마우스 방향으로 총을 발사시 캐릭터가 돌아가게 하는 기능 추가

  private void Turn()
  {
      //지정된 벡터를 향해서 회전시켜주는 함수 - 3D기능
      //현재 플레이어 위치에 방향벡터를 더해서 우리가 나아가는 쪽으로 오브젝트를 회전한다는 뜻
      //#1.키보드에 의한 회전
      transform.LookAt(transform.position + moveVec);

      //#2.마우스에 의한 회전
      if (fDown)
      {
          //스크린 -> 월드로 Ray를 쏘는 함수
          Ray ray = followCamera.ScreenPointToRay(Input.mousePosition);
          RaycastHit rayHit;
          if (Physics.Raycast(ray, out rayHit, 100))
          {
              Vector3 nextVec = rayHit.point - transform.position;
              nextVec.y = 0; //마스크가 없기에 부피가 있는 콜라이더 물체를 클릭시 캐릭터가 빙빙 도는 현상을 막기위해 y =0 추가
              transform.LookAt(transform.position + nextVec);
          }
      }
  }

followCamera는 현재 화면을 비추는 mainCamera를 가지고있다.

스키린화면(우리가 보는 화면)에서 월드(3D 월드스페이스를 말함)로 레이저를 쏘는데 그방향이 '마우스 위치'로 레이저를 쏜다.

RaycastHit는 레이저가 발사해서 맞은 곳의 data를 가져온다. 

마우스의 위치가 저 상태면 바닥의 Floor 오브젝트의 정보를 가져온다.

즉, 캐릭터가 향하는 방향 nextVec를 벡터의 뺼셈을 이용하여 방향벡터를 구하고 

현재 캐릭터의 위치에 방향벡터를 더하여 방향을 수정해준다. 

 


https://www.youtube.com/watch?v=07q9RUTRq4M   

: 원거리 무기 로직 구현 및 총알 오브젝트 및 탄피 생성 방법 

'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글

TIL : 2024-09-09(월) :: GitHub 기초  (0) 2024.09.09
TIL : 2024-09-07(토)  (0) 2024.09.07
TIL : 2024-09-05(목)  (0) 2024.09.05
TIL : 2024-09-04(수)  (0) 2024.09.04
TIL : 2024-09-03(화)  (0) 2024.09.03

+ Recent posts