Unity - Online 3D Action

[TOC]

머티리얼

SkyBox 설정

안개 설정

지형에 레이어 설정

캐릭터

캐릭터 컴포넌트

캐릭터 이동

//목적지까지 방향 벡터를 구하고 정규화한다.
Vector3 direction = (destination - transform.position).normalized;
//목적지까지 거리를 구한다.
float distance = Vector3.Distance(transform.position, destination);
//매 프레임 이동할 속도 벡터를 구한다.
Vector3 velocity = direction * speed * Time.deltaTime;
//매 프레임 이동할 벡터를 현재 위치에 더해서 이동한다.
transform.position += velocity;
//목적지에 도착했는지 확인(간단히)
if(velocity.magnitude < 0.01f){
    //도착
}
//목적지에 도착했는지 확인(정확히)
//속도 벡터의 크기와 목적지까지의 거리의 차가 특정값(도착거리범위) 이하일 경우
//이 조건은 목적지를 지나친 경우도 판단할 수 있다.
if(distance - velocity.magnitude <= 0.01f){
    //도착
}

캐릭터의 가속, 감속 이동

속도시간기울기가속도가속감속 \[\begin{equation*} Lerp( a,b,t) =a+_{(} b-a) \times t \end{equation*}\]
//특정 조건으로 목표속도를 설정한다.
if(distance > 1.0f){//목표지점과 거리가 1.0이상일 경우 가속
	goalVelocity = 10.0f;//목표속도 10.0f
} else {//목표지점과 거리가 1.0이하일 경우 감속
	goalVelocity = 0.0f;//목표속도 0.0f
}
//현재속도와 목표속도를 선형보간하여 가속도를 구한다.
accelate = Vector3.Lerp(currentVelocity, goalVelocity, 0.1f);//두 속도차의 10%씩 가속한다.
//현재속도에 가속도를 더한다.
currentVelocity += accelate;

캐릭터의 지면 이동

Character Controller 컴포넌트

접지 판정

캐릭터 회전

Vector를 향하는 Quaternion 구하기

// Quaternion
// 요약:
//     Creates a rotation with the specified forward and upwards directions.
//
// 매개 변수:
//   forward:
//     The direction to look in.
//
//   upwards:
//     The vector that defines in which direction up is.
[FreeFunction("QuaternionScripting::LookRotation", IsThreadSafe = true)]
public static Quaternion LookRotation(Vector3 forward, [DefaultValue("Vector3.up")] Vector3 upwards);

Quaternion 회전량 보간하기

// Quaternion
// 요약:
//     Rotates a rotation from towards to.
// 매개 변수:
//   from:
//   to:
//   maxDegreesDelta: 360도 기반 회전량, 프레임당 회전속도를 얻으려면 deltaTime을 곱해서 전달한다.
public static Quaternion RotateTowards(Quaternion from, Quaternion to, float maxDegreesDelta);

목표방향으로 회전을 마쳤는지 확인

// Vector3
// 요약:
//     Dot Product of two vectors.
// 매개 변수:
//   lhs:(Left-Hand Side)
//   rhs:(Right-Hand Side)
public static float Dot(Vector3 lhs, Vector3 rhs);

캐릭터 이동 코드

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

//캐릭터 이동
public class CharacterMove : MonoBehaviour
{
    //중력값
    const float GravityPower = 9.8f;
    //목적지에 도착했다고 보는 정지 거리
    const float StoppingDistance = 5.6f;

    //현재 이동 속도
    Vector3 velocity = Vector3.zero;
    //캐릭터 컨트롤러의 캐시
    CharacterController characterController;
    //도착했는지 여부(true:도착, false:미도착)
    public bool arrived = false;
    //방향을 강제로 지시하는가?
    bool forceRotate = false;
    //강제로 향하게 하고 싶은 방향
    Vector3 forceRotateDirection;
    //목적지
    public Vector3 destination;
    //이동속도
    public float walkSpeed = 6.0f;
    //회전속도
    public float rotationSpeed = 360.0f;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
        destination = transform.position;
    }

    void Update()
    {
        //이동속도 velocity를 갱신한다.
        if (characterController.isGrounded)
        {
            //test
            //destination = transform.position + Vector3.forward;
            //수평면에서 이동을 고려하므로 XZ만 다룬다.
            Vector3 destinationXZ = destination;
            //목적지와 현재 위치 높이를 똑같이 한다.
            destinationXZ.y = transform.position.y;

            //목적지까지 거리와 방향을 구한다.
            Vector3 direction = (destinationXZ - transform.position).normalized;
            float distance = Vector3.Distance(transform.position, destinationXZ);

            //현재 속도를 보관
            Vector3 currentVelocity = velocity;

            //목적지에 가까이 왔으면 도착
            if (arrived || distance < StoppingDistance)
            {
                arrived = true;
            }

            //이동속도
            if (arrived)
            {
                velocity = Vector3.zero;
            }
            else
            {
                velocity = direction * walkSpeed;
            }

            //이동속도를 부드럽게 보간 처리
            velocity = Vector3.Lerp(currentVelocity, velocity, Mathf.Min(Time.deltaTime * 2.0f, 1.0f));
            velocity.y = 0;

            if (!forceRotate)
            {
                //이동중이라면 이동하는 방향으로 회전한다
                if (velocity.magnitude > 0.1f && !arrived)
                {
                    Quaternion characterTargetRotation = Quaternion.LookRotation(direction);//지정하는 방향에 대한 회전값을 구하고
                    transform.rotation = Quaternion.RotateTowards(transform.rotation, characterTargetRotation, rotationSpeed * Time.deltaTime);//주어진 최댓값이내에서 목표 회전값까지 회전한 값을 구한다.
                }
            }
            else
            {
                //강제로 방향을 지정한다. (목표 지점을 항상 바라본다.)
                Quaternion characterTargetRotation = Quaternion.LookRotation(forceRotateDirection);
                transform.rotation = Quaternion.RotateTowards(transform.rotation, characterTargetRotation, rotationSpeed * Time.deltaTime);
            }
        }

        //중력
        velocity += Vector3.down * GravityPower * Time.deltaTime;//GravityPower값을 조절하여 Time.deltaTime을 곱하지 않게해도 된다.

        //땅에 닿아 있으면 지면을 꽉 누른다.(지면에 따라 튀어오를 수 있기 때문)
        Vector3 snapGround = Vector3.zero;
        if (characterController.isGrounded)
        {
            snapGround = Vector3.down;
        }

        characterController.Move(velocity * Time.deltaTime + snapGround);

        //도착
        if(characterController.velocity.magnitude < 0.1f)
        {
            arrived = true;
        }

        //강제 방향 변경 해제
        if(forceRotate && Vector3.Dot(transform.forward, forceRotateDirection) > 0.99f)
        {
            forceRotate = false;
        }
    }
}

입력

Input 클래스

GetButtonDown

GetButtonUp

GetButton

GetAxis

GetKeyDown

mousePosition

슬라이드 조작

//슬라이드 시작
if (Input.GetButtonDown("Fire1"))
{
	slideStartPosition = GetCursorPosition();
}
//슬라이드인지 아닌지 판단
if(Input.GetButton("Fire1"))
{
    if(Vector2.Distance(slideStartPosition, GetCursorPosition()) >= Screen.width * 0.1f)
    {
    	moved = true;
    }
}
//슬라이드 종료
if(!Input.GetButtonUp("Fire1") && !Input.GetButton("Fire1"))//버튼을 뗀 프레임까지 슬라이드로 간주
{
	moved = false;
}

슬라이드 시 마우스 이동량 구하기

다른 게임 오브젝트에 있는 컴포넌트 가져오기

public 변수에 Inspector에서 지정

FindObjectOfType(), FindObjectsOfTypes()로 찾는다

//하나의 객체만 찾는다
PlayerCtrl playerCtrl = FindObjectOfType<PlayerCtrl>();
PlayerCtrl playerCtrl = FindObjectOfType(typeof(PlayerCtrl)) as PlayerCtrl;
//모든 객체를 찾는다
PlayerCtrl[] plyaerCtrls = FindObjectsOfTypes<PlayerCtrl>();
PlayerCtrl[] plyaerCtrls = FindObjectsOfTypes(typeof(PlayerCtrl)) as PlayerCtrl;

게임 오브젝트에 태그를 설정하고 FindGameObjectWithTag()로 찾는다.

GameObject go = GameObject.FindGameObjectWithTag("Player");
PlayerCtrl playerCtrl = go.GetComponent<PlayerCtrl>();

Ray

Ray 만들기

Ray ray = new Ray(startVector, directionVector);

카메라에서 화면 안쪽을 향하는 Ray 만들기

Ray ray = Camera.main.ScreenPointToRay();

Ray 광선이 지면과 충돌한 위치 구하기

public static bool Raycast(Ray ray, out RaycastHit hitInfo, float maxDistance, int layerMask);

RaycastHit

    //
    // 요약:
    //     Structure used to get information back from a raycast.
    [NativeHeader("Runtime/Interfaces/IRaycast.h")]
    [NativeHeader("PhysicsScriptingClasses.h")]
    [NativeHeader("Runtime/Dynamics/RaycastHit.h")]
    [UsedByNativeCode]
    public struct RaycastHit
    {
        //
        // 요약:
        //     The Collider that was hit.
        public Collider collider { get; }
        //
        // 요약:
        //     The impact point in world space where the ray hit the collider.
        public Vector3 point { get; set; }
        //
        // 요약:
        //     The normal of the surface the ray hit.
        public Vector3 normal { get; set; }
        //
        // 요약:
        //     The barycentric coordinate of the triangle we hit.
        public Vector3 barycentricCoordinate { get; set; }
        //
        // 요약:
        //     The distance from the ray's origin to the impact point.
        public float distance { get; set; }
        //
        // 요약:
        //     The index of the triangle that was hit.
        public int triangleIndex { get; }
        //
        // 요약:
        //     The uv texture coordinate at the collision location.
        public Vector2 textureCoord { get; }
        //
        // 요약:
        //     The secondary uv texture coordinate at the impact point.
        public Vector2 textureCoord2 { get; }
        [Obsolete("Use textureCoord2 instead. (UnityUpgradable) -> textureCoord2")]
        public Vector2 textureCoord1 { get; }
        //
        // 요약:
        //     The Transform of the rigidbody or collider that was hit.
        public Transform transform { get; }
        //
        // 요약:
        //     The Rigidbody of the collider that was hit. If the collider is not attached to
        //     a rigidbody then it is null.
        public Rigidbody rigidbody { get; }
        //
        // 요약:
        //     The uv lightmap coordinate at the impact point.
        public Vector2 lightmapCoord { get; }
    }

layerMask

1 << 8;
1 << LayerMask.NameToLayer("Ground");
1 << LayerMask.NameToLayer("Ground") | 1 << LayerMask.NameToLayer("Player");

SendMessage

SendMessage("FuncName", arg);//FuncName 함수를 호출한다.

카메라

대상 추적 카메라

추적 대상의 위치

Vector3 lookPosition = lookTarget.position + offset;

대상 추적 카메라 위치 및 회전

//플레이어 위치를 기준으로하는 카메라 위치 구하기
Vector3 relativePos = Quaternion.Euler(verticalAngle, horizontalAngle, 0) * new Vector3(0, 0, -distance);
//카메라의 실제 위치
Vector3 cameraPos = lookPosition + relativePos;

카메라를 타깃으로 향하기

transform.LookAt(lookPosition);

화면 슬라이드로 카메라 회전 갱신

//화면 전체를 슬라이드 했을 때 회전할 양, 회전 최댓값
float rotAngle = 180.0f;
//픽셀당 회전할 각도
float anglePerPixel = rotAngle / Screen.width;
//이번 프레임에 슬라이드된 거리(픽셀)
Vector2 slideDelta;

//좌우 회전값
horizontalAngle += slideDelta.x * anglePerPixel;
//Repeat 함수로 회전값을 0~360도 사이에서 순환되도록 한다.
horizontalAngle = Mathf.Repeat(horizontalAngle, 360.0f);
//상하 회전값
verticalAngle -= slideDelta.y * anglePerPixel;
//Clamp 함수로 회전값을 -60~60도를 벗어나지 않게 한다.
verticalAngle = Mathf.Clamp(verticalAngle, -60.0f, 60.0f);

스크립트 처리 순서 바꾸기

애니메이션

매카님(Mecanim)

애니메이션 대상 변경(Retargeting)

상태 머신(State Machines)

애니메이션 대상 변경(Retargeting) 설정

애니메이션 클립(Animation Clip)

애니메이션 이벤트

애니메이션 전환

애니메니션 컨트롤러(Animation Controller)

애니메이션 스테이트 전환 파라미터

애니메이션 스테이트 전환

트랜지션(Transition)

Animator에 Animator Controller 적용

스켈레탈 애니메이션(Skeletal animation)

충돌

컬라이더(Collider) 컴포넌트

컬라이더 속성

컬라이더 충돌 이벤트

컬라이더 충돌 이벤트 조건

리지드바디 컴포넌트(Rigidbody)

리지드바디 속성

리지드바디(Rigidbody) 컴포넌트와 충돌 판정

캐릭터 컨트롤러(Character Controller) 컴포넌트와 충돌 판정

Character Controller, Rigidbody, Collider의 충돌 연관성

  Collider Collider(Rigidbody) Character Controller
Collider X O O
Collider(Rigidbody) O O O
Character Controller O O X

충돌 관련 레이어 설정

레이어 설정

레이어의 접촉 검출(Layer Collision Matrix) 설정

충돌 스크립트

캐릭터 스테이트 구현

AI

캐릭터 컴포넌트 관계도

InputManagerCharacterMovePlayerCtrlCharacterStatusAttackAreaHitAreaCharacterAnimation대미지 통보상태 갱신이동, 회전 명령공격력 등 참조애니메이션 상태 참조하여캐릭터 상태 갱신인풋 상태 확인FollowCamera인풋 상태 확인캐릭터 상태 참조하여애니메이션 갱신

파티클

파티클 시스템 레퍼런스

메인 모듈

속성 설명
Duration 에미터(emitter(방사체), 파티클의 발생원)에서 파티클이 계속 발생하는 시간. 파티클의 수명이 아니다.
Looping 설정한 Duration 시간이 되었을 때 파티클 발생을 반복하는 설정
Prewarm 미리 파티클을 발생시킨다. 파티클은 씬에 만들어지는 곧바로 발생하므로, 시작 부분을 감춰 자연스럽게 만들 때 이 속성을 사용한다. Looping이 On일 때만 효과가 있다.
Start Delay 에미터가 씬에 만들어지고 파티클을 발생시킬 때까지 시간을 지연시킬 수 있다. Prewarm이 Off일 때만 효과가 있다.
Start Lifetime 파티클의 수명. 발생하고 사라지기까지의 시간을 설정한다.
Start Speed 파티클 발생 속도를 설정한다(단위:m/s)
Start Size 파티클이 발생할 때 크기를 설정한다.
Start Rotation 파티클이 발생할 때 회전 각도를 설정한다. 기본 파티클로는 알 수 없다.
Start Color 파티클이 발생할 때 색을 설정한다.
Gravity Multiplier 중력 설정. 플러스 값은 아래쪽 방향, 마이너스 값은 위쪽 방향으로 가속한다.
Inherit Velocity 에미터가 움직일 때 움직인 방향과 속도에 따라 파티클의 초기 속도에 더한다. Simulation Space 항목이 World일 때만 유효하다.
Simulation Space 파티클이 항상 에미터 위치에서 상대적인 위치가 되는지(=local), 발생 후에는 에미터의 위치를 고려하지 않는지(=world) 설정할 수 있다.
Play On Awake 에미터가 만들어질 때부터 자동으로 파티클을 발생시킬지 설정할 수 있다.
Max Particles Duration으로 지정한 시간 내에 발생할 파티클의 최대 수를 지정할 수 있다.

속성값의 타입

Emission 모듈

속성 기능
Rate over Time(시간당 방출량) 시간 단위당 방출되는 파티클의 수
Rate over Distance(거리당 방출량) 이동한 거리 단위당 방출되는 파티클의 수
Bursts 버스트는 파티클을 생성하는 이벤트. 이 설정을 통해 지정된 시점에 파티클을 방출할 수 있다.
Time 파트클 시스템이 시작된 이후로 버스트를 방출할 시점을 초 단위로 설정
Count 방출되는 파티클 수를 설정
Cycles 버스트를 반복할 횟수를 설정
Interval 버스트가 반복되는 시간 사이의 간격(초)을 설정

Velocity over Lifetime 모듈

Color over Lifetime 모듈

Size over Lifetime 모듈

Renderer 모듈

Shape 모듈

파티클에 텍스쳐 적용

사운드

오디오 클립 프로퍼티

사운드 포맷 선택

오디오 클립

Audio Source 컴포넌트 프로퍼티

오디오 리스너

한 번만 재생하는 AudioSource 생성

빌드

PlayerSettings

네트워크

권한 서버(Authoritative Server)

비 권한 서버(Non-Authoritative Server)

마스터 서버

네트워크 통신 방법

원격 프로시저 호출(RPC, Remote Procedure Calls)

상태 동기화(State Synchronization)

NAT(Network Address Translation - 네트워크 주소 변환)

NAT 퍼실리테이터(Facilitator)

서버와 클라이언트 상호연결

회선 속도

마스터 서버와 NAT 퍼실리테이터 서버에 연결하기

//마스터 서버 변경
MasterServer.ipAddress = "[ip주소]";
MasterServer.port = [포트번호];

쿼터니언으로 벡터 회전 시키기

Vector3 offset = Quaternion.Euler(0.0f, angle, 0.0f) * Vector3.forward;

코루틴

Unity Doc

Time.deltaTime

RectTransform

단축키

Animator Window 바닥 움직이기

Mathf

Repeat

// 요약:
//     Loops the value t, so that it is never larger than length and never smaller than 0.
//     t의 값이 length를 넘어서면 0으로 돌아간 값을 반환한다.
public static float Repeat(float t, float length);

Clamp

// 요약:
//     Clamps value between min and max and returns value.
//     value가 min, max를 넘지 않은 값을 반환한다.
public static int Clamp(int value, int min, int max);

Random

/// <summary>
///   <para>Returns a random point inside a circle with radius 1 (Read Only).</para>
/// </summary>
public static Vector2 insideUnitCircle {
	get;
}

에러

Camera.main이 null인 경우

영어

참고