CodingTrain / website-archive

Archive of the Coding Train website (first version)
https://codingtrain.github.io/website-archive/
MIT License
5.72k stars 5.65k forks source link

Different framerates for different functions inside draw function (question) #224

Closed RoseCrime closed 7 years ago

RoseCrime commented 7 years ago

So...let's imagine just for fast simple example.

We have 2 functions inside draw functions:

Income - increase player's money. game - whole game function.

I want to run game() with max frameRate I can. I also want Income() to run like once in 5s.

function draw () {
  game();
  income();
} 

Here's few problems: a)I can use setInterval,but I don't want player to gain money while tab isn't active. b)frameRate too low on some kind of phones - player gain money too fast while game is going slow.

So...Guess right direction is - dependence between frame rate and income rate.

And...I have 2 ideas right now :

1.I can reduce gold and re-calculate everythin' to let it add gold per frame. But it's not the way I want it to work.

2.Guess I can use frameCount () ; And just divide it by some number to get income rate I need...

But maybe here exist some other more simple way and I just don't see it?

Merijn-DH commented 7 years ago

So you want income() to be triggered every so often without losing the proportion between game() and income() when using slower devices or while ran in a different tab? Personally, I would just use

function draw() {
   game();
   if (frameCount() % 5*frameRate == 0)
      income();
}

as you already described as idea 2. Is this not already an easy enough solution?

RoseCrime commented 7 years ago

Yep,your code made it much more simple, nice way to count same "seconds" , but depending on speed too:)

But few missed pieces here:

           if (frameCount % floor(5*frameRate()) == 0)
               income();

frameCount is variable,frameRate is function and we need it to return result of function , so we need brackets...and also it returns values like this "60.734892195579235",so we need floor () function .

RoseCrime commented 7 years ago

Actually...even this one doesn't work perfectly :D It can sometimes miss triggering things or even trigger too often and I'll show why:

let's call frameRate () - rate and frameCount - count to be short.

Misses:

rate = 60 count = 59 Nothing triggered;

Rate 58 count 60 Nothing triggered;

Rate 59 count 61 Nothing triggered;

Rate 60 count 62 Nothing triggered;

E.t.c

Repeating:

Rate 59 count 59 Trigger;

Rate 60 count 60 Trigger

E.t.c. (mult by 5 to get results for above example)

So,in this case I guess I need to completely remake condition. Best solution I see right now - reduce count each rate(); And then if count-rate >=0 -> trigger and reset counter to 0; Still not perfect,but seems workable.

And since we can't change frameCount - we need our own counter. so...

let counter = 0;

function draw() {
    if (counter - 5*frameRate() >= 0) {
        //triggerThings;
        counter = 0;
    }
    counter++;
}
ghost commented 7 years ago

setFrameRate(val)

If I understood correctly, maybe this could be of help: https://p5js.org/reference/#/p5/frameRate

For example, the function call frameRate(30) will attempt to refresh 30 times a second. This is the same as setFrameRate(val).

P.S.: Calling frameRate() with arguments that are not of the type numbers or are non positive also returns current framerate.

Merijn-DH commented 7 years ago

@RoseCrime, you're absolutely right. I hadn't thought about that yet. Don't know if there is a more elegant solution but for now your solution works fine.

RoseCrime commented 7 years ago

@gvitalie I Know we can set frameRate ,but that doesn't mean we will always get exactly number we set in here , as I got from just a bit of tests.

with frameRate(30);

frameRate returns always values until 30.9 (I hope that's higher) and below.So getting 29 is pretty common thing(even for my PC,on phones it may differ even more),which will be cause of same bug (unstable calls).

Also,my method isn't perfect too,cause in this case if we'll want to make more functions depending on time - we'll need counters for each one.

ghost commented 7 years ago

Good morning, Dear @RoseCrime :1st_place_medal:

Cite:

I want to run game() with max frameRate I can. I also want doComPort() to run like once in 5s.

... If so, I think, game() could run at frameRate we have,
and doComPort() calculate depending of time{start, pause, stop} only when draw() is running.

Tobias thinking:

Tobias name meaning - http://behindthename.com

when draw() running and get settled event time for doComPort function,
then run doComPort() function

function doComPort(){ -fbody- }
function doCountTime(period){ -fbody- }

function draw(){
    if(doCountTime(period)){
          doComPort();
    } else {
         zDopDup();
    }
}

where doComPort() is doincome() function.

Bibliography:

Zdob si Zdub - DJ Vasile - YouTube

Note: Clinking on image below will open a video on YouToBee:

10PRINT style

Note: I saw in these videos some design patterns we could do with P5JS in 10PRINT style.

Merijn-DH commented 7 years ago

What about something like this

let previousRate = 30;

function draw() {
   game();
   if (frameCount() % 5*previousRate == 0)
      previousRate = frameRate;
      income();
}

This way it won't skip any calls to income() and it will try to keep up with the framerate the device currently has. Do you think the lag/error of using the previous framerate would be too significant?

RoseCrime commented 7 years ago

Addition for my previews comment with more tests:

With frameRate set to 60 frameRate() returns from like 57 to 63 , so it may be higher too. (as if we will not set it,standart is 60 I guess)

And I found an easy way to emulate lags and tested it too: It was frameRate(40). http://savepic.net/10209771.htm I even got 0 as first frameRate() :D that's fun,but doesn't affect anything ,just first instant call,it's ok(at least it doesn't affect anything in my case). http://savepic.net/10186219.htm from 29 to 44

@gvitalie sorry,I didn't get what exactly your method should do and how work :)

@Merijn-DH I changed your code a bit :

let previousRate;
function setup() {
    previousRate = floor(frameRate());
}

function draw() {
    game();

    if (frameCount % 5 * previousRate == 0) {
        previousRate = floor(frameRate());
        income();
    }
}

So: It takes device's frameRate();

Then checks if left 5 loops of it's rate (about 5 seconds,but if it will lag or jump from like 30 to 50 it may be more or less then 5s) Then changes previousRate to device's frameRate() again.

Floor to take numbers like 29 , instead of 29,2398172 as it usually return :)

So,as result : 1.We don't need many variables which is probably good. (+) 2.It will work good if only frameRate() will not jump alot in this 5 seconds. (+||-) From my tests its (-) 3.If we'll need to make function for longer time range - it will not be exactly this time. (-)

As example let's take my tests:

prevRate = 40; ... it means we should have 40 draw calls(frames) in this second -and only after we should trigger income.

44 35 30 30 e.t.c

Average miss about 7 frames, so in this second we lost about 0.175s (40/7) As real result instead of 1s in this second it took 1.175 which means with same results for 5 seconds it will take almost 6s

Right,not so bad for 5 seconds,but what if we'll need 10 seconds,20? 1 min ? :D

Yep,it will work good if we haven't lags or our phone can handle our game perfect and will not run some background thing accidentally as phones can do :D Cause we have like about same jumps higher and lower usually like 57\63 58\62 e.t.c But even so - it will have tiny misses still :)

I guess I'll use my thing with subtraction and if I'll need alot timers - I'll just make function for them.

P.s. That's also interesting,how I get ~60 frameRate() result with set to 50 frameRate(50); And ~30 with rate set to 40.

ghost commented 7 years ago

@RoseCrime

I did this experiment. Live demo: https://gvitalie.github.io/demolution/lemma/
Source: https://github.com/gvitalie/demolution/tree/gh-pages/lemma

moving mouse on X axis will change frameRate:

function draw() {
...
frameRate(map(mouseX, 0, width, 1, 60));
...

RoseCrime commented 7 years ago

@gvitalie yep,that's exactly what I mean, frameRate can jump a lot and be case of unstable calls. Even on PC for me frameRate jumps from 14 to 30 fps :D

So...since I didn't understood your method mine is still most stable :D even if mess up code a bit =(

ghost commented 7 years ago

Good morning, Dear @RoseCrime

Have fun too! :)

Your issue inspired me to think about this: what if ...? Enough words, let's see! :1234:

Live Demo example - https://gvitalie.github.io/demolution/Muse/

RoseCrime commented 7 years ago

Nice work :) And here's actions every 300 frames ...:) (frameCount % 300 == 0) ? elements.make() : elements.show();

I'm using it in my tiny game concept

And just thought what if I'll actually make game for phones,I'll for sure want to be sure my income(or other timed in-game functions) will depend from seconds and will not be affected by lags or frameRate jumps. That's why this question exist :D