오늘은 캐릭터의 손(Sprites) 애니메이션과 동작을 관리하는 Hand 스크립트를 작성했습니다. 각 손의 위치와 회전을 캐릭터의 방향(뒤집힘 여부)에 맞춰 조정하고, 다양한 아이템에 따라 손에 착용할 스프라이트를 변경할 수 있도록 했습니다.
Hand 클래스 정의
Hand 클래스는 MonoBehaviour를 상속받아, 왼손과 오른손을 구분할 수 있는 isLeft 변수와 해당 손의 SpriteRenderer를 담는 spriter 변수를 포함합니다.
player 변수로 부모 오브젝트의 SpriteRenderer를 받아와, 캐릭터의 방향성을 확인합니다.
손의 위치와 회전 값 설정
오른손과 왼손에 대한 위치(rightPos, rightPosReverse)와 회전(leftRot, leftRotReverse) 값을 미리 정의해 두었습니다.
캐릭터의 방향(뒤집힘 여부)에 따라 적절한 값으로 손의 위치와 회전을 조정합니다.
LateUpdate 메소드
LateUpdate() 메소드에서는 player.flipX 값을 통해 캐릭터가 뒤집어졌는지 여부를 확인합니다.
왼손(isLeft = true)일 때는 회전 값과 flipY를 조정하고, 오른손일 때는 위치와 flipX 값을 조정하여 손의 스프라이트를 반전시킵니다.
또한, 손이 앞쪽인지 뒤쪽인지에 따라 sortingOrder 값을 조정하여 적절한 레이어링을 적용했습니다.
Hand 배열 초기화
Hand[] hands = GetComponentsInChildren(true); 코드로 Hand 배열을 초기화하고, 각 손 오브젝트를 가져옵니다.
아이템에 따라 손 스프라이트 변경
player.hands[(int)data.itemType]로 해당 아이템 타입에 맞는 손을 선택하고, hand.spriter.sprite = data.hand를 통해 손의 스프라이트를 변경합니다.
선택된 손 오브젝트를 활성화(hand.gameObject.SetActive(true))하여 아이템이 캐릭터 손에 장착되도록 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Hand : MonoBehaviour
{
public bool isLeft;
public SpriteRenderer spriter;
SpriteRenderer player;
Vector3 rightPos = new Vector3(0.09f, -0.11f, 0);
Vector3 rightPosReverse = new Vector3(-0.09f, -0.11f, 0);
Quaternion leftRot = Quaternion.Euler(0.06f, -0.14f, -100);
Quaternion leftRotReverse = Quaternion.Euler(0, 0, -100);
private void Awake()
{
player = GetComponentsInParent<SpriteRenderer>()[1];
}
private void LateUpdate()
{
bool isReverse = player.flipX;
if (isLeft)
{
transform.localRotation = isReverse ? leftRotReverse : leftRot;
spriter.flipY = isReverse;
spriter.sortingOrder = isReverse ? 4 : 6;
}
else
{
transform.localPosition = isReverse ? rightPosReverse : rightPos;
spriter.flipX = isReverse;
spriter.sortingOrder = isReverse ? 6 : 4;
}
}
}
public class Player : MonoBehaviour
{
public Scanner scanner;
public Vector2 playerVector; // 플레이어의 이동 방향을 저장할 변수
public float playerSpeed; // 플레이어 이동 속도
public Hand[] hands;
private Rigidbody2D rb; // 물리 연산에 사용할 Rigidbody2D
private SpriteRenderer sprite; // 스프라이트 방향 변경에 사용할 SpriteRenderer
private Animator animator; // 애니메이션 제어에 사용할 Animator
private StateMachine stateMachine; // 상태를 관리할 상태 머신
private void Awake()
{
// 컴포넌트 참조 초기화
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>();
scanner = GetComponent<Scanner>();
// 상태 머신 초기화 및 IdleState로 시작
stateMachine = new StateMachine();
stateMachine.SetState(new IdleState(stateMachine, animator));
hands = GetComponentsInChildren<Hand>(true);
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices.WindowsRuntime;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.InputSystem;
public class Weapon : MonoBehaviour
{
public int id;
public int prefabId;
public float damage;
public int count;
public float speed;
private float timer;
Player player;
private void Awake()
{
player = GameManager.Instance.player;
}
void Update()
{
switch (id)
{
case 0:
transform.Rotate(Vector3.back * speed * Time.deltaTime);
break;
default:
timer += Time.deltaTime;
if(timer > speed)
{
timer = 0f;
Fire();
}
break;
}
}
public void Init(ItemData data)
{
// 베이직 셋팅
name = "Weapon" + data.itemId;
transform.parent = player.transform;
transform.localPosition = Vector3.zero;
//셋팅
id = data.itemId;
damage = data.baseDamage;
count = data.baseCount;
for(int index = 0; index < GameManager.Instance.objectPool.enemy.Length; index++)
{
if(data.projectile == GameManager.Instance.objectPool.enemy[index])
{
prefabId = index;
break;
}
}
switch(id) {
case 0:
speed = 150;
WeaponCount();
break;
case 1:
default:
count++;
speed = 0.5f;
break;
}
// 핸드 셋팅
Hand hand = player.hands[(int)data.itemType];
hand.spriter.sprite = data.hand;
hand.gameObject.SetActive(true);
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
public void LevelUp(float damage, int count)
{
this.damage = damage;
this.count += count;
if (id == 0)
WeaponCount();
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
private void WeaponCount()
{
for (int index = 0; index < count; index++)
{
Transform bullet;
if(index < transform.childCount)
{
bullet = transform.GetChild(index);
}
else
{
bullet = GameManager.Instance.objectPool.Get(prefabId).transform;
bullet.parent = transform;
}
bullet.localPosition = Vector3.zero;
bullet.localRotation = Quaternion.identity;
Vector3 rotVec = Vector3.forward * 360 * index / count;
bullet.Rotate(rotVec);
bullet.Translate(bullet.up * 1.5f, Space.World);
bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // 무한 관통 무기
}
}
private void FixedUpdate()
{
if (Input.GetKey(KeyCode.Space))
{
LevelUp(20, 5);
}
}
private void Fire()
{
if (!player.scanner.nearestTarget)
return;
Vector3 targetPos = player.scanner.nearestTarget.position;
Vector3 dir = targetPos - transform.position;
dir = dir.normalized;
Transform bullet = GameManager.Instance.objectPool.Get(prefabId).transform;
bullet.position = transform.position;
float angle = Mathf.Atan2(dir.y , dir.x) * Mathf.Rad2Deg - 90f;
// bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
bullet.rotation = Quaternion.Euler(new Vector3(0, 0, angle));
bullet.GetComponent<Bullet>().Init(damage, count, dir);
}
}
LateUpdate()에서 캐릭터의 방향성을 처리하는 로직을 통해, 손의 위치와 회전, 레이어링을 한 번에 관리할 수 있어 코드가 깔끔해졌습니다.
손 스프라이트를 아이템에 따라 쉽게 교체할 수 있도록 했기 때문에, 다양한 아이템을 추가할 때 유연하게 대응할 수 있을 것 같습니다.
