Hey guys so just as a disclaimer I'm relatively new to programming so if I'm making some super obvious mistake please go easy on me
So I'm trying to create a higher customizable Countdown timer for my game, and I want it to be able to be accurate to 0.01 Seconds. I decided I would use the Coroutine method for creating my timer instead of the delta-time one I have seen a couple of times, thinking that this would be a more efficient approach. My game is not very intensive and thus easily runs on hundreds of frames per second, so I thought that using Waitforseconds(0.01) is going to work better because it only needs to be called 100 times every second rather than multiple hundreds. however, I have come into a major issue with my timer. It is EXTREMELY slow. I ran the countdown timer on google and mine side by side starting at 25 seconds and it beat mine out by ten seconds. I even tried adding a artifical delay thinking the waitforseconds function was overshooting, so I would have the time tick down 0.01 seconds when a bit less then that had passed, but my results ended up being sort of inconsistent. Here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TimerScript : MonoBehaviour
{
public Text textDisplay;
private double secondsLeft = 30;
public bool takingAway = false;
private string Texttodisplay;
public int Milisecondsdigits = 2;
void Start()
{
textDisplay = GetComponent<Text>();
Texttodisplay = "00:" + secondsLeft;
if(Milisecondsdigits == 0)
{
Milisecondsdigits = -1;
}
}
void Update()
{
if (takingAway == false && secondsLeft > 0)
{
StopAllCoroutines();
StartCoroutine(TimerTake());
}
if(Texttodisplay.Length > 8 - (Mathf.Abs(Milisecondsdigits -2)))
{
Texttodisplay = Texttodisplay.Substring(0,8- (Mathf.Abs(Milisecondsdigits -2)));
}
textDisplay.text = Texttodisplay;
}
IEnumerator TimerTake()
{
takingAway = true;
yield return new WaitForSeconds(0.01f);
secondsLeft -= 0.01;
if(secondsLeft < 10)
{
Texttodisplay = "00:0" + secondsLeft;
}
else
{
Texttodisplay = "00:" + secondsLeft;
}
takingAway = false;
}
}
could somebody please let me know how I could cause this to become more accurate or why it's acting extremely inaccurate currently :/
Coroutine events like WaitForSeconds trigger at a defined point in Unity's event cycle, which takes place after Update() is processed (see
https://docs.unity3d.com/Manual/ExecutionOrder.html). This defined execution point might not line up exactly with the timer delay. This means it may wait longer than you want it to.
In your example, you tell it to wait for 0.01 seconds. Let's say that you are running a game at 30 frames per second. The frame time for 30 frames per second is 1/30 seconds, or approximately 0.0333 seconds. WaitForSeconds will then wait for the next frame, and 0.0333 seconds passes until the next frame. Then the next WaitForSeconds event cycle, it sees that the delay has passed and triggers, but you actually waited over 3 times as long as you wanted to because of the delay between event cycles. Since your code assumes that WaitForSeconds had only waited for 0.01 seconds, you will end up waiting longer than you intended to. This normally doesn't matter in a lot of applications, but when accumulating with frequent short delays it certainly does.
To solve this, you have two choices:
Accumulate time manually using Time.deltaTime in Update().
Coroutines likely check their completion status per frame in a
similar way in the yield WaitForSeconds event. If coroutines
check if they need to continue every frame, doing this manually with
Time.deltaTime might not be any less efficient at all than a
coroutine. You will have to benchmark to find out, because coroutines being more efficient isn't a safe assumption.
Use Time.time (or Time.realtimeSinceStartup if you want it unscaled) to measure the actual span of time that elapsed after the WaitForSeconds trigger, and use that as what you subtract from your remaining time.
There is also an additional consideration here that if you want your
text display to update at specific regular intervals, you will want
to dynamically adjust what value you pass into WaitForSeconds to
compensate for its drift.
Do you try to do it without Corroutine in a fixed update? Fixed update refresh every 0.02 seconds by default but you can settup to run in 0.01 seconds in Edit > Settings > Time > Fixed Timestep.
Replace the corroutine with a function in FixedUpdate
There is a link with better explation how works fixedupdate.
FixedUpdate Unity
Related
I'm working on a timer that needs to do some calculations and run some functions at a certain interval. I'm trying to keep the interval as big as possible, but I need it to be kind of fine grained.
Here is the periodic timer with some of the stuff that needs to happen.
So as you can see, every second (the milliseconds passed % 1000 == 0) it will do some stuff if some conditions are met. But also every 10 milliseconds I need to check some stuff.
It seems this is a bit too much, and after running the timer for 2 minutes it already drags 1 second behind. I guess I'm approaching this the wrong way. Could/should I somehow put all that logic in a function that just runs async so the timer can just keep going.
It's not the end of the world if the timer display drags for a few milliseconds every now and then, if it catches up later. But now the whole timer just drags.
_timer = Timer.periodic(Duration(milliseconds: 10), (timer) {
passedMilliseconds = passedMilliseconds + 10;
// METRONOME BEEP AND BLINK
if (passedMilliseconds % currentTimerSettingsObject.milliSecondDivider == 0) {
_playMetronomeBeep();
_startMetronomeBlink();
}
// ONE SECOND
if (passedMilliseconds % 1000 == 0) {
secondsDuration--;
// COUNTDOWN
if (secondsDuration < currentTimerSettingsObject.countDown + 1) {
_player.play('sounds/beep.mp3');
}
// SET COUNTDOWN START VALUES
if (secondsDuration == currentTimerSettingsObject.countDown) {
isCountdown = true;
}
notifyListeners();
}
// TIME IS UP
if (secondsDuration < 0) {
switchToNextTimer();
notifyListeners();
}
});
}
You cannot rely on a timer to deliver events exactly on time. You need to use a more exact method than simply incrementing a counter by 10 on every tick. One example would be to start a Stopwatch before the timer and then (knowing that your ticks will only be on approximately 10ms intervals) read stopwatch.elapsedMilliseconds and base your decisions on that.
You will need to change your logic a bit. For example, you want to know when you pass a 1 second boundary. Previously, with your exact increments of 10 you knew you would eventually reach a round 1000. Now, you might see 995 followed by 1006, and need to deduce that you've crossed a second boundary to run your per second logic.
if __name__ == '__main__':
rospy.init_node('gray')
settings = termios.tcgetattr(sys.stdin)
pub = rospy.Publisher('cmd_vel', Twist, queue_size=1)
x = 0
th = 0
node = Gray()
node.main()
We make the publisher(cmd_vel) in main, and run the main function of class gray.
def __init__(self):
self.r = rospy.Rate(10)
self.selecting_sub_image = "compressed" # you can choose image type "compressed", "raw"
if self.selecting_sub_image == "compressed":
self._sub = rospy.Subscriber('/raspicam_node/image/compressed', CompressedImage, self.callback, queue_size=1)
else:
self._sub = rospy.Subscriber('/usb_cam/image_raw', Image, self.callback, queue_size=1)
self.bridge = CvBridge()
init function makes a subscriber, which runs 'callback' when it gets data.
def main(self):
rospy.spin()
Then it runs the spin() function.
v, ang = vel_select(lvalue, rvalue, left_angle_num, right_angle_num, left_down, red_dots)
self.sendv(v, ang)
Inside the callback function, it gets a linear speed and angular speed value, and runs a sendv function to send it to the subscribers.
def sendv(self, lin_v, ang_v):
twist = Twist()
speed = rospy.get_param("~speed", 0.5)
turn = rospy.get_param("~turn", 1.0)
twist.linear.x = lin_v * speed
twist.angular.z = ang_v * turn
twist.linear.y, twist.linear.z, twist.angular.x, twist.angular.y = 0, 0, 0, 0
pub.publish(twist)
and... sendv function sends it to the turtlebot.
It has to move continuously, because if we do not publish data, it still has to move with the speed it got from the last publish. Also, callback function runs every 0.1 seconds, so it keeps sending data.
But it does not move continously. It stops for a few seconds, and go for a very short time, and stops again, and go for a very short time, and so on. The code which selects the speed works correctly, but the code who sents it to the turtlebot does not work well. Can anyone help?
Also, callback function runs every 0.1 seconds.
I believe this is incorrect. I see that you have made a self.r object but never used it anywhere in the code to achieve an update rate of 10hz. If you want to run the main loop at every 0.1 seconds, you will have to call your commands within the following loop (see rospy-rates) before calling rospy.spin():
self.r = rospy.Rate(10)
while not rospy.is_shutdown():
<user commands>
self.r.sleep()
However, this would not help you either since your code is publishing to /cmd_vel within the subscriber callback which gets called only on receiving some data from the subscriber. So basically, your /cmd_vel is not being published at the rate of 10hz but rather at the rate at which you are receiving the data from the subscribed topic ('/raspicam_node/image/compressed'). Since these are image topics, they might be taking a lot of time to be updated hence the delay in your velocity commands to the robot.
Apologies if the question is poorly phrased, I'll do my best.
If I have a sequence of values with times as an Observable[(U,T)] where U is a value and T is a time-like type (or anything difference-able I suppose), how could I write an operator which is an auto-reset one-touch barrier, which is silent when abs(u_n - u_reset) < barrier, but spits out t_n - t_reset if the barrier is touched, at which point it also resets u_reset = u_n.
That is to say, the first value this operator receives becomes the baseline, and it emits nothing. Henceforth it monitors the values of the stream, and as soon as one of them is beyond the baseline value (above or below), it emits the elapsed time (measured by the timestamps of the events), and resets the baseline. These times then will be processed to form a high-frequency estimate of the volatility.
For reference, I am trying to write a volatility estimator outlined in http://www.amazon.com/Volatility-Trading-CD-ROM-Wiley/dp/0470181990 , where rather than measuring the standard deviation (deviations at regular homogeneous times), you repeatedly measure the time taken to breach a barrier for some fixed barrier amount.
Specifically, could this be written using existing operators? I'm a bit stuck on how the state would be reset, though maybe I need to make two nested operators, one which is one-shot and another which keeps creating that one-shot... I know it could be done by writing one by hand, but then I need to write my own publisher etc etc.
Thanks!
I don't fully understand the algorithm and your variables in the example, but you can use flatMap with some heap-state and return empty() or just() as needed:
int[] var1 = { 0 };
source.flatMap(v -> {
var1[0] += v;
if ((var1[0] & 1) == 0) {
return Observable.just(v);
}
return Observable.empty();
});
If you need a per-sequence state because of multiple consumers, you can defer the whole thing:
Observable.defer(() -> {
int[] var1 = { 0 };
return source.flatMap(v -> {
var1[0] += v;
if ((var1[0] & 1) == 0) {
return Observable.just(v);
}
return Observable.empty();
});
}).subscribe(...);
I'm currently working on a Time of Day system, and in that system I want to be able to control at which hour SUNRISE starts, DAY start, SUNSET starts and NIGHT starts. At the moment this is possible in the system and when the "clock" hits the next start hour I start a Coroutine to start the rotating of the sun and moon.
My coroutine looks like this:
IEnumerator RotateSun(Vector3 fDegrees, float SecondsDuringTimeSet)
{
Quaternion quaFromPosition = _lSunLight.transform.rotation;
Quaternion quaToPostion = Quaternion.Euler(fDegrees);
for (float t = 0.0f; t < 1; t += Time.deltaTime / SecondsDuringTimeSet)
{
_lSunLight.transform.rotation = Quaternion.Lerp(quaFromPosition, quaToPostion, t);
yield return null;
}
}
And as you see I pass values into that for each time I run it. This is done like this:
if (_fCurrGameHour == _fTimeStartSunrise && _bMoveSunAndMoonOnlyOnce == true)
{
// Which TIMESET should it be:
_en_CurrTimeSet = TimeSet.SUNRISE;
// How long should the SUN move and how many which degree should it reach before that time ends
vec3MoveSun = new Vector3(10.0f, 0, 0);
StartCoroutine(RotateSun(vec3MoveSun, _fConvertSunriseToGTSeconds));
// How long should the MOON move and how many which degree should it reach before that time ends
vec3MoveMoon = new Vector3(180.0f, 0, 0);
StartCoroutine(RotateMoon(vec3MoveMoon, _fConvertSunriseToGTSeconds));
// Tell bool that we don't need to move the sun and moon more then once
_bMoveSunAndMoonOnlyOnce = false;
}
And as I wrote this system works at the moment. However it is kind of a static system. So at the moment I need to make sure that the starting hour of the game is the sunrise hour. Otherwise my coroutine would break since it would not rotate to the correct position before the day start hour. And I can definitly not start the game close to sunset then the sun rotates the complete wrong way since it's closer to rotate the wrong way then (I assume).
So my first question is: Could I somehow make the system more dynamic? I want to be able to still set at which hours SUNRISE, DAY, SUNSET and NIGHT should start so during different season for example I could have different lengths on my day. I want to be able to change these hours in the game and then if the SUNSET would be set later the sun would start to rotate a little slower since it is supposed to take longer to reach it's SUNSET position.
Onto my second question: Would it also be possible to rewrite my coroutine so I could start the game at any hour I want and the sun would still start at the right degree rotation?
The sun will always set at the same rotation (170 degrees) and rise at the same rotation (350 degree). I just want to control the time it takes before it reaches to those rotations. Maybe I could somehow move this to my update phase instead of using a Coroutine for this? I have no clue how to change this in my system so if anyone have any ideas. Please help.
You can get away from the coroutine by using a State design pattern.
public int SunRise = 8;
public int SunSet = 15;
public Quaternion RiseRotation;
public Quaternion SetRotation;
public int StartHour = 8;
float time; // our time on a 24 hr system
bool wasSun; // what was our previous state?
void Start()
{
GoToHour(StartHour);
ToggleState(IsSun());
}
void Update()
{
bool isSun = IsSun();
if (isSun != wasSun)
ToggleState(isSun);
time = (time + Time.deltaTime) % 24; // wrap at 24 hrs back to 0
if (isSun)
UpdateSun();
else
UpdateMoon();
}
bool IsSun()
{
return time >= SunRise && time < SunSet
}
void UpdateSun()
{
float t = (time - SunRise) / (SunSet - SunRise);
UpdateRotation(_lSunLight.transform, RiseRotation, SetRotation, t);
}
void UpdateMoon()
{
float t;
if (time >= SunRise)
t = time - SunRise;
else
t = time + (24 - SunSet);
t = t / ((24 - SunSet) + SunRise));
UpdateRotation(_lMoonLight.transform, RiseRotation, SetRotation, t);
}
void GoToHour(int hour)
{
t = hour / 24;
}
void ToggleState(bool isSun)
{
if (isSun)
{
// Custom logic here, turn on sun, turn off moon?
}
else
{
// Custom logic here, turn on moon, turn off sun?
}
wasSun = isSun;
}
static void SetRotation(Transform target, Quaternion start, Quaternion end, float t)
{
target.rotation = Quaternion.Lerp(start, end, t);
}
Completely untested code, but this is a base idea for how you can use a class to update either the sun or the moon. This is a binary implementation, so either the sun is showing, or the moon is. If you want to have overlap, you can of course customize it do that easily. You will want different hours for the moon rise and moon set, and then not if/else the moon and sun update.
The code is also pretty basic and makes a lot of assumptions (sunrise never overlaps the 24 hour mark.) All of these can be cleaned up. You can also create variables to store calculations that are done each frame (regarding the duration of the sunrise/set) at your discretion. If you want to do that, just set it in the GoToHour method.
The SetRotation isn't super necessary, but it serves to keep the code clean. If you later decide you want to use a Quaternion.Slerp, you only have to change it in one place.
I exposed Quaternions in the inspector, you may rather using just a float for degrees, or a Euler Vecter3. If you do either, you can just do a conversion in the Start.
I hope this answered all your questions, if not, feel free to clarify on what isn't answered.
I am still new to cocos2d and was wondering if any of you guys can help me make a score timer. I seen some questions like this but looks like it is for a way older version of cocos2d. I want the timer to start at zero and go up in mins and seconds. Really appreciate any help or advice I get.
Start a NSTimer that runs every second and calls a method that updates the timer.
Define an integer called seconds and another called minutes in the header.
-(void)updateTheTime {
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
}
label.text = [NSString stringWithFormat:#"%i:%i", minutes, seconds];
}