728x90
반응형
이전글: https://lektion-von-erfolglosigkeit.tistory.com/250
개요
이전글에서 그냥 String 비교로 구현했던 CheckAvailableCommand함수를 Command Tree로 다시 구현
근데 만들다보니 ScriptableObject로 그냥 Inspector에서 추가하는게 더 좋은 방법 아닐까라는 생각이 들었다.
Animation 같은 다양한 걸 정하려면 그쪽이 더 편할듯
이 방법은 branch에 따로 남겨두고 ScriptableObject로 다시 구현해볼 예정
그래서 CheckAvailableCommand는 건들지 않음
조건
전체적으로 Unity Animation Graph와 비슷한 형태로
- List<string>을 Tree 형태로 변환하여 저장
- press, release, hold, repeat 등의 다양한 입력 형태
- Tree 시각화
- Root node에서 Ground Tree, Air Tree로
Node 조건
- Name
- Input condition
- Sub nodes
CommandNode
Variable
public
- string name;
Node의 이름, 필요한 경우에만 사용 - string condition;
이 Node로 넘어가는 커맨드 조건 ex) X, ~ S+Y - List<CommandNode> subNodes;
이 Node의 SubNode들
Method
Constructor
- CommandNode ()
아무런 정보도 없는 빈 CommandNode 생성 - CommandNode(string name, string codition)
name과 condition을 가진 CommandNode 생성
public
- void AddNode(CommandNode node)
node를 이 Node의 subNode로 추가
InputManager
Variable
private
- CommandNode ground
g_commandList에서 만들어지는 ground Command Tree의 root - CommandNode air
a_commandList에서 만들어지는 air Command Tree의 root
Method
private
- void InitializeRootNodes()
CommandTree의 root가 되는 node들을 생성 - void CreateCommandTree()
CreateCommandTreeFromList를 각각 g_commandList와 a_commandList를 parameter로 호출 - void CreateCommandTreeFromList(List<string> targetList)
targetList에 따라 Command Tree를 생성더보기ZX
ZXCV
UIO
UIOP
0. root의 subNode에 z가 있는지 확인
1. 없으므로 root -> z 추가
2. 현재 노드를 z로 변경
3. z -> x 추가
4. 다음 입력이 없음
5. 현재 노드를 root로 변경
6. root의 subNode에 z가 있는지 확인
7. 있으므로 현재 노드를 z로 변경
8. z의 subNode에 x가 있는지 확인
9. 있으므로 현재 노드를 x로 변경
10. x의 subNode에 c가 있는지 확인
11. 없으므로 x -> c 추가
12. 현재 노드를 c로 변경
13. c의 subNode에 v가 있는지 확인
14. 없으므로 c -> v 추가
15. 다음입력이 없음
16. 현재 노드를 root로 변경
...
전체코드
CommandNode
더보기
public class CommandNode
{
public string name;
public string condition;
public List<CommandNode> subNodes;
public CommandNode()
{
name = string.Empty;
condition = string.Empty;
subNodes = new List<CommandNode>();
}
public CommandNode(string name, string condition)
{
this.name = name;
this.condition = condition;
subNodes = new List<CommandNode>();
}
public void AddNode(CommandNode node)
{
this.subNodes.Add(node);
}
}
InputManager
더보기
using System.Collections;
using System.Collections.Generic;
using System.Net;
using Unity.VisualScripting;
using Unity.VisualScripting.Antlr3.Runtime;
using UnityEngine;
public class InputManager : MonoBehaviour
{
[Header("For Test")]
[SerializeField] private bool grounded = true;
[Space(20)]
[SerializeField] private List<string> g_commandList = new List<string>();
[SerializeField] private List<string> a_commandList = new List<string>();
[Space(20)]
[SerializeField] private string inputStream;
[Space(20)]
[SerializeField] private string rawInput;
[SerializeField] private string combineKey;
[SerializeField] private string complexInput;
[SerializeField] private string currentInput;
[SerializeField] private float resetTime;
private float resetTimer;
[SerializeField] private float holdTime;
private float holdTimer;
[SerializeField] private float delayTime;
private float delayTimer;
[SerializeField] private float repeatTime;
private float clickTime;
[SerializeField] private bool pressed;
[SerializeField] private bool released;
[SerializeField] private bool holding;
private bool held;
[SerializeField] private bool repeating;
private bool repeated;
[SerializeField] private bool delaying;
private bool delayed;
private CommandNode currentNode;
private CommandNode air;
private CommandNode ground;
private void Awake()
{
ResetStream();
ResetInputs();
ResetTimers();
clickTime = -1.0f;
InitializeRootNodes();
CreateCommandTree();
}
void Update()
{
CheckInput();
CheckReset();
//DebugInputs();
}
private void DebugInputs()
{
if(pressed)
{
Debug.Log("Press");
}
if(holding)
{
Debug.Log("Hold");
}
if(released)
{
Debug.Log("Release");
}
if(delaying)
{
Debug.Log("delay");
}
if(repeating)
{
Debug.Log("Repeat");
}
}
private void CheckInput()
{
CheckCombineKey();
CheckRawInput(1);
CheckRawInput(0);
CheckComplexInput();
CheckInputDelay();
UpdateCurrentInput();
}
private void UpdateCurrentInput()
{
if(combineKey != "")
{
currentInput = combineKey + "+" + rawInput + complexInput;
}
else
{
currentInput = rawInput + complexInput;
}
}
private void CheckRawInput(int button)
{
if(Input.GetMouseButtonDown(button))
{
if(button == 0 && combineKey != "W")
{
combineKey = "";
}
rawInput = (button==0) ? "X" : "Y";
pressed = true;
holding = false;
released = false;
CheckRepeat();
ResetTimers();
AddToStream(rawInput);
}
else if(Input.GetMouseButton(button))
{
pressed = false;
holdTimer += Time.deltaTime;
if(holdTimer > holdTime)
{
holding = true;
held = true;
}
//release = false;
}
else if(Input.GetMouseButtonUp(button))
{
//press = false;
holding = false;
released = true;
}
}
private void CheckInputDelay()
{
if(released == true)
{
delayTimer += Time.deltaTime;
if(delayTimer >= delayTime)
{
delaying = true;
delayed = true;
rawInput = "";
AddDelayToStream();
}
else
{
delaying = false;
//repeat = false;
}
}
}
private void CheckRepeat()
{
if((Time.time - clickTime) < repeatTime)
{
repeating = true;
repeated = true;
}
else
{
clickTime = Time.time;
}
}
private void CheckCombineKey()
{
if(Input.GetKey(KeyCode.W))
{
combineKey = "W";
}
else if(Input.GetKey(KeyCode.S))
{
combineKey = "S";
}
else if(Input.GetKey(KeyCode.A))
{
combineKey = "A";
}
else if(Input.GetKey(KeyCode.D))
{
combineKey = "D";
}
else
{
combineKey = "";
}
}
private void CheckComplexInput()
{
if(holding)
{
complexInput = "~";
AddHoldToStream();
}
else if(repeating && !delaying)
{
complexInput = "+";
AddRepeatToStream();
}
else
{
complexInput = "";
holding = false;
repeating = false;
}
}
private void CheckReset()
{
if(holding == true)
{
resetTimer = 0;
return;
}
if(delayed == true)
{
resetTimer += Time.deltaTime;
if(resetTimer > resetTime)
{
resetTimer = 0;
ResetInputs();
ResetTimers();
ResetStream();
}
}
}
private void ResetInputs()
{
Debug.Log("Reset");
ResetPrevInputs();
holding = false;
delaying = false;
repeating = false;
rawInput = "";
combineKey = "";
}
private void ResetPrevInputs()
{
pressed = false;
released = false;
held = false;
delayed = false;
repeated = false;
}
private void ResetTimers()
{
resetTimer = 0;
holdTimer = 0;
delayTimer = 0;
}
private void ResetStream()
{
inputStream = "";
}
private void AddToStream(string input)
{
if (repeating == false)
{
if(combineKey == "")
{
inputStream += input;
}
else
{
Debug.Log("CombineKey");
inputStream += combineKey + "+" + input;
}
}
CheckAvailableCommand();
}
private void AddHoldToStream()
{
if (inputStream.EndsWith(complexInput))
{
return;
}
repeating = false;
inputStream += complexInput;
CheckAvailableCommand();
}
private void AddRepeatToStream()
{
if (inputStream.EndsWith(complexInput))
{
return;
}
holding = false;
inputStream += complexInput;
CheckAvailableCommand();
}
private void AddDelayToStream()
{
if (inputStream.EndsWith("_"))
{
return;
}
inputStream += "_";
CheckAvailableCommand();
}
private void CheckAvailableCommand()
{
if(string.IsNullOrEmpty(inputStream))
{
return;
}
if(grounded)
{
CheckAvailableFromList(g_commandList);
}
else
{
CheckAvailableFromList(a_commandList);
}
}
private void CheckAvailableFromList(List<string> list)
{
for(int i = 0; i < list.Count; i++)
{
if (list[i].StartsWith(inputStream))
{
//Debug.Log("Available: [" + g_commandList[i] + "=" + inputStream + "]");
Debug.Log("Available");
return;
}
else
{
//Debug.Log("Unavailable input");
//Debug.Log("Not Available: [" + inputStream + "]");
}
}
inputStream = rawInput;
ResetInputs();
}
private void InitializeRootNodes()
{
currentNode = new CommandNode();
air = new CommandNode("air", "");
ground = new CommandNode("ground", "");
}
private void CreateCommandTree()
{
CreateCommandTreeFromList(g_commandList);
CreateCommandTreeFromList(a_commandList);
}
private void CreateCommandTreeFromList(List<string> targetList)
{
CommandNode targetNode = new CommandNode();
for(int i = 0; i < targetList.Count; i++)
{
if(targetList == g_commandList)
{
targetNode = ground;
}
else if(targetList == a_commandList)
{
targetNode = air;
}
Debug.Log("Add with " + targetList[i]);
for(int j = 0; j < targetList[i].Length; j++)
{
string _command = ""+targetList[i][j];
CommandNode newNode = new CommandNode();
if(_command == "W" || _command == "A" || _command == "S" || _command == "D")
{
Debug.Log("New Node " + targetList[i]);
newNode.condition = targetList[i];
targetNode.AddNode(newNode);
break;
}
else
{
if(targetNode.subNodes.Count == 0)
{
Debug.Log("New Node " + _command);
newNode.condition = _command;
targetNode.AddNode(newNode);
targetNode = newNode;
continue;
}
else
{
Debug.Log("SubNodes exist");
for(int k = 0; k < targetNode.subNodes.Count; k++)
{
Debug.Log("Check duplicated Node with" + _command);
if(_command == targetNode.subNodes[k].condition)
{
Debug.Log("Duplicated Node Exists;" + targetNode.subNodes[k].condition);
targetNode = targetNode.subNodes[k];
break;
}
else
{
Debug.Log("Duplicated Node Not Exist");
Debug.Log("New Node " + _command);
newNode.condition = _command;
targetNode.AddNode(newNode);
targetNode = newNode;
break;
}
}
}
//Debug.Log("New Input Node with " + _command);
}
}
}
}
}
'작업일지 > Unity' 카테고리의 다른 글
[Unity] Android Build에서 streamingAssetPath 파일 접근하기 (0) | 2024.05.13 |
---|---|
[Unity] Complex Input System for Stylish Action game (1) | 2024.03.04 |
[Unity] Hexagon TileMap 만들기 (0) | 2023.05.10 |
[Unity] New Input System 테스트 (0) | 2023.01.02 |
[Unity] Netcode for Gameobject 테스트 (0) | 2022.11.19 |