I have multiple classes that use a countdown timer, but when I use more than one timer, the output returns the same value, not two separate values specific to their own class.
If I use one timer, everything works perfectly with no issues, it is literally just splitting those timers into separate timers.
I'm sure I have missed something simple here, but can't figure out why they are returning the same output.
Edit: clarity - mfCurrentTime and mfTimeTravelling return the same value, and they should return different values from each timer, but they are merging together as if there is only one timer.
They aren't acting like two separate timers, they are acting as if it is one timer
Edit 2: When used individually, both timers return different values, the correct values that they should be returning. It's only when both timers are running at the same time that the issue occurs.
for example:
mfCurrentTime will always return the current time elapsed as it is continually calling Countdown Update
mfTimeTravelling returns the time elapsed when the bool _mbIsTravelling is true.
When both classes are active, mfCurrentTime is correct, and when the bool is true for mbIsTravelling in the other class, it takes the time of mfCurrentTime. The issue is that they should be two completely separate timers, not one timer.
Edit 3: #Abion47's help lead to the discovery that everything is working as it should, I had just not included a flag for the update in the Update method. I was so fixed on the way I was calling Countdown being the issue, when the real error was a massive oversight on my behalf. I added a condition to the update, and everything is fine.
if(_mbIsTravelling)
{
mIsTravellinTimer.Update();
}
-
This class is using a Countdown timer.
public class Level_TimeTaken : MonoBehaviour
{
public EndOfLevelElement UIElement;
[HideInInspector]
public float mfCurrentTime;
[HideInInspector]
public string msTimeTaken;
private bool _mbLevelIsActive;
private Countdown mLevelTime = new Countdown(9999.0f);
void Start()
{
mLevelTime.Reset();
_mbLevelIsActive = true;
}
void Update()
{
mLevelTime.Update();
TimerRunning();
UIElement.GetTimeTaken(msTimeTaken);
}
private void TimerRunning()
{
if (_mbLevelIsActive)
{
mfCurrentTime = mLevelTime.GetTimeElapsed();
msTimeTaken = mfCurrentTime.ToString("#.00");
}
}
}
This class is also using a Countdown timer.
public class Level_DistanceTravelled : MonoBehaviour
{
public EndOfLevelElement UIElement;
[HideInInspector]
public float mfTimeTravelling;
[HideInInspector]
public string msDistanceTravelled;
private bool _mbIsTravelling;
private Countdown mIsTravellinTimer = new Countdown(6000.0f);
void Start()
{
mIsTravellinTimer.Reset();
}
void Update()
{
mIsTravellinTimer.Update();
DistanceTravelling();
UIElement.GetDistanceTravelled(msDistanceTravelled);
}
private void DistanceTravelling()
{
if (GMM.Instance.Input.ShouldLHorizontalKeyLeft() || GMM.Instance.Input.ShouldLHorizontalKeyRight() || GMM.Instance.Input.ShouldLHorizontalAxis() != 0.0f)
{
_mbIsTravelling = true;
}
else
{
_mbIsTravelling = false;
}
if (_mbIsTravelling)
{
// add math for working out distance so the string displays meters spooled
mfTimeTravelling = mIsTravellinTimer.GetTimeElapsed();
msDistanceTravelled = mfTimeTravelling.ToString("#.00");
}
}
}
and Countdown class, (a generic class for handling timers etc) is here
public class Countdown
{
private float _mfCountdownTime;
private float _mfCurrentTime;
public Countdown(float lfCountdownTime)
{
_mfCountdownTime = lfCountdownTime;
_mfCurrentTime = 0f;
}
public void Update()
{
Update(Time.deltaTime);
}
public void Update(float lfTimeStep)
{
_mfCurrentTime += lfTimeStep;
if (_mfCurrentTime > _mfCountdownTime)
{
_mfCurrentTime = _mfCountdownTime;
}
}
public bool IsComplete()
{
return _mfCurrentTime >= _mfCountdownTime;
}
public void SetComplete()
{
_mfCurrentTime = _mfCountdownTime;
}
public void Reset()
{
_mfCurrentTime = 0f;
}
public float GetTimeElapsed()
{
return _mfCurrentTime;
}
public float GetTimeRemaining()
{
return _mfCountdownTime - _mfCurrentTime;
}
public float GetProgress()
{
return Mathf.Clamp01(_mfCurrentTime / _mfCountdownTime);
}
}
Thanks
In both of your scripts, you are getting the value from GetTimeElapsed. This value is the amount of time that has passed since the Countdown had started, which if both Countdowns were started at the same time would give you the same number.
Perhaps you meant to call GetTimeRemaining, which returns a value that takes the total time given into account? Or maybe GetProgress?
Related
I have an EventSystem for managing my turn-based game in Unity.
public class EventSystem : MonoBehaviour
{
private static List<Action> _commandsQueue = new List<Action>();
private bool _canExecuteCommand = true;
public void AddToQueue(Action command)
{
_commandsQueue.Add(command);
}
private void StartCommandExecution()
{
_commandsQueue[0]();
_canExecuteCommand = false;
}
public void CommandExecutionComplete()
{
_canExecuteCommand = true;
}
public void PlayFirstCommandFromQueue()
{
if (_commandsQueue.Any() && _canExecuteCommand)
{
StartCommandExecution();
}
else
{
Debug.LogError("No Command In Queue");
}
}
}
How do I put a method in Update() until _canExecuteCommand is true again but only for some methods?
It is quite broad what you are trying to do but in general you would use an endless loop within a Coroutine.
You can create a generic routine which invokes any Action you pass in as parameter once a frame like e.g.
private IEnumerator InvokeEveryFrame(Action action)
{
// This looks strange but is okey in a Coroutine as long as you yield somewhere within
while(true)
{
action?.Invoke();
// This tells Unity to "pause" this routine here
// render the current frame and continue from here in the next frame
yield return null;
}
}
So all that's left is starting the routine using MonoBehaviour.StartCoroutine like e.g.
Coroutine routine = StartCoroutine(SomeParameterlessMethod);
or if you need parameters
Coroutine routine = StartCoroutine(() => SomeMethod(x, y, z));
and then at some point later stop it using MonoBehaviour.StopCoroutine and the stored Coroutine reference like e.g.
StopCoroutine(routine);
how exactly you store that reference is up to you of course up to you.
I have a class that creates a timer in the constructor.
The timer does exactly what I need it to do but I would also like to be able to use .Stop(); and .Start(); from the main program.
There is a lot more than this but this is enough to recreate my exact problem.
In the example below I have access to Monsters[index].M_timer but .Stop(); gives an error.
class Program
{
static void Main(string[] args)
{
int index = 0;
string name = "Spider";
monster[] Monsters = new monster[100];
Monsters[index] = Create_Monster(name);
/*
Monsters[1].M_timer.Stop(); <- not how I will be using this but I need the functionality here
*/
}
public static monster Create_Monster(string _name)
{
int timer = 0;
if (_name == "Spider")
{
timer = 4000;
}
monster build = new monster(false, timer);
return build;
}
}
class monster
{
public bool can_act;
public int _timer;
public object M_timer;
public monster(bool _can_act, int _timer)
{
can_act = _can_act;
Timer M_timer = new Timer();
M_timer.Interval = _timer;
M_timer.AutoReset = true;
M_timer.Enabled = true;
M_timer.Elapsed += TimerEvent;
}
public void TimerEvent(object source, ElapsedEventArgs e)
{
can_act = true;
}
}
An initial glance looks like you have a couple of issues. first, is scope. You have in your constructor a variable called M_timer which covers the class field M_timer. You are not acting upon the same objects here. You'd have to say something like this.M_timer = M_timer.
The second issue is that you'd have to cast the class field when you want to use it because it's a generic object. so you'd have to say something like ((Timer)Monsters[1].M_timer).Stop()
Damn, I should have given this just a few more minutes:
public object M_timer;
replaced with
public Timer M_timer;
Tried to title my post as best I could, but here goes.
I have 5 classes for various "effect" animation stuff that can be done to an animation (alpha change, color change, position change, rotation change, scale change). They are all the same aside from variable types. They all use an abstract class for methods that could be shared between them all along with methods that are override in the derived classes.
I have a class that is for handling all the animations of a game object called AnimationHandler. What it does is store all the effect animations (along with the sprite animations) for my game's objects and handling them.
What I want to do is store all the effects into one dictionary that is then used to handle all changes to an animation for that object. I was wondering if it was possible to do or if it would be just easier to have 5 separate dictionaries to handle each effect separately?
The issue that I'm trying to figure out is how to access the variables that aren't in the abstract class.
Here is the sample code for my base class and a derived class:
abstract class EffectAnimation
{
protected EffectInfo Info;
public EffectInfo info
{
get
{ return Info; }
}
protected EffectType TypeOfEffect;
public EffectType typeofeffect
{
get
{ return TypeOfEffect; }
}
public abstract void NewAnimation();
public void Update(double time)
{
AnimationDone(time);
if (!info.Done)
{
if (UtilityAnimation.ReadyForNextFrame(time, Info.FrameLength))
{
Info.NextFrameTime = time + Info.FrameLength;
ChangeValue();
}
}
}
public void Start(double time)
{
Info.StartTime = time;
Info.NextFrameTime = time + Info.FrameLength;
}
public abstract void ChangeValue();
public abstract void Clamp();
protected abstract void AnimationDone(double time);
}
class AlphaAnimation : EffectAnimation
{
private float Change;
public float change
{
get
{ return Change; }
set
{ Change = value; }
}
private float End;
public float end
{
get
{ return End; }
set
{ End = value; }
}
private float Total;
public float total
{
get
{ return Total; }
set
{ Total = value; }
}
public void NewAnimation(EffectInfo stuff, float starting, float ending, float partialtotal)
{
Total = starting + partialtotal;
Info = stuff;
End = ending;
Info.Initialize();
Change = UtilityAnimation.MakeFadeAmount(stuff.AnimationLength, starting, ending, stuff.FPS);
}
public void ChangeValue()
{ Total += Change; }
private void Clamp()
{
if (Change > 0) // animation is positive
{
if (Total > End)
{
Total = End;
}
}
else // animation is negative
{
if (Total < End)
{
Total = End;
}
}
}
private void AnimationDone(double time)
{
Clamp();
if ((Total == End) && (time >= Info.DoneTime()))
{ Info.Done = true; }
}
}
What you ask for (accessing for instance change of AlphaAnimation via a reference of type EffectAnimation) is impossible without reflection or checking for the actual type. That being said, if possible, the design should be changed such that the desired effect of the animation can be triggered without knowing its type. This can be difficult depending on the case; in some cases it might make no sense altogether.
This is a possible solution;
List<EffectAnimation> animations = new List<EffectAnimation>();
animations.Add(new AlphaAnimation());
EffectAnimation item = animations[0];
if(item is AlphaAnimation)
{
AlphaAnimation alphaItem = item as AlphaAnimation;
float total = alphaItem.Total;
float change = alphaItem.Change;
}
This is basically just casting it to the right class and then you can easily access the properties you need. You can still add all your animations to the same list - just make sure to check if it is the right one when you take one out.
I have an assignment to make a thread safe logging class that writes to a file. Every ten frames I am supposed to push some information of my choice to the logging class from a separate script. I was wondering how to do that. Here is my code so far.
public class Threading
{
public bool Execute = true;
public Vector3 player;
public Vector3 WriteTime;
System.Collections.Generic.Queue<float> values;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
public void execute()
{
while (Execute)
{
System.Threading.Thread.Sleep(500);
values.Enqueue(player.x);
UnityEngine.Debug.Log("value");
}
System.IO.StreamWriter write = new System.IO.StreamWriter("values.txt"); // writes to file every 5 seconds
while (values.Count > 0)
{
WriteTime = write.WriteLine(values.Dequeue().ToString());
}
write.Close();
}
public void Lock() // applied Lock threading
{
while(true)
{
lock (this)
{
// dont have anything here yet. Trying to figure out locks
}
}
}
Thank you.
i like to get some thoughts on how to implement a tick based system.
Every action a player or non player got has a initial time to perform and a cooldown time. Once a creatures cooldown time has passed it gets to choose a new action. If the player has to choose a action the game is "paused".
Example:
1: Player heavy swing (50 ticks to perform, 50 ticks to cool down)
2: Game goes on for 50 ticks.
3: NPC's can set actions.
4: Player swings and cools down for 50 ticks.
5: NPC's can set actions.
6: Game paused for the player.
What i currently have works but is not efficient. I have a class with each action as a static method. These method output a struct containing all the data. This will be passed to a actioncue of a individual creature.
Every update loop call the cue and start counting down the attack time if the player has put in a action. Once the attack should be solved i call a static method in the actions class again. And i start counting down the cooldown timer.
So what i should have is probably a list holding all actions and sorting that list skipping unnecessary time/ticks and go straight to the next action. But there will be different types of actions like move, attack, ability and i cant wrap my head around a good implementation of this.
When a creature performs a basic attack this gets called (attack is the creatures own instanced attack struct)
attack = Actions.BasicAttack(this, player, rand);
This is how the Actions class looks like.
public struct Attack
{
public int Damage;
public string Type;
public int Time;
public int Cooldown;
public Creature target;
public bool solved;
}
public static Attack BasicAttack(Creature attacker, Creature defender, Random rand)
{
Attack attack = new Attack();
attack.Damage = rand.Next(attacker.MinBaseDmg, attacker.MaxBaseDmg + 1);
attack.Type = "Melee";
attack.Time = 50;
attack.Cooldown = 30;
attack.target = defender;
attack.solved = false;
return attack;
}
And this gets called in the update method of each creature when the player has a action cued. Tick = 0 if player has no action cued and tick = 1 when player has a action cued up.
protected void ActionCue(int tick)
{
if (attack.target != null)
{
if (attack.Time > 1)
{
Console.WriteLine(attack.Time);
attack.Time -= tick;
this.free = false;
}
else if (!attack.solved)
{
Actions.SolveAttack(attack.Damage, attack.Type, attack.target);
attack.solved = true;
}
else if (attack.solved && attack.Cooldown > 1)
{
//Console.WriteLine(attack.Cooldown);
attack.Cooldown -= tick;
}
else
free = true;
}
}
Consider something like this (i will use pseudocode - its far from being optimized etc. but it might be just fast enough, or set you on your way to optimize what youre trying to do)
class CombatEventList
{
public static AddEvent(CombatEvent event, int ticksTillHappens)
}
virtual class CombatEvent
{
public virtual void CombatAction()
}
class PlayerActionChoice : ComabtEvent
{
public void CombatAction
{
var playerAction = GetUserDecision();//returns i.e CombatEvent PlayerMeeleAttack
CombatEventList.AddEvent(playerAction, 0);
}
}
class PlayerMeeleAttack : CombatEvent
{
int cooldownInTicks = 50;
public void CombatAction
{
MakeAttack()//damages the moster etc - all the stuff the attack is supposed to do
var nextEvent = new PlayerActionChoice();
CombatEventList.AddEvent(nextEvent, cooldownInTicks);
}
}
So, how this works?
We got a list of events.
The list checks all the events that are supposed to happen now and, executes their CombatAction.
In their CombatAction, the events add new events to the list. For example a PlayerMeeleAttack event sets the PlayerActionChoice event after an appropriate cooldown, so that he can take another action later.
After all current CombatEvents are resolved and have added their own CombatEvents to the list, the list checks the next Event (lowest delay)
The list sleeps for the specified number of ticks (the delay of the next Event). Once its done sleeping, it lowers the cooldowns on all events by an appropriate amount, and handles all the current events (those that just hit 0 delay)
This goes in a loop
The list starts with the CombatStartEvent on it, thats going to happen right away(delay 0). It sets the PlayerActionChoice and MonsterActionChoice events in the CombatAction method.
Of course this is far from being optimal, its just a sketch, or an idea for you to think through. There may be better ideas, i didnt give the problem very much thought - but this is obviously more efficient than your current solution :)
Ok after a couple of hours it seems i got this working. Here is the code for anyone who needs it. I am open for feedback too.
This is the ability class, everything needed can be added here. For the tick based system just the time variables are important.
public string AbilityName { get; private set; }
public int minDamage { get; private set; }
public int maxDamage { get; private set; }
public int ActivationTime { get; private set; }
public int CooldownTime { get; private set; }
public int Timer;
public Ability(string AbilityName)
{
if (AbilityName == "attack")
{
this.AbilityName = AbilityName;
minDamage = 10;
maxDamage = 20;
ActivationTime = 20;
CooldownTime = 30;
Timer = ActivationTime;
iconPath = "ability/icon/attack";
}
}
This is the task class, the ability, attacker and targets get passed as parameters, a ability name or type can be used to perform different kinds/types of abilities such as movement vs attacking.
public Ability ability { get; private set; }
public bool onCooldown;
public Creature attacker { get; private set; }
List<Creature> targets = new List<Creature>();
/// <summary>
/// Initiates a attack task
/// </summary>
/// <param name="attacker"></param>
/// <param name="defender"></param>
public Task(Creature attacker, List<Creature> targets, Ability ability)
{
this.ability = ability;
this.attacker = attacker;
this.targets = targets;
onCooldown = false;
}
public void Perform()
{
//performce abilty
Console.WriteLine(attacker.Name + " performce ability");
}
The player or AI can now create a task from the abilities they own like so:
targets.Add(player); //This is just a basic attack so only one "creature" gets in the list
task = new Task(this, targets, abilityList[0]); //Task is created
taskList.Add(task); //Task is added to a list i manage in a main class
free = false; //creature is put on hold and cant do anything till task is completed
This is where most of the magic happens. In the main class this method is being called each update if the player is not "free". I update all the tasks before i do anything with the task next in line because i dont want to edit its stats after updating its status.
private void TaskHandler()
{
int ticksToAdvance = 0;
// get the next task requiring a action
taskList.Sort((x, y) => x.ability.Timer.CompareTo(y.ability.Timer));
//get the amount of cooldown left
ticksToAdvance = taskList[0].ability.Timer;
//Update all tasks
foreach (Task t in taskList)
{
t.ability.Timer -= ticksToAdvance;
}
//check if this task is on cooldown
if (taskList[0].onCooldown)
{
//Reset ability timer, free creature and remove task from the list.
taskList[0].ability.Timer = taskList[0].ability.ActivationTime;
taskList[0].attacker.free = true;
taskList.RemoveAt(0);
}
else
{
//perform ability
taskList[0].Perform();
//set timer to cooldown
taskList[0].onCooldown = true;
taskList[0].ability.Timer = taskList[0].ability.CooldownTime;
}
}