using System.Collections;
using UnityEngine;
[System.Serializable]
public class NPCMove
{
[Tooltip("NPCMove를 체크하면 NPC가 움직임")]
public bool NPCmove;
public string[] direction;//NPC가 움직일 방향설정
[Range(1, 5)]
[Tooltip("1=천천히, 2=조금 천천히, 3= 보통, 4.빠르게, 5=연속적으로")]
public int frequency;//NPC가 얼마나 빠른 속도로 움직일 것인가
}
public class NPCManager : MovingObjcet
{
[SerializeField]
public NPCMove npc;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
StartCoroutine(MoveCoroutine());
}
public void SetMove()
{
}
public void SetNoMove()
{
}
IEnumerator MoveCoroutine()
{
if(npc.direction.Length != 0)
{
for(int i =0; i< npc.direction.Length; i++)
{
switch (npc.frequency)
{
case 1:
yield return new WaitForSeconds(4f);
break;
case 2:
yield return new WaitForSeconds(3f);
break;
case 3:
yield return new WaitForSeconds(2f);
break;
case 4:
yield return new WaitForSeconds(1f);
break;
case 5:
break;
}
yield return new WaitUntil(() => npcCanMove);
base.Move(npc.direction[i], npc.frequency);
//실질적인 이동 구간
if (i == npc.direction.Length - 1)
{
i = -1;
}
}
}
}
}
using System.Collections;
using UnityEngine;
public class MovingObjcet : MonoBehaviour
{
//케릭터 이동속도
public float speed;
//픽셀 단위로 움직이기 구현
public int walkCount;
protected int currentWalkCount;
protected bool npcCanMove = true;
//벡터(케릭터의 이동 방향)
protected Vector3 vector;//protected는 부모자식간에 참조는 가능하지만 인스펙터창과 외부클레스는 접근불가
public BoxCollider2D boxCollider;//박스콜라이더
public LayerMask layerMask;//통과불가 에이리어 설정
public Animator animator;//애니메이터
protected void Move(string _dir, int _frequency)
{
npcCanMove = false;
StartCoroutine(MoveCoroutine(_dir, _frequency));
}
IEnumerator MoveCoroutine(string _dir, int _frequency)
{
vector.Set(0, 0, vector.z);
switch (_dir)
{
case "UP":
vector.y = 1f;
break;
case "DOWN":
vector.y = -1f;
break;
case "RIGHT":
vector.x = 1f;
break;
case "LEFT":
vector.x = -1f;
break;
}
//에니메이터 불러오기
animator.SetFloat("DirX", vector.x);
animator.SetFloat("DirY", vector.y);
animator.SetBool("Walking", true);
while (currentWalkCount < walkCount)
{
transform.Translate(vector.x * speed, vector.y * speed, 0);
currentWalkCount++;
yield return new WaitForSeconds(0.01f);
}
currentWalkCount = 0;
if(_frequency != 5)
{
animator.SetBool("Walking", false);
}
animator.SetBool("Walking", false);
npcCanMove = true;
}
}
using System.Collections;
using UnityEngine;
public class PlayerManager : MovingObjcet
{
static public PlayerManager instance;
//public string currentMapName;
public string walkSound_1;
public string walkSound_2;
public string walkSound_3;
public string walkSound_4;
private AudioManager theAudio;
//public AudioClip walkSound_1;//사운드 파일
//public AudioClip walkSound_2;
private AudioSource audioSource;//사운드 플레이어
//뛰기,평범하게 걷기
public float runSpeed;
private float applyRunSpeed;
private bool applyRunFlag = false;
//한칸한칸 이동할때 플레그 형식으로 구현
private bool canMove = true;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
if (instance == null)
{
DontDestroyOnLoad(this.gameObject);
boxCollider = GetComponent<BoxCollider2D>();
//audioSource = GetComponent<AudioSource>();
animator = GetComponent<Animator>();
theAudio = FindAnyObjectByType<AudioManager>();
instance = this;
}
else
{
Destroy(this.gameObject);
}
}
//코루틴으로 이동제어 한칸 한칸 이동하게
IEnumerator MoveCoroutine()
{
while (Input.GetAxisRaw("Vertical") != 0 || Input.GetAxisRaw("Horizontal") != 0)
{
//시프트 키 입력 여부에 따라 뛰기 걷기 구분
if (Input.GetKey(KeyCode.LeftShift))
{
applyRunSpeed = runSpeed;
applyRunFlag = true;
}
else
{
applyRunSpeed = 0;
applyRunFlag = false;
}
//이동방향 벡터 움직임 구현
vector.Set(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"), transform.position.z);
//좌상,좌하 등 대각선 방향이동 장금
if (vector.x != 0)
vector.y = 0;
//에니메이터 불러오기
animator.SetFloat("DirX", vector.x);
animator.SetFloat("DirY", vector.y);
RaycastHit2D hit;//레이케스트
Vector2 start = transform.position; //캐릭터의 현제 위치 값
Vector2 end = start + new Vector2(vector.x * speed * walkCount, vector.y * speed * walkCount);
//캐릭터가 이동하고자하는 위치 값 그리고 사이즈
boxCollider.enabled = false;
hit = Physics2D.Linecast(start, end, layerMask);
boxCollider.enabled = true;
if (hit.transform != null)
break;
animator.SetBool("Walking", true);
int temp = Random.Range(1, 4);
switch (temp)
{
case 1:
theAudio.Play(walkSound_1);
break;
case 2:
theAudio.Play(walkSound_2);
break;
case 3:
theAudio.Play(walkSound_3);
break;
case 4:
theAudio.Play(walkSound_4);
break;
}
theAudio.SetVolumn(walkSound_2, 0.5f);
//캐릭터 이동구현 코드
while (currentWalkCount < walkCount)
{
if (vector.x != 0)
{
//Translate는 현제 값에서 +를 해주는 코드
transform.Translate(vector.x * (speed + applyRunSpeed), 0, 0);
}
else if (vector.y != 0)
{
transform.Translate(0, vector.y * (speed + applyRunSpeed), 0);
}
if (applyRunFlag)
currentWalkCount++;//위의 applyRunFlas가 되면 추가되는 카운트 만약 아니라면 더해지지 않음
currentWalkCount++;//위의 카운트와는 별개의 카운트로 구분 됨
yield return new WaitForSeconds(0.01f);//0.01초마다 대기
/*if(currentWalkCount % 9 == 2)
{
int temp = Random.Range(1, 2);
switch (temp)
{
case 1:
//audioSource.clip = walkSound_1;
//audioSource.Play();
break;
case 2:
//audioSource.clip = walkSound_2;
//audioSource.Play();
break;
}
}*/
}
currentWalkCount = 0;//0을 넣어서 반복문 초기화
}
animator.SetBool("Walking", false);
//한칸한칸 이동할때 bool값
canMove = true;
}
//매 프레임마다 이동명령을 받음
void Update()
{
if (canMove)
{
//이동 방향키 입력을 할때 쯔구르 처럼 상하좌우 구분하게 하는 코드
if (Input.GetAxisRaw("Horizontal") != 0 || Input.GetAxisRaw("Vertical") != 0)
{
//한칸 한칸 이동할때 bool값으로 canMove를 이용함
canMove = false;
StartCoroutine(MoveCoroutine());
}
}
}
}
MovingObject라고 된 플레이어 스크립트에서 NPC를 만들때 겹치는 부분을 수정해 줄 것이다
위 스크립트를 참고하여 MovingObject에는 겹치는 부분을 작성해주고 우리가 기존에 쓰던 Player오브젝트에는
PlayerManager 스크립트를 작성해준다
그리고 작성하면서 MovingObject를 만들고 PlayerManager을 만들면 기존 함수가 수정이 안되어서 miss콘솔에 경고로 뜰 수 있다
편하게 작업하려면 Ctrl+Shift+F 키를 눌러서 MovingObject를 검색하면 비주얼 스크립트가 쉽게 찾아준다
이렇게!
protected : 그리고 새로 프로텍티드라는 걸 사용해서 참조를 준다
부모자식간에 참조가 가능하지만 인스펙터와 외부클레스에서는 접근을 하지 못하게 하는 지시자다
그리고 작성이 다 끝나면
위와 같이 인스팩터도 다시 잡아준다
NPCMov에는 이렇게 작성해준다
bool 값으로 NPCmove를 설정해주고
문자열 string[]로 dirction을 만들어주는데
이동할때 명령어를 텍스트로 입력하기 위해 만들어주는 것이다
그리고 위에 Tooltip로 텍스트가 작성되어 있는데 인스팩터에 설명을 표시하기 위해 만들어 준 것이다
이렇게!
그리고 NPCManager에서 커스텀 클레스를 만들었을텐데
이렇게 작성하면 가져올 수 있다
사용할때 [SerializeField]를 잊지말자
그리고 새로운 캐릭터를 만들어주고 지난 강좌들을 참고하여 NPC 캐릭터이 이동과 픽셀 나누기를 하여 만들어 준다
https://ruripanda.tistory.com/142
02.캐릭터 이동
using System.Collections;using UnityEngine;public class MovingObjcet : MonoBehaviour{ //케릭터 이동속도 public float speed; //벡터(케릭터의 이동 방향) private Vector3 vector; //뛰기,평범하게 걷기 public float runSpeed; private floa
ruripanda.tistory.com
NPC무브를 이렇게 작성해준다
위코드는 다이렉션의 길잎만큼 코드를 돌려주고 다이렉션을 -1로 만들어 무한이 돌려주는 코드다
그리고 스위치 문은 한 행동을 하고 몇초 쉬는지 스위치 문으로 나누어 주었다
MovingObject에 이렇게 작성해준다
캐릭터 이동과 관련 된 값이고 스위치 형식이다
인스펙터에는 텍스트로 움직임을 string(문자열)로 입력하게 된다
그리고 작성할때 vector.Set(0,0,vector.z)로 초기화를 잊지 말자
그리고 무빙오브젝트에 있던 기존 값을 이렇게 새로 작성해준다
애니메이터도 이렇게 작성해준다
이제 유니티엔진으로 돌아가서 인스펙터를 npc로 보여주면
이렇게 우리가 작성한게 보여진다
빈칸이 있다면 채워주자
위에서 Direction이 다이렉션으로 string(문자열)로 우리가 어디 이동할지 지시해줬다
Frequency로 이동하고 쉬는 시간도 구현되어 있다
그리고 재현을 하고 보면 유니티 엔진이 튕기는 현상이 발생할 수 있다
위에 NPC무브에서 무한히 돌리는 함수가 버그를 일으켜서 정지가 된 것이다
이걸 바로 잡아준다
무빙 오브젝트에 이렇게 대기모드를 작성해주자
맨 마지막 줄에도 있으므로 잊지말고 작성해주자
PlayerMoving으로 이동해서 대기모드를 잡아주는 것을 이렇게 작성하자
이건 람다식으로 작성되어 있다
이렇게 작성하고 유니티에서 테스트를 해주면 유니티에서 튕기는 것이 사라졌을 것이다
그런데 이렇게 해주면 NPC의 움직임이 연속으로 움직 일때 이상하다
프리컨시(_frequency)를 인수로 잡아준다
또 아래에 이렇게 작성해준다
이렇게 되면 4이하의 속도로 움직여도 문제 없이 캐릭터 에니메이션이 실행될 것이다
오늘은 여기까지...
ps.
엉덩이 아퍼...
'쯔꾸르식 유니티 게임 공부' 카테고리의 다른 글
11.이벤트 캐릭터 이동명령 이벤트 (0) | 2025.01.15 |
---|---|
10.5 추가설명 (0) | 2025.01.14 |
6.씬과 씬 이동 개량 완료 (0) | 2025.01.09 |
6.개량 씬과 씬 이동하면서 위치 지정하기 (0) | 2025.01.05 |
09.BGM매니져(배경음) (1) | 2025.01.03 |