DINO NET
[Unity] FPS 코드 백업 본문
FPS 게임 만들 때 사용했었던 코드
1. player의 움직임 구현 (PlayerMove.cs)
- 키보드의 입력 값을 받아 player 움직임
- 점프 space로 구현 (중력 계산)
2. 카메라 조정 (PlayerRotate.cs, CamFollow.cs, CamRotate.cs)
- camera가 player에 붙는 1인칭. 마우스로 시선을 따라가는 느낌.
- camera가 수직으로 넘어가지 않게 상하 제한 두기 (y축 회전 제한)
- 회전은 Quatation!
3. 공격 및 체력 시스템 (PlayerFire.cs, EnemyFSM.cs)
- 마우스로 클릭 -> ray로 mass 감지(건물 및 적에게 rigidbody가 존재해야함) -> 부딪힌 rigidbody에 이펙트 효과(총알이 맞는 효과)
- player의 공격으로 ray에 감지되면, 총이 갖는 공격력만큼 적의 체력 깎기
- player 또한 적에게 공격을 당하면 체력 깎기
- Enemy animation system
(1) Idel: default 상태
(2) Move: 움직임. 일정 거리 이내에 player 감지 시, 공격을 받았을 경우 player를 따라 옴
(3) Attack: player가 일정 거리에 도달했을 때, delay 존재
(4) Return: enemy가 일정 거리를 벗어났을 때, 제자리로 복귀. (rotate까지 챙기는거 잊지 말기)
(5) Damaged: 해당 애니메이션이 존재하지 않아 잠시 멈추는 것 밖에 구현되지 않음. (체력만 깎임)
(6) Die: enemy의 체력이 0 이하일 때, 앞 혹은 뒤로 넘어짐. 이후 Dieprocess를 거쳐 component 삭제.
PlayerMove.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
//이동 속도
public float moveSpeed = 5.0f;
CharacterController cc; //charactercontroller 자체가 unity에서 기본으로 주어지는 물리엔진?
//중력
float gravity = -20f;
//수직 속력 변수
float yvelocity = 0;
//점프력 변수
public float jumpPower = 2.0f;
//플레이어 체력 변수
public int hp = 10;
//플레이어 피격 함수
//EnemyFSM 스크립트(다른 스크립트)에서 사용할 함수이므로 public값 부여
public void DamageAction(int damage)
{
//enemy의 공격력만큼 체력을 깎기
hp -= damage;
//체력이 음수가 될 경우 0으로 초기화
if(hp < 0)
{
hp = 0;
print("You are die!");
}
//player 체력 가시화
print("Player HP: " + hp);
}
// Start is called before the first frame update
void Start()
{
cc = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
//입력 값을 받음
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
//이동 방향 설정
Vector3 dir = new Vector3(h, 0, v);
dir = dir.normalized; //정규화 과정을 거쳐서 매끄럽게 만듦
//main camera를 기준으로 방향 변환
dir = Camera.main.transform.TransformDirection(dir);
//main camera의 방향을 가져오는 함수임
//실제 이동
//transform.position += dir * moveSpeed * Time.deltaTime;
//점프를 적용 후에 중력 값을 적용해야 하므로 중력 위에 위치해야 한다.
//스페이스바 버튼을 누르면 점프
if(Input.GetButtonDown("Jump"))
{
yvelocity = jumpPower;
}
//수직 속도에 중력 값 적용
yvelocity += gravity * Time.deltaTime;
dir.y = yvelocity;
cc.Move(dir * moveSpeed * Time.deltaTime); //실제 이동을 cc를 이용해서 다시 계산
}
}
PlyerRotate.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerRotate : MonoBehaviour
{
public float rotSpeed = 200f;
float mx = 0; //몸통이 앞구르기를 할 필요는 없으므로 mx만
// Update is called once per frame
void Update()
{
float mouse_X = Input.GetAxis("Mouse X");
mx += mouse_X * rotSpeed * Time.deltaTime;
transform.eulerAngles = new Vector3(0, mx, 0);
}
}
PlayerFire.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerFire : MonoBehaviour
{
//figure effectt object
public GameObject bulletEffect;
//피격 이펙트의 particle system
ParticleSystem ps; //component 변수는 start에서 할당
public int weaponPower = 5;
// Start is called before the first frame update
void Start()
{
ps = bulletEffect.GetComponent<ParticleSystem>();
}
// Update is called once per frame
void Update()
{
//마우스 왼쪽 버튼을 누르면 총알 발사
if(Input.GetMouseButtonDown(0))
{
//ray 생성
//main camera 위치, 앞방향으로 ray 생성
Ray ray = new Ray(Camera.main.transform.position, Camera.main.transform.forward);
//ray가 부딪힐 대상 저장
RaycastHit hitinfo = new RaycastHit();
//부딪힌 대상이 있다면 피격 이펙트 표시
if(Physics.Raycast(ray, out hitinfo))
{
//만약 ray에 부딪힌 대상의 레이어가 Enemy라면 Damaged 함수 실행
if(hitinfo.transform.gameObject.layer == LayerMask.NameToLayer("Enemy"))
{
EnemyFSM eFSM = hitinfo.transform.GetComponent <EnemyFSM>();
eFSM.hitEnemy(weaponPower);
}
//피격 이펙트의 위치를 레이가 부딪힌 지점으로 이동
bulletEffect.transform.position = hitinfo.point; //ray는 position이 아닌 point로 표시됨
//피격 이펙트 play
ps.Play();
}
}
}
}
CamFollow.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamFollow : MonoBehaviour
{
//Cam Position의 Transform
public Transform target; //외부 값을 가져오므로 public 실시간으로 값을 받아올 것임.
// Update is called once per frame
void Update()
{
//cam position의 위치에 main camera의 위치가 계속해서 동일하게 따라 오라는 뜻임
transform.position = target.position;
}
}
CamRotate.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CanRotate : MonoBehaviour
{
//회전 속도
public float rotSpeed = 200f; //float에 f 붙이는거 꼭 기억해두기
//회전 값
float mx = 0;
float my = 0; //마우스를 움직일 때마다 값을 계속해서 누적시켜서 이동할 수 있게 만드는 것임
// Update is called once per frame
void Update()
{
// 마우스 입력 값을 받기
float mouse_X = Input.GetAxis("Mouse X"); //input 값을 받는 함수 GetAxis 축 값 받아오고 "" 속 받아올 값 이름
float mouse_Y = Input.GetAxis("Mouse Y"); //C#은 ;로 닫는거 잊지 말기
//회전 값 누적
mx += mouse_X * rotSpeed * Time.deltaTime; //거리 = 속력 * 시간
my += mouse_Y * rotSpeed * Time.deltaTime;
//my 값을 -90~90으로 제한 (y축으로 회전하면 뒤집히므로)
my = Mathf.Clamp(my, -90, 90); //제한해주는 함수
//실제 회전
transform.eulerAngles = new Vector3(-my, mx, 0); //eulerAngles -> 회전을 도와줌
//위 아래로 회전 -> x 값 변환. +와 - 값이 반대임
//가로로 회전 -> y 값 변환. +와 - 값 그대로
}
}
EnemyFSM.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyFSM : MonoBehaviour
{
//enemy 상태 상수
//여러 상태가 있을 때는 상태 상수들로 정의를 해줌
enum EnemyState
{
Idle, //대기 상태
Move, //움직임 상태
Attack, //공격 상태
Return, //복귀 상태
Damaged, //피격 상태
Die //체력 0 죽음상태
}
//enemy 상태 변수
EnemyState m_State;
//Player 발견 범위
public float findDistance = 8f;
//Player.transform 참조
Transform player; //transform에 직접 접근 가능
//GameObject Player; //만약 이렇게 가져오면 transform도 함께 참조해야함
//공격가능 범위
public float attackDistance = 2f;
//Enemy 이동 속도
public float moveSpeed = 5f;
CharacterController cc;
//누적 시간
float currentTime = 0;
//공격 딜레이 시간
float attackDelay = 2f;
//enemy 공격력
public int attackPower = 3;
//enemy 초기 위치 저장
Vector3 originPos;
Quaternion originRot; //rotation 값
//이동 가능 범위
public float moveDistance = 20f;
//Enemy 체력
public int hp = 15;
//애니메이터 변수
Animator anim;
public void hitEnemy(int hitPower)
{
hp -= hitPower;
if (hp > 0)
{
m_State = EnemyState.Damaged;
print("상태 변환: Damaged");
Damaged();
}
else
{
m_State = EnemyState.Die;
print("상태 전환: Die");
anim.SetTrigger("Die");
Die();
}
}
// Start is called before the first frame update
void Start()
{
//player transform component 받아오기
//public으로 player를 선언하였으면 엔진 상에서 드래그 앤 드롭으로 가져올 수 있음
//but 이번에는 script 상에서 연결해줄 예정
player = GameObject.Find("Player").transform; //hierarchy에 있는 Player를 가져오기
//최초의 Enemy 상태를 대기로 설정
m_State = EnemyState.Idle;
cc = GetComponent<CharacterController>();
//자신의 초기 위치 저장
originPos = transform.position;
originRot = transform.rotation;
//Enemy의 자식 object인 zombie의 animator를 가져오기
anim = transform.GetComponentInChildren<Animator>();
}
// Update is called once per frame
void Update()
{
//프레임마다 현재 상태 체크 후 기능 실행
switch (m_State)
{
case EnemyState.Idle:
Idle();
break;
case EnemyState.Move:
Move();
break;
case EnemyState.Attack:
Attack();
break;
case EnemyState.Return:
Return();
break;
case EnemyState.Damaged:
break;
case EnemyState.Die:
break;
}
}
void Idle()
{
//만약 player와 enemy의 거리가 findDistance 이내라면 Move 상태로 전환
if (Vector3.Distance(transform.position, player.position) < findDistance)
{
m_State = EnemyState.Move;
print("상태 전환: Idle -> Move");
//이동 애니메이션으로 전환
anim.SetTrigger("Idle to Move");
}
}
void Move()
{
//만약 현재 위치가 초기 위치에서 이동가능 범위를 벗어난다면
if (Vector3.Distance(transform.position, originPos) > moveDistance)
{
m_State = EnemyState.Return;
print("상태 전환: Move -> Return");
}
else if (Vector3.Distance(transform.position, player.position) > attackDistance) //만약 player와 enemy의 거리가 공격범위 밖이라면 이동
{
//이동 방향 설정
Vector3 dir = (player.position - transform.position).normalized;
//실제 이동
cc.Move(dir * moveSpeed * Time.deltaTime);
//플레이어를 향해 방향 전환
transform.forward = dir;
}
else //그렇지 않다면, 현재 상태를 공격으로 전환
{
m_State = EnemyState.Attack;
print("상태 전환: Move -> Attack");
//누적 시간을 공격 딜레이 시간만큼 미리 진행
currentTime = attackDelay; //닿자마자 공격 한 번
anim.SetTrigger("Move to AD");
}
}
void Attack()
{
//만약 플레이어가 공격 범위 이내라면 공격 상태
if (Vector3.Distance(transform.position, player.position) < attackDistance)
{
currentTime += Time.deltaTime;
if (currentTime > attackDelay)
{
//플레이어의 PlayerMove 스크립트 중 DamageAction 함수 호출
player.GetComponent<PlayerMove>().DamageAction(attackPower);
print("Attack!");
currentTime = 0;
anim.SetTrigger("Start Attack");
}
}
else //그렇지 않으면 이동상태로 전환 (재추격)
{
m_State = EnemyState.Move;
print("상태 전환: Attack -> Move");
currentTime = 0;
anim.SetTrigger("Attack to Move");
}
}
void Return()
{
//만약 초기 위치에서 거리가 0.1 이상이면 초기 위치로 이동
if (Vector3.Distance(transform.position, originPos) > 0.1)
{
Vector3 dir = (originPos - transform.position).normalized;
cc.Move(dir * moveSpeed * Time.deltaTime);
transform.forward = dir;
}
else
{
transform.position = originPos;
transform.rotation = originRot;
m_State = EnemyState.Idle;
print("상태 전환: Return -> Idle");
anim.SetTrigger("Move to Idle");
}
}
void Damaged()
{
StartCoroutine(DamageProcess());
}
IEnumerator DamageProcess()
{
//0.5초 대기
yield return new WaitForSecondsRealtime(0.5f);
//이동 상태로 전환
m_State = EnemyState.Move;
print("상태 전환: Damaged -> Move");
anim.SetTrigger("Damaged to Move");
}
void Die()
{
StopAllCoroutines();
StartCoroutine(DieProcess());
}
IEnumerator DieProcess()
{
cc.enabled = false; //character controller 없앰
yield return new WaitForSeconds(2f);
print("소멸");
Destroy(gameObject); //해당 scene에서 gameObject를 삭제 시킴
}
}'program() > Unity' 카테고리의 다른 글
| [Unity] 2D 게임 Title, Quit button, Scene 전환 (0) | 2023.11.16 |
|---|