Gradually ramp up a parameter

I'm working on a game concept where players can earn money steadily over time (e.g. $50 per second). However, I don't want the money to increase in jumps of $50 every second; instead, I want it to smoothly increase.

setInterval(increaseMoney, 1000);
function increaseMoney() {
  money = money + moneyPerSecond;
}

My current approach involves dividing the earning rate into two categories: one earns $1 every 1/moneyPerSecond seconds (where moneyPerSecond < 1000), and the other category earns y dollars every 0.001 seconds.

However, there are two challenges: I initially avoided floats to maintain precision, but now I realize that using integers may cause imprecision issues. Edit: It's okay to use floats! I could also enhance precision by increasing the time interval between updates, even though this might make the increments less smooth.

My questions:

  • Am I on the right path with this approach?
  • How can I implement the solution effectively to minimize imprecision?

Requirements: Floats are acceptable, and accuracy is crucial for the implementation.

Answer №1

1. simple game loop

Experience a basic game loop structure with a start function for initializing the game and an update function that runs every frame. Modify the target value in real-time to witness the smooth transition using lerp -

let money;
let target;

// initialization
function start ()
{ money = document.forms.example.money
  target = document.forms.example.target
}

// frame by frame process
function update (delta)
{ const next = lerp(Number(money.value), Number(target.value), delta/1e2)
  money.value = next.toFixed(2)
}

// helper functions
function lerp (v0, v1, t, p = 1e-3)
{ const next = (1 - t) * v0 + t * v1
  if (Math.abs(v1 - next) < p)
    return v1
  else
    return next
}

function sleep (ms)
{ return new Promise(r => setTimeout(r, ms)) }

function time ()
{ let now = Date.now()
  let last
  return _ =>
  { last = now
    now = Date.now()
    return now - last
  }
}

async function loop ()
{ const deltaTime = time()
  await start()               // call `start` to initialize
  while (true)
  { await update(deltaTime()) // call `update` each frame
    await sleep(50)
  }
}

// running the game
loop()
<form id="example">
  target: <input type="number" name="target" value="3000">
  &larr; try a big value like <b>99999999</b><br>
  money: <output name="money">0.00</output><br>
</form>


2. plain html form

If you prefer to see this concept outside of a traditional game loop scenario, we can illustrate it through a simple Html form. The implementation of lerp here (similar to above) is inspired by Wikipedia's Linear Interpolation article -

function lerp (v0, v1, t, p = 1e-3)
{ const next = (1 - t) * v0 + t * v1
  if (Math.abs(v1 - next) < p)
    return v1
  else
    return next
}

function sleep (ms)
{ return new Promise(r => setTimeout(r, ms)) }

async function onSubmit (event)
{ // preventing default form behavior
  event.preventDefault()

  // retrieving input values from form as Numbers
  const f = event.target
  let from = Number(f.from.value)
  let to = Number(f.to.value)

  // execute lerp operation!
  do
  { f.output.value = from.toFixed(2)
    from = lerp(from, to, 0.33)
    await sleep(50)
  } while(from < to)
}

// adding form listener
document.forms.example.addEventListener("submit", onSubmit)
<form id="example">
  from: <input name="from" value="100"><br>
  to: <input name="to" value="3000"><br>
  output: <output name="output"></output><br>
  <button type="submit">Lerp it!</button>
</form>


3. basic html form with generator

We can also streamline the onSubmit listener by implementing lerp as a generator, reducing complexity significantly -

function* lerp (v0, v1, t, p = 1e-3)
{ do
  { yield v0
    v0 = (1 - t) * v0 + t * v1
  } while (Math.abs(v1 - v0) > p)
  yield v1
}

function sleep (ms)
{ return new Promise(r => setTimeout(r, ms)) }

async function onSubmit (event)
{ event.preventDefault()
  const f = event.target

  // iterating over lerp generator
  for (const v of lerp(Number(f.from.value), Number(f.to.value), 0.33))
  { f.output.value = v.toFixed(2)
    await sleep(50)
  }
}

document.forms.example.addEventListener("submit", onSubmit)
<form id="example">
  from: <input name="from" value="123.45"><br>
  to: <input name="to" value="3000.99"><br>
  output: <output name="output"></output><br>
  <button type="submit">Lerp it!</button>
</form>


Answer №2

To efficiently handle decimals in your code, it's recommended to internally track them and only display whole numbers to users without showing the decimals. Utilize the floor() function to achieve this.

While this might be considered excessive for your specific needs, below is a demonstration of how I manage multiple calculations and present whole numbers to the user.

class TimerCalc {
  constructor(incAmt, interval, updater) {
    this.incAmt = incAmt;
    this.interval = interval;
    this.updater = updater;
    this.lastTimeRun = performance.now();
    this.calc();
  }
  calc() {
    const diff = performance.now() - this.lastTimeRun;
    this.lastTimeRun = performance.now();

    const incAmt = diff / this.interval * this.incAmt;
    this.updater(incAmt);

    this.timer = window.setTimeout(() => this.calc(), 10);
  }
  kill() {
    this.timer && window.clearTimeout(this.timer);
  }
}

class Bank {
  constructor(initialAmount, outputSelector) {
    this.funds = initialAmount || 0;
    this.outputElem = document.querySelector(outputSelector);
    this.timers = {};
  }
  updateAccount(diff) {
    this.funds += diff;
    this.updateScreen();
  }
  updateScreen() {
    this.outputElem.textContent = Math.floor(this.funds).toLocaleString();
  }
  addResource(name, rate, interval) {
    this.timers[name] = new TimerCalc(rate, interval, this.updateAccount.bind(this));
  }
}

const myBank1 = new Bank(0, '#out1');
myBank1.addResource("fast", 1, 1000);

const myBank2 = new Bank(0, '#out2');
myBank2.addResource("slow", 1, 10000);

const myBank3 = new Bank(0, '#out3');
myBank3.addResource("mine1", 1, 10);
myBank3.addResource("worker1", -1.50, 1000);
myBank3.addResource("worker2", -2.50, 1000);
myBank3.addResource("manager", -5.50, 1000);
<div id="out1"></div>
<div id="out2"></div>
<div id="out3"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Guide on executing a PHP file from HTML or Javascript

I've been searching for a while now without any luck in finding an answer to this seemingly simple question. I don't mind if the page reloads or shows results instantly; all I want is to have a button on my website trigger the execution of a PHP ...

Verify whether the value is considered false, true, or null

When dealing with variables in JavaScript, I often need to determine if a variable is false, true, or null. If the variable is null or undefined, I want to assign an array to it by default. While this syntax works well in other languages, in JS assigning a ...

Using AngularJS to retrieve JSON data with the HTTP module

I am a beginner in the world of angularjs and I need some guidance. Below is the code I have written: <!DOCTYPE HTML> <html ng-app="myapp"> <head> <meta charset="utf-8"> <title>Angularjs project</title> <script type= ...

Issue with interactions between datepicker and jQuery dialog box

I am working with a datatable that displays user data, and each row has an edit button to open an edit form. I am opening this form using a jQuery dialog box and submitting it using ajax submit. However, when loading the form, the datepickers are not worki ...

Apologies, but it seems that the function Buffer.write(string, encoding, offset[, length]) is no longer supported

Currently, I am diving into the world of ExpressJS and Node.js. However, I have hit a roadblock when a user enters data in a form and clicks submit, resulting in an error. Despite my extensive research on this issue, I have been unable to find a satisfacto ...

Dynamic Translation Service for Ionic 2 and Angular 2 with ngx-translate

Currently working on an Ionic 2 app and looking to implement object translation directly from the server response instead of using a JSON file. Utilizing ngx-translate Here's an example of the object received: { "name": ["Hello", "Hola", "Bon Jour" ...

Removing duplicate elements from an array using lodash

What is the best way to remove duplicate values from an array? var numbers = [1, 1, 5, 5, 4, 9]; I want my result to be: var numbers = [4, 9]; Is there a method in lodash that can help me achieve this? ...

The strategy of magnifying and shrinking graphs on Google Finance for better analysis and

I am seeking to understand the logic behind a zoom-able graph similar to the one on Google Finance. I am aware that there are pre-made components available, but I am interested in a simple example that breaks down the underlying logic. ...

Locate the parent and child elements within an array

Within the array provided below, there are parent items as well as children. I am currently able to identify parents (depth 0) and their immediate children (depth 1), however I am unsure how to handle deeper nested levels. For reference, you can view the ...

Display a loading bar or prevent any user interface actions until the server has finished generating the file

I am currently developing a force directed layout using d3.js on data retrieved from an MS SQL server database. I'm creating the json file with a python script and running it on a local server using python -m SimpleHTTPServer. My goal is to establish ...

Angular application across multiple subdomains

Currently creating an angular application that allows users to generate and share digital books with a subdomain link like mycoolbook.theappsite.com. However, I've set up the routes so that editing books are at the URL "mycoolbook.theappsite.com/sett ...

JSFiddle Functioning Properly, But Documents Are Not Loading

My JSFiddle is functioning properly, but the files on my computer aren't. It seems like there might be an issue with how they are linking up or something that I may have overlooked. I've checked the console for errors, but nothing is popping up. ...

Tips for locating the index of the previously selected active class

I am currently working on a slider and have made progress up to this point. However, I am facing an issue where I cannot proceed because I need to identify the index of the item from which I removed the last active class before the click event occurs. My ...

Successfully Passing GET Parameters to ngResource in AngularJS

I'm struggling to figure out how to pass a simple id parameter to my created resource. The service in question is: angular. module('shared.testUser'). factory('TestUser', ['$resource', function($resource) { ...

Can the vacant space on an HTML page be determined through calculation?

Could someone please help me figure out the amount of empty space on a html page? https://i.sstatic.net/VXzbz.png Is it possible to determine the area highlighted in red using javascript? Apologies if this question seems trivial. ...

I recently discovered how to modify the video source (src) within an html5 source tag, but I am encountering varied outcomes when using it within a function or with inline javascript

When attempting to change the src of a video tag using Javascript, I encountered an issue. The code works fine when placed inline, but as soon as I try to include it within a function or in the "onclick" event of a button tag, nothing happens. There are no ...

Determining when all $http requests have completed in AngularJS

After running multiple $http calls, I need to trigger an event only when all of them have been processed. Additionally, I must be informed if any call has failed along the way. Despite attempting solutions found on stackoverflow, such as using an intercept ...

When transitioning to a different page, Angular is creating a duplicate of $scope

My webpage features a section where I showcase all the individuals stored in the database, referred to as the All Persons Page. Within this page, there are two primary options: Add New Person Delete Person (Any selected individual) An issue arises when ...

What is the proper method for implementing a scrollable effect to the <v-bottom-sheet> element within the Vuetify framework?

Within my Vue.js project, I am utilizing the v-bottom-sheet component from Vuetify framework (version 2.1.12). Upon reviewing my code, you can observe that the v-card is enclosed within the v-bottom-sheet. The issue arises with the scrollable parameter of ...

Trie-based autocomplete functionality

I am currently creating an auto-completion script and I'm considering utilizing a trie data structure. My main concern is that I want all possible matches to be returned. For instance, when I type in the letter r, I expect to see all entries beginning ...