I am working on an RPG and started working on ability cooldowns. I have a player that is controlled by the user (player1), and a player controlled by the computer (player2) for testing purposes. The cooldown for abilities work perfectly for the user controlled player, but when it comes to the AI, the computer will not execute the ability unless the computer goes first (when the ability is off cooldown by default). Below is the code I think is relevant to the question, but if more information is needed I will update the post.
The ability class:
using UnityEngine;
public class attackList : MonoBehaviour{
public int dmg;
public float coolDown;
public float _attackTimer;
public void Attack1(basePlayer user, basePlayer target, int D)
{
coolDown = 2.5f;
if (user._attackTimer == 0)
{
dmg = D;
user._attackTimer = coolDown;
target.curHealth -= dmg;
}
}
public void cooldown(basePlayer user)
{
if (user._attackTimer > 0) {
user._attackTimer -= Time.deltaTime;
if (user._attackTimer < 0) {
user._attackTimer = 0;
}
}
}
}
The attack logic class:
public class attackLogic : MonoBehaviour {
private attackList _attackList = new attackList();
private playerList _playerList = new playerList();
public bool player1_turn = false; //player1 is not allowed to go first by default
public bool player2_turn = false; //player2 is not allowed to go first by default
void Start()
{
int rnd = Random.Range (1,3); // random value between 1 and 2 generated, which will determine what player goes first
if (rnd == 1)
{
player1_turn = true;
}
if (rnd == 2)
{
player2_turn = true;
}
//current health set to the maximum health at the beginning of each fight
_playerList.player1.curHealth = _playerList.player1.maxHealth;
_playerList.player2.curHealth = _playerList.player2.maxHealth;
}
void Update()
{
Logic(); //run through the logic of the battle
}
public void Logic()
{
if (player1_turn == true)
{
_attackList.cooldown(_playerList.player1);
if (Input.GetKeyUp("1"))
{
_attackList.Attack1(_playerList.player1, _playerList.player2, 5);
player1_turn = false;
player2_turn = true;
}
}
if (player2_turn == true)
{
_attackList.cooldown(_playerList.player2);
_attackList.Attack1(_playerList.player2, _playerList.player1, 5);
player1_turn = true;
player2_turn = false;
}
}
}
1) I have a nasty suspicion that the actual problem is in Time.deltaTime, but you didn't show the code so I can't confirm this.
2) Since you have time-based code here you can't expect it to behave the same when you step through the code. Thus it often helps to dust off some of the old techniques--namely, print statements. Of course things have changed enough over the years that it's become write but the ideas still work. Take a part of the screen that isn't important to what you're testing and write the variables out that are involved in what you are doing. I think you'll find player 2 is cooling down extremely slowly.
3) I think you need a better understanding of objects. You appear to be simply using them as containers. I also dislike player1 and player2--you should simply have a list of players. Remember how the WOPR was defeated in War Games?--that can be very useful at times in balancing things.
Related
I am very new to programming and Unity so please have patience. I am trying to disable a collider for a specified amount of time upon pressing a button (In this case, the button "s"). I have been trying to do this by using the "WaitForSeconds" command but I have never used it before and I don't know it works, but I tried anyway and as I expected it didn't work. I have to mention that the simulation does run with no errors but when I press "s" the collider doesn't disable at all. As I have mentioned I am very new to programming and Unity so I apologize if I don't understand some solutions you may suggest. Anyways here is the code
{
public bool IsFacingR = true;
public bool IsFacingL = false;
public Rigidbody2D RB;
public BoxCollider2D m_col;
void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
IsFacingR = true;
IsFacingL = false;
}
if (Input.GetKeyDown(KeyCode.A))
{
IsFacingR = false;
IsFacingL = true;
}
if (Input.GetKeyDown(KeyCode.S))
{
Slider();
StartCoroutine(SlideCol());
m_col.enabled = false;
}
}
void Slider()
{
if (IsFacingR == true)
{
RB.AddForce(Vector2.right * 9999f);
}
if (IsFacingL == true)
{
RB.AddForce(-Vector2.right * 9999f);
}
}
IEnumerator SlideCol()
{
yield return new WaitForSeconds(1);
m_col.enabled = true;
}
}
Well i tried your code in my project and it works fine. I can see collider disable for one second. Maybe there is someting that makes your collider always enable.
I've been working on a game as a project and I've gotten round to introducing invincibility frames when the player takes a heart of damage. In this case I want it so that the player model flashes roughly once every 0.1 seconds and to have the invincibility last for 2 seconds.
I've written this code and I can't figure out why it isn't working. By the way using this code when the player takes damage they cannot take damage afterwards so something is really messed up (it isn't just the visual invincibility being an issue).
(Thank you)
private void loseHealth()
{
if (invinTimerCounter == 0)
{
curHealth -= 1;
invinTimerCounter = invinTimer;
invincibilityBlink();
}
}
private void invincibilityBlink()
{
for (int i = 0; i < 10; i++)
{
Invoke("spriteDisable", 1);
Invoke("spriteEnable", 1);
}
}
private void spriteEnable()
{
this.spriteRenderer.enabled = true;
}
private void spriteDisable()
{
this.spriteRenderer.enabled = false;
}
private void Update()
{
if (invinTimerCounter < 0)
{
invinTimerCounter -= Time.deltaTime;
}
}
In addition to jmalenfant's comment, I'd like to rewrite your invincibilityBlink() method to use a coroutine:
private IEnumerator invincibilityBlink()
{
for (int i = 0; i < 10; i++)
{
spriteDisable();
yield return new WaitForSeconds(0.1f);
spriteEnable();
yield return new WaitForSeconds(0.1f);
}
invincible = false;
}
Then, here:
if (!invincible)
{
curHealth -= 1;
invincible = true;
StartCoroutine(invincibilityBlink());
}
Oh, and we'll change that messy float to a boolean and let the coroutine handle it too, so if you decide to change the invincibility time, you only need to change things in one place.
I'm making a board game in Unity with multiple branching paths where the players can decide which one to follow. (Think the Mario Part game board) But the player input on which path to follow isn't working.
So I have a script that has the tokens follow a set path and (Linked List for the path) with a for loop to have the tokens move their allocated amount. Within the loop, I have an If statement that checks how many paths the tile has and if there are more than one if runs a separate script that pauses the game and offers a menu prompt for the players to pick one path or the other.
I've tried tossing in a few contitions for continuing the game within the loop but they either ignore the player input, are an input behind or just break the code.
`for (int i = 0; i < spacesToMove; i++)
{
if (final_Waypoint == null)
{
final_Waypoint = Starting_Waypoint;
}
else
{
if (final_Waypoint.Next_Waypoint == null || final_Waypoint.Next_Waypoint.Length == 0)
{
//we are overshooting the final waypoint, so just return some nulls in the array
//just break and we'll return the array, which is going to have nulls at the end.
Debug.Log(Current_Waypoint);
break;
}
else if (final_Waypoint.Next_Waypoint.Length > 1) // && if playerID == 0 so it only appears for players.
{
menu.Pause_for_Choice();
//if (menu.ButtonPress == true)
final_Waypoint = final_Waypoint.Next_Waypoint[menu.choice_int];
//***There's a bug here. It calls menu as per normal, and the game pauses.
//But when I click 'no', choice_int isn't updated first: this function finishes first,
//so this function gets old choice_int. THEN the button function executes.
Debug.Log("test 3 " + menu.choice_int);
}
else
{
final_Waypoint = final_Waypoint.Next_Waypoint[0];
}
}
listOfWaypoints[i] = final_Waypoint;
Debug.Log("i:" + i);
}
return listOfWaypoints;
}`
`{
public GameObject choice_Menu;
public bool isPaused = true;
public int choice_int=0;
public int choice_placeholder = 0;
public void Pause_for_Choice()
{
isPaused = true;
choice_Menu.SetActive(true);
Time.timeScale = 0;
}
public void Yes()
{
choice_placeholder = 0;
UnPause_Game();
}
public void No()
{
choice_placeholder = 1;
UnPause_Game();
}
public void UnPause_Game()
{
isPaused = false;
choice_Menu.SetActive(false);
Time.timeScale = 1;
}
}`
I have 3 checkboxes
public bool stateForward = false, stateReverse = false, stateRandom = false;
I want that i will be able to chose each time only one checkbox. But also in editor mode and also when the game is running. And when the game is running i want to make it effect on the game.
In the top of the script i added:
[ExecuteInEditMode]
I tried to do in the Start function
void Start()
{
while (true)
{
if (stateForward == true)
{
stateRandom = false;
stateReverse = false;
}
else if (stateReverse == true)
{
stateRandom = false;
stateForward = false;
}
else if (stateRandom == true)
{
stateForward = false;
stateReverse = false;
}
}
anims = GetComponent<Animations>();
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
objectsToMove = GameObject.FindGameObjectsWithTag("Robots");
originalPosition = objectsToMove[0].transform.position;
}
But i'm getting on anims: Unreachable code detected
And in the Update function:
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
DrawLinesInScene();
anims.PlayState(Animations.AnimatorStates.RUN);
WayPointsAI();
}
}
And in WayPointsAI
private void WayPointsAI()
{
if (stateForward == true)
{
if (targetIndex == waypoints.Length)
targetIndex = 0;
}
if (stateReverse == true)
{
if (targetIndex == 0)
targetIndex = waypoints.Length;
}
waypoint = waypoints[targetIndex].transform;
float distance = Vector3.Distance(objectsToMove[0].transform.position, waypoint.transform.position);
objectsToMove[0].transform.rotation = Quaternion.Slerp(objectsToMove[0].transform.rotation, Quaternion.LookRotation(waypoint.position - objectsToMove[0].transform.position), rotationSpeed * Time.deltaTime);
//move towards the player
if (distance < 30)
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * slowDownSpeed * Time.deltaTime;
}
else
{
objectsToMove[0].transform.position += objectsToMove[0].transform.forward * moveSpeed * Time.deltaTime;
}
if (distance < 2)
{
if (stateForward == true)
targetIndex++;
if (stateReverse == true)
targetIndex--;
}
}
First off, you are never going to get out of your Start() method. You have a while(true) loop that you never break out of, so you will be stuck there.
Instead of multiple checkboxes that you can only choose one of, just use an enumeration. Unity will give you a drop down and you can choose one of the three available states.
enum MyStateEnum{ Forward, Reverse, Random }
public MyStateEnum State;
If you want them to be exclusive, you should probably use a single enum field instead of three bool fields. Your enum would have values Forward, Reverse, and Random, while your MonoBehavior would have a single field of your enum type. This ensures that only one will be chosen at any given time, and the editor should show a drop-down for selecting which value rather than a series of checkboxes. This not only fixes the editor UI issue, but will also lead to cleaner code with fewer potential bugs.
I have a co-routine that is triggered when the bool of a toggle button changes, when the bool is changed again that co-routine should be stopped and another one should start. This is my code:
public class thermoPowerControlPanel : MonoBehaviour {
private ThermoElectric thermo;
public bool toggleBool1;
public int temperature;
private int tempUp = 10;
private int tempDown = 1;
public thermoPowerControlPanel (){
temperature = 100;
}
public void turbine1State (bool toggleBool1) {
if (toggleBool1 == false) {
Debug.Log (toggleBool1);
Invoke("ReduceTemperatureEverySecond", 1f);
}
if (toggleBool1 == true) {
Debug.Log (toggleBool1);
Invoke("IncreaseTemperatureEverySecond", 1f);
}
}
private void ReduceTemperatureEverySecond()
{
if (toggleBool1 == true)
{
Debug.Log("I was told to stop reducing the temperature.");
return;
}
temperature = temperature - tempDown;
Debug.Log (temperature);
Invoke("ReduceTemperatureEverySecond", 1f);
}
private void IncreaseTemperatureEverySecond()
{
if (toggleBool1 == false)
{
Debug.Log("I was told to stop increasing the temperature.");
return;
}
temperature = temperature + tempUp;
Debug.Log (temperature);
Invoke("ReduceTemperatureEverySecond", 1f);
}
}
When the function turbine1State(bool t1) receives the first bool (false), the routine decreaseTemperatureEverySecond() starts but it stops immediately after, sending the Debug.Log message, it should keep reducing the temperature until the bool (activated by the toggle button) turned true.
.
Can you help?
It is this easy!
public Toggle tog; // DONT FORGET TO SET IN EDITOR
in Start ...
InvokeRepeating( "Temp", 1f, 1f );
... and then ...
private void Temp()
{
if (tog.isOn)
temperature = temperature + 1;
else
temperature = temperature - 1;
// also, ensure it is never outside of 0-100
temperature = Mathf.Clamp(temperature, 0,100);
}
If you ever need to "totally stop" that action (both up and down), just do this
CancelInvoke("Temp");
So easy!
NOTE purely FYI, the other pattern I explained is this:
bool some flag;
Invoke("Temp", 1f);
private void Temp()
{
if (some flag is tripped) stop doing this
.. do something ..
Invoke( ..myself again after a second .. )
}
In real life, it is usually better to "keep Invoking yourself" rather than use InvokeRepeating.
In this simple example, just use InvokeRepeating, and then CancelInvoke.
You can stop coroutine only by it name.
Just try some like StartCoroutine("increaseTemperature"); and then StopCoroutine("increaseTemperature");.
http://docs.unity3d.com/Manual/Coroutines.html
There are a number of ways to call StartCoroutine. You can "stop" a coroutines IF you start it with the "string" method, like this StartCoroutine("FunctionNameAsStringHere"); If you start it like this StartCoroutine(FunctionNameAsStringHere()); you cannot stop it by name.
(You can also access the actual enumerator to stop coroutines, but that is far beyond the scope of a beginner using coroutines.)