2026-05-15 공격과 피격

This commit is contained in:
2026-05-15 15:28:13 +09:00
parent 764bc305f7
commit ec353d0fd4
81 changed files with 879 additions and 57 deletions

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 66c32bbc793f98d49adc0346594251e9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using UnityEngine;
[CreateAssetMenu(fileName = "AttackData", menuName = "Combat/AttackData")]
public class AttackData : ScriptableObject
{
public string AttackName;
public string AnimationState;
public Vector2 Offset = new Vector2(0.5f, 0f);
public float Radius = 0.5f;
public int Damage = 10;
public float Cooldown = 0.3f;
[Header("Timing")]
public float HitTiming = 0.15f;
public float HitDuration = 0f;
public float MotionDuration = 0.3f;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 526ebe3dc7ff32e4faa2cfcdc1670638

View File

@@ -0,0 +1,26 @@
using System;
using UnityEngine;
public enum ComboInputType
{
Punch,
Kick
}
[Serializable]
public class ComboTransition
{
public ComboInputType Trigger;
public ComboNode Next;
public float ForwardStep = 0f;
public float ForwardStepDuration = 0.1f;
}
[CreateAssetMenu(fileName = "ComboNode", menuName = "Combat/ComboNode")]
public class ComboNode : ScriptableObject
{
public string NodeName;
public AttackData Attack;
public float ComboWindow = 0.8f;
public ComboTransition[] Transitions;
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d4d02e4d65b341140b34a24e39798ff6

View File

@@ -0,0 +1,4 @@
public interface IDamageable
{
void TakeDamage(int amount);
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 48f6e522039b1f44f8cce75191bcefec

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 48e440de30939c14d9f50089d72d7c9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using UnityEngine;
[RequireComponent(typeof(Collider2D))]
public class Enemy : MonoBehaviour, IDamageable
{
[Header("Stats")]
[SerializeField] private int _maxHealth = 30;
[Header("Hit Feedback")]
[SerializeField] private float _hitFlashDuration = 0.1f;
[SerializeField] private Color _hitFlashColor = Color.red;
private int _currentHealth;
private SpriteRenderer _spriteRenderer;
private Color _originalColor;
private float _flashTimer;
private void Awake()
{
_currentHealth = _maxHealth;
_spriteRenderer = GetComponentInChildren<SpriteRenderer>();
if (_spriteRenderer != null)
_originalColor = _spriteRenderer.color;
}
private void Update()
{
if (_flashTimer > 0f)
{
_flashTimer -= Time.deltaTime;
if (_flashTimer <= 0f && _spriteRenderer != null)
_spriteRenderer.color = _originalColor;
}
}
public void TakeDamage(int amount)
{
if (_currentHealth <= 0) return;
_currentHealth -= amount;
Debug.Log($"{name} 피격: -{amount} (HP: {_currentHealth}/{_maxHealth})");
if (_spriteRenderer != null)
{
_spriteRenderer.color = _hitFlashColor;
_flashTimer = _hitFlashDuration;
}
if (_currentHealth <= 0)
Die();
}
private void Die()
{
Debug.Log($"{name} 사망");
Destroy(gameObject);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 945e2c8fc675cb64c97b4eff6d2e8d56

View File

@@ -109,6 +109,24 @@ public @GameInput()
""processors"": """",
""interactions"": """",
""initialStateCheck"": false
},
{
""name"": ""Punch"",
""type"": ""Button"",
""id"": ""3dcb1343-a166-469d-990e-24ccedf2d09e"",
""expectedControlType"": """",
""processors"": """",
""interactions"": """",
""initialStateCheck"": false
},
{
""name"": ""Kick"",
""type"": ""Button"",
""id"": ""1af50460-846d-4f2a-8030-f9712ad7df6b"",
""expectedControlType"": """",
""processors"": """",
""interactions"": """",
""initialStateCheck"": false
}
],
""bindings"": [
@@ -177,6 +195,28 @@ public @GameInput()
""action"": ""Jump"",
""isComposite"": false,
""isPartOfComposite"": false
},
{
""name"": """",
""id"": ""9f28cd37-d35c-4f36-8af0-a0020d508486"",
""path"": ""<Keyboard>/z"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Punch"",
""isComposite"": false,
""isPartOfComposite"": false
},
{
""name"": """",
""id"": ""43e56be6-df9b-4845-ba12-cd109ac1ffa0"",
""path"": ""<Keyboard>/x"",
""interactions"": """",
""processors"": """",
""groups"": """",
""action"": ""Kick"",
""isComposite"": false,
""isPartOfComposite"": false
}
]
}
@@ -187,6 +227,8 @@ public @GameInput()
m_Player = asset.FindActionMap("Player", throwIfNotFound: true);
m_Player_Move = m_Player.FindAction("Move", throwIfNotFound: true);
m_Player_Jump = m_Player.FindAction("Jump", throwIfNotFound: true);
m_Player_Punch = m_Player.FindAction("Punch", throwIfNotFound: true);
m_Player_Kick = m_Player.FindAction("Kick", throwIfNotFound: true);
}
~@GameInput()
@@ -269,6 +311,8 @@ public int FindBinding(InputBinding bindingMask, out InputAction action)
private List<IPlayerActions> m_PlayerActionsCallbackInterfaces = new List<IPlayerActions>();
private readonly InputAction m_Player_Move;
private readonly InputAction m_Player_Jump;
private readonly InputAction m_Player_Punch;
private readonly InputAction m_Player_Kick;
/// <summary>
/// Provides access to input actions defined in input action map "Player".
/// </summary>
@@ -289,6 +333,14 @@ public struct PlayerActions
/// </summary>
public InputAction @Jump => m_Wrapper.m_Player_Jump;
/// <summary>
/// Provides access to the underlying input action "Player/Punch".
/// </summary>
public InputAction @Punch => m_Wrapper.m_Player_Punch;
/// <summary>
/// Provides access to the underlying input action "Player/Kick".
/// </summary>
public InputAction @Kick => m_Wrapper.m_Player_Kick;
/// <summary>
/// Provides access to the underlying input action map instance.
/// </summary>
public InputActionMap Get() { return m_Wrapper.m_Player; }
@@ -320,6 +372,12 @@ public void AddCallbacks(IPlayerActions instance)
@Jump.started += instance.OnJump;
@Jump.performed += instance.OnJump;
@Jump.canceled += instance.OnJump;
@Punch.started += instance.OnPunch;
@Punch.performed += instance.OnPunch;
@Punch.canceled += instance.OnPunch;
@Kick.started += instance.OnKick;
@Kick.performed += instance.OnKick;
@Kick.canceled += instance.OnKick;
}
/// <summary>
@@ -337,6 +395,12 @@ private void UnregisterCallbacks(IPlayerActions instance)
@Jump.started -= instance.OnJump;
@Jump.performed -= instance.OnJump;
@Jump.canceled -= instance.OnJump;
@Punch.started -= instance.OnPunch;
@Punch.performed -= instance.OnPunch;
@Punch.canceled -= instance.OnPunch;
@Kick.started -= instance.OnKick;
@Kick.performed -= instance.OnKick;
@Kick.canceled -= instance.OnKick;
}
/// <summary>
@@ -391,5 +455,19 @@ public interface IPlayerActions
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnJump(InputAction.CallbackContext context);
/// <summary>
/// Method invoked when associated input action "Punch" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
/// </summary>
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnPunch(InputAction.CallbackContext context);
/// <summary>
/// Method invoked when associated input action "Kick" is either <see cref="UnityEngine.InputSystem.InputAction.started" />, <see cref="UnityEngine.InputSystem.InputAction.performed" /> or <see cref="UnityEngine.InputSystem.InputAction.canceled" />.
/// </summary>
/// <seealso cref="UnityEngine.InputSystem.InputAction.started" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.performed" />
/// <seealso cref="UnityEngine.InputSystem.InputAction.canceled" />
void OnKick(InputAction.CallbackContext context);
}
}

View File

@@ -10,6 +10,8 @@ public class InputManager : MonoBehaviour, GameInput.IPlayerActions
public event Action<Vector2> OnMove_Event;
public event Action OnJump_Event;
public event Action OnPunch_Event;
public event Action OnKick_Event;
private void Awake()
{
@@ -38,4 +40,18 @@ public void OnJump(InputAction.CallbackContext ctx)
if (ctx.phase == InputActionPhase.Started)
OnJump_Event?.Invoke();
}
public void OnPunch(InputAction.CallbackContext ctx)
{
if (ctx.phase == InputActionPhase.Started)
OnPunch_Event?.Invoke();
}
public void OnKick(InputAction.CallbackContext ctx)
{
if (ctx.phase == InputActionPhase.Started)
OnKick_Event?.Invoke();
}
}

View File

@@ -1,29 +1,76 @@
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[Header("Movement")]
[SerializeField] private float moveSpeed = 5f;
[SerializeField] private float _moveSpeed = 5f;
private float _moveInputX = 0f;
[Header("Jump")]
[SerializeField] private float jumpForce = 12f;
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundCheckRadius = 0.1f;
[SerializeField] private LayerMask groundLayer;
[SerializeField] private float _jumpForce = 8f;
[SerializeField] private Transform _groundCheck;
[SerializeField] private float _groundCheckRadius = 0.1f;
[SerializeField] private LayerMask _groundLayer;
private bool _isGrounded;
private Rigidbody2D rb;
private float moveInputX = 0f;
private bool isGrounded;
[Header("WallSlide")]
[SerializeField] private Transform _wallCheckLeft;
[SerializeField] private Transform _wallCheckRight;
[SerializeField] private float _wallCheckRadius = 0.1f;
[SerializeField] private float _wallSlideSpeed = 2f;
[SerializeField] private Vector2 _wallJumpForce = new Vector2(4f, 5f);
[SerializeField] private float _wallJumpInputLockDuration = 0.15f;
private bool _isTouchingLeftWall;
private bool _isTouchingRightWall;
private bool IsTouchingWall => _isTouchingLeftWall || _isTouchingRightWall;
private int _wallDirection;
private float _inputLockTimer;
[Header("Attack")]
[SerializeField] private ComboNode _punchRootNode;
[SerializeField] private ComboNode _kickRootNode;
[SerializeField] private LayerMask _enemyLayer;
[SerializeField] private string _idleAnimationState = "Idle";
[SerializeField] private float _bufferOpenTime = 0.1f;
[SerializeField] private float _bufferLifetime = 0.5f;
private ComboInputType? _pendingInput;
private float _pendingInputTime = -1f;
private float _attackCooldownTimer;
private ComboNode _currentNode;
private float _comboWindowTimer;
private CancellationTokenSource _attackCts;
private AttackData _lastAttackGizmoData;
private float _lastAttackGizmoTime = -1f;
[SerializeField] private float _hitGizmoFadeDuration = 0.5f;
private AttackData _lastHitData;
private Vector2 _lastHitCenter;
private float _lastHitTime = -1f;
[Header("Debug")]
[SerializeField] private bool _showAttackDebug = true;
private float _attackStartTime = -1f;
private bool _hitFired;
private Rigidbody2D _rb;
private Animator _anim;
private SpriteRenderer _spriteRenderer;
private void Awake()
{
rb = GetComponent<Rigidbody2D>();
_rb = GetComponent<Rigidbody2D>();
_anim = GetComponent<Animator>();
_spriteRenderer = GetComponentInChildren<SpriteRenderer>();
}
private void Start()
{
InputManager.Instance.OnMove_Event += OnMoveInput;
InputManager.Instance.OnJump_Event += OnJumpInput;
InputManager.Instance.OnPunch_Event += OnPunchInput;
InputManager.Instance.OnKick_Event += OnKickInput;
}
private void OnDestroy()
@@ -32,30 +79,356 @@ private void OnDestroy()
{
InputManager.Instance.OnMove_Event -= OnMoveInput;
InputManager.Instance.OnJump_Event -= OnJumpInput;
InputManager.Instance.OnPunch_Event -= OnPunchInput;
InputManager.Instance.OnKick_Event -= OnKickInput;
}
_attackCts?.Cancel();
_attackCts?.Dispose();
}
private void FixedUpdate()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
rb.linearVelocity = new Vector2(moveInputX * moveSpeed, rb.linearVelocity.y);
_isGrounded = Physics2D.OverlapCircle(_groundCheck.position, _groundCheckRadius, _groundLayer);
_isTouchingLeftWall = Physics2D.OverlapCircle(_wallCheckLeft.position, _wallCheckRadius, _groundLayer);
_isTouchingRightWall = Physics2D.OverlapCircle(_wallCheckRight.position, _wallCheckRadius, _groundLayer);
_wallDirection = _isTouchingRightWall ? 1 : (_isTouchingLeftWall ? -1 : 0);
if (_attackCooldownTimer > 0f)
_attackCooldownTimer -= Time.fixedDeltaTime;
if (_attackCooldownTimer <= 0f && _pendingInput.HasValue)
{
ComboInputType buffered = _pendingInput.Value;
bool stillValid = Time.time - _pendingInputTime <= _bufferLifetime;
_pendingInput = null;
if (stillValid) ExecuteComboInput(buffered);
}
if (_comboWindowTimer > 0f)
{
_comboWindowTimer -= Time.fixedDeltaTime;
if (_comboWindowTimer <= 0f)
_currentNode = null;
}
if (_inputLockTimer > 0f)
_inputLockTimer -= Time.fixedDeltaTime;
else
_rb.linearVelocity = new Vector2(_moveInputX * _moveSpeed, _rb.linearVelocity.y);
if (IsTouchingWall && !_isGrounded && _rb.linearVelocity.y < -_wallSlideSpeed)
_rb.linearVelocity = new Vector2(_rb.linearVelocity.x, -_wallSlideSpeed);
}
private void OnMoveInput(Vector2 value)
{
moveInputX = value.x == 0f ? 0f : Mathf.Sign(value.x);
_moveInputX = value.x == 0f ? 0f : Mathf.Sign(value.x);
if (_moveInputX != 0f && _spriteRenderer != null)
_spriteRenderer.flipX = _moveInputX < 0f;
}
private void OnJumpInput()
{
if (isGrounded)
rb.linearVelocity = new Vector2(rb.linearVelocity.x, jumpForce);
if (_isGrounded)
{
_rb.linearVelocity = new Vector2(_rb.linearVelocity.x, _jumpForce);
}
else if (IsTouchingWall)
{
_rb.linearVelocity = new Vector2(-_wallDirection * _wallJumpForce.x, _wallJumpForce.y);
_inputLockTimer = _wallJumpInputLockDuration;
}
}
private void OnPunchInput() => HandleComboInput(ComboInputType.Punch);
private void OnKickInput() => HandleComboInput(ComboInputType.Kick);
private void HandleComboInput(ComboInputType input)
{
if (_attackCooldownTimer > 0f)
{
float elapsed = Time.time - _attackStartTime;
if (_lastAttackGizmoData != null && elapsed >= _bufferOpenTime)
{
_pendingInput = input;
_pendingInputTime = Time.time;
}
return;
}
ExecuteComboInput(input);
}
private void ExecuteComboInput(ComboInputType input)
{
if (_comboWindowTimer > 0f && _currentNode != null)
{
foreach (var transition in _currentNode.Transitions)
{
if (transition.Trigger != input) continue;
if (transition.Next == null || transition.Next.Attack == null) continue;
ApplyForwardStep(transition.ForwardStep, transition.ForwardStepDuration);
PerformAttack(transition.Next.Attack);
_currentNode = transition.Next;
_comboWindowTimer = transition.Next.ComboWindow;
return;
}
}
ComboNode root = input switch
{
ComboInputType.Punch => _punchRootNode,
ComboInputType.Kick => _kickRootNode,
_ => null
};
if (root == null || root.Attack == null) return;
PerformAttack(root.Attack);
_currentNode = root;
_comboWindowTimer = root.ComboWindow;
}
private async void PerformAttack(AttackData data)
{
_attackCts?.Cancel();
_attackCts?.Dispose();
_attackCts = CancellationTokenSource.CreateLinkedTokenSource(destroyCancellationToken);
CancellationToken token = _attackCts.Token;
_attackCooldownTimer = data.Cooldown;
_lastAttackGizmoData = data;
_attackStartTime = Time.time;
_hitFired = false;
if (_anim != null && !string.IsNullOrEmpty(data.AnimationState))
_anim.Play(data.AnimationState);
try
{
await HitRoutine(data, token);
}
catch (System.OperationCanceledException) { }
}
private async Awaitable HitRoutine(AttackData data, CancellationToken token)
{
float attackStartTime = Time.time;
if (data.HitTiming > 0f)
await Awaitable.WaitForSecondsAsync(data.HitTiming, token);
_lastAttackGizmoTime = Time.time;
_lastHitData = data;
_hitFired = true;
if (data.HitDuration <= 0f)
{
ApplyDamageInArea(data, null);
}
else
{
var alreadyHit = new HashSet<IDamageable>();
float elapsed = 0f;
while (elapsed < data.HitDuration)
{
token.ThrowIfCancellationRequested();
ApplyDamageInArea(data, alreadyHit);
await Awaitable.NextFrameAsync(token);
elapsed += Time.deltaTime;
}
}
float remaining = data.MotionDuration - (Time.time - attackStartTime);
if (remaining > 0f)
await Awaitable.WaitForSecondsAsync(remaining, token);
if (_anim != null && !string.IsNullOrEmpty(_idleAnimationState))
_anim.Play(_idleAnimationState);
}
private void ApplyDamageInArea(AttackData data, HashSet<IDamageable> alreadyHit)
{
Vector2 center = GetAttackCenter(data.Offset);
_lastHitData = data;
_lastHitCenter = center;
_lastHitTime = Time.time;
Collider2D[] hits = Physics2D.OverlapCircleAll(center, data.Radius, _enemyLayer);
foreach (var hit in hits)
{
if (!hit.TryGetComponent<IDamageable>(out var target)) continue;
if (alreadyHit != null)
{
if (alreadyHit.Contains(target)) continue;
alreadyHit.Add(target);
}
target.TakeDamage(data.Damage);
}
}
private Vector2 GetAttackCenter(Vector2 offset)
{
float facing = _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f;
offset.x *= facing;
return (Vector2)transform.position + offset;
}
private void ApplyForwardStep(float distance, float duration)
{
if (distance <= 0f) return;
float safeDuration = Mathf.Max(duration, 0.02f);
float facing = _spriteRenderer != null && _spriteRenderer.flipX ? -1f : 1f;
float vx = facing * (distance / safeDuration);
_rb.linearVelocity = new Vector2(vx, _rb.linearVelocity.y);
_inputLockTimer = safeDuration;
}
private void OnDrawGizmos()
{
if (_lastHitData == null || _lastHitTime < 0f) return;
float since = Time.time - _lastHitTime;
if (since < 0f || since > _hitGizmoFadeDuration) return;
float t = Mathf.Clamp01(since / _hitGizmoFadeDuration);
float alpha = Mathf.Lerp(0.85f, 0f, t);
float radius = _lastHitData.Radius;
Gizmos.color = new Color(1f, 0.2f, 0.2f, alpha * 0.35f);
Gizmos.DrawSphere(_lastHitCenter, radius);
Gizmos.color = new Color(1f, 0f, 0f, alpha);
Gizmos.DrawWireSphere(_lastHitCenter, radius);
}
private void OnDrawGizmosSelected()
{
if (groundCheck == null) return;
Gizmos.color = isGrounded ? Color.green : Color.red;
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
if (_groundCheck != null)
{
Gizmos.color = _isGrounded ? Color.green : Color.red;
Gizmos.DrawWireSphere(_groundCheck.position, _groundCheckRadius);
}
if (_wallCheckLeft != null)
{
Gizmos.color = _isTouchingLeftWall ? Color.green : Color.red;
Gizmos.DrawWireSphere(_wallCheckLeft.position, _wallCheckRadius);
}
if (_wallCheckRight != null)
{
Gizmos.color = _isTouchingRightWall ? Color.green : Color.red;
Gizmos.DrawWireSphere(_wallCheckRight.position, _wallCheckRadius);
}
DrawLastAttackGizmo();
}
private void DrawLastAttackGizmo()
{
if (_lastAttackGizmoData == null) return;
if (!Application.isPlaying) return;
float elapsed = Time.time - _lastAttackGizmoTime;
if (elapsed < 0f) return;
AttackData data = _lastAttackGizmoData;
float activeDuration = Mathf.Max(data.HitDuration, 0.05f);
float fadeDuration = 0.4f;
float total = activeDuration + fadeDuration;
if (elapsed > total) return;
float alpha = elapsed < activeDuration
? 1f
: 1f - (elapsed - activeDuration) / fadeDuration;
alpha = Mathf.Clamp01(alpha);
Vector2 center = GetAttackCenter(data.Offset);
Gizmos.color = new Color(1f, 0.3f, 0.3f, alpha * 0.35f);
Gizmos.DrawSphere(center, data.Radius);
Gizmos.color = new Color(1f, 0f, 0f, alpha);
Gizmos.DrawWireSphere(center, data.Radius);
}
private void OnGUI()
{
if (!_showAttackDebug || _lastAttackGizmoData == null) return;
AttackData data = _lastAttackGizmoData;
float elapsed = Time.time - _attackStartTime;
string status = _hitFired
? (elapsed < data.HitTiming + Mathf.Max(data.HitDuration, 0.01f) ? "HIT" : "DONE")
: "WINDUP";
string cooldownText = _attackCooldownTimer > 0f
? $"{_attackCooldownTimer:F3}s left"
: "READY";
string bufferText = _pendingInput.HasValue
? $"<color=#ffcc00>BUFFERED: {_pendingInput.Value}</color>"
: (elapsed >= _bufferOpenTime && _attackCooldownTimer > 0f ? "ready to buffer" : "-");
string info =
$"<b>{(string.IsNullOrEmpty(data.AttackName) ? data.name : data.AttackName)}</b> [{status}]\n" +
$"Elapsed : {elapsed:F3} s\n" +
$"HitTiming : {data.HitTiming:F3} s\n" +
$"HitDuration : {data.HitDuration:F3} s\n" +
$"Cooldown : {data.Cooldown:F3} s ({cooldownText})\n" +
$"ComboWindow : {_comboWindowTimer:F3} s\n" +
$"Buffer : {bufferText}";
GUIStyle style = new GUIStyle(GUI.skin.box)
{
fontSize = 26,
alignment = TextAnchor.UpperLeft,
richText = true,
padding = new RectOffset(16, 16, 12, 12),
normal = { textColor = Color.white }
};
GUI.Box(new Rect(10, 10, 520, 282), info, style);
DrawTimelineBar(data, elapsed);
}
private void DrawTimelineBar(AttackData data, float elapsed)
{
float barX = 10f;
float barY = 302f;
float barW = 520f;
float barH = 36f;
float totalTime = Mathf.Max(
data.HitTiming + Mathf.Max(data.HitDuration, 0.05f),
data.Cooldown,
data.MotionDuration,
0.3f);
GUI.color = new Color(0f, 0f, 0f, 0.6f);
GUI.DrawTexture(new Rect(barX, barY, barW, barH), Texture2D.whiteTexture);
float hitX = barX + (data.HitTiming / totalTime) * barW;
float hitEndX = barX + ((data.HitTiming + data.HitDuration) / totalTime) * barW;
GUI.color = new Color(1f, 0.3f, 0.3f, 0.5f);
GUI.DrawTexture(new Rect(hitX, barY, Mathf.Max(hitEndX - hitX, 4f), barH), Texture2D.whiteTexture);
float bufferX = barX + Mathf.Clamp01(_bufferOpenTime / totalTime) * barW;
GUI.color = new Color(0.2f, 1f, 0.4f, 0.9f);
GUI.DrawTexture(new Rect(bufferX - 2f, barY, 4f, barH), Texture2D.whiteTexture);
float cdEndX = barX + Mathf.Clamp01(data.Cooldown / totalTime) * barW;
GUI.color = new Color(0.3f, 0.7f, 1f, 0.9f);
GUI.DrawTexture(new Rect(cdEndX - 2f, barY, 4f, barH), Texture2D.whiteTexture);
float cursorX = barX + Mathf.Clamp01(elapsed / totalTime) * barW;
GUI.color = Color.yellow;
GUI.DrawTexture(new Rect(cursorX - 2f, barY - 4f, 4f, barH + 8f), Texture2D.whiteTexture);
GUI.color = Color.white;
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ed10d659a73ec5646a3c118373bbfea7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,14 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!62 &6200000
PhysicsMaterial2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Player_NoFriction
serializedVersion: 2
friction: 0
bounciness: 0
m_FrictionCombine: 3
m_BounceCombine: 3

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d6340da9be911d04bb908e5c78fea72c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 6200000
userData:
assetBundleName:
assetBundleVariant:

8
Assets/05_Data.meta Normal file
View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 874c09e27a1996a488f4c8fcc0463f1c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c30e407ce5cfbd4ca6ad0704d9d03c6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: edf7db29ac692e346969f62ba6affb99
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9eab77b3aa491b14082559d7670fb8fa
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 66ae598bd05e97a4b87b3f49a06eed1d
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: df671596c8a54114789564d1ae9e7b58
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7bd5ebeb5044dfb44864673343c5b63f
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 961b750a6648ff24d88f8cf5dbcfe9c3
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4f208be54919bfa4cb1feb32daaf1c9e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e868141c61f9d3c41ab8c631d68e5058
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4c94ebe82d46ebf4d88a17ce859ebd55
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e6bcd60837177ac458507946d733c3dd
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 62ff3f93ff39de341ab516bb3dfe4b75
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 979e8f13c305e1c4bbdeb2ab83add475
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 460f8d2a9a57dd14999125a99069a485
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -23,6 +23,24 @@
"processors": "",
"interactions": "",
"initialStateCheck": false
},
{
"name": "Punch",
"type": "Button",
"id": "3dcb1343-a166-469d-990e-24ccedf2d09e",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
},
{
"name": "Kick",
"type": "Button",
"id": "1af50460-846d-4f2a-8030-f9712ad7df6b",
"expectedControlType": "",
"processors": "",
"interactions": "",
"initialStateCheck": false
}
],
"bindings": [
@@ -91,6 +109,28 @@
"action": "Jump",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "9f28cd37-d35c-4f36-8af0-a0020d508486",
"path": "<Keyboard>/z",
"interactions": "",
"processors": "",
"groups": "",
"action": "Punch",
"isComposite": false,
"isPartOfComposite": false
},
{
"name": "",
"id": "43e56be6-df9b-4845-ba12-cd109ac1ffa0",
"path": "<Keyboard>/x",
"interactions": "",
"processors": "",
"groups": "",
"action": "Kick",
"isComposite": false,
"isPartOfComposite": false
}
]
}

Binary file not shown.