static

"한정자"로서 메소드나 변수 등을 수식한다.

한정자: 다른 요소들을 꾸며주는 역할

C# 프로그램의 각 요소는 코드가 실행되는 시점에 비로소 메모리에 할당하는 반면,

Static 키워드로 수식되는 코드는 프로그램이 처음 구동될 때부터 메모리에 할당이된다.

 

 

CLR : Common Language Runtime

C#으로 만든 프로그램이 실행되는 환경.

C#뿐만 아니라 CLS(Common Language Specification) 규격을 따르는 모든 언어로 작성된 프로그램을 지원하기때문에 이름이 붙여짐.

 

void

"비어있는" 라는 뜻이다. 이 메소드가 어떠한 결과도 돌려주지 않을 것이라는걸 컴파일러에게 알려주는 기능

'TIL' 카테고리의 다른 글

TIL-2024/02/21(버튼을 눌렀을때 저장법)  (0) 2024.02.21
TIL-2024/02/08  (0) 2024.02.08
TIL-2024/02/02  (0) 2024.02.02
TIL-2024/02/01  (0) 2024.02.01
TIL-2024/01/30  (0) 2024.01.30

Json에 저장/불러오기

  public void SaveData()
  {
      string data = JsonUtility.ToJson(playerData,true);
      path = Path.Combine(Application.dataPath,playerData.name +"JSon");
      File.WriteAllText(path, data);
  }
   public void LoadData()
   {
       string data = File.ReadAllText(path);
       playerData = JsonUtility.FromJson<PlayerData>(data);
   }

 

저장,로드씬하는 dataManager.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System.IO;
using UnityEngine.UI;

public class DataManager : MonoBehaviour
{
    public class PlayerData
    {
        public string name;
      
    }
    public static DataManager instance;
    public int CurrentIndex;
    public PlayerData playerData = new PlayerData();
    string path;

    public Text playerName;
 
    private void Awake()
    {
        if(instance == null)
        {
            instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);
    }

    public void SaveData()
    {
        string data = JsonUtility.ToJson(playerData,true);
        path = Path.Combine(Application.dataPath,playerData.name +"JSon");
        File.WriteAllText(path, data);
    }
    public void LoadData()
    {
        string data = File.ReadAllText(path);
        playerData = JsonUtility.FromJson<PlayerData>(data);
    }
   
    public void LoadSceneMainScene()
    {
       playerData.name = playerName.text;
        SceneManager.LoadScene("MainScene");
        Debug.Log(playerName.text);
        
    }
}

'TIL' 카테고리의 다른 글

TIL-2024/03/12  (0) 2024.03.12
TIL-2024/02/08  (0) 2024.02.08
TIL-2024/02/02  (0) 2024.02.02
TIL-2024/02/01  (0) 2024.02.01
TIL-2024/01/30  (0) 2024.01.30
스크립터블 오브젝트(Scriptable Object)
  • 스크립터블 오브젝트는 Unity에서 데이터를 저장하고 관리하는 유연한 데이터 컨테이너입니다.
  • 게임에서 재사용 가능한 데이터 또는 설정을 저장하는 데 사용됩니다.
  • 코드와 데이터를 분리하여 코드를 더 깔끔하고 관리하기 쉽게 만듭니다.
  • 하나의 스크립터블 오브젝트를 여러 게임 오브젝트에서 참조하거나 재사용할 수 있습니다.
  • Unity 에디터와 통합되어 인스펙터 창에서 직접 수정하고 관리할 수 있습니다.

우선 스텟 만들기

캐릭터는 HP / 스피드 / 공격력 으로 구성.

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

public enum StatsChangeType
{
    Add,        [더하기]
    Multiple,   [곱하기]
    Override,   [덮어쓰기]
}

[Serializable]
public class CharacterStats
{
    public StatsChangeType statsChangeType;
    [Range(1, 100)] public int maxHealth;
    [Range(1f, 20f)] public float speed;
    public AttackSO attackSO;
}

 

AttackSo를 쓰기위해서 스크립터블 오브젝트를 만들자.

빈폴더 생성 > 이름 ScriptableObject > 하위에 또 빈폴드오브젝트 생성 ,이름은 Script > Script생성 > AttackSO

얘는 만들 오브젝트인 ScriptableObject 를 상속받음

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


[CreateAssetMenu(fileName ="DefaultAttackData", menuName ="TopDownController/Attacks/Default", order = 0)]
public class AttackSO : ScriptableObject
{
    [Header("Attack Info")]
    public float size;
    public float delay;
    public float power;
    public float speed;
    public LayerMask target;

    [Header("Knock Back Info")]
    public bool isOnKnockback;
    public float knockbackPower;
    public float knockbackTime;
}

 

Scriptable하위에 RangeAttackData를 만들어줄차례

단적인 예로 화살, 즉 원거리 기술을 써줄것이기 때문에 Range데이터를 만들어야한다. 

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

[CreateAssetMenu(fileName = "RangedAttackData", menuName = "TopDownController/Attacks/Ranged", order = 1)]
public class RangedAttackData : AttackSO
{
    [Header("Ranged Attack Data")]
    public string bulletNameTag;
    public float duration;
    public float spread;
    public int numberofProjectilesPerShot;
    public float multipleProjectilesAngel;
    public Color projectileColor;
}

 

이렇게하면 빈오브젝트를 생성할때 만들기를 누르면 옆에 뜨게된다.

요런식으로

 

Scriptable Object 하위에 Datas 폴더를 만들고 거기에 RangedAttackData를 생성한다.

그럼 헤더에 붙여준것들이 뜨게된다.

지금은 플레이어를 가져왔고 이것도 몬스터한테도 따로 해줄수있을듯?

 

아래로 설정해준다.

이건 공용이다, 한놈이 바뀌면 다바뀌기 때문에 우회하는 코드를 써주어야한다.

 

 

이를 우회하는게 Handler코드.

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

public class CharacterStatsHandler : MonoBehaviour
{
    [SerializeField] private CharacterStats baseStats;
    public CharacterStats CurrentStates { get; private set; }
    public List<CharacterStats> statsModifiers  = new List<CharacterStats>();

    private void Awake()
    {
        UpdateCharacterStats();    
    }

    private void UpdateCharacterStats()
    {
        AttackSO attackSO = null;
        if (baseStats.attackSO != null)
        {
            attackSO = Instantiate(baseStats.attackSO);
        }

        CurrentStates = new CharacterStats { attackSO = attackSO };
        // TODO
        CurrentStates.statsChangeType = baseStats.statsChangeType;
        CurrentStates.maxHealth = baseStats.maxHealth;
        CurrentStates.speed = baseStats.speed;

    }
}

 

끝나며 플레이한테 StatHandler를 달아줌

 

 

이렇게하면 일단 스텟은 잡힘

 

TopDownMovement에서 수정

--------------------- 생략 ---------------------
private CharacterStatsHandler _stats;
--------------------- 생략 ---------------------
private void Awake()
{
    _controller = GetComponent<TopDownCharacterController>();
    _stats = GetComponent<CharacterStatsHandler>();
    _rigidbody = GetComponent<Rigidbody2D>();
}
--------------------- 생략 ---------------------
private void ApplyMovment(Vector2 direction)
{
    direction = direction * 5;
    direction = direction * _stats.CurrentStates.speed;

    _rigidbody.velocity = direction;
}

 

TopDownCharacterController 수정

--------------------- 생략 ---------------------
public event Action<Vector2> OnMoveEvent;
public event Action<Vector2> OnLookEvent;
public event Action OnAttackEvent;

private float _timeSinceLastAttack = float.MaxValue;
protected bool IsAttacking { get; set; }

protected CharacterStatsHandler Stats { get;private set; }

protected virtual void Awake()
{
    Stats = GetComponent<CharacterStatsHandler>();
}

--------------------- 생략 ---------------------

private void HandleAttackDelay()
{
    if (Stats.CurrentStates.attackSO == null)
        return;

		if(_timeSinceLastAttack <= 0.2f)    // TODO
    if(_timeSinceLastAttack <= Stats.CurrentStates.attackSO.delay)
    {
        _timeSinceLastAttack += Time.deltaTime;
    }
    
    if(IsAttacking && _timeSinceLastAttack > 0.2f)
    if(IsAttacking && _timeSinceLastAttack > Stats.CurrentStates.attackSO.delay)
    {
        _timeSinceLastAttack = 0;
        CallAttackEvent();
    }
}

 

PlayerInputController 수정

private void Awake()
protected override void Awake()
{
    base.Awake();
    _camera = Camera.main;
}

'게임개발 > Unity' 카테고리의 다른 글

캐릭터 이동+조준  (0) 2024.02.12
3D-캐릭터  (0) 2024.02.02
3D- 환경  (0) 2024.02.02
3D- 빛  (0) 2024.02.02

우선 먼저 캐릭터를 만들어준다!

Player라는 게임오브젝트(Empty folder)를 하나 만들어주고

그 아래에  이미지(Image)를 만들고 (MainSprite) 사용될 이미지를 컴포넌트에 넣어준다.

중앙이 안맞춰질경우 (Main Sprite)의 이미지의 Y값을 올려줘도된다.

 

Controller폴더를 만들어주고

TopDownPlayerController를 만들어준다.

이 스크립트에서는 기본적인 이동방법 에 대한 코드를 작성할 것이다.

 

원초적인 방법대신 유니티가 제공하는 기능으로 만들어볼 예정

 

1.준비하기

Window → Package Manager → Unity Register → Input System → Install

2.New Input Sytem 준비하기

Create → Input Action 클릭 → Top Down Controller 2D로 수정

 

이동은W,S,A,D 

바라보는 방향은 마우스값을 가져온다.

 

이런식으로 Action을 만들어준다.
그럼 이제 스크립트에서  코드로 연결해주어야한다.

이건 TopDownPlayerController.cs에 작성한다.

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


public class TopDownCharacterController : MonoBehaviour
{
    public event Action<Vector2> OnMoveEvent;
    public event Action<Vector2> OnLookEvent;


    public void CallMoveEvent(Vector2 direction)
    {
        OnMoveEvent?.Invoke(direction);
    }

    public void CallLookEvent(Vector2 direction)
    {
        OnLookEvent?.Invoke(direction);
    }

}

Control Type이 Vector2이기 때문에 CallMoveEvent나 CallLookEvent는 Vertor2의 값을 가져온다.

그 뒤에 direction 은 w는 위 S는 아래처럼 방향을 가르킨다. 

 

이렇게 Action을 연결해주었다면

실제로 wasd의 인풋값을 받아 Action을 실행해줄 스크립트도 하나 필요하다.

TopDownPlayerController은 Player에 집어넣어줄 필요가없다.

PlayerInputController를 달아주면된다. 이는 TopDownPlayerController를 상속받기때문.


Player에게는 PlayerInputController를 달고, PlayerInput이라는 컴포넌트를 추가하고

거기에 아까 만들어둔 Action을 넣어준다.

 

 

PlayerInputController.cs를 만들어주자 이 스크립트는 TopDownPlayerController를 상속받는다.

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

public class PlayerInputController : TopDownCharacterController
{
    private Camera _camera;
    private void Awake()
    {
        _camera = Camera.main;
    }
    [캐릭터 이동할때 캐릭터를 중심으로 카메라를 고정하기 위해 작성]

    public void OnMove(InputValue value)
    {
        
        Vector2 moveInput = value.Get<Vector2>().normalized;
        CallMoveEvent(moveInput);
    }

    public void OnLook(InputValue value)
    {
      
        Vector2 newAim = value.Get<Vector2>();
        Vector2 worldPos = _camera.ScreenToWorldPoint(newAim);
        newAim = (worldPos - (Vector2)transform.position).normalized;

        if (newAim.magnitude >= .9f)
				// Vector 값을 실수로 변환
        {
            CallLookEvent(newAim);
        }
    }

    public void OnFire(InputValue value)
    {
        Debug.Log("OnFire" + value.ToString());
    }
}

 

여기까지했다면 PlayerInputController으로 TopDownPlayerController을 호출하는데 까지 온것이다.

 

그럼 실제로 이동을 시켜야한다. 

TopDownMovement.cs 생성

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

public class TopDownMovement : MonoBehaviour
{
    private TopDownCharacterController _controller;
    [현재 Player에게는 PlayerInputController가 걸려있는데 이보다 더 상위인 Controller를 받아옴] 
    private Vector2 _movementDirection = Vector2.zero;
    private Rigidbody2D _rigidbody;

    private void Awake()
    {
        _controller = GetComponent<TopDownCharacterController>();
        _rigidbody = GetComponent<Rigidbody2D>();
    }

    private void Start()
    {
        _controller.OnMoveEvent += Move;
    }

    private void FixedUpdate()
    {
        ApplyMovment(_movementDirection);
    }

    private void Move(Vector2 direction)
    {
        _movementDirection = direction;
    }

    private void ApplyMovment(Vector2 direction)
    {
        direction = direction * 5;

        _rigidbody.velocity = direction;
    }
}

 

조준

마우스의 위치에 따라

무기는 360도로 회전이 가능하게할것이며,

캐릭터는 좌우로 반전이 되게할것이다.

 

MainSprite밑에 WeaponPivot 빈오브젝트 넣기

Pivot하위에 무기가 될 이미지 투입!

SortingOrder 캐릭터보다 위로!

그 하위에 BulletSpawnPoint 오브젝트 생성.

스크립트 생성

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

public class TopDownAimRotation : MonoBehaviour
{
    [SerializeField] private SpriteRenderer armRenderer; [WeaponSprite]
    [SerializeField] private Transform armPivot;  [Weapon Pivot]

    [SerializeField] private SpriteRenderer characterRenderer; [MainSprite]

    private TopDownCharacterController _controller; [바라보는 방향으로 쏠것]

    private void Awake()
    {
        _controller = GetComponent<TopDownCharacterController>();    
    }

    void Start()
    {
        _controller.OnLookEvent += OnAim;
    }

    public void OnAim(Vector2 newAimDirection)
    {
        RotateArm(newAimDirection);
    }

    private void RotateArm(Vector2 direction)
    {
        float rotZ = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

        armRenderer.flipY = Mathf.Abs(rotZ) > 90f;
        characterRenderer.flipX = armRenderer.flipY;
        armPivot.rotation = Quaternion.Euler(0, 0, rotZ);
    }
}

 

공격

우선 화살이 날아가게 해줄것이기 떄문에 Arrow 만들어주기

 

 

PlayerInputController.cs 에서 OnFire 재정의

public void OnFire(InputValue value)
{
    IsAttacking = value.isPressed;
}

 

 

TopDownCharacterController에서  

어택에 속도를 조절해준다.

0.2초마다 공격이 가능하게 설정 하는 코드

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


public class TopDownCharacterController : MonoBehaviour
{
  //public event Action<Vector2> OnMoveEvent;
  //public event Action<Vector2> OnLookEvent;
    public event Action OnAttackEvent;
    
    private float _timeSinceLastAttack = float.MaxValue;
    protected bool IsAttacking {get ; set;}

    protected virtual void Update()
    {
       HandleAttackDelay();
    }
    
    private void HandleAttackDelay()
    {
        if(_timeSinceLastAttack <= 0.2f)    // TODO
         {
            _timeSinceLastAttack += Time.deltaTime;
         }
    
        if(IsAttacking && _timeSinceLastAttack > 0.2f)
         {
            _timeSinceLastAttack = 0;
            CallAttackEvent();
         }
     }


  //public void CallMoveEvent(Vector2 direction)
  // {
  //      OnMoveEvent?.Invoke(direction);
  //  }

   // public void CallLookEvent(Vector2 direction)
   // {
   //   OnLookEvent?.Invoke(direction);
   //}

     public void CallAttackEvent()
     {
      OnAttackEvent?.Invoke();
     }
}

 

 

 

TopShooting.cs 에서는 진짜 쏘는것 위에서는 호출하는것 까지!

 

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

public class TopDownShooting : MonoBehaviour
{
    private TopDownCharacterController _contoller;

    [SerializeField] private Transform projectileSpawnPosition;
    private Vector2 _aimDirection = Vector2.right;

    public GameObject testPrefab;

    private void Awake()
    {
        _contoller = GetComponent<TopDownCharacterController>();
    }

    // Start is called before the first frame update
    void Start()
    {
        _contoller.OnAttackEvent += OnShoot;
        _contoller.OnLookEvent += OnAim;
    }

    private void OnAim(Vector2 newAimDirection)
    {
        _aimDirection = newAimDirection;
    }

    private void OnShoot()
    {
        CreateProjectile();
    }

    private void CreateProjectile()
    {
        Instantiate(testPrefab, projectileSpawnPosition.position,Quaternion.identity);
    }
}

'게임개발 > Unity' 카테고리의 다른 글

캐릭터 스텟만들기  (0) 2024.02.13
3D-캐릭터  (0) 2024.02.02
3D- 환경  (0) 2024.02.02
3D- 빛  (0) 2024.02.02

ATM 과제 중 기능을 구현하였지만 GameManager.cs에 몰아서 하였다.

하지만 Class를 나눠야해야 나중에 협업할때도 좋고  유지보수도 좋은 점을 알고있어서

스크립트를 분리하려다가 클라스를 나누는 기준?점을 인지하고있지 않아 실패를 하였다.

 

해설 강의를 보고 좀 스크립트를 나누고 하는법을 알게되었다.

 

UI에서 바뀌는 텍스트값을 연결시켜주기

 

[UserData.cs]

[System.Serializable]
public class UserData
{
    public string Name;
    public int Balance;
    public int Cash;

    

    public UserData(string name,int balance,int cash)
    {
        Name = name;
        Balance = balance;
        Cash = cash;
  

    }

 

 

[GameManager.cs] 에다가 UserData.cs을 붙여준다.

public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    public UserData User;


    private void Awake()
    {
        if(Instance == null)
            Instance = this;
        else
            Destroy(gameObject);
    }

 

 

UI를 관리하는 [PopUpBank.cs]에서 gamaManager을 통해 UserData값을 가져온다.

public class PopUpBank : MonoBehaviour
{
    [SerializeField] private Text userName;
    [SerializeField] private Text balance;
    [SerializeField] private Text cash;
    [SerializeField] private GameObject popupError;
    
    
    private void Start()
    {
        Refresh();
    }
    
     private void Refresh()
    {
     userName.text = GameManager.Instance.User.Name;
     balance.text = GameManager.Instance.User.Balance.ToString();
     cash.text = GameManager.Instance.User.Cash.ToString();
  
    }
 }

 

유니티에서 해당 텍스트들을 끌여다주면된다.

 

게임매니저에게는 이렇게 노출해주고싶은걸 넣어주면된다.

 

그럼 게임페이지에서 노출이 이렇게된다.

'TIL' 카테고리의 다른 글

TIL-2024/03/12  (0) 2024.03.12
TIL-2024/02/21(버튼을 눌렀을때 저장법)  (0) 2024.02.21
TIL-2024/02/02  (0) 2024.02.02
TIL-2024/02/01  (0) 2024.02.01
TIL-2024/01/30  (0) 2024.01.30

강의를 듣다가 자꾸 궁금한게 생겨 딴곳으로 빠지게된다..ㅠ

InputActionPhase.뒤에 어떤것들이 올수있을까? 

if(context.phase == InputActionPhase.Performed)

 

 

  • Started: 실행 시작 시 호출
  • Performed: 실행 확정 (완전히 실행) 시 호출
  • Canceled: 실행 종료 시 호출
  • Disabled: 액션이 활성화되지 않음
  • Waiting: 액션이 활성화되어있고 입력을 기다리는 상태

일반적인 버튼 입력 상황 (Default)에서는 Started와 Performed가 같은 타이밍에 실행(실행 순서는 Started가 먼저)
그리고 Canceled는 버튼을 뗐을 때 실행됨

   public void OnMoveInput(InputAction.CallbackContext context)
   {
       if(context.phase == InputActionPhase.Performed) // 만약 입력을 받고있다면,마우스가 움직인다면
       {
           curMovementInput = context.ReadValue<Vector2>();
       }

       else if(context.phase == InputActionPhase.Canceled) //안움직이면
       {
           curMovementInput = Vector2.zero;
       }
   }

 

 

강의와 예외로 구글링을 하다가 Hold에 대한걸 찾았다.

입력 시스템의 Interaction 중 Hold에 대한 예시를 들어보면,

Hold를 감지하기 위한 pressPoint가 있고, 이 pressPoint를 넘어가는 순간 started가 실행됩니다.
그리고 그 상태에서 duration만큼의 시간이 흐르면 그때가 되어서야 performed가 실행됩니다.

스마트폰 홈 화면에서 앱을 누르고 홀드하는 것과 비슷한 경우입니다.
그냥 터치했을 때는 앱이 실행되고, 오래 터치하고 있으면 앱 위치를 이동하는 모드로 변경되는 것처럼요.

 

뭔가 꾹누르고있을때 요리를한다던가 이런곳에 쓸수있을것같다.

아래어떻게 쓰는지 몰라서 gpt에게 물어본 코드이다.

using UnityEngine.InputSystem;

public class AppController : MonoBehaviour
{
    private InputAction touchAction;
    private bool isHolding = false;

    void Start()
    {
        touchAction = new InputAction(type: InputActionType.PassThrough, binding: "<Touchscreen>/primaryTouch");
        touchAction.AddInteraction("hold(duration=2)"); // 지속시간을 2초로 설정
        touchAction.started += context => OnTouchStarted(context);
        touchAction.performed += context => OnTouchHeld(context);
        touchAction.Enable();
    }

    private void OnTouchStarted(InputAction.CallbackContext context)
    {
        isHolding = false;
        Debug.Log("App is activated");
        // App activation code goes here
    }

    private void OnTouchHeld(InputAction.CallbackContext context)
    {
        if (!isHolding)
        {
            isHolding = true;
            Debug.Log("Switch to app moving mode");
            // App moving mode code goes here
        }
    }
}

 

 

이해가 가지 않는 코드해석해보기 -1
 public void OnLookInput(InputAction.CallbackContext context) 
 {
     mouseDelta = context.ReadValue<Vector2>(); 
    
 }

 

 public void OnLookInput(InputAction.CallbackContext context) ==

OnLookInput 함수를 실행할때 Player에 붙여둔 Events 내에 설정해둔 Look을 매개체로 쓰겠다).

 

Look Action에 할당된건 Delta[Mouse]이므로

 

 mouseDelta = context.ReadValue<Vector2>(); 를 읽어온다.

 

 

 

이해가 가지 않는 코드해석해보기 -2

 

    void CameraLook()  
    {
        camCurXRot += mouseDelta.y * lookSensitivity;
        camCurXRot = Mathf.Clamp(camCurXRot,minXLook,maxXLook); 
                                                               
        cameraContainer.localEulerAngles = new Vector3(-camCurXRot, 0, 0); 
        transform.eulerAngles += new Vector3(0, mouseDelta.x * lookSensitivity, 0);
    }

  

 

    카메라가 바라보는 코드 ,마우스로 움직일수있게할꺼임 ()
    {

     [첫째 줄]
        x축이 축이되어야 y의 값이 변하기 때문에, x = 마우스위아래(Y)하는거리 * 민감도(속력)

    [둘째 줄]
       위의 끄덕임을 더 돌아가지않게 가두기
       Mathf.Clamp (첫번째 매개변수 value는 집어 넣을값(=검사할값) , 두번째 매개변수 min 최소값 , 세번째 매개변수                max최대값) 

    [셋째 줄]
    마우스를 상하로 움직이는 각도를 조절하는 "localEulerAngles" ,

    x축에 ( - ) 를 붙여주는 이유는 ??

    실제로 로테이션을 돌렸을떄 마우스를 밑으로 내리면서 시야가 위로 향할때 x가 -가되기떄문


     [넷째 줄] 

    도리도리가능하게 y축을 (x축의거리*속력) 만큼 이동
    }

'TIL' 카테고리의 다른 글

TIL-2024/02/21(버튼을 눌렀을때 저장법)  (0) 2024.02.21
TIL-2024/02/08  (0) 2024.02.08
TIL-2024/02/01  (0) 2024.02.01
TIL-2024/01/30  (0) 2024.01.30
TIL-2024/01/29  (0) 2024.01.29

player추가

player하위에 CameraContainer 빈폴더 생성

아래로 Main Camera 끌어다놓고 reset해주기

 

input Action을 만들어주자

 

inputAction 만들어주고 세이브

 

플레이어에게 capsule Collider 추가 키는 170으로 할것이기떄문에

center값의 y를 0.85,

반지름 0.25

height 를 1.7 설정.

 

 

Rigidbody 추가

무게 20

Freeze Rotation xyz 활성화

 

만들어둔 컨트롤러와 스크립트 먹이기

 

Invoke Unity Events로 변경

 

Scripts에서 지정해준 변수 지정

 

 

MoveAction에 playerController 추가해주기

 

 

PlayerController Script

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

public class PlayerController : MonoBehaviour
{
    [Header("Movement")]
    public float moveSpeed;
    private Vector2 curMovementInput;
    public float jumpForce;
    public LayerMask groundLayerMask; // 딛고 있는곳이 그라운드인지 체크용도

    [Header("Look")]
    public Transform cameraContainer;
    public float minXLook;
    public float maxXLook;
    private float camCurXRot;       // 카메라가 바라보게할때 씀
    public float lookSensitivity;   // 회전 민감도

    private Vector2 mouseDelta;

    [HideInInspector]
    public bool canLook = true;

    private Rigidbody _rigidbody;

    public static PlayerController instance;  // 플레이어가 단일 플레이어여서 간편하게 만듬

    private void Awake()
    {
        instance = this;
        _rigidbody = GetComponent<Rigidbody>();
    }
    void Start()
    {
        Cursor.lockState = CursorLockMode.Locked; // 커서가 안보이게!
    }


    private void FixedUpdate()
    {
        Move();
    }

    private void LateUpdate()  //모든 처리가 끝나고 발동 ,카메라 작업에 많이 쓰임
    {
       if(canLook)
        {
            CameraLook();
        }
    }

    private void Move()  // 이걸 받아와서 실행되는 함수 OnMoveInput(InputAction.CallbackContext context)
    {
        Vector3 dir = transform.forward * curMovementInput.y + transform.right * curMovementInput.x; //transform.forward 와 transform.right는 유니티에서 정해놓은 방향값 left나이런건 사용안됌 y축은 foward, x축은right
        dir *= moveSpeed;  // 입력받은 값에 speed 곱하기 
        dir.y = _rigidbody.velocity.y;  // y축으로 가면 날아가기 떄문에 y를없애야하는데 없애는 대신 y를 방향.y = 리지드버디.가속도.y로 지정해준다.

        _rigidbody.velocity = dir;
    }

    void CameraLook()  // 카메라가 바라보는 코드 ,마우스로 움직일수있게할꺼임
    {
        camCurXRot += mouseDelta.y * lookSensitivity; //마우스를 위아래로 했을때 x축으로 끄덕끄덕거리기떄문
        camCurXRot = Mathf.Clamp(camCurXRot,minXLook,maxXLook); // 위의 끄덕임을 더 돌아가지않게 가두기
                                                                // 첫번째 매개변수 value는 집어 넣을값(=검사할값)두번째 매개변수 min 최소값.세번째 매개변수 max 최대값.반환은 결과 값 입니다.
        cameraContainer.localEulerAngles = new Vector3(-camCurXRot, 0, 0); //마우스를 상하로 움직이는것에 대해서 움직일수있게 -를 붙여주는 이유는 실제로 로테이션을 돌렸을떄 마우스를 밑으로 내렸을떄 x가 -가되기떄문
        transform.eulerAngles += new Vector3(0, mouseDelta.x * lookSensitivity, 0); // 도리도리가능하게 y축을 거리*속력 만큼 이동
    }


    public void OnLookInput(InputAction.CallbackContext context) // 입력을 받아서 처리하는곳
    {
        mouseDelta = context.ReadValue<Vector2>(); //이벤트랑 연결 예정, 마우스델타는 vector2로 읽어오겠다.
    }

    public void OnMoveInput(InputAction.CallbackContext context)
    {
        if(context.phase == InputActionPhase.Performed) // 만약 context.상태가 
        {
            curMovementInput = context.ReadValue<Vector2>();
        }

        else if(context.phase == InputActionPhase.Canceled)
        {
            curMovementInput = Vector2.zero;
        }
    }

    public void OnJumpInput(InputAction.CallbackContext context)
    {
        if(context.phase == InputActionPhase.Started)
        {
            if(IsGrounded())
            _rigidbody.AddForce(Vector2.up * jumpForce, ForceMode.Impulse);
        }
    }

    private bool IsGrounded()
    {
        Ray[] rays = new Ray[4]
        {
            new Ray(transform.position + (transform.forward * 0.2f) + (Vector3.up * 0.01f) , Vector3.down),
            new Ray(transform.position + (-transform.forward * 0.2f)+ (Vector3.up * 0.01f), Vector3.down),
            new Ray(transform.position + (transform.right * 0.2f) + (Vector3.up * 0.01f), Vector3.down),
            new Ray(transform.position + (-transform.right * 0.2f) + (Vector3.up * 0.01f), Vector3.down),
        };

        for (int i = 0; i < rays.Length; i++)
        {
            if (Physics.Raycast(rays[i], 0.1f, groundLayerMask))
            {
                return true;
            }
        }

        return false;
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawRay(transform.position + (transform.forward * 0.2f), Vector3.down);
        Gizmos.DrawRay(transform.position + (-transform.forward * 0.2f), Vector3.down);
        Gizmos.DrawRay(transform.position + (transform.right * 0.2f), Vector3.down);
        Gizmos.DrawRay(transform.position + (-transform.right * 0.2f), Vector3.down);
    }

}

'게임개발 > Unity' 카테고리의 다른 글

캐릭터 스텟만들기  (0) 2024.02.13
캐릭터 이동+조준  (0) 2024.02.12
3D- 환경  (0) 2024.02.02
3D- 빛  (0) 2024.02.02
스카이박스

 

게임 세계의 배경을 둘러싸는 환경 매핑 기술입니다. 큐브 맵(Cube Map)과 구체형 스카이박스(Sphere Map) 등이 있으며, 주로 다음과 같은 특징을 가집니다.

  • 스카이박스는 6개의 텍스처로 구성된 큐브 맵 또는 하나의 구체로 텍스처가 매핑된 구체형 스카이박스로 구성된다.
  • Unity에서는 씬의 배경으로 사용되며, 게임 환경을 확장시키는데 활용된다.
  • 주로 하늘, 구름, 산 등의 자연적인 배경을 표현하는 데 사용됨.
  • 미리 만들어진 스카이박스를 사용하거나 직접 만들어서 Unity에서 적용할 수 있다.
  • 게임 중에 스카이박스를 동적으로 변경하여 낮과 밤 등의 시간대나 특정 이벤트에 맞게 배경을 변화시킬 수 있다.
  • 성능에 영향을 미치므로 최적화에 주의해야 함!

 

Rigidbody - ForceMode

 

아래 Rigidbody - ForceMode 를 적절히 활용하여 게임 오브젝트에 원하는 물리적인 움직임과 효과를 부여 가능하다.

 

주요한 ForceMode 종류는 다음과 같다.

Force: 힘을 지속적으로 적용합니다.

Rigidbody.AddForce(Vector3 force, ForceMode.Force);

 

Acceleration: 가속도를 적용합니다. 이전 힘의 누적에 따라서 점진적으로 더 빠르게 움직이게 됩니다.

Rigidbody.AddForce(Vector3 force, ForceMode.Acceleration);

 

Impulse: 순간적인 힘을 적용합니다. 짧은 시간에 갑작스러운 움직임이 발생합니다.

 

Rigidbody.AddForce(Vector3 force, ForceMode.Impulse);

 

VelocityChange: 변화하는 속도를 적용합니다. 물체의 현재 속도를 변경하면서 움직입니다.

Rigidbody.AddForce(Vector3 force, ForceMode.VelocityChange);

 

이러한 **ForceMode**를 적절히 활용하여 게임 오브젝트에 원하는 물리적인 움직임과 효과를 부여할 수 있습니다.

'게임개발 > Unity' 카테고리의 다른 글

캐릭터 스텟만들기  (0) 2024.02.13
캐릭터 이동+조준  (0) 2024.02.12
3D-캐릭터  (0) 2024.02.02
3D- 빛  (0) 2024.02.02
Light 컴포넌트

라이트 소스: 게임 또는 3D 랜더링에 광원을 추가하는 데 사용됩니다. 이것은 특정 위치 또는 방향에서 발생하는 빛을 나타냅니다.

Type

-Directional : 현실세계의 태양과 같은 것. 전 범위에 걸쳐서, 동일한 세기와 각도로 빛을 내리쬐는 것. 

-Spot : spotlight처럼 쭉 뻗어있다.

-Point : 일반 전구라고 생각하면 됨. 동그랗게 빛이 퍼져나간다. 

-Area: bake 전용인데, tv라고 생각하면 됨. 직사각형 모양에서 빛이 새어나온다고 생각하면 됨.

 

Color: 말그대로 조명의 색을 바꾸는 것.

 

Mode

-Realtime: 매 프레임마다 광원을 계산해서, 어느 위치에 그림자를 뿌리고, 밝기조정을 해주는 것. 매 프레임마다 계산하므로, 과부하가 걸리기 쉬움. 최적화를 시킬 때는 realtime을 되도록 사용하지 않아야 한다. 

-Mixed: Realtime과 Baked가 섞였다. 

-Baked: 최적화를 위해 나온 것. 조명을 최초 1회 계산하고, 그 다음부터 계산하지 않고 그 상태 그대로 보여주는 것.

(한 번 고정된 배경 등에 많이 활용됨)

baked 된 상태에서 새로운 객체가 추가되면, 빛에 영향을 받지 않게 됨. (새객체를 추가하고, game object를 생성해서, game object에 light probe group을 추가해서 주변 광을 흡수할 수 있게 해줄 수 있음 - chap5. 참조)

 

Intensity : 빛의 세기. 올릴수록 빛의 세기가 커짐

 

Indirect Multiplier : 간접광의 세기.

직접광: 태양에서 나오는 빛. 이 직접광이 표면에 반사되어 object를 비추게 될 때 그 빛을 간접광이라고 한다. (Indirect Multiplier가 클수록 간접광의 영향을 많이 받는다.)

 

Shadow Types

- No Shadow : 그림자가 사라짐

- Hard Shadow : 테두리가 선명한 그림자

- Soft Shadow : 테두리가 흐릿한 그림자

strength : 빛의 세기

resolution : 그림자의 퀄리티

bias : 그림자자체를 밀어낸다. (버그나, 부자연스러운 그림자가 있을 때 사용)

normal bias: 그림자를 쪼글어들게(?) 한다. (버그 체크 시 사용)

near plane: 태양의 경우에는 해당되지 않고, clipping 같은것. 가까이 있는 것부터 멀리있는 것까지 사라지도록 하는 것. 

 

Cookie : 빛을 방해하는 존재

 

Draw Halo : 광원효과(후광)

Flare : 카메라가 태양을 바라봤을 때, 어떤 렌즈 효과가 생기는지

 

RenderMode

- Important : 품질이 좋아짐

- Not Important : 대충처리 (배경 등)

 

Culling Mask : 특정 layer를 비출지 안비출지 (조명에 영향을 받을 layer를 고르는 것)

 

public calss light
{
   private Light thelight;
  
  // 현재 Intensity를 targetIntensity값에 강제로 맞춰주도록 변수 2개설정
   private float targetIntensity;
   private float curIntensity;
   
   
      void Start()
     {
       thelight = GetComponent<Light>();
       curIntensity = thelight.intensity;  
       targetIntensity = Random.Range(0.4f ,1f); 
      }
      
      
      void Update()
      { 
       if(Mathf.Abs(tragetIntensity - curIntensity) >= 0.01) //두값이 다른경우 계속 반복
       { 
           if(targetIntensity -curIntensity >=0)
            {
               curIntensity +=(Time.deltaTime /2) *3f;
            }
            else
            { 
                curIntensity -=(Time.deltaTime /2) *3f;
            }
          
             thelight.intensity = curIntensity; //실시간으로 intensity값을 반영해줌
             thelight.range = curIntensity +10; // 10언저리 범위에서 왔다갔다
         }
         else
         { 
            targetIntensity = Random.Range(0.4f,1f);
         }
  }

'게임개발 > Unity' 카테고리의 다른 글

캐릭터 스텟만들기  (0) 2024.02.13
캐릭터 이동+조준  (0) 2024.02.12
3D-캐릭터  (0) 2024.02.02
3D- 환경  (0) 2024.02.02
GameObject
 함수 이름 설명 
 Find  오브젝트 이름으로 검색하여 가장 처음에 나오는 오브젝트를 GameObject로 반환한다.
 FindWIthTag  태그 이름으로 검색해서 가장 처음에 나타난 오브젝트를 GameObject로 반환한다.
 FindGameObjectsWithTag
 태그 이름으로 검색해서 나타난 오브젝트 여러개를 GameObject 배열로 반환한다.
 GameObject.FindObjectOfType  오브젝트형(혹은 컴포넌트의 형)으로 검색해서 가장 처음 나타난 오브젝트를 GameObject로 반환한다.
(유효한 오브젝트만)
 GameObject.FindObjectsOfType  오브젝트형(혹은 컴포넌트의 형)으로 검색해서 가장 처음 나타난 오브젝트 여러개를 GameObject 배열로 반환한다.
(유효한 오브젝트만) 

 

Transform
 함수 이름 설명 
 Find  Object의 이름으로 자식 오브젝트를 검색해, 가장 처음에 나타난 자식 오브젝트를 반환한다.
 GetComponentInChildren  컴포넌트 형으로 자식 오브젝트를 검색해서 처음 나타난 자식 오브젝트를 반환한다.
 GetComponentsInChildren
 컴포넌트 형으로 자식 오브젝트를 검색해서 나타난 자식 오브젝트들의 배열을 반환한다.
 GetComponentInParent  컴포넌트 형으로 부모 오브젝트를 검색해, 가장 처음에 나타난 부모 오브젝으를 반환한다.
 GetComponentsInParent  컴포넌트 형으로 부모오브젝트를 검색해서 나타난 부모 오브젝트들의 배열을 반환한다.
 Transform.FindObjectOfType  오브젝트형(혹은 컴포넌트의 형)으로 검색해서 가장 처음 나타난 오브젝트를 반환한다.
(유효한 오브젝트만)
 Transform.FindObjectsOfType   오브젝트형(혹은 컴포넌트의 형)으로 검색해서 나타난 여러개의 Object들을 배열의 형태로 반환한다.
(유효한 오브젝트만) 

'TIL' 카테고리의 다른 글

TIL-2024/02/08  (0) 2024.02.08
TIL-2024/02/02  (0) 2024.02.02
TIL-2024/01/30  (0) 2024.01.30
TIL-2024/01/29  (0) 2024.01.29
TIL-2024/01/25  (0) 2024.01.25

맵이 큰  경우 플레이어가 이곳저곳 돌아다니는데 카메라도 같이 따라 움직이게하는 기능이다.

 

이 코드는 내가 작성한게 아니지만 배울수있어 추가해본다.

 

public class CameraController : MonoBehaviour
{
    private Transform player;
    private float position_x;
    private float position_y;

    void Update()
    {
        player = GameManager.instance.player.transform;

        if (player.position.x < -29 || player.position.x > 34)
        {
            if (player.position.x < 0)
            {
                position_x = -29;
            }
            else
            {
                position_x = 34;
            }
        }
        else
        {
            position_x = player.position.x;
        }
        if (player.position.y < -32.5 || player.position.y > 27)
        {
            if (player.position.y < 0)
            {
                position_y = -32.5f;
            }
            else
            {
                position_y = 27;
            }
        }
        else
        {
            position_y = player.position.y;
        }

        transform.position = new Vector3(position_x, position_y, transform.position.z);
    }
}

'TIL' 카테고리의 다른 글

TIL-2024/02/02  (0) 2024.02.02
TIL-2024/02/01  (0) 2024.02.01
TIL-2024/01/29  (0) 2024.01.29
TIL-2024/01/25  (0) 2024.01.25
TIL-2024/01/24  (0) 2024.01.24

+ Recent posts