/// <summary> /// Return the next value of the effector. /// </summary> /// <param name="obj"></param> /// <returns></returns> protected virtual T2 GetNext(EffectableObject obj) { var next = Func(ElapsedTime, ElapsedFrames); if (Prev != null) { // Combined is just the value after performing the operation O(x, y) // where x is the previous value and y is the next value. var combined = Op.Operate(Prev, next); if (ValueType == EffectorValueType.RelativeToStart) { var init = InitialValues[obj]; #region Explanation of why we must check if the effector is InPlace and RelativeToStart. // Alright here's the breakdown. The following table outlines the // general term for a given ValueType and OperatorType, where x0 is // the initial value, F is Func, and tn and fn are the time and frame // counts at the nth call to UpdateEffector respectively: // // ______________________________________________________________ // | | RelativeToStart | Absolute | // |------------------------------------------------------------| // | InPlace: | xn = F(tn, fn) | = F(tn, fn) | // |----------------|------------------------|------------------| // | Additive: | xn = x0 + Σ F(tn, fn) | = Σ F(tn, fn) | // |----------------|------------------------|------------------| // | Multiplicative | xn = x0 * Π F(tn, fn) | = Π F(tn, fn) | // -------------------------------------------------------------- // // Notice how for Inplace and RelativeToStart, the general term is // F(tn, fn). Although this is technically correct and makes sense with // respect to the rest of the values (xn = O(x(n - 1), F(tn, fn)) in general, // where O is the operation, so for InPlace O(x, y) = y means xn = F(tn, fn)), // it feels unnatural or unintuitive. // // Intuitively, it makes more sense that InPlace and RelativeToStart would mean // the general term would be x0 + F(tn, fn), since that is really what "Relative // To Start" means. // // Mechanically, it also makes sense that you would want InPlace and RelativeToStart // to do something different from InPlace and Absolute. For example, if F(tn, fn) = tn, // and x0 = 3, then you would expect the general term to be 3 + tn, not just tn. // If InPlace and RelativeToStart didn't do that, then there would be no way of // achieving the desired outcome (Additive and RelativeToStart would give 3 + Σ tn). // // Thus, if the effector is both InPlace and RelativeToStart, then the general // term is actually xn = x0 + F(tn, fn), instead of the term predicted by the pattern // xn = O(x(n - 1), F(tn, fn)). #endregion return(inPlace_and_relativeToStart ? Combine_InPlaceAndRelativeToStart(init, combined) : Op.Operate(init, combined)); } return(combined); } return(next); }
protected override void AssignNextValue(EffectableObject obj, float nextVal) { var prevVec = obj.Get <Vector2>(EffectedPropertyName); prevVec.X = nextVal; obj.Set(EffectedPropertyName, prevVec); }
public void UpdateEffector(EffectableObject obj) { if (!Reusable) { obj = Object; } var next = GetNext(obj); Prev = next; AssignNextValue(obj, next); }
public virtual void InternalInitialize(EffectableObject obj) { if (Initialized) { string exceptionMessage = "The most likely cause for this is the same effector " + "was added to multiple objects. Check to ensure you aren't reusing the same " + "effector instance."; if (Anonymous) { exceptionMessage = String.Format( "Anonymous effector of type '{0}' was initialized twice.", this.GetType()) + exceptionMessage; } else { exceptionMessage = String.Format( "Effector with name '{0}' and type '{1}' was initialized twice.", EffectorName, this.GetType()) + exceptionMessage; } throw new EffectorException(exceptionMessage); } if (ValueType == EffectorValueType.RelativeToStart) { InitialValues.Add(obj, CoerceTo((T1)obj.Get(EffectedPropertyName))); } // If this effector is reusable, then it may be used for multiple // ArtemisObject instances, meaning we can't store an internal reference // to any single one. // // The advantage of using a non-reusable effector over a reusable one is // that the effector can initialize it's own properties based on it's // assigned instance. if (!Reusable) { Object = obj; } Initialize(); Initialized = true; }
protected abstract void AssignNextValue(EffectableObject obj, T2 t2);
protected override void AssignNextValue(EffectableObject obj, T val) { obj.Set(EffectedPropertyName, val); }