0

I am developing a program where I have many classes that need to store both the current value, and the previous value. For example, a Player class that would need to store its current x (and previous x), its current y (and prev y), etc. In addition, I also want to be able to get the interpolated versions of these values. So for example I could get the halfway point between the previous x and the current x.

At the moment I am doing it like this:

class Player {
  private _x: number;
  private _prevX: number;

  constructor() {
    this._x = 0;
    this._prevX = 0;
  }

  get x() {
    return this._x;
  }

  set x(val: number) {
    this._prevX = this._x;

    this._x = val;
  }

  interX(t: number) {
    return (1 - t) * this._prevX + this._x * t;
  }
}

And this is nice because I can just do player.x to get the current x value, and player.x = 5 to set the current x value (and also automatically set the previous value). Getting the current value is an extremely common operation.

However, it's not so nice because of all the boilerplate. When I have 5 different values I want to store both the current and previous values of, it gets very verbose. So, I thought to try this instead:

class Interpolate {
  prev: number;
  curr: number;

  constructor(prev: number, curr: number) {
    this.prev = prev;
    this.curr = curr;
  }

  set(val: number) {
    this.prev = this.curr;

    this.curr = val;
  }

  interpolate(t: number) {
    return (1 - t) * this.prev + this.curr * t;
  }
}

class Player {
  x: Interpolate;
  y: Interpolate;

  constructor() {
    this.x = new Interpolate(0, 0);
    this.y = new Interpolate(0, 0);
  }
}

And while this is nice, now it has the problem that to get the current value, I have to do player.x.curr and player.y.curr. Not so nice!

Is there any way to have my cake and eat it too? Ideally, I would like to be able to do something like this:

let foo = player.x; // gets currentX value
player.x = 55; // also sets prevX
player.x.inter(0.5); // gets halfway point between previous and current

But the only way I know how to do this is with the first solution, which fills all of my classes with a ton of boilerplate and repetitious error-prone logic.

Also, if possible, the solution needs to be pretty performant (this is for a game, so I doubt I can use something like a complex proxy for a property like the player's position)

Ryan Peschel
  • 11,087
  • 19
  • 74
  • 136
  • Would you be comfortable doing this with [defineProperties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) as in [this SO answer](https://stackoverflow.com/q/12696250/1426891)? To appease TypeScript, it might require explicitly declaring the property on the interface Player that the class Player would otherwise implicitly define, but you'd be able to reduce boilerplate as your Interpolate class does right now. – Jeff Bowman Apr 28 '22 at 22:46
  • Hmm, I don't know.. seems kind of error-prone.. It's not on just the `Player` class, so I would basically need to remember to call `defineProperties` on every type of object I create in the game, no? Hmm, it's an idea.. but not sure how comfortable I am with it, if I'm understanding it properly. Though, I'm not entirely sure I understand the implications of what you're suggesting entirely. – Ryan Peschel Apr 28 '22 at 22:52
  • Unless.. can I call `defineProperties` in the constructor on the object on itself? Maybe that could work.. Ahh, I'm just not sure.. Because then as you said I would need to create an interface for every object type which would be cumbersome as well.. Just swapping one verbosity with another – Ryan Peschel Apr 28 '22 at 22:57
  • 1
    Well, you could make a class factory function to take care of the boilerplate for you, possibly like [this](https://tsplay.dev/WzOVew). If that meets your needs I could write up an answer. If not, what am I missing? – jcalz Apr 29 '22 at 01:50

0 Answers0