애니메이션 동기화
이번 포스팅에서는 캐릭터의 공격을 만들고 애니메이션을 동기화 해보자.
기획상의 문제긴 하지만 나는 마우스 왼쪽 클릭을 하면 일반 공격 2종 중 하나가 랜덤하게 나가는 방향으로 정했다.
스킬같은 경우도 애니메이션이 있지만 이는 나중에 처리할 예정이다.
기존에 있던 MouseInput을 받아주는 곳에 코드를 추가해준다.
public void OnClickMouseInputEvent()
{
_moveTime += Time.deltaTime;
if (Input.GetMouseButton(1) && _moveTime >= 0.3f)
{
if (State == CreatureState.Skill) return;
Ray ray = cm.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out RaycastHit hit))
{
// TODO : 이동 패킷
//MoveTarget(hit.point);
C_Move movePacket = new C_Move() { PosInfo = new PositionInfo() { Pos = new Positions() } };
movePacket.PosInfo.State = CreatureState.Moving;
Positions pos = new Positions() { PosX = hit.point.x, PosY = hit.point.y, PosZ = hit.point.z };
movePacket.PosInfo.Pos = pos;
Managers.Network.Send(movePacket);
_moveTime = 0;
}
}
else if (Input.GetMouseButtonDown(0))
{
if (State == CreatureState.Skill || State == CreatureState.Dead || State == CreatureState.Wait) return;
// idle 상태인지 검증 -> 아니라면 멈춤 패킷 보냈다가 공격 패킷 보냄
if (State != CreatureState.Idle && State == CreatureState.Moving)
{
C_StopMove moveStopPacket = new C_StopMove() { PosInfo = new PositionInfo() };
moveStopPacket.PosInfo.Pos = new Positions() { PosX = transform.position.x, PosY = transform.position.y, PosZ = transform.position.z };
Vector3 rotationEuler = transform.rotation.eulerAngles;
moveStopPacket.PosInfo.Rotate = new RotateInfo() { RotateX = rotationEuler.x, RotateY = rotationEuler.y, RotateZ = rotationEuler.z };
Managers.Network.Send(moveStopPacket);
return;
}
// 작업
int attackRand = Random.Range(1, 3);
if (attackRand < 0 || attackRand > 2)
{
attackNum = -1;
return;
}
else
{
C_MeleeAttack meleeAttack = new C_MeleeAttack() { Info = new SkillInfo() };
meleeAttack.Info.SkillId = attackRand;
Managers.Network.Send(meleeAttack);
State = CreatureState.Wait;
}
}
}
공격을 할 수 있는 조건은 움직이지 않는 상태, 즉 Idle일 상태를 제외하고는 전부 공격할 수 없다.
만약 마우스 왼쪽 클릭을 눌렀을 때 플레이어가 움직이는 중이었다면 Stop패킷을 만들어서 보낸다.
그게 아니라면 일반 공격 패킷을 만들어서 보낸다. 여기서 SkillId를 1 또는 2로 만들어서 SkillData.json에서 가져올 수 있도록 한다.
public void HandleMeleeAttack(Player player, C_MeleeAttack meleeAttack)
{
if (player == null)
return;
ObjectInfo info = player.Info;
if (info.PosInfo.State != CreatureState.Idle)
return;
info.PosInfo.State = CreatureState.Skill;
S_MeleeAttack meleeAttackServer = new S_MeleeAttack() { Info = new SkillInfo() };
meleeAttackServer.ObjectId = player.Id;
meleeAttackServer.Info.SkillId = meleeAttack.Info.SkillId;
Broadcast(meleeAttackServer);
// 실제로는 데미지 입힌 후 idle로 변환해야함
info.PosInfo.State = CreatureState.Idle;
}
패킷을 받은 서버 쪽에서는 일반 공격을 허락하고 데미지를 입히는 과정을 진행한다. 실제 데미지를 입고 target을 찾는 행위는 몬스터가 추가된 후에 진행할 예정이다.
public override void OnAttack(SkillInfo info)
{
Skill skill = null;
Managers.Data.SkillDict.TryGetValue(info.SkillId, out skill);
if (skill == null) return;
State = CreatureState.Skill;
attackNum = info.SkillId;
_anim.SetInteger("AttackNum", attackNum);
StartCoroutine(CoAttackMotion(skill.cooldown));
}
public IEnumerator CoAttackMotion(float coolDown)
{
yield return new WaitForSecondsRealtime(0.2f);
_anim.SetInteger("AttackNum", -1);
yield return new WaitForSecondsRealtime(coolDown - 0.2f);
State = CreatureState.Idle;
attackNum = -1;
}
서버로 부터 클라가 패킷을 받게되면 해당하는 객체의 애니메이션을 틀어준다 그리고 attactMotion 코루틴에서 후딜레이를 넣어줘서 연속적인 공격을 할 수 없게 체크하는 과정인 것이다.
{
"skills": [
{
"id": "1",
"name": "일반공격1",
"cooldown": "1.7",
"skillType": "SkillMeleeAttack",
"skillDatas": [
{
"attackTime": "0.65",
"damage": "1",
"range": {
"width": "1",
"depth": "2",
"nonDepth": "0.1",
"height": "1.5"
}
}
]
},
{
"id": "2",
"name": "일반공격2",
"cooldown": "1.5",
"skillType": "SkillMeleeAttack",
"skillDats": [
{
"attackTime": "0.8",
"damage": "1",
"range": {
"width": "1",
"depth": "2",
"nonDepth": "0.4",
"height": "1.5"
}
}
]
},
{
"id": "3",
"name": "파천강기",
"cooldown": "3.5",
"skillType": "SkillMeleeAttack",
"skillDats": [
{
"attackTime": "0.8",
"damage": "20",
"range": {
"width": "0.6",
"depth": "2",
"nonDepth": "0.7",
"height": "1.5"
}
},
{
"attackTime": "1.35",
"damage": "10",
"range": {
"width": "2",
"depth": "2",
"nonDepth": "0.7",
"height": "1.5"
}
}
]
}
]
}
Json 파일이고 이는 DataManager에서 게임 시작 시 파싱해서 Class와 Dictionary로 보관하고 있다.
실제 시연 영상은 다음과 같다.
'Unity > 온라인 RPG' 카테고리의 다른 글
[Unity 3D] 오브젝트 전투 및 몬스터의 플레이어 추격 (0) | 2024.05.13 |
---|---|
[Unity 3D] 몬스터 움직임 동기화 with Dedicated Server (0) | 2024.05.02 |
[Unity 3D] 캐릭터 이동 동기화 (0) | 2024.04.23 |
[Unity 3D] Navigation을 이용한 캐릭터 이동 (0) | 2024.04.17 |
[Unity 3D] 캐릭터 선택창 및 캐릭터 생성 (0) | 2024.04.13 |