유선랜으로 네트워크 패킷 모니터링만을 하려면 아래 링크로 가서 진행하면 됩니다.

 

 

[무료유틸] 와이어샤크(Wireshark) 유선 네트워크 패킷 모니터링 무설치

와이어샤크(Wireshark) https://www.wireshark.org Wireshark · Go Deep. What is SharkFest? SharkFest™, launched in 2008, is a series of annual educational conferences staged in various parts of the g..

boox.co.kr

 

우선 무선랜 카드의 패킷 모니터링을 하려면 무선랜 카드가 "모니터 모드"를 지원해야지만 사용가능합니다.

 

일단 무선랜 카드가 "모니터 모드"를 지원하는지부터 확인해보자.

Cmd 창에서 "netsh wlan show wirelesscapabilities | findstr 모니터"를 입력해보자.

"지원됨"이 나왔다면 성공!!! 기뻐하자 ^^~ 지원되지 않으면 지원되는 무선랜 카드를 구입해야 한다.

 

 

 

 

무선 네트워크를 포함하여 네트워크 패킷 모니터링하는 것을 설명합니다.

 

와이어샤크(Wireshark)
https://www.wireshark.org
 

Wireshark · Go Deep.

What is SharkFest? SharkFest™, launched in 2008, is a series of annual educational conferences staged in various parts of the globe and focused on sharing knowledge, experience and best practices among the Wireshark® developer and user communities. Shar

www.wireshark.org

 

Download를 클릭하여 이동하고 아래 PortableApps를 다운로드 합니다.

 

2021.04월 기준 버전 3.4.4입니다.

 

WiresharkPortable_3.4.4.paf.z01
10.00MB
WiresharkPortable_3.4.4.paf.z02
10.00MB
WiresharkPortable_3.4.4.paf.z03
10.00MB
WiresharkPortable_3.4.4.paf.zip
6.59MB

 

* WiresharkPortable_3.x.x.paf.exe를 실행하여 원하는 폴더에 시스템에 맞는 무설치 프로그램을 생성합니다.

 

* PC에 이미지 Packet capture driver가 설치되어 있다면 아래의 PCap 드라이버 설치는 패스~

 

* WiresharkPortable.exe를 실행하면 아래와 같이 Packet Capture driver를 설치해야 한다고 나옵니다.

 

* 무선랜 카드 호환을 위해 Npcap을 설치합니다. 기존 WinPcap이 설치되어 있다면 제거합니다.

  - 티스토리 자동저장 지원이 되지 않는군요 ㅠ. 업로드 진행중에 멈추어버리니 그냥 날아간. ㅜ.,ㅡ;

 

 

* Npcap 설치

 

Download로 이동하여 "Npcap 1.3 installer for Windows"를 다운로드합니다.

npcap-1.30.exe
0.76MB

 

* 설치시 중요한 건 아래와 같이 무선랜 지원을 활성화해주는 것입니다.

* 해당 항목을 선택하고 설치하면 무선랜카드 패킷 스니퍼링이 가능했습니다.

 

* 아래와 같이 Wlanhelper로 직접 수정하지 말고 맨 아래 설명하는 Winshark 실행시 설정하여 진행하는 것이 안전합니다. 안될시에 아래 주의사항 부분 도전!

* 혹시 되지 않는 분들은 Npcap 설치 완료후 아래와 같이 Cmd 프롬프트를 사용하여 모드 변경을 할 수 있습니다.

 

### 주의 ###

monitor mode로 변경시 무선랜 접속이 끊어집니다. 작업중이던 내용이 있다면 저장하고 진행하세요.. 그래서 티스토리 작성 중이던게 날아감 ㅠ.,ㅜ;;

사실 monitor 모드가 아니었음에도 무선랜 패킷 스니퍼링은 가능했습니다.

- Managed: Radiotab 데허를 포함한 802.11 데이터 패킷만 모니터링 가능.

- Monitor: 모든 802.11 패킷 모니터링 가능.

 

이제 WinsharkPortable.exe를 실행시켜 봅니다.

 

 

 

Wi-Fi가 모니터링됨을 확인할 수 있고, 좌상단 상어 지느러미 아이콘을 클릭하거나, Wi-Fi를 더블클릭하여 패킷 모니터링을 시작할 수 있습니다.

 

* Wireshark Capture Options을 진입하면 아래와 같은 창이 나타나고 우측에 [v] Monitor 기능을 설정하여 [Start]할 수 있습니다.

 

실행하니 monitor 모드로 자동 변경해서 구동하네요.. 마찬가지로 Wi-Fi 연결은 끊어졌습니다.

와이어샤크(Wireshark)
https://www.wireshark.org
 

Wireshark · Go Deep.

What is SharkFest? SharkFest™, launched in 2008, is a series of annual educational conferences staged in various parts of the globe and focused on sharing knowledge, experience and best practices among the Wireshark® developer and user communities. Shar

www.wireshark.org

 

Download를 클릭하여 이동하고 아래 PortableApps를 다운로드 합니다.

 

2021.04월 기준 버전 3.4.4입니다.

 

WiresharkPortable_3.4.4.paf.z01
10.00MB
WiresharkPortable_3.4.4.paf.z02
10.00MB
WiresharkPortable_3.4.4.paf.z03
10.00MB
WiresharkPortable_3.4.4.paf.zip
6.59MB

 

* WiresharkPortable_3.x.x.paf.exe를 실행하여 원하는 폴더에 시스템에 맞는 무설치 프로그램을 생성합니다.

 

* PC에 이미지 Packet capture driver가 설치되어 있다면 아래의 PCap 드라이버 설치는 패스~

 

* WiresharkPortable.exe를 실행하면 아래와 같이 Packet Capture driver를 설치해야 한다고 나옵니다.

 

* Winpcap.org 설치

 

Download로 이동하여 "Installer for Windows"를 다운로드합니다.

2021.04월 기준 버전 4.1.3

 

WinPcap_4_1_3.exe
0.87MB

 

설치는 간단히 끝~

 

WinsharkPortable.exe을 다시 실행하면 아래와 같이 메시지가 뜨지 않습니다. 준비 완료.

- 좌상단의 상어 지느러미 아이콘을 클릭하면 "이더넷 2" 유선랜을 통한 모든 데이터 패킷을 검사할 수 있습니다.

 

* 난 무선랜이 기본인데!?!? 그렇다.. 난 무선랜만 사용해서 아무것도 캡쳐링되지 않는다..

만약 무선랜을 사용한다면 아래 링크로 무선랜을 위한 Winshark 사용을 보기 바랍니다.

 

 

 

 

[무료유틸] 와이어샤크(Wireshark) 무선 네트워크 패킷 모니터링 무설치

유선랜으로 네트워크 패킷 모니터링만을 하려면 아래 링크로 가서 진행하면 됩니다. [무료유틸] 와이어샤크(Wireshark) 유선 네트워크 패킷 모니터링 무설치 와이어샤크(Wireshark) https://www.wireshark.or

boox.co.kr

 

 

* 문자 집합(Charset)이란 컴퓨터에서 사용하는 문자들의 집합입니다. 그 집합들을 어떻게 모아두었느냐에 따라 이름을 정해주어야 구분해서 쓸 수 있겠지요. 문자집합의 이름으로는 유명한 ASCII, ISO8859, 유니코드 등이 있습니다.

 

* 인코딩은 해당 문자를 어떻게 코드화하겠느냐는 방식을 나타내며, 디코딩은 반대로 어떤 코드를 어떻게 문자화하겠느냐는 방식을 나타냅니다. 마찬가지로 방식에 대한 이름이 있어야 구분해서 사용할 수 있겠지요. 유니코드 인코딩, UTF, Base64, EUC-KR, CP949 등의 이름이 있습니다. 예상할 수 있듯이 문자 집합과 인코딩은 서로 뗄 수 없는 관계이기에 같은 이름이 많네요.

 

<아스키(ASCII)> - 최초의 문자집합이며 7bit를 사용합니다. ASCII라는 문자 집합을 구성하지 않고 컴퓨터를 개발했다면.. 정말 상상하기도 싫군요..

Ascii_Code_Table_Innosoft2021.pdf
0.38MB
The ASCII Code Table by Innosoft.kr

 

<ISO/IEC 8859>

  - 서유럽의 움라우트와 같은 특수 문자를 포함하기 위해 8bit를 사용하는 표준을 제정.

  - ASCII 부분을 유지하므로 당연히 ASCII와 호환됨.

 

<DBCS (Double-Byte Character Set>

  - 한글, 일어, 중국어 등 많은 문자가 필요로 하다보니 더이상 1byte로 표현불가하니 만들어 낸 문자집합.

  - ASCII 부분은 유지를 하고, 최상위 비트 0과 1로 확장 부분을 구분. 따라서 ASCII 호환됩니다.

  - 확장 부분은 2byte를 사용해서 문자를 구성합니다.

  - EUC-KR, CP949 등

 

<EUC-KR>의 문자 집합 00~7F (ASCII 호환)

  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0                                
10                                
20   ! " # $ % & ' ( ) * + , - . /
30 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
40 @ A B C D E F G H I J K L M N O
50 P Q R S T U V W X Y Z [ \ ] ^ _
60 ` a b c d e f g h i j k l m n o
70 p q r s t u v w x y z { | } ~  

<EUC-KR>의 문자 집합 B0A0~B0FF

  +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
B0A0  
B0B0
B0C0
B0D0
B0E0
B0F0  

위와 같이 정해진 위치에 확정된 글자가 명시되어 있으므로 "완성형"입니다. 해당하지 않는 문자를 표현할 수 없는 것이지요. 좀더 추가된 문자를 포함하여 만든 것이 CP949입니다. 마찬가지로 완성형이고, 포함되지 않는 문자는 표현할 수 없습니다.

 

"안녕Hello"라는 문자열은 총 4byte + 5byte = 9byte를 사용하게 됩니다.

 

 

<유니코드(Unicode)>

  - 위와 같이 언어별 인코딩이 다른 불편함을 해결하고자 ISO에서 동일한 규칙을 사용하도록 만든 문자집합입니다.

  - 유니코드 문자집합을 코드화하기 위한 인코딩에는 UTF-8, UTF-16, UTF-32등이 있습니다.

 

<UTF-8> 1byte~6byte 가변 문자 길이를 사용합니다. ASCII와 호환.

en.wikipedia.org/wiki/UTF-8

 

메모장에서 "안녕Hello"를 입력하고 UTF-8로 저장.

 

텍스트 파일을 Hexa Editor로 열어보기

 

앞의 3문자 "EF BB BF"는 UTF-8을 나타내는 BOM입니다. [아래에 설명]

"안"은 "EC 95 88", "녕"은 "EB 85 95", Hello는 "48 65 6C 6C 6F"임을 알 수 있습니다.

 

<UTF-8 인코딩 과정>

  - "안" = 0xEC 0x95 0x88 = 1110 1100   1001 0101   1000 1000

  - UTF-8 인코딩 테이블을 보면 U+0800 1110으로 시작하는 문자이고 3바이트를 사용하여 표현되고 있습니다.

  - U+0800에서 구분을 위하여 사용되는 "1110", "10", "10"을 제외한 나머지를 가지고 변환합니다.

  - 1110 1100   1001 0101   1000 1000 => 1100010101001000 => 0xC548

unicodemap.org/details/0xC548/

 

<UTF-16>

  - 2byte 또는 4byte를 고정으로 사용하기 때문에 ASCII와 호환되지 않습니다.

 

<BOM(Byte Order Mark)>

en.wikipedia.org/wiki/Byte_order_mark

UTF-16의 BE(Big Endian), LE(Little Endian)의 바이트 순서에 따라 바이트를 읽는 순서를 다르게 처리해주어야 합니다.

 

UTF-8은 Byte Order와 무관하여 "EF BB BF"를 저장하며 처리하는 프로그램도 있고(메모장같은), 처리하지 않는 프로그램도 있습니다. 그래서 Visual Studio나 웹페이지 개발시에 문서의 인코딩 에러가 가끔 나타나는 문제가 있지요. 각각의 프로그램에 맞게 BOM을 삭제하거나 추가하여 사용하면 문제를 해결할 수 있습니다.

 

 

패스트캠퍼스에서 진행한 "올인원 패키지 : 유니티 포트폴리오 완성 올인원 패키지 Online." 과정을 수강하였습니다.

예전부터 유니티에 관심도 있었고, 관련 업무에서 Unity를 활용하는 일이 생겨 겸사겸사 수강을 하게되었습니다.

매일 연속 50일 작성.. 사실 못할줄 알고 시작한건데 끝을 보게 되네요 ^^;
블로그도 열심히 해보려했지만 업무등에 치이게 되면 멈춰버리는 경우가 많아, 내 자신에 대한 도전이라는 마음으로 진행을 했는데 마음은 뿌듯합니다. ㅎㅎ

특히 Unity 업무를 진행하고 있었기 때문에 하나라도 더 듣고 내것으로 만들자는 마음으로 더 열심히 했던 것 같습니다. 사실 시작한 김에 꾸준히 더 작성을 하려는 욕심은 있었는데 연말이라 ㅠ.,ㅜ; 쉽지 않네요..

 

자~ 유니티 게임 포트폴리오 완성 강의에 대한 전반적인 내용을 작성해 봅니다.

 

 

 



제가 수강한 강좌입니다.

"올인원 패키지 : 유니티 포트폴리오 완성 올인원 패키지 Online."은 기초 과정이 아닙니다.
기초 과정은 "C#과 유니티로 배우는 게임 개발 올인원 패키지 Online"라는 과정이 따로 있습니다.
저는 C#은 오랜 기간 사용을 해왔었고, Unity는 책을 보고 따라해본 경험이 있어서 초급 과정은 건너 뛰었습니다.
초급에서 중급을 넘어가시려는 분들이나 중급인데 디아블로나 배틀로얄과 같은 게임을 실무적으로 구현하는 방법을 알고 싶다하시는 분들에게 추천드립니다.


게임 포트폴리오라는 제목으로도 유추할 수 있듯이 "취업준비생"분들을 위해 실무에서 게임 개발시 필요한 지식들을 많이 알려줍니다.
사실 저는 취업준비생도 아니고 포트폴리오를 구성할 것은 아니었지만, "포트폴리오"라는 말처럼 Unity의 다양한 부분들을 접할 수 있을 것이라고 생각하고 접근을 하였습니다.

처음에 목차를 볼 때에는 여러 가지 게임을 만드는 포트폴리오 구성을 생각했었는데..
실제로는 "디아블로"와 "배틀로얄" 2가지 게임을 집중적으로 구현해 보게 됩니다.



디아블로 구현 모습입니다.

한 단계씩 정리가 잘 되어 있습니다.
Rigidbody를 시작으로 해서 Character Controller, NavMeshAgent를 활용하여 AI 이동 등을 구현해보게 됩니다.
Animation에 대해서도 설명이 이어지지만 Unity 설정에 대해서는 자세하게 진행하지는 않습니다. 기초강좌가 아니기 때문입니다.
Animation의 Transition에 대해서 자세한 설명이 이어집니다.
TopDown Camera를 구현하여 어떻게 화면을 구성하는지를 구현해봅니다.
이 외에도 Light, FSM, AI, 시야, 전투시스템, 원거리공격, 인벤토리 구현, 문/함정 구현 등을 진행하게 됩니다.


이것이 끝이 아니라 위와 같이 Firebase를 활용하여 LeaderBoard를 구현하고 서버를 통해 연동해보는 실습도 포함되어 있습니다.
사실 디아블로에서는 그렇게 중요한 비중을 차지하는 부분은 아니지만 간단한 캐쥬얼 게임을 만들때는 많이 사용하게 되지 유용하다고 볼 수 있습니다.




이어서 배틀로얄(=배틀그라운드) 구현 모습입니다.

배틀로얄은 따라가기 조금 버거울 수 있습니다. 다만 인내심을 가지고 꾸준히 따라하시는 분은 굉장히 유용한 실무에서도 많이 사용될 Tool등을 만들어 볼 수 있습니다.

강의 시작에서는 취업준비생분들을 타겟으로 하여 실무 관련 많은 이야기를 합니다.
저는 지금은 다른 필드에서 먹고살고 있는 프로그래머이지만, 소프트웨어 업계의 공통적인 어려움과 문제점들은 비슷하다고 봐야겠지요.
그렇기 때문에 꼭 게임의 구현에 집중된 것이 아니라, 게임 개발에 플러스적으로 필요한 요소들 즉 관리 툴 같은 것들이 필요하게 됩니다.
저 또한 어떤 소프트웨어 업종에서 일하건간에 간단하게 구현하여 도움을 주는 툴들은 많이 개발을 해왔습니다.
실무에서 귀찮다고 안 하시는 분들이 대다수라고 봐야겠지요.. 너무 잘하셔서 안하시는 분들은 예외겠지만.. 여튼..

EffectTool, SoundTool에 이어 Behaviour 구현, Weapon 구현, Health, Alert, Shoot, Health 등 배틀로얄 게임을 구동하기 위한 다양한 클래스와 함수들을 구현해볼 수 있습니다.

조금 아쉬웠던 부분은 너무 많은 기능, 옵션을 설명해주시려고 하다보니 변수 선언, 초기화, 사용 등 따라해야할 코딩량이 너무 많았다는 느낌입니다.
최종적으로 배틀로얄 구현을 위해서 다 알아야 하는 부분도 맞고, 직접 코딩을 계속 여러번 해보면서 자신만의 코드가 되는 것이 맞습니다..
하지만 온라인 강의이므로 최소 옵션으로 해서 코딩을 진행하고 다른 부분은 완성된 코드를 가지고 설명에 집중해주셨더라면 더 좋지 않았을까 싶은 개인적인 아쉬움을 남겨봅니다.
그래도 적은 강의 시간에 담기에 쉽지 않은 내용이라 충분히 감안하고 시청하였습니다.
특히 실무에서 사용되는 알찬 정보들을 원하신다면 강력 추천드립니다.


현재 Unity 업무를 진행함에 많은 도움을 받았습니다.
특히 NavMesh 개념과 Transition 처리 등은 참 멋진 개념이며 멋진 설명이었습니다.

앞으로도 좋은 강의 많이 부탁드립니다.

감사합니다.


패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

  1. 유니티맨 2021.02.03 03:41

    리스펙합니다!

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 50회차 미션 시작합니다.

05. 배틀그라운드 - 03, 04, 05 번을 진행합니다.




Enemy Data 준비에 대한 시작입니다.

구글 스프레드 시트는 좋기는 한데 너무 느려서 애매하고, 엑셀은 정말 많이 사용하고, 툴은 좋긴 하지만 지속 누군가가 수정을 해줘야 하는데 그렇게 여유로는 모바일 게임 개발사는 없을 것이기에.. 결국 Table of Data를 처리하기 위한 방법들이 필요합니다.




GeneralStats 스크립트를 해당 위치에 생성하고 작성을 시작합니다.

[CreateAssetMenu(menuName="PluggableAI/GeneralStats")]
public class GeneralStats : ScriptableObject
{
    [Header("General")]
    [Tooltip("npc 정찰 속도 clear state")]
    public float patrolSpeed = 2f;
    [Tooltip("npc 따라오는 속도 warning state")]
    public float chaseSpeed = 5f;
    [Tooltip("npc 회피하는 속도 engage state")] // 교전시
    public float evadeSpeed = 15;
    [Tooltip("웨이포인트에서 대기하는 시간")]
    public float patrolWaitTime = 2f;
    [Header("Animation")]
    public LayerMask obstacleMask; // 장애물 레이어 마스크
    public float angleDeadZone = 5f; // 조준시 깜빡임을 피하기 위한 최소 확정 앵글
    public float speedDampTime = 0.4f; // 속도 댐핑 시간
    public float angularSpeedDampTime = 0.2f; // 각속도 댐핑 시간
    public float angleResponseTime = 0.2f; // 각속도 안에서 각도 회전에 따른 반응 시간
    [Header("Cover")]
    public float aboveCoverHeight = 1.5f; // 장애물에 숨었을 때 고려해야할 최소높이값
    public LayerMask coverMask; // 장애물 레이어마스크
    public LayerMask shotMask; // 사격 레이어마스크.
    public LayerMask targetMask; // 타겟 레이어마스크.
}




위의 CreateAssetMenu 함수를 통해 생성된 메뉴를 볼 수 있으며, 이를 클릭합니다.



생성된 파일 이름을 EnemyCommonStats라고 지정하고, 우측의 Inspector에 설정값들을 적용해 줍니다.



EntityTable을 클릭해보면 엑셀 파일이 열리는데 무기에 대한 설정값들이 적용되어 있는 것을 볼 수 있습니다.

 



EntityTable에서 우클릭하여 "XLS Import Settings..."를 클릭합니다.
그러면 엑셀 파일을 읽어서 보여주는 ExcelImporterMaker 창이 나타납니다.

AimOffset 값을 Float으로 설정하고 불필요한 것의 [ ]enable을 꺼주는 등의 설정을 해주고, [create] 버튼을 클릭합니다.




EntityTable에서 다시 우클릭하여 Reimport를 클릭하여 줍니다.




그러면 위와 같이 EntityTable 데이터 셋이 생성된 것을 확인할 수 있습니다.

복잡한 듯 하지만 엑셀을 잘 활용하여 다양한 데이터들을 처리하는 것이 여러가지 확장성에서 좋다라고 보면 됩니다.

사실 엑셀은 이제 실무에서 어떠한 형태로는 사용안되는 경우가 없는 듯해요 ^^. 데이터 관리, 로그, 레포팅, 분석 등 정말 다양한 형태로 활용되죠.. ㅎㅎ





Enemy AI 기본 클래스들 생성입니다.




StateMachine 폴더를 만들고, 하위 폴더로 Action, Decision, State, Transition 폴더를 만듭니다.
그리고 각각의 폴더 안애 위와 같이 스크립트들을 미리 생성해 둡니다.
이렇게 한 이유는 어떤 스크립트들이 만들어지고 어떻게 사용될지를 미리 보기 위함입니다.

우선 StateController를 더블클릭하여 코딩을 시작합니다 ^^~
(Enemy AI 기본 클래스 생성을 마치기 위하여 하나 더 진행..)


/// <summary>
/// state -> actions update -> transition (decision) check..
/// state에 필요한 기능들. 애니메이션 콜백들..
// 시야 체크, 찾아놓은 엄폐물 장소중 가장 가까운 위치를 찾는 기능.
/// </summary>
public class StateController : MonoBehaviour
{
    public GeneralStats generalStats;
    public ClassStats statData;
    public string classID; // PISTAL, RIFLE, AK
    
    public ClassStats.Param classStats {
        get {
            foreach (ClassStats.Sheet sheet in statData.sheets)
                foreach (ClassStats.Param parm in sheet.list)
                    if (parm.ID.Equals(classID)) return parm;
            return null;
        }
    }
    
    public State currentState;
    public State remainState;
    public Transition aimTarget;
    public List<Transition> patrolWaypoints;
    public int bullets;
    [Range(0,50)]
    public float viewRadius;
    [Range(0,360)]
    public float viewAngle;
    [Range(0,25)]
    public float perceptionRadius;
   
    [HideInInspector] public float nearRadious;
    [HideInInspector] public NavMeshAgent nav;
    [HideInInspector] public int wayPointIndex;
    [HideInInspector] public maximumBurst = 7;
    [HideInInspector] public float blindEngageTime = 30f;
    [HideInInspector] public bool targetInSight;
    [HideInInspector] public bool focusSight;
    [HideInInspector] public bool reloading;
    [HideInInspector] public bool hadClearShot; // before
    [HideInInspector] public bool haveClearShot; // now
    [HideInInspector] public int coverHash = -1; // 장애물마다 고유코드가 있어서 캐릭터들이 몰리지 않고 적당한 장애물을 찾도록 하기 위해..
    [HideInInspector] public EnemyVariables variables;
    [HideInInspector] public Vector3 personalTarget = Vector3.zero;
    
    private int magBullets;
    private bool aiActive;
    private static Dictionary<int, Vector3> coverSpot; // static
    private bool strafing;
    private bool aiming;
    private bool checkedOnLoop, blockedSight;
    
    [HideInInspector] public EnemyAnimation enemyAnimation;
    [HideInInspector] public CoverLookUp coverLookUp;
    
    public Vector3 CoverSpot {
        get { return coverSpot[GetHashCode()]; }
        set { coverSpot[GetHashCode()] = value; }
    }
    
    public void TransitionToState(State nextState, Decision decision) {
        if (nextState != remainState) currentState = nextState;
    }
}




strafing에 대해 구글에서 검색하여 내용을 찾아본 설명입니다..

 

50회 챌린지 미션은 할 수 있을까 싶었는데 결국 완료는 하였네요 ^^;

50일 동안 하루도 거르지 않고 무언가를 한다는 것이 어려울거라 예상은 했지만 생각보다 훨씬 어렵네요. 일단 뿌듯합니다. ㅎㅎ



<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 49회차 미션 시작합니다.

05. 배틀그라운드 - 01, 02 번을 진행합니다.


04.배틀로얄에서 05.배틀그라운드 라서 다른 게임인 줄 알았더니 같은 게임의 연속이네요 ^^;




복습과 전체 리뷰를 합니다.

유니티를 실행하여 우선 전체 이동하는 것과 소리 등을 확인합니다.



이제 무기를 집는 위치에 가서 설명합니다.

무기에는 Collider가 2개가 있고, 집는 동작을 확인하는 자체 Collider와 "Pick Rifle"이 뜨고 집는 반경을 확인하기 위한 Trigger Collider가 존재합니다.

무기를 집고 조준도 해보고.. 카메라 회전을 하고나서 캐릭터가 약간 딜레이를 주어 따라 회전하는 것을 확인할 수 있습니다.

총을 조준하고 쏴보고 소리도 듣고 바닥에도 쏴봅니다.




Quad GameObject에 BulletHole Material을 씌워서 만든 내용이었습니다.. 라지만...
R 키를 눌러서 재장전도 확인합니다.
Animator에 대한 설정이나 교육 등은 Youtube에서 검색해서 보면 된다고 합니다!?




PlayerCharacter의 Animator 설정어니 Rigidbody 등을 했었습니다.
BehaviourController에 대한 설명..
힘드셨죠... 에 대한 계속 반복적인 설명...






프로젝트에 들어가기 전에 Enemy 적군이 어떻게 움직이게 될지 미리 예습을 하는 시간입니다.

또 계속 코딩이 이어질 것이라.. 우선 Enemy가 어떻게 동작하는지를 보여준다고 합니다. 무지막지한 코딩이 계속되면 실행화면을 볼 시간이 없을 것이라고 합니다 ^^;




적의 Patrol 기능 설명입니다. 특정 위치를 왔다 갔다.. 하다가.. 캐릭터의 발자국 소리가 감지되면 장애물 뒤로 숨어서 공격을 시작하는 것에 대한 설명입니다.




실제 플레이를 해보면 적 캐릭터가 패트롤을 하다가.. 캐릭터의 소리가 들리면 장애물 뒤로 숨어서 사격을 시작하는 등을 확인할 수 있습니다.




적군의 State 상태에 대한 설명입니다.

어떤 조건 Decision을 맞족하면 특정 상태로 트랜지션된다..이지요...

 



Attack 상태에서의 트랜지션 가능한 모든 상태에 대한 설명입니다.
State 표는 좋은데.. 너무 당연한 게임이 어떻게 동작하는지에 대해서는 왜이리 설명이 긴 것일까요 ^^;

장애물찾기(FindCover) State에 대한 설명도 이어집니다.

NavigationMesh를 사용한다고 합니다.

찾기(Search) State에 대한 설명도 이어집니다.

공격 가능 지점(GotoShotSpot)으로 이동에 대한 State 설명도 이어집니다..

State가 꽤 많습니다. 머 당연하겠지요. ㅎㅎ 배틀그라운드의 상태가 그만큼 복잡한 상태를 왔다갔다하며 구동되도록 되어 있습니다.

장애물로 복귀(ReturnToCover) State에 대한 설명도 이어집니다..

장애물로 이동(TakeCover)에 대한 설명도 이어집니다... ㅎㅎ 구현시작하면 계속 코딩이 이어질 것 같네요.

강제이동, 강제대기 등에 대한 State 설명도 이어집니다.


한 State에서 사용가능한 Action은 여러개.. Transition이 일어나기 위한 조건을 Decision으로 하여 진행하겠습니다.

여러가지 Behaviour Tree 방법이 있는데 그 중에서 양갈래식 Tree 구조를 가지는 시스템을 구현한다고 합니다.

다음 시간부터 본격적으로 만들기 시작한다고 합니다.

기능들을 잘 쪼개놓으면 여러 기능을 재활용하는 것들도 해보게 됩니다.

플레이어 캐릭터에서 사용한 시스템에서 그치지 않고 업그레이드된 방식으로 진행을 합니다.

다음 영상에서는 위에서 설명한 것들을 하나씩 만들어보는 시간이 됩니다.






<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 48회차 미션 시작합니다.

04. 배틀로얄 - 41, 42 번을 진행합니다.




Shoot Behaviour 테스트를 위한 Unity 세팅을 진행해 줍니다.

 


Interactive Weapon 관련 세팅도 진행해 줍니다.




그리고 Unity를 플레이하면 무기 집고 조준 및 총 쏘기, 장전 등 잘 동작하는 것을 확인할 수 있습니다.




자 이제 생명력 처리를 하는 과정의 시작입니다.




Player/PlayerHealth 스크립트를 생성하고 코딩을 시작합니다.

/// 플레이어의 생명력을 담당
/// 피격시 피격 이팩트를 표시하거나 UI 업데이트를 함.
/// 죽었을 경우 모든 동작 스크립트 동작을 멈춤.
public class PlayerHealth : HealthBase
{
    public float health = 100f;
    public float criticalHealth = 30f;
    public Transform healthHUD;
    public SoundList deathSound;
    public SoundList hitSound;
    public GameObject hurtPrefab;
    public float decayFactor = 0.8f;
    
    private float totalHealth;
    private RectTransform healthBar, placeHolderBar;
    private Text healthLabel;
    private float originalBarScale;
    private bool critical;
    
    private void Awake() {
        myAnimator = GetComponent<Animator>();
        totalHealth = health;
        
        healthBar = healthHUD.Find("HealthBar/Bar").GetComponent<RectTransform>();
        placeHolderBar = healthHUD.Find("HealthBar/Placeholder").GetComponent<RectTransform>();
        healthLabel = healthHUD.Find("HealthBar/Label").GetComponent<Text>();
        originalBarScale = healthBar.sizeDelta.x;
        healthLabel.text = "" + (int)health;
    }
    
    private void Update() {
        if (placeHolderBar.sizeDelta.x > healthBar.sizeDelta.x) {
            placeHolderBar.sizeDelta = Vector2.Lerp(placeHolderBar.sizeDelta, healthBar.sizeDelta, 2f * Time.deltaTime);
        }
    }
    
    public bool IsFullLife() {
        return Mathf.Abs(health - totalHealth) < float.Epsilon;
    }
    
    private void UpdateHealthBar() {
        healthLabel.text = "" + (int)health;
        float scaleFactor = health / totalHealth;
        healthBar.sizeDelta = new Vector2(scaleFactor * originalBarScale, healthBar.sizeDelta.y);
    }
    
    private void Kill() {
        IsDead = true;
        gameObject.layer = TagAndLayer.GetLayerByName(TagAndLayer.LayerName.Default);
        gameObject.tag = TagAndLayer.TagName.Untagged;
        healthHUD.gameObject.SetActive(false);
        healthHUD.parent.Find("WeaponHUD").gameObject.SetActive(false);
        myAnimator.SetBool(AnimatorKey.Aim, false);
        myAnimator.SetBool(AnimatorKey.Cover, false);
        myAnimator.SetFloat(AnimatorKey.Speed, 0);
        foreach (GenericBehaviour behaviour in GetComponentsInChildren<GenericBehaviour>()) {
            behaviour.enabled = false;
        }
        SoundManager.Instance.PlayOneShotEffect((int)deathSound, transform.position, 5f);
    }
    
    public override void TakeDamage(Vector3 location, Vector3 direction, float damage, Collider bodyPart = null, GameObject origin = null) {
        health -= damage;
        UpdateHealthBar();
        if (health <= 0) Kill();
        else if (health <= criticalHealth && !critical) critical = true;
        SoundManager.Instance.PlayOneShotEffect((int)hitSound, location, 1f);
    }
}



PlayerHealth Script를 Attach하고 관련 설정을 하여 줍니다.



그리고 Unity 플레이를 진행해 보면 좌하단에 HealthBar 100 이 표시되는 것을 확인할 수 있습니다.



 

 

 

 



이제 플레이어 발소리 컴포넌트 제작을 시작합니다.




PlayerFootStep 스크립트를 생성하고 코딩을 시작합니다.

/// <summary>
/// 발자국 소리를 출력.
/// </summary>
public class PlayerFootStep : MonoBehaviour
{
    public SoundList[] stepSounds;
    private Animator myAnimator;
    private int index;
    private Transform leftFoot, rightFoot;
    private float dist;
    private int groundedBool, coverBool, aimBool, crouchFloat;
    private bool grounded;
    
    public enum Foot { LEFT, RIGHT, }
    private Foot step = Foot.LEFT;
    private float oldDist, maxDist = 0;
    
    private void Awake() {
        myAnimator = GetComponent<Animator>();
        leftFoot = myAnimator.GetBonTransform(HumanBodyBones.LeftFoot);
        rightFoot = myAnimator.GetBonTransform(HumanBodyBones.RightFoot);
        groundedBool = Animator.StringToHash(AnimatorKey.Grounded);
        coverBool = Animator.StringToHash(AnimatorKey.Cover);
        aimBool = Animator.StringToHash(AnimatorKey.Aim);
        crouchFloat = Animator.StringToHash(AnimatorKey.Crouch);
    }
    
    private void PlayFootStep() {
        if (oldDist < maxDist) return;
        oldDist = maxDist = 0;
        int oldIndex = index;
        while (oldIndex == index) {
            index = Random.Range(0, stepSounds.Length - 1);
        }
        SoundManager.Instance.PlayOneShotEffect((int)stepSounds[index], transform.position, 0.2f);
    }
    
    private void Update() {
        if (!grounded && myAnimator.GetBool(groundedBool)) PlayFootStep();
        grounded = myAnimator.GetBool(groundedBool);
        float factor = 0.15;
        if (grounded && myAnimator.velocity.magnitude > 1.6f) { // 움직이고 있다면..
            oldDist = maxDist;
            switch( step) {
                case Foot.LEFT:
                    dist = leftFoot.position.y - transform.position.y;
                    maxDist = dist > maxDist ? dist : maxDist;
                    if (dist <= factor) {
                        PlayFootStep();
                        step = Foot.RIGHT;
                    }
                    break;
                case Foot.RIGHT:
                    dist = rightFoot.position.y - transform.position.y;
                    maxDist = dist > maxDist ? dist : maxDist;
                    if (dist <= factor) {
                        PlayFootStep();
                        step = Foot.LEFT;
                    }
                    break;
            }
        }
    }
}

 



PlayFootStep 스크립트를 Attach하고 StepSound를 설정해 줍니다.




Unity 플레이를 하여 재생하여 캐릭터를 움직여 보면 걸을 때마다 소리가 재생되는 것을 확인할 수 있습니다.




<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 47회차 미션 시작합니다.

04. 배틀로얄 - 39, 40 번을 진행합니다.




저번에 이어 사격 동작 만들기 2가 이어집니다.

코드를 따라 치는 형태를 진행하지만 Unity 실행화면도 모르고 결과를 모르니 타이핑만 치게 되는게 맞다고 하시네요..
동작하는 것을 보면 코드가 이해될 것이라고 합니다...
하지만 그랬다면 완성된 코드로 구동을 보여주며 주요 코드를 설명해주시고, 주요 코드가 빠진 프로젝트에서 주요 코드를 타이핑 해보는 방법으로 진행하셨다면 어땠을까.. 또는 기본옵션으로 동작하는 간단한 구동 코드를 먼저 하고 다양한 옵션을 하나씩 추가해본다거나 했으면 어땠을까.. 하는 마음이 드네요 ^^


public class ShootBehaviour : MonoBehaviour
{
    // 이전 코드..
    
    private void ShootWeapon(int weapon, bool firstShot = true) {
        if (!isAiming || isAimBlocked || behaviourController.GetAnimator.GetBool(reloadBool) || !weapons[weapon].Shoot(firstShot)) return;
        else {
            burstShotCount++;
            behaviourController.GetAnimator.SetTrigger(shootingTrigger);
            aimBehaviour.crossHair = shootCrossHair;
            behaviourController.GetCamScript.BounceVertical(weapons[weapon].recoilAngle);
            
            Vector3 imprecision = Random.Range(-shootErrorRate, shootErrorRate) * behaviourController.playerCamera.forward; // 약간 흔들기
            Ray ray = new Ray(behaviourController.playerCamera.position, behaviourController.playerCamera.forward + imprecision);
            RaycastHit hit = default(RaycastHit);
            if (Physics.Raycast(ray, out hit, 500f, shotMask)) {
                if (hit.collider.transform != transform) { // 내가 아니고
                    bool isOrganic = (organicMask == (organicMask | (1 << hit.transform.root.gameObject.layer)));
                    DrawShoot(weapons[weapon].gameObject, hit.point, hit.normal, hit.collider.transform, !isOrganic, !isOrganic);
                    if (hit.collider) {
                        hit.collider.SendMessageUpwards("HitCallback", new HealthBase.DamageInfo(hit.point, ray.direction, weapons[weapon].bulletDamage, hit.collider), SendMessageOptions.DontRequireReceiver);
                    }
                }
            }
            else {
                Vector3 destination = (ray.direction * 500f) - ray.origin;
                DrawShoot(weapons[weapon].gameObject, destination, Vector3.up, null, false, false);
            }
            
            SoundManager.Instance.PlayOneShotEffect((int)weapons[weapon].shotSound, gunMuzzle.position, 5f);
            GameObject gameController = GameObject.FindGameObjectWithTag(TagAndLayer.TagName.GameController);
            gameController.SendMessage("RootAlertNearBy", ray.origin, SendMessageOptions.DontRequireReceiver);
            shotInterval = originalShotInterval;
            isShotAlive = true;
        }
    }
    
    public void EndReloadWeapon() {
        behaviourController.GetAnimator.SetBool(reloadBool, false);
        weapons[activeWeapon].EndReload();
    }
    
    private void SetWeaponCrossHair(bool armed) {
        if (armed) aimBehaviour.crossHair = aimCrossHair;
        else aimBehaviour.crossHair = originalCrossHair;
    }
    
    private void ShotProgress() {
        if (shotInterval > 0.2f) {
            shotInterval -= shootRateFactor * Time.deltaTime;
            if (shotInterval <= 0.4f) {
                SetWeaponCrossHair(activeWeapon > 0);
                muzzleFlash.SetActive(false);
                if (activeWeapon > 0) {
                

               
                }
            }
        }
        else {
            isShotAlive = false;
            behaviourController.GetCamScript.BounceVertical(0);
            burstShotCount = 0;
        }
    }
    
    private void ChangeWeapon(int oldWeapon, int newWeapon) {
        if (oldWeapon > 0) {
            weapons[oldWeapon].gameObject.SetActive(false);
            gunMuzzle = null;
            weapons[oldWeapon].Toggle(false);
        }
        
        while (weapons[newWeapon] == null && newWeapon > 0) {
            newWeapon = (newWeapon + 1) % weapons.Count;
        }
        
        if (newWeapon > 0) {
            weapons[newWeapon].gameObject.SetActive(true);
            gunMuzzle = weapons[newWeapon].transform.Find("muzzle");
            weapons[newWeapon].Toggle(true);
        }
        activeWeapon = newWeapon;
        if (oldWeapon != newWeapon) {
            behaviourController.GetAnimator.SetTrigger(changeWeaponTrigger);
            behaviourController.GetAnimator.SetInteger(weaponType, weapons[newWeapon] ? (int)weapons[newWeapon].weaponType : 0);
        }
        SetWeaponCrossHair(newWeapon > 0);
    }
    



    /// <summary>
    /// 인벤토리 역할을 하게 될 함수
    /// </summary>
    public void AddWeapon(InteractiveWeapon newWeapon) {
        newWeapon.gameObject.transform.SetParent(rightHand);
        newWeapon.transform.localPosition = newWeapon.rightHandPosition;
        newWeapon.transform.localRotation = Quaternion.Euler(newWeapon.relativeRotation);
        



    }
    
    private bool CheckforBlockedAim() {
        isAimBlocked = Physics.SphereCast(transform.position + castRelativeOrigin, 0.1f, behaviourController.GetCamScript.transform.forward, out RaycastHit hit, distToHand - 0.1f);
        isAimBlocked = isAimBlocked && hit.collider.transform != transform;
        behaviourController.GetAnimator.SetBool(blockedAimBool, isAimBlocked);
        Debug.DrawRay(transform.position + castRelativeOrigin, behaviourController.GetCamScript.transform.forward * distToHand, isAimBlocked ? Color.red : Color.cyan);
        return isAimBlocked;
    }
    
    public void OnAnimatorIK(int layerIndex) {
        if (isAiming && activeWeapon > 0) {
            if (CheckforBlockedAim()) return;
            Quaternion targetRot = Quaternion.Euler(0, transform.eulerAngles.y, 0);
            targetRot *= Quaternion.Euler(initialRootRotation);
            targetRot *= Quaternion.Euler(initialHipsRotation);
            targetRot *= Quaternion.Euler(initialSpineRotation);
            behaviourController.GetAnimator.SetBoneLocalRotation(HumanBodyBones.Spine, Quaternion.Inverse(hips.rotation) * targetRot);
            
            float xcamRot = Quaternion.LookRotation(behaviourController.playerCamera.forward).eulerAngles.x;
            targetRot = Quaternion.AngleAxis(xcamRot + armsRotation, transform.right);
            if (weapons[activeWeapon] && weapons[activeWeapon].weaponType == InteractiveWeapon.WeaponType.LONG) {
                targetRot *= Quaternion.AngleAxis(9f, transform.right);
                targetRot *= Quaternion.AngleAxis(20f, transform.up);
            }
            targetRot *= spine.rotation;
            targetRot *= Quaternion.Euler(initialCheckRotation);
            behaviourController.GetAnimator.SetBoneLocalRotation(HumanBodyBones.Chest, Quaternion.Inverse(spine.rotation) * targetRot);
        }
    }
    
    private void LateUpdate() {
        if (isAiming && weapons[activeWeapon] && weapons[activeWeapon].weaponType == InteractiveWeapon.WeaponType.SHORT) {
            leftArm.localEulerAngles = leftArm.localEulerAngles + leftArmShortAim;
        }
    }
}


What is the purpose of this lecture?
Ooops. BattleRoyale and BattleGround is the same project... so should I continue?





<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 46회차 미션 시작합니다.

04. 배틀로얄 - 37, 38 번을 진행합니다.




생명력 컴퍼넌트와 경고 체크 컴포넌트 개발을 시작합니다.



위와 같이 Manager 폴더를 하나 만들고, DataManager, SoundManager, EffectManager를 만든 후에 각각의 Manager Script를 드래그하여 연결시켜 줍니다.




설정을 잘 확인후에 Unity 플레이를 진행해 봅니다. 무기 근처에 가니 "Pick Rifle" 글자가 잘 나오긴 하는데 Console을 보니 에러가 발생하고 있습니다.




GetKeyDown() -> GetButtonDown()으로 변경해주어야 합니다.




다시 Unity 플레이를 하여 무기를 집어봅니다...
그런데 총 모양이 다르네요 ^^; 아직 버그인건지 설정 문제인지는 모르는 상황.. ㅎㅎ
Weapon Sprite에서 "rifle_HUD"로 설정해주어서 해결됩니다.

바닥의 총이 사라지고 캐릭터가 집지 않은 건 아직 ShootBehaviour를 작성하지 않았기 때문입니다.


해당 위치에 HealthBase 스크립트를 생성하여 줍니다. 그리고 코드 작업을 시작합니다.


pubic class HealthBase : MonoBehaviour
{
    public class DamageInfo {
        public Vector3 location, direction; // 맞은 위치와 방향
        public float damage;
        public Collider bodyPart;
        public GameObject origin;
        
        public DamageInfo(Vector3 location, Vector3 direction, float damage, Collider bodyPart = null, GameObject origin = null) {
            this.location = location;
            this.direction = direction;
            this.damage = damage;
            this.bodyPart = bodyPart;
            this.origin = origin;
        }
    }
    
    [HideInInspector] public bool IsDead;
    protected Animator myAnimator;
    
    public virtual void TakeDamage(Vector3 location, Vector3 direction, float damage, Collider bodyPart = null, GameObject origin = null) {
    }
    
    public void HitCallBack(DamageInfo damageInfo) {
        TakeDamage(damageInfo.location, damageInfo.direction, damageInfo.damage, damageInfo.bodyPart, damageInfo..origin);
    }
}




TagAndLayer 스크립트에 LayerMasking 클래스를 하나 작성해 줍니다. 코딩 편의를 위하여 ㅎㅎ..



AlertChecker 스크립트를 생성하고 작성을 시작합니다.


public class AlertChecker : MonoBehaviour
{
    [Range(0,50)] public float alertRadius;
    public int extraWaves = 1;
    
    public LayerMask alertmask = TagAndLayer.LayerMasking.Enemy;
    private Vector3 current;
    private bool alert;
    
    private void Start() {
        InvokeRepeating("PingAlert", 1, 1);
    }
    
    private void AlertNearBy(Vector3 origin, Vector3 target, int wave = 0) {
        if (wave > extraWaves) return;
        Collider[] targetsInViewRadius = Physics.OverlapSphere(origin, alertRadius, alertMask);
        
        foreach (Collider obj in targetsInViewRadius) {
            obj.SendMessageUpwards("AlertCallback", target, SendMessageOptions.DontRequireReceive);
            AlertNearBy(obj.transform.position, target, wave + 1);
        }
    }
    
    public void RootAlertNewBy(Vector3 origin) {
        current = origin;
        alert = true;
    }
    
    void PingAlert() {
        if (alert) {
            alert = false;
            AlertNearBy(current, current);
        }
    }
}



사격 동작 만들기 시작입니다. 대략 500 라인 정도된다고 합니다.

/// <summary>
/// 사격 기능 : 사격이 가능한지 여부를 체크하는 기능
/// 발사 키 입력받아서 애니메이션 재생, 이펙트 생성, 충돌 체크 기능
/// UI 관련해서 십자선 표시 기능
/// 발사 속도 조정
/// 캐릭터 상체를 IK를 이용해서 조준 시점에 맞춰 회전.
/// 벽이나 충돌체 총알이 피격되었을 경우 피탄 이팩트를 생성하고
/// 인벤토리 역할. 무기를 소지하고 있는지 확인용
/// 재장전과 무기 교체 기능까지 포함하고 있음.
/// </summary>
public class ShootBehaviour : GenericBehaviour
{
    public Texture2D aimCrossHair, shootCrossHair;
    public GameObject muzzleFlash, shot, sparks;
    public Material bulletHole;
    public int MaxBulletHoles = 50;
    public float shootErrorRate = 0.01f;
    public float shootRateFactor = 1f;
    pubic float armsRotation = 8f;
    
    public LayerMask shotMask = ~(TagAndLayer.LayerMasking.IgnoreRayCast | TagAndLayer.LayerMasking.IgnoreShot | TagAndLayer.LayerMasking..ConvertInvisible | TagAndLayer.LayerMasking.Player);
    public LayerMask organicMask = TagAndLayer.LayerMasking.Player | TagAndLayer.LayerMasking.Enemy;
    // 짧은총, 피스톨 같은 총을 들었을 때 조준시 왼팔의 위치 보정.
    public Vector3 leftArmShortAim = new Vector3(-4.0f, 0.0f, 2.0f);
    
    private int activeWeapon = 0;
    // animator value
    private int weaponType;
    private int changeWeaponTrigger;
    private int shootingTrigger;
    private int aimBool, blockedAimBool, reloadBool;
    
    private List<InteractiveWeapon> weapons; /// 소지하고 있는 무기들.
    private bool isAiming, isAimBlocked;
    private Transform gunMuzzle;
    private float distToHand;
    private Vector3 castRelativeOrigin;
    
    private Dictionary<InteractiveWeapon.WeaponType, int> slotMap;
    
    private Transform hips, spine, chest, rightHand, leftArm;
    private Vector3 initialRootRotation;
    private Vector3 initialHipsRotation;
    private Vector3 initialSpineRotation;
    private Vector3 initialCheckRotation;
    
    private float shotInterval, originalShotInterval = 0.5f;
    private List<GameObject> bulletHoles;
    private int bulletHoleSlot = 0;
    private int brustShotCount = 0;
    private AimBehaviour aimBehaviour;
    private Texture2D originalCrossHair;
    private bool isShooting = false;
    private bool isChangingWeapon = false;
    private bool isShotAlive = false;
    
    void Start() {} // 각 변수들의 초기화............ Vomiting........
    


}





<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

[패스트캠퍼스 수강 후기] 올인원 패키지 : 유니티 포트폴리오 완성 100% 환급 챌린지 45회차 미션 시작합니다.

04. 배틀로얄 - 35, 36 번을 진행합니다.



이번시간에는 무기를 집고, 던지고 등의 무기와의 Interactive 동작들을 하는 스크립트를 작성할 예정입니다.




사용하게될 Prefabs가 위와 같이 준비되어 있습니다.



그리고 드디어 전에 만들었던 Effect Tool 창에서 Effect에 대한 설정을 해줍니다.



SoundTool 설정해보다가 문제가 있어서 확인해보니 코드 변경이 필요한 부분이 있네요.
위와 같이 수정을 해줍니다 ^^;;;


SoundTool도 마찬가지로 위와 같이 설정을 해줍니다.



Contents 폴더를 생성하고 InteractiveWeapon 스크립트를 생성합니다.
Player 폴더 밑에 ShootBehaviour 스크립트를 생성합니다.



public class ShootBehaviour : GenericBehaviour
{
    /// <summary>
    /// 인벤토리 역할을 하게 될 함수.
    /// </summary>
    public void AddWeapon(InteractiveWeapon weapon) {
    }
}



/// <summary>
/// 충돌체를 생성해서 무기를 주을 수 있도록 한다.
/// 루팅했으면 충돌체는 제거.
/// 무기를 다시 버릴 수도 있어야 하며, 충돌체를 다시 붙여줍니다.
/// 관련해서 UI도 컨트롤할 수 있어야 하고 ShootBehaviour에 주은 무기를 넣어주게 됩니다.
/// </summary>
public class InteractiveWeapon : MonoBehaviour
{
    public string label_weaponName; // 무기이름.
    public SoundList shotSound, reloadSound, pickSound, dropSound, noBulletSound;
    public Sprite weaponSprite;
    public Vector3 rightHandPosition; // 플레이어 오른손 보정 위치
    public Vector3 relativeRotation; // 플레이어 맞춘 보정을 위한 회전값.
    public float bulletDamage = 10f;
    public float recoilAngle; // 반동
    public enum WeaponType { NONE, SHORT, LONG, }
    public enum WeaponMode { SEMI, BURST, AUTO, }
    public WeaponType weaponType = WeaponType.NONE;
    public WeaponMode weaponMode = WeaponMode.SEMI;
    public int burstSize = 1;
    
    public int currentMagCapacity, totalBullets; // 현재 탄창 양과 소지하고 있는 전체 총알량
    private int fullMag, maxBullets; // 재장전시 꽉 채우는 탄의 양과 한번에 채울 수 있는 최대 총알수
    private GameObject player, gameController;
    private ShootBehaviour playerInventory;
    private BoxCollider weaponCollider;
    private SphereCollider interactiveRadius; // 무기 감지 반경.
    private Rigidbody weaponRigidbody;
    private bool pickable;
    // UI
    public GameObject screenHUD;
    public WeaponUIManager weaponHUD;
    private Transform pickHUD;
    public Text pickupHUD_Label;
    
    public Transform muzzleTransform;
    
    private void Awake() {
        gameObject.name = label_weaponName;
        gameObject.layer = LayerMask.NameToLayer(TagAndLayer.LayerName.IgnoreRayCast);
        foreach (Transform tr in transform)
            tr.gameObject.layer = LayerMask.NameToLayer(TagAndLayer.LayerName.IgnoreRayCast);
        player = GameObject.FindGameObjectWithTag(TagAndLayer.TagName.Player);
        playerInventory = player.GetComponent<ShootBehaviour>();
        gameController = GameObject.FindGameObjectWithTag(TagAndLayer.TagName.GameController);
        
        if (weaponHUD == null) {
            if (screenHUD == null) screenHUD = GameObject.Find("ScreenHUD");
            weaponHUD = screenHUD.GetComponent<WeaponUIManager>();
        }
        if (pickHUD == null) pickHUD = gameController.transform.Find("PickupHUD");
        
        // 인터랙션을 위한 충돌체 설정
        weaponCollider = transform.GetChild(0).gameObject.AddComponent<BoxCollider>(); // Child에는 Box, 본인에게는 Sphere를 붙이려고..
        CreateInteractiveRadius(weaponCollider.center);
        weaponRigidbody = gameObject.AddComponent<Rigidbody>();
        
        if (weaponType == WeaponType.NONE) weaponType = WeaponType.SHORT;
        fullMag = currentMagCapacity;
        maxBullets = totalBullets;
        pickHUD.gameObject.SetActive(false);
        if (muzzleTransform == null) muzzleTransform = transform.Find("muzzle");
    }
    
    private void CreateInteractiveRadius(Vector3 center) {
        interactiveRadius = gameObject.AddComponent<SphereCollider>();
        interactiveRadius.center = center;
        interactiveRadius.radius = 1;
        interactiveRadius.isTrigger = true;
    }
    
    private void TogglePickHUD(bool toggle) {
        pickHUD.gameObject.SetActive(toggle);
        if (toggle) {
            pickHUD.position = transform.position + Vector3.up * 0.5f;
            Vector3 direction = player.GetComponent<BehaviourController>().playerCamera.forward;
            direction.y = 0;
            pickHUD.rotation = Quaternion.LookRotation(direction);
            pickupHUD_Label.text = "Pick " + gameObject.name;
        }
    }
    
    private void UpdateHUD() {
        weaponHUD.UpdateWeaponHUD(weaponSprite, currentMagCapacity, fullMag, totalBullets);
    }
    
    public void Toggle(bool active) {
        if (active) SoundManager.Instance.PlayOneShotEffect((int)pickSound, transform.position, 0.5f);
        weaponHUD.Toggle(active);
        UpdateHUD();
    }
    
    private void Update() {
        if (pickable && Input.GetKeyDown(ButtonName.Pick)) {
            // disable physics weapon
            weaponRigidbody.isKinematic = true;
            weaponCollider.enabled = false;
            playerInventory.AddWeapon(this);
            Destory(interactiveRadius);
            Toggle(true);
            pickable = false;
            TogglePickHUD(false);
        }
    }
    
    private void OnCollisionEnter(Collision collision) {
        if (collision.collider.gameObject != player && Vector3.Distance(transform.position, player.transform.position) <= 5f) {
            SoundManager.Instance.PlayOneShotEffect((int)dropSound, transform.position, 0.5f);
        }
    }
   
    private void OnTriggerExit(Collider other) {
        if (other.gameObject == player) {
            pickable = false;
            TogglePickHUD(false);
        }
    }
    
    private void OnTriggerStay(Collider other) {
        if (other.gameObject == player && playerInventory && playerInventory.isActiveAndEnabled) {
            pickable = true;
            TogglePickHUD(true);
        }
    }
    
    public void Drop() {
        gameObject.SetActive(true);
        transform.position += Vector3.up;
        weaponRigidbody.isKinematic = false;
        transform.parent = null;
        CreateInteractiveRadius(weaponCollider.center);
        weaponCollider.enabled = true;
        weaponHUD.Toggle(false);
    }
    
    public bool StartReload() {
        if (currentMagCapacity == fullMag || totalBullets == 0) return false;
        else if (totalBullets < fullMag - currentMagCapacity) {
            currentMagCapacity += totalBullets;
            totalBullets = 0;
        }
        else {
            totalBullets -= fullMag - currentMagCapacity;
            currentMagCapacity = fullMag;
        }
        return true;
    }
    
    public void EndReload() {
        UpdateHUD();
    }
    
    public bool Shoot(bool firstShot = true) {
        if (currentMagCapacity > 0) {
            currentMagCapacity--;
            UpdateHUD();
            return true;
        }
        if (firstShot && noBulletSound != SoundList.None) {
            SoundManager.Instance.PlayOneShotEffect((int)noBulletSound, muzzleTransform.position, 5f);
        }
        return false;
    }
    
    public void ResetBullet() {
        currentMagCapacity = fullMag;
        totalBullets = maxBullets;
    }
}




이제 Unity에서 Interactive Weapon을 위와 같이 설정해 줍니다.



Unity 실행화면 입니다. Interactive가 되지 않는 버그가!? ㅎㅎ 좀더 세팅이 필요하기에 계속 이어진다고 합니다.




<위의 코드들은 제가 보면서 주요한 함수, 코드를 확인하기 위해 타이핑한 용도로, 전체 소스코드가 아님에 주의해 주세요. 전체 코드는 교육 수강을 하면 완벽하게 받으실 수가 있답니다 ^^>

패스트캠퍼스 - 올인원 패키지 : 유니티 포트폴리오 완성 bit.ly/2R561g0

 

유니티 게임 포트폴리오 완성 올인원 패키지 Online. | 패스트캠퍼스

게임 콘텐츠 프로그래머로 취업하고 싶다면, 포트폴리오 완성은 필수! '디아블로'와 '배틀그라운드' 게임을 따라 만들어 보며, 프로그래머 면접에 나오는 핵심 개념까지 모두 잡아 보세요!

www.fastcampus.co.kr

 

+ Recent posts