Monday
The way I want the combo system to work is to only allow the player to enter a combo when they are locked on. I can do this easily by adding a check if they are locked on and only then run the combo code.
private void Update()
{
if (plos.lockedOn)
{
Combo();
}
}
The way the system will work is that when the player is locked on, they can enter a number of inputs to try and complete a combo move. I will have a pre-determined combo that the player can attempt to input. Each time the player presses an attack button, it is to be checked against the pre-determined combo. If they entered correctly, then the player can press the next button. If not, then the player must restart the combo.
My first run at the script was as follows:
public class ComboSystem : MonoBehaviour
{
public char currentButton;
public char[] combo_arr = { 'A', 'A', 'B', 'B', 'X' };
PlayerLockOnSystem plos;
private void Start()
{
plos = GetComponent<PlayerLockOnSystem>();
}
private void Update()
{
if (plos.lockedOn)
{
Combo();
}
}
void DetectComboButtons()
{
if (Input.GetButton("Joystick A"))
{
currentButton = 'A';
}
if (Input.GetButton("Joystick X"))
{
currentButton = 'X';
}
if (Input.GetButton("Joystick B"))
{
currentButton = 'B';
}
}
void Combo()
{
for (int i = 0; i < combo_arr.Length; i++)
{
DetectComboButtons();
if (currentButton == combo_arr[i])
{
Debug.Log("Correct: " + currentButton);
}
else
{
i = 0;
Debug.Log("Incorrect");
}
}
}
}
The first issue for this code is that it causes an unbreakable loop, and by doing so, causing the editor to crash and require a force quit. I had no idea on how to fix this so posted a question on StackOverflow to see if I could get some help.
This is what the script looks like after using the code given on StackOverflow.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ComboSystem : MonoBehaviour
{
public char[] combo_arr;
int currentIndex;
PlayerLockOnSystem plos;
private const char CHAR_THAT_MEANS_THAT_THE_PLAYER_DIDNT_BREAK_THE_CHAIN = '0';
private int _currentPlaceInTheComboChain = 0;
private void Start()
{
plos = GetComponent<PlayerLockOnSystem>();
combo_arr = new char[5];
combo_arr[0] = 'A';
combo_arr[1] = 'A';
combo_arr[2] = 'B';
combo_arr[3] = 'B';
combo_arr[4] = 'X';
}
private void Update()
{
if (plos.lockedOn)
{
Combo();
}
}
char DetectComboButtons()
{
if (Input.GetButton("Joystick A"))
{
return 'A';
}
if (Input.GetButton("Joystick X"))
{
return 'X';
}
if (Input.GetButton("Joystick B"))
{
return 'B';
}
return CHAR_THAT_MEANS_THAT_THE_PLAYER_DIDNT_BREAK_THE_CHAIN;
}
void Combo()
{
char currentButton = DetectComboButtons();
if (currentButton == CHAR_THAT_MEANS_THAT_THE_PLAYER_DIDNT_BREAK_THE_CHAIN)
{
return; //the player didn't continue the combo but didn't break it (yet)
}
if (currentButton == combo_arr[_currentPlaceInTheComboChain])
{
_currentPlaceInTheComboChain++;//wait for the next button, will only be checked the next time update is called
Debug.Log("Correct: " + currentButton);
if (_currentPlaceInTheComboChain == combo_arr.Length)
{ //this was the last button in the combo
_currentPlaceInTheComboChain = 0; //for a new combo
Debug.Log("Combo completed");
}
}
else
{
_currentPlaceInTheComboChain = 0; //player broke the chain
Debug.Log("Incorrect " + currentButton);
}
}
}
After receiving some help, I was able to fix the unbreakable loop. The next issue came with that the combo check didn’t appear to be working. I could enter the correct buttons but it wouldn’t say the combo was completed.
After querying the response on StackOverflow, it was due to the simple reason that I had used GetButton and not GetButtonDown. This meant that the the button was being pressed each frame and not just when I have pressed it once.
Tuesday & Thursday
Now that the framework is in place for the combo mechanic, I am able to add some more depth to the system by adding a timer. The idea is that after the player activates the combo, they must keep a consistent rhythm to each button press. This will be called a “Perfect Combo”. If the player doesn’t succeed in keeping to the rhythm, they will only receive a normal combo.
The first step was to implement a check when the player pressed a correct button. This utilises the second if statement in the block below.
if (currentButton == enemyController.combo[currentIndex])
{
if ((elapsed > 0.75f) && (elapsed < 1.25f))
{
currentIndex++;
Debug.Log("Perfect");
}
else
{
currentIndex++;
}
I created a new variable called elapsed which counts how much time has passed since the previous button was pressed. I used a range of 0.75 and 1.25 to give the player a buffer as timing a second perfectly would be impossible for a human.
The next step was to actually increment the timer when the player has pressed a button. My first thought was to increase time in the first if statement. However this didn’t work as the timer would only start when the button is actually pressed and not continue to count until the next.
I fixed this by making the timer increase in “player isn’t pressing a button” case.
if (currentButton == defaultChar)
{
elapsed += Time.deltaTime;
//Debug.Log(elapsed);
return; /
}
This way, the timer increments each whenever the player isn’t pressing the button and is reset when the player does press the button.
In order to determine what is considered a perfect/not perfect combo, I created a new variable called perfectCounter. Then, incremented the counter by 1 every time the player scored a perfect button press. (As shown below with perfectCounter++).
if ((elapsed > 0.75f) && (elapsed < 1.25f))
{
perfectCounter++;
currentIndex++;
Debug.Log("Perfect");
elapsed = 0f;
}
Finally, the perfectCounter variable is compared with the length of the combo to determine if the player has completed a perfect combo. If the numbers match, then the player was perfect for each button press.
if (currentIndex == enemyController.combo.Length)
{ //this was the last button in the combo
if (perfectCounter == enemyController.combo.Length)
{
Debug.Log("Perfect Combo Completed");
currentIndex = 0; //for a new combo
//do all the fun stuff
}
else
{
Debug.Log("Not perfect combo completed");
//do more fun stuff but not as fun
}
}
In the original check to see if the player has pressed all buttons, a second if statement is used to check whether perfectCounter is the same as the combo length.
As well as the timing system, I was also able to implement an easily adjustable damage system for the combo. The more button presses the player, a multiplier value increases, multiplying the amount of damage the enemy takes.
int multiplier;
if (currentButton == enemyController.combo[currentIndex])
{
multiplier++;
I created a new variable called multiplier. This then gets incremented by 1 when the player presses a correct button. I also made sure to reset the multiplier when the player gets it incorrect.
else
{
multiplier = 0; //resetting multiplier if the combo breaks
currentIndex = 0; //player broke the chain
Debug.Log("Incorrect " + currentButton);
}
After getting the mutlipler to work, I worked on creating a system that would calculate what damage is inflicted based on the button the player presses. The first step was to create an int array method.
int[] GetDamageValues()
Inside the method, I created another int array that stores the damage values based on the button, and also created an if statement to detect what button was pressed.
if (Mathf.Abs(Input.GetAxis("Joystick L_Trigger")) > 0.1f)
{
if (Input.GetButtonDown("Joystick A"))
{
damageValues[0] = 30;
damageValues[1] = 5;
return damageValues;
}
if (Input.GetButtonDown("Joystick B"))
{
damageValues[0] = 20;
damageValues[1] = 15;
return damageValues;
}
}
else
{
if (Input.GetButtonDown("Joystick A"))
{
damageValues[0] = 10;
damageValues[1] = 10;
return damageValues;
}
if (Input.GetButtonDown("Joystick B"))
{
damageValues[0] = 20;
damageValues[1] = 20;
return damageValues;
}
if (Input.GetButtonDown("Joystick X"))
{
damageValues[0] = 30;
damageValues[1] = 30;
return damageValues;
}
}
The method then assigns values the damage to health at index 0 of the array, and the mana damage at index 1. It then returns the damageValues array so that it can be accessed in other areas of the script.
To actually damage the enemy, I first created one last int array called DamageToTake and assigned that to the return value of the previous int method.
int[] damageToTake = GetDamageValues();
I lastly added a new line in the if statement that checks if the player has pressed the right button.
if (currentButton == enemyController.combo[currentIndex])
{
multiplier++;
Debug.Log(damageToTake[0]* multiplier + " " + damageToTake[1]* multiplier);
enemyController.Takedamage((damageToTake[0] * multiplier), (damageToTake[1] * multiplier));
Leave a Reply