

# 실습 영상
조준점이 없어서 맞추기가 어려운데ㅋㅋ
적 애니메이션, 총 발사, 맵 이동, 카메라 이동 등을 위주로 공부했음
# CameraRotate.cs
플레이어 마우스에 따라 카메라 회전
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 마우스의 입력값을 이용해서 회전하고 싶다.
public class CameraRotate : MonoBehaviour
{
float rx;
float ry;
public float rotSpeed = 200;
public Transform body;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// 1. 마우스의 입력값을 이용해서
float mx=Input.GetAxis("Mouse X");
float my = Input.GetAxis("Mouse Y");
// deltatime은 transform에 있는 정보를 변경할땐 무조건 적어야함
// 근데 deltaTime은 대략 60분의 1초로 mx, my값을 대략 60분의 1로 감소시키기 때문에 증폭시켜 줄 필요가 있음
rx += rotSpeed * my * Time.deltaTime; //y축 회전이 x 방향과 관련
ry += rotSpeed * mx * Time.deltaTime; //x축 회전이 y 방향과 관련
//print(mx+","+my);
// rx의 회전 값을 제한하고 싶다(마우스의 위아래 각도를 제한한다) -> 고개를 위아래로 360도 회전이 불가능하기 때문
// +80도 -80도 사이로 제한
rx = Mathf.Clamp(rx, -80, 80);
// 2. 회전하고 싶다
transform.eulerAngles = new Vector3(-rx, ry, 0); //마우스를 위로하면 아래를 보고, 아래로하면 위를 보게 해야하기 때문에 rx에 -1곱해줌
body.transform.eulerAngles = new Vector3(0, ry, 0);
}
}
# AgentTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentTest : MonoBehaviour
{
NavMeshAgent agent;
public Transform target; // 목적지
// Start is called before the first frame update
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
// Update is called once per frame
void Update()
{
agent.destination = target.position;
}
}
# Enemy.cs
플레이어와의 거리에 따라 적의 행동 변경
플레이어가 적의 공격 범위안에 들어오면 플레이어를 공격하도록 설정
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
// FSM(상태머신)
// FSM으로 상태를 제어하고 싶다.
// 정지, 이동, 공격, 죽음
public class Enemy : MonoBehaviour
{
NavMeshAgent agent;
public enum State
{
Idle,
Move,
Attack,
Die,
}
public State state;
//Animator>Animator Controller>Animation Clips
public Animator anim;
PlayerHP php;
// Start is called before the first frame update
void Start()
{
agent = GetComponent<NavMeshAgent>();
state = State.Idle;
target = GameObject.Find("Player");
php = target.GetComponent<PlayerHP>();
}
// Update is called once per frame
void Update()
{
if (state == State.Idle)
{
UpdateIdle();
}
else if (state == State.Move)
{
UpdateMove();
}
}
GameObject target;
public float findDistance = 5;
private void UpdateIdle()
{
// target이 감지거리 안에 들어오면 Move로 전이하고 싶다.
// 1. 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 2. 만약 그 거리가 감지거리보다 작으면
if (distance < findDistance)
{
// 3. Move상태로 전이하고 싶다.
state = State.Move;
anim.SetTrigger("Move");
agent.destination = target.transform.position;
}
}
public float speed = 1;
public float attackDistance = 1.5f;
private void UpdateMove()
{
// target방향으로 이동하다가 target이 공격거리 안에 들어오면 Attack으로 전이하고 싶다.
// 1. target 방향으로 이동하고 싶다. P=P0+vt
/*
Vector3 dir = target.transform.position - transform.position;
dir.Normalize();
transform.position += dir * Time.deltaTime * speed;
*/
agent.destination = target.transform.position;
// 2. 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 3. 만약 그 거리가 공격거리보다 작으면
if (distance < attackDistance)
{
// 4. Attack 상태로 전이하고 싶다.
state = State.Attack;
anim.SetTrigger("Attack");
agent.isStopped = true; //멈춤
}
}
internal void OnEventAttack() // internal은 같은 프로그램 내에서만 public
{
// 공격 행위
// 5. 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 6. 그 거리가 공격거리보다 크다면
if (distance > attackDistance) // 공격 실패
{
// 7. Move상태로 전이하고 싶다.
state = State.Move;
anim.SetTrigger("Move");
agent.isStopped = false;
}
else // 공격 성공
{
// 4. 플레이어를 공격하고
php.AddDamage();
// HitManager의 Hit함수를 호출 하고 싶다.
HitManager.instance.Hit();
}
}
float currentTime;
float attackTime = 1;
private void UpdateAttack()
{
// 일정시간마다 공격을 하되 공격시점에 target이 공격거리 밖에 있으면 Move상태로 전이하고 싶다. 그렇지 않으면 계속 반복해서 공격!
// 1. 시간이 흐르다가
currentTime += Time.deltaTime; //계속계속 더해져서 currentTime이 1이 되면 1초가 흐른것
// 2. 만약 현재시간이 공격시간이 되면
if (currentTime > attackTime)
{
// 3. 현재시간을 초기화하고
currentTime = 0;
// 5. 나와 target의 거리를 구해서
float distance = Vector3.Distance(transform.position, target.transform.position);
// 6. 그 거리가 공격거리보다 크다면
if (distance > attackDistance) // 공격 실패
{
// 7. Move상태로 전이하고 싶다.
state = State.Move;
agent.isStopped = false;
}
else // 공격 성공
{
// 4. 플레이어를 공격하고
php.AddDamage();
// HitManager의 Hit함수를 호출 하고 싶다.
HitManager.instance.Hit();
}
}
}
public void AddDamage(int damage)
{
agent.isStopped = true; //정지
state = State.Die;
anim.SetTrigger("Die");
Destroy(gameObject, 3); //3초후 파괴
} //destroy함수는 즉각 실행이 아니고 요청하는 것이기 때문에
// 요청하면 자기자신의 Ondestroy가 실행
private void OnDestroy()
{
EnemyManager.instance.COUNT--;
}
}
# EnemyAnimationEvent.cs
적 애니메이션 동작 설정
이벤트 발생 시 컴포넌트에 알려주는 역할
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 애니메이션 공격 동작에 호출할 이벤트 함수를 제작하고 싶다.
// 이벤트가 발생하면 Enemy컴포넌트에게 알려주고 싶다.
public class EnemyAnimationEvent : MonoBehaviour
{
public Enemy enemy;
public void OnEventAttack()
{
//print("onEventAttack");
enemy.OnEventAttack(); // 비주얼스튜디오에서 자동 함수생성-> ctrl+. 또는 alt+enter
// 함수를 찾을려면 OnEventAttack에 포커스하고 f12누르면 자동으로 찾아줌
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
# EnemyManager.cs
일정 시간마다 적 스폰
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 일정 시간마다 적공장에서 적을 생성해서 내 위치에 가져다 놓고 싶다.
public class EnemyManager : MonoBehaviour
{
public static EnemyManager instance;
private void Awake()
{
instance = this;
}
public GameObject enemyFactory;
public float createTime = 2;
int count;
public int COUNT
{
get { return count; }
set { count = value;
if (count < 0)
{
count = 0;
}
if (count > maxCount)
{
count = maxCount;
}
}
}
public int maxCount = 2;
// Start is called before the first frame update
IEnumerator Start()
{
while (true)
{
if (count < maxCount)
{
// 1. 적 공장에서 적을 생성하고
GameObject enemy = Instantiate(enemyFactory);
// 2. 내 위치에 가져다 놓고 싶다.
enemy.transform.position = this.transform.position + new Vector3(Random.value * 2, 0, Random.value * 2);
// 3. 내 방향과 일치시키고 싶다.
enemy.transform.forward = transform.forward; //forward 대신 right, rotation도 가능
count++;
}
// 4. 생성시간동안 대기하고 싶다.
yield return new WaitForSeconds(createTime);
}
}
// Update is called once per frame
void Update()
{
}
}
# Gun.cs
Ray 사용해서 바라보고 있는 곳에 마우스로 총 쏘기
벡터를 사용해서 총알 자국 남기기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Ray를 이용해서 바라보고 닿은 곳에 총을 쏘고 싶다.
// 총알 자국을 남기고 싶다.
public class Gun : MonoBehaviour
{
public ParticleSystem bulletImpact; //재생하기 위해 gameObject가 아니라 particleSystem사용
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//만약 마우스 왼쪽버튼을 누르면
if (Input.GetButtonDown("Fire1"))
{
// 1. 시선을 만들고
Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward); //원점과 방향을 넣어줘야함
// 2. 그 시선을 이용해서 바라봤는데 만약 닿은 곳이 있다면?
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo))
{
// 3. 닿은 곳에 총알자국을 가져다 놓고 싶다.
bulletImpact.transform.position = hitInfo.point; //hitInfo의 transform.position은 그 물체의 정중앙을 말함(속) 따라서 우리가 보는 겉면에 표시할려면 point사용
// 4. 총알 자국 VFX를 재생하고 싶다 (우리가 factory처럼 만드는게 아니라서 재생시켜줘야함)
bulletImpact.Stop();
bulletImpact.Play();
// 5. 총알 자국의 방향을 닿은 곳의 Normal방향으로 회전하고 싶다.(법선벡터)
// =총알 자국의 forward방향(총알이 forward방향(파란색화살표)으로 튀기 때문)과 닿은 곳의 Normal방향을 일치시키고 싶다.
bulletImpact.transform.forward = hitInfo.normal;
// 만약 hitInfo가 Enemy컴포넌트를 가지고 있다면?
Enemy enemy = hitInfo.transform.GetComponent<Enemy>();
if (enemy != null)
{
// enemy의 AddDamage함수를 호출하고 싶다.
enemy.AddDamage(1);
}
}
}
}
}
# HitManager.cs
코루틴을 사용해서 적이 플레이어 공격했을 때 공격받았다는 이미지 보여주기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HitManager : MonoBehaviour
{
public static HitManager instance;
private void Awake()
{
instance = this;
}
public GameObject imageHit;
// Start is called before the first frame update
void Start()
{
// 태어날 때 imageHit를 보이지 않게 하고 싶다.
imageHit.SetActive(false);
}
public void Hit()
{
// 깜빡거리고 싶다.
// 1. imageHit를 보이게 하고 싶다. 2. 0.1초 후에 3. imageHit를 안보이게 하고 싶다. -> 함수는 한꺼번에 실행되고 끝나기 때문에 1번이 동작하지 않는 것처럼 보일 수 있음
// 따라서 코루틴(Coroutine)함수 사용 -> 동시에 실행되는 함수 -> 여러개의 진입점을 가짐
StartCoroutine("IeHit"); // StartCoroutine(leHit());도 가능
}
IEnumerator IeHit()
{
// 1. imageHit를 보이게 하고 싶다.
imageHit.SetActive(true);
// 2. 0.1초 후에
yield return new WaitForSeconds(0.1f);
// 3. imageHit를 안보이게 하고 싶다.
imageHit.SetActive(false);
}
// Update is called once per frame
void Update()
{
// 적이 플레이어를 공격했을 때 imageHit를 깜빡이게 하고 싶다.
if (Input.GetButtonDown("Jump"))
{
Hit();
}
}
}
# PlayerHP.cs
플레이어 HP 초기화
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// 태어날 때 체력을 최대 체력으로 하고 싶다.
// 적이 플레이어를 공격할 때 체력을 감소하고 싶다.
// 체력이 변경되면 UI에도 반영하고 싶다.
public class PlayerHP : MonoBehaviour
{
int hp;
public int maxHP=3;
public Slider sliderHP;
//property 는 변수처럼 사용할 수 있는 함수
public int HP
{
get { return hp; }
set
{
hp = value;
sliderHP.value = value;
}
}
public void AddDamage()
{
HP -= 1; // get, set함수가 불림
}
// Start is called before the first frame update
void Start()
{
sliderHP.maxValue = maxHP;
HP = maxHP;
}
// Update is called once per frame
void Update()
{
}
}
# PlayerMove.cs
사용자의 입력에 따른 이동
중력을 반영해서 속도 계산
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// 사용자의 입력에 따라 앞뒤좌우로 이동하고 싶다.
// 점프를 뛰고 싶다. 중력, 뛰는 힘, y속도
public class PlayerMove : MonoBehaviour
{
// -크기
public float speed = 5;
public float gravity = -9.81f;
public float jumpPower = 10;
float yVelocity;
CharacterController cc;
// Start is called before the first frame update
void Start()
{
cc = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
// 1. 속도에 중력을 계속 더하고 싶다. -> 중력을 계속 반영해야하기 때문
yVelocity += gravity * Time.deltaTime;
// 2. 만약 사용자가 점프버튼을 누르면 y속도에 뛰는 힘을 대입하고 싶다.
if (Input.GetButtonDown("Jump"))
{
yVelocity = jumpPower;
}
// 1. 사용자의 입력에 따라
float h = Input.GetAxis("Horizontal"); //좌우
float v = Input.GetAxis("Vertical"); //앞뒤
// 2. 방향을 만들고
Vector3 dir = Vector3.right * h + Vector3.forward * v; //위아래가 아니고 앞뒤라서 forward
// 카메라가 바라보는 방향을 앞방향으로 하고 싶다.
dir = Camera.main.transform.TransformDirection(dir);
// dir크기(vector)를 1로 만들어줌
dir.Normalize();
// 3. y속도를 최종 dir의 y에 대입하고싶다.
dir.y = yVelocity;
// 3. 그 방향으로 이동하고 싶다.
// P=P0+vt
cc.Move(dir * speed * Time.deltaTime); //Move함수는 충돌검사를 먼저해줌
}
}
코드 외에도 컴포넌트 설정하는 부분이나 mesh 설정
애니메이션 설정 등 여러가지 내용이 추가적으로 있지만 너무 양이 많아져서 따로 적진 않음
'etc. > 🕹️unity' 카테고리의 다른 글
VR 실습 (0) | 2022.06.11 |
---|---|
2D Shooting (0) | 2022.06.11 |
unity shader, effect, light & 지형 만들기 (0) | 2022.06.11 |
간단 실습 - fps 게임 (0) | 2022.06.11 |
사용자 입력 제어 및 오브젝트 이동 처리 (0) | 2022.06.11 |
댓글