Basic fighting game.

Need kenney art
could do a lot of other shit, combos, jumping, crouching.
Attacking while jumping/crouuching
Balance stuff
Special moves
This commit is contained in:
2021-02-09 20:20:34 -06:00
commit 505ba54e8e
70 changed files with 9008 additions and 0 deletions
+87
View File
@@ -0,0 +1,87 @@
using System;
using UnityEngine;
public class AttackAnimationState : StateMachineBehaviour
{
private enum HitBoxState : byte
{
Startup,
Active,
Recovery
}
private float _startTime;
private HitBox _hitBox;
private HitBoxState _currentHitBoxState;
public float activeStartTime;
public float activeEndTime;
public AttackState attack;
// OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
_currentHitBoxState = HitBoxState.Startup;
_startTime = Time.time;
var player = animator.GetComponent<Player>();
switch (attack)
{
case AttackState.Idle:
Debug.LogError("AttackAnimationState should not be associated to idle!");
break;
case AttackState.Light:
_hitBox = player.lightHitBox;
break;
case AttackState.Medium:
_hitBox = player.mediumHitBox;
break;
case AttackState.Heavy:
_hitBox = player.heavyHitBox;
break;
default:
throw new ArgumentOutOfRangeException();
}
}
// OnStateUpdate is called on each Update frame between OnStateEnter and OnStateExit callbacks
public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
if (Time.time >= activeStartTime + _startTime && _currentHitBoxState == HitBoxState.Startup)
{
_currentHitBoxState = HitBoxState.Active;
_hitBox.DoesDealDamage = true;
}
if (Time.time >= activeEndTime + _startTime && _currentHitBoxState == HitBoxState.Active)
{
_currentHitBoxState = HitBoxState.Recovery;
_hitBox.DoesDealDamage = false;
}
if (Time.time >= stateInfo.length + _startTime)
{
// we done!
var player = animator.GetComponent<Player>();
player.StopAttack();
}
}
// OnStateExit is called when a transition ends and the state machine finishes evaluating this state
// public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
// {
//
// }
// OnStateMove is called right after Animator.OnAnimatorMove()
//override public void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
//{
// // Implement code that processes and affects root motion
//}
// OnStateIK is called right after Animator.OnAnimatorIK()
//override public void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
//{
// // Implement code that sets up animation IK (inverse kinematics)
//}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce794600e85f9c24c9bec9e94963c9da
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+33
View File
@@ -0,0 +1,33 @@
using UnityEngine;
public class EnemyLogic : InputBase
{
private Transform _transform;
private float _currentDirection;
public Transform target;
private void Start()
{
_transform = transform;
}
private void Update()
{
var horizontalDistance = target.position.x - _transform.position.x;
if (Mathf.Abs(horizontalDistance) > 1.8f)
{
_currentDirection = horizontalDistance / Mathf.Abs(horizontalDistance);
}
else
{
_currentDirection = -horizontalDistance / Mathf.Abs(horizontalDistance);
AttackCallback(AttackState.Heavy);
}
}
public override float GetMovementDirection()
{
return _currentDirection;
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84c0ec2cd4edfde4aa36e2b68bac7b96
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+23
View File
@@ -0,0 +1,23 @@
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Image))]
public class HealthBar : MonoBehaviour
{
private Image _healthBar;
private Material _material;
public Player target;
private void Start()
{
_healthBar = GetComponent<Image>();
_material = Instantiate(_healthBar.material);
_healthBar.material = _material;
}
private void Update()
{
_material.SetFloat("_Value", target.HealthPercent());
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e16e7cca04243f74f92959565ea79ddc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+34
View File
@@ -0,0 +1,34 @@
using System;
using UnityEngine;
[RequireComponent(typeof(BoxCollider2D))]
public class HitBox : MonoBehaviour
{
public bool DoesDealDamage;
public float DamageAmount;
public float hitStunDuration;
public Action<float, float> TakeDamage;
private BoxCollider2D _collider;
private void Start()
{
_collider = GetComponent<BoxCollider2D>();
}
private void OnTriggerStay2D(Collider2D other)
{
var otherHitBox = other.GetComponent<HitBox>();
if (otherHitBox != null && DoesDealDamage)
{
// do damage.
Physics2D.IgnoreCollision(_collider, other);
otherHitBox.TakeDamage(DamageAmount, hitStunDuration);
}
}
private void OnTriggerExit2D(Collider2D other)
{
Physics2D.IgnoreCollision(_collider, other, false);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20f3662ca1aaf554598d2538cb18c0e7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+69
View File
@@ -0,0 +1,69 @@
using System;
using UnityEngine;
using UnityEngine.InputSystem;
public abstract class InputBase : MonoBehaviour
{
public Action<AttackState> AttackCallback;
public abstract float GetMovementDirection();
}
public class Input : InputBase
{
// Will want to create an "Input Actions" asset, this should allow the user to set their bindings in a menu and I'll be able to load that during the game.
// The above is super important for this prototype, but could be used for all other prototypes.
public InputActionAsset controlScheme;
private InputActionMap _inputActionMap;
private InputAction _movementAction;
//private InputAction _jumpAction;
//private InputAction _crouchAction;
private InputAction _lightPunch;
private InputAction _mediumPunch;
private InputAction _heavyPunch;
// This looks dumb, but I think will make the AI setup better.
public override float GetMovementDirection()
{
return _movementAction.ReadValue<float>();
}
private void Start()
{
_inputActionMap = controlScheme.FindActionMap("P1");
_movementAction = _inputActionMap["MovementAction"];
_movementAction.Enable();
_lightPunch = _inputActionMap["LightPunch"];
_lightPunch.Enable();
_lightPunch.performed += LightPunchPerformed;
_mediumPunch = _inputActionMap["MediumPunch"];
_mediumPunch.Enable();
_mediumPunch.performed += MediumPunchPerformed;
_heavyPunch = _inputActionMap["HeavyPunch"];
_heavyPunch.Enable();
_heavyPunch.performed += HeavyPunchPerformed;
}
// TODO: probably need to handle input better.
// In terms of performance this is good, 0 latency from when the program gets the input and the animation starting.
// Obviously this will change as I think buffered input is good, but it's nice to know that I'm starting at 0ms.
// Also as combos and buffered input become a thing I'll want to handle this differently, maybe a flag/bitmask?
private void HeavyPunchPerformed(InputAction.CallbackContext obj)
{
AttackCallback(AttackState.Heavy);
}
private void MediumPunchPerformed(InputAction.CallbackContext obj)
{
AttackCallback(AttackState.Medium);
}
private void LightPunchPerformed(InputAction.CallbackContext obj)
{
AttackCallback(AttackState.Light);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d0524c116968b874985f7154bca3e653
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+148
View File
@@ -0,0 +1,148 @@
using UnityEngine;
using UnityEngine.InputSystem;
public enum AttackState : byte
{
Idle = 0,
Light,
Medium,
Heavy
}
[RequireComponent(typeof(Rigidbody2D), typeof(Animator), typeof(InputBase))]
public class Player : MonoBehaviour
{
// Will want to create an "Input Actions" asset, this should allow the user to set their bindings in a menu and I'll be able to load that during the game.
// The above is super important for this prototype, but could be used for all other prototypes.
public float speed;
public float jumpForce;
public HitBox bodyHitBox;
public HitBox lightHitBox;
public HitBox mediumHitBox;
public HitBox heavyHitBox;
public float maxHealth;
private Rigidbody2D _rigidbody;
private bool _isGrounded;
private Vector2 _verticalVelocity;
private Animator _animator;
private AttackState _currentAttack;
private int _currentAnimationStateIndex;
private InputBase _input;
private float _health;
private float _hitStunStartTime;
private float _hitStunDuration;
private bool _isHitStun;
private const string CurrentAttackText = "CurrentAttack";
private bool IsAttacking => _currentAttack != AttackState.Idle;
private void Start()
{
_health = maxHealth;
_rigidbody = GetComponent<Rigidbody2D>();
_animator = GetComponent<Animator>();
_input = GetComponent<InputBase>();
_input.AttackCallback = ChangeAttackingState;
_rigidbody.gravityScale = 0;
if (_animator.parameterCount < 1)
{
Debug.LogAssertion("Animator needs to have one property named \"CurrentState\"");
}
foreach (var parameter in _animator.parameters)
{
if (parameter.name == CurrentAttackText)
{
_currentAnimationStateIndex = parameter.nameHash;
}
}
bodyHitBox.DamageAmount = 0;
bodyHitBox.DoesDealDamage = false;
bodyHitBox.TakeDamage += DealDamage;
lightHitBox.TakeDamage += DealDamage;
mediumHitBox.TakeDamage += DealDamage;
heavyHitBox.TakeDamage += DealDamage;
}
private void DealDamage(float damage, float hitStunDuration)
{
_health -= damage;
_hitStunDuration = hitStunDuration;
_hitStunStartTime = Time.time;
_isHitStun = true;
_animator.SetBool("IsHitStun", _isHitStun);
if (_health <= 0)
{
Debug.Log("You are dead!");
}
}
public float HealthPercent() => _health / maxHealth;
public void StopAttack()
{
_currentAttack = AttackState.Idle;
}
private void ChangeAttackingState(AttackState state)
{
_currentAttack = state;
}
private void JumpPerform(InputAction.CallbackContext callbackContext)
{
if (_isGrounded)
{
_verticalVelocity.y = jumpForce;
_verticalVelocity.x = _input.GetMovementDirection() * speed;
_isGrounded = false;
}
}
private void Update()
{
if (_isGrounded)
{
if (Time.time >= _hitStunStartTime + _hitStunDuration)
{
_isHitStun = false;
_animator.SetBool("IsHitStun", _isHitStun);
}
if (!IsAttacking && !_isHitStun)
{
var dir = _input.GetMovementDirection();
var pos = _rigidbody.position;
pos.x += dir * speed * Time.deltaTime;
_rigidbody.position = pos;
}
_animator.SetInteger(_currentAnimationStateIndex, (int)_currentAttack);
}
else
{
var pos = _rigidbody.position;
pos += _verticalVelocity * Time.deltaTime;
_rigidbody.position = pos;
_verticalVelocity += Physics2D.gravity * Time.deltaTime;
}
}
private void OnCollisionEnter2D(Collision2D other)
{
foreach (var contact in other.contacts)
{
// Depending how crouch is implemented this might break or be fickle.
if (contact.point.y < _rigidbody.transform.position.y)
{
_isGrounded = true;
break;
}
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a1ef10236b718a4b851d400ea760226
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: