【问题标题】:Setting target field values设置目标字段值
【发布时间】:2022-08-20 04:03:36
【问题描述】:

我正在制作一款游戏,其中有多个我想为其设置目标值的字段。例如,我的 Camera 类有:

public double zoomLevel

目前,如果zoomLevel 是(比如说)1.0 并且我想逐渐将它增加到(比如说)2.0,我有以下其他字段来支持这个:

private double targetZoomLevel
private double zoomIncrement

然后我有一个Camera.SetZoom(double target, double increment) 方法来设置所需的furure 缩放级别,然后是Camera.Update() 方法,它使用增量将当前缩放级别移向目标级别。

这一切都很好,但我真的很想为其他领域(例如相机世界位置,玩家大小,玩家位置等)实现相同的行为。使用我当前的方法,我需要为每个字段添加 2 个额外的“支持”字段。

我很确定我当前的解决方案是次优的,但不确定如何改进它。我正在考虑实现一个 Property<T> 类,该类将这种行为封装为一个值,但不确定如何概括 Update() 方法以将当前值移向其目标。

谢谢!

  • 这是团结吗?然后请将其添加为标签。也许 Unity 已经对此提供了支持
  • 嗨@HansKesting 不,不是 Unity。这是我在 MonoGame 之上编写的自定义引擎。我添加了一个 MonoGame 标签! :)
  • 如果您在 Camera 类中有多个属性,则方法 Update() 必须更新所有更改的属性?
  • @Timothyyes 没错。我只是想知道是否有比我目前的方法更具可扩展性的方法。

标签: c# monogame


【解决方案1】:

你所描述的听起来很像动画。动画是几个框架中的正式概念(想到CSSWPF)。

动画的目标是将某物从一个值过渡到下一个值。

有多种方法可以实现这种转变。有时您希望 2D 点跟随 Bézier curve,因为曲线的 t 变量会在一段时间内从 0 线性变为 1。其他时候,您希望通过在color wheel 周围走很长一段路,使颜色从红色平滑过渡到蓝色。

这是一个可以抽象该概念的接口:

public interface IAnimation<T>
{
  T Current { get; }

  void Update(double progress); // progress is a number between 0 and 1
}

例如,如果您希望 2D 点在 800 单位宽和 2 单位高的正弦波上进行动画处理:

public sealed class Point2DAnimation : IAnimation<Point2D>
{
  public Point2D Current { get; private set; }

  public void Update(double progress)
  {
    Current = new Point2D(progress * 800, Math.Sin(progress * Math.PI * 2));
  }
}

还有多种方法可以推动过渡。驱动它的一种常见方式是说“我希望这个动画在 X 秒内尽可能顺利地发生”。但有时您可能希望动画从头开始重复几次。或者,也许您希望动画先向前然后向后再向前,以此类推。

我们可以定义一个接口来抽象出IAnimation&lt;T&gt; 可以被驱动的不同方式。一种实现可能在内部有一个计时器,它的滴答频率足以产生平滑的错觉,因为progress 从 0 变为 1 与每个滴答时刻经过的时间量成正比。

但我想在这里暂停片刻,问一个问题。

抽象的消费者需要什么?

如果您的所有代码都需要对T 和一个名为Update() 的方法的读取访问权限,那么Property&lt;T&gt; 听起来就像您的票:

public sealed class Property<T>
{
  readonly Func<T, T> _update;

  public Property(T initial, Func<T, T> update)
  {
    Value = initial;
    _update = update;
  }

  public T Value { get; private set; }

  public void Update()
  {
    Value = _update(Value);
  }
}

该类封装了一个可读值,并为您提供了一个Update() 方法来调用。不需要IAnimation&lt;T&gt; 接口或任何其他花哨的装饰。当然,您必须使用 update 委托来实例化 Property&lt;T&gt; 的实例,该委托可以满足您的需求。但是您可以在某处添加一些静态方法,以便以最常用的方式进行设置。例如:

public static class PropertyHelpers
{
  public Property<double> CreateZoomLevelProperty(double initial, double increment, double target)
  {
    return new Property<double>(initial, old =>
    {
      var @new = old + increment;
      if (increment > 0 && @new > target || increment < 0 && @new < target)
        return target;
      else
        return @new;
    });
  }
}

另一方面,如果您想将值视为由代码中其他内容控制的值流,并且您只需要在它们到达时获得对值的读取访问权限,那么可能是IObservable&lt;T&gt;(和Reactive NuGet 包)是您所追求的。例如:

public class Camera
{
  double _zoomLevel;

  public IDisposable UpdateZoomLevel(IObservable<double> zoomLevels)
  {
    return zoomLevels.Subscribe(zoomLevel => _zoomLevel = zoomLevel);
  }
}

当然,其他代码有责任在某处提出IObservable&lt;double&gt;,以您想要的方式发布缩放级别。

所以我想我的答案可以总结为:

这取决于。

免责声明:也许上面的代码都不会编译

【讨论】:

    猜你喜欢
    • 2017-05-02
    • 1970-01-01
    • 2023-01-26
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 2022-11-01
    • 2012-04-20
    相关资源
    最近更新 更多