pc와 모바일 동시 빌드시 input system 설정
Move value Vector2 <Keyboard>/w, <Keyboard>/s, <Keyboard>/a, <Keyboard>/d
TouchDrag Pass Through <Touchscreen>/primaryTouch/delta
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private PlayerInputActions inputActions;
private Vector2 moveInput;
private Vector2 touchDelta;
public float moveSpeed = 5f;
private void Awake()
{
inputActions = new PlayerInputActions();
}
private void OnEnable()
{
inputActions.Player.Enable();
#if UNITY_STANDALONE || UNITY_EDITOR // PC 빌드 (Standalone & Editor)
inputActions.Player.Move.performed += OnMove;
inputActions.Player.Move.canceled += OnMove;
#endif
#if UNITY_ANDROID || UNITY_IOS // 모바일 빌드
inputActions.Player.TouchDrag.performed += OnTouchDrag;
inputActions.Player.TouchDrag.canceled += OnTouchDrag;
#endif
}
private void OnDisable()
{
#if UNITY_STANDALONE || UNITY_EDITOR
inputActions.Player.Move.performed -= OnMove;
inputActions.Player.Move.canceled -= OnMove;
#endif
#if UNITY_ANDROID || UNITY_IOS
inputActions.Player.TouchDrag.performed -= OnTouchDrag;
inputActions.Player.TouchDrag.canceled -= OnTouchDrag;
#endif
inputActions.Player.Disable();
}
private void OnMove(InputAction.CallbackContext context)
{
#if UNITY_STANDALONE || UNITY_EDITOR
moveInput = context.ReadValue<Vector2>();
#endif
}
private void OnTouchDrag(InputAction.CallbackContext context)
{
#if UNITY_ANDROID || UNITY_IOS
touchDelta = context.ReadValue<Vector2>();
if (touchDelta.y > 50) moveInput = new Vector2(0, 1); // 위로 드래그 → W 입력
else if (touchDelta.y < -50) moveInput = new Vector2(0, -1); // 아래로 드래그 → S 입력
else if (touchDelta.x > 50) moveInput = new Vector2(1, 0); // 오른쪽 드래그 → D 입력
else if (touchDelta.x < -50) moveInput = new Vector2(-1, 0); // 왼쪽 드래그 → A 입력
else moveInput = Vector2.zero;
#endif
}
private void Update()
{
Vector3 moveDir = new Vector3(moveInput.x, 0, moveInput.y);
transform.position += moveDir * moveSpeed * Time.deltaTime;
}
}
player input system에 키보드 입력과 터치 입력 (touchDelta)를 활용하여 입력 시스템을 구축하고 빌드별 전처리기문을 통하여 빌드별 입력시스템을 나눌수있다 이를 버튼으로 적용한다면
inpusystem 세팅
Jump button <Keyboard>/w, <Touchscreen>/primaryTouch/delta
Slide // //
MoveLeft // //
MoveRight // //
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private PlayerInputActions inputActions;
private Vector2 touchDelta;
public float jumpForce = 5f;
public float slideDuration = 1f;
public float laneDistance = 3f; // 좌우 이동 거리
private int currentLane = 1; // 0 = 왼쪽, 1 = 중앙, 2 = 오른쪽
private bool isSliding = false;
private Rigidbody _rigidbody;
private Animator _animator;
private void Awake()
{
inputActions = new PlayerInputActions();
_rigidbody = GetComponent<Rigidbody>();
_animator = GetComponent<Animator>();
}
private void OnEnable()
{
inputActions.Player.Enable();
#if UNITY_STANDALONE || UNITY_EDITOR // PC 빌드 (키보드)
inputActions.Player.Jump.performed += OnJump;
inputActions.Player.Slide.performed += OnSlide;
inputActions.Player.MoveLeft.performed += OnMoveLeft;
inputActions.Player.MoveRight.performed += OnMoveRight;
#endif
#if UNITY_ANDROID || UNITY_IOS // 모바일 빌드 (터치)
inputActions.Player.TouchDrag.performed += OnTouchDrag;
inputActions.Player.TouchDrag.canceled += OnTouchDrag;
#endif
}
private void OnDisable()
{
#if UNITY_STANDALONE || UNITY_EDITOR
inputActions.Player.Jump.performed -= OnJump;
inputActions.Player.Slide.performed -= OnSlide;
inputActions.Player.MoveLeft.performed -= OnMoveLeft;
inputActions.Player.MoveRight.performed -= OnMoveRight;
#endif
#if UNITY_ANDROID || UNITY_IOS
inputActions.Player.TouchDrag.performed -= OnTouchDrag;
inputActions.Player.TouchDrag.canceled -= OnTouchDrag;
#endif
inputActions.Player.Disable();
}
private void OnJump(InputAction.CallbackContext context)
{
if (isSliding) return; // 슬라이딩 중 점프 방지
_rigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
_animator.SetTrigger("Jump");
}
private void OnSlide(InputAction.CallbackContext context)
{
if (isSliding) return;
isSliding = true;
_animator.SetTrigger("Slide");
Invoke(nameof(EndSlide), slideDuration);
}
private void EndSlide()
{
isSliding = false;
}
private void OnMoveLeft(InputAction.CallbackContext context)
{
if (currentLane > 0)
{
currentLane--;
MoveToLane();
}
}
private void OnMoveRight(InputAction.CallbackContext context)
{
if (currentLane < 2)
{
currentLane++;
MoveToLane();
}
}
private void MoveToLane()
{
Vector3 targetPosition = new Vector3((currentLane - 1) * laneDistance, transform.position.y, transform.position.z);
transform.position = targetPosition;
}
private void OnTouchDrag(InputAction.CallbackContext context)
{
#if UNITY_ANDROID || UNITY_IOS
touchDelta = context.ReadValue<Vector2>();
if (touchDelta.y > 50) OnJump(new InputAction.CallbackContext()); // 위로 드래그 → 점프
else if (touchDelta.y < -50) OnSlide(new InputAction.CallbackContext()); // 아래로 드래그 → 슬라이드
else if (touchDelta.x > 50) OnMoveRight(new InputAction.CallbackContext()); // 오른쪽 드래그 → 오른쪽 이동
else if (touchDelta.x < -50) OnMoveLeft(new InputAction.CallbackContext()); // 왼쪽 드래그 → 왼쪽 이동
#endif
}
}
각 입력값 별 실행 메서드를 작성해주고 touchDrag에서 delta.y와 delta.x 값별 실행 메서드를 달아주는것이 바람직하다.
제너릭 오브젝트풀
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool<T> where T : MonoBehaviour
{
private Dictionary<string, Queue<T>> poolDictionary = new Dictionary<string, Queue<T>>();
private Transform parentTransform; // 풀의 부모 Transform (Hierarchy 정리용)
public ObjectPool(Transform parent = null)
{
parentTransform = parent;
}
/// <summary>
/// 특정 프리팹을 기반으로 오브젝트를 풀에서 가져오거나 새로 생성한다.
/// </summary>
public T GetFromPool(T prefab, Vector3 position, Quaternion rotation)
{
string key = prefab.name;
if (!poolDictionary.ContainsKey(key) || poolDictionary[key].Count == 0)
{
// 풀에 오브젝트가 없으면 새로 생성
T newObj = Object.Instantiate(prefab, position, rotation);
newObj.name = key; // 이름 유지
return newObj;
}
// 풀에서 오브젝트 가져오기
T obj = poolDictionary[key].Dequeue();
obj.transform.SetPositionAndRotation(position, rotation);
obj.gameObject.SetActive(true);
return obj;
}
/// <summary>
/// 사용한 오브젝트를 다시 풀에 반환한다.
/// </summary>
public void ReturnToPool(T obj)
{
string key = obj.name;
if (!poolDictionary.ContainsKey(key))
{
poolDictionary[key] = new Queue<T>();
}
obj.gameObject.SetActive(false);
obj.transform.SetParent(parentTransform); // 정리용
poolDictionary[key].Enqueue(obj);
}
/// <summary>
/// 특정 프리팹을 미리 지정된 개수만큼 생성하여 풀에 채운다.
/// </summary>
public void Preload(T prefab, int count)
{
string key = prefab.name;
if (!poolDictionary.ContainsKey(key))
{
poolDictionary[key] = new Queue<T>();
}
for (int i = 0; i < count; i++)
{
T obj = Object.Instantiate(prefab);
obj.name = key;
obj.gameObject.SetActive(false);
obj.transform.SetParent(parentTransform);
poolDictionary[key].Enqueue(obj);
}
}
}
오브젝트 풀을 딕셔너리<string, Queue<T>> 로 구성하게 되면 다양한 T 객체의 다양한 오브젝트를 string name으로 만들수 있다
이때 주의점은 instantiate된 프리팹의 객체의 이름을 원래의 이름으로 돌려주지 않으면 string key값을 제대로 받지 못하게 된다 이를 통해 retuntopool 역시 제네릭으로 구성 가능하다
public void CollectObjects()
{
if (MapManager.Instance == null)
{
return;
}
CollectObjects<Obstacle>(this.transform, MapManager.Instance.obstaclePool);
CollectObjects<Structure>(this.transform, MapManager.Instance.structurePool);
CollectObjects<Item>(this.transform, MapManager.Instance.itemPool);
CollectCoins();
}
void CollectObjects<T>(Transform parentTransform, ObjectPool<T> pool) where T : MonoBehaviour
{
if (parentTransform == null) return;
T[] objects = parentTransform.GetComponentsInChildren<T>(true); // 모든 자식 T 객체 가져오기
foreach (T obj in objects)
{
if (obj == null) continue;
pool.ReturnToPool(obj,obj.gameObject); // 풀로 반환
}
}
메서드 역시 제너릭으로 구성하면 코드 중복없이 활용이 가능하다.