2

I am trying to create a system that can easily modify objects on the fly. For example, lets say I have an Entity2D that inherits from Entity. Entity2D has a Position property. I also have a class called ModifyPosition that inherits from Modifier.

Here is the code:

public class Entity
{
    /// <summary>Applies the modifier to this entity.</summary>
    /// <param name="modifier">The modifier to apply.</param>
    public void ApplyModifier(Modifier modifier)
    {
        modifier.Apply(this);
    }
}

/// <summary>Modifies an entities position</summary>
public class ModifyPosition : Modifier
{
    /// <summary>/// Initializes a new instance of the <see cref="ChangePosition"/> 
    class./// </summary>
    /// <param name="position">The position.</param>
    public ChangePosition(Vector2 position)
    {
        this.Position = position;
        this.IsModifyingChildren = false;
    }

    /// <summary>Gets the position.</summary>
    /// <value>The position.</value>
    public Vector2 Position { get; private set; }

    /// <summary>Applies this change to the specified entity.</summary>
    /// <param name="entity">The entity.</param>
    internal override void Apply(Entity entity)
    {
        ((Entity2D)entity).X += this.Position.X;
        ((Entity2D)entity).Y += this.Position.Y;
    }
}

If you are calling this multiple times per second, I would think that the casting would slow it down. Is there another way to go about this, without having to cast?

Gnemlock
  • 5,263
  • 5
  • 28
  • 58
Chris Watts
  • 123
  • 1
  • 6
  • This "architecture" is pretty horrible, just by the way. The only place I could see this making sense is an editor where you undo and redo commands (modifiers). Which you won't be doing "on the fly" or "multiple times per second". Is there another way to do this? Yes - just modify the values directly! In game development you should never get more fancy than you need to. – Andrew Russell Jul 23 '10 at 06:59
  • I have seen multiple particle engines use this type of method for modifying particles at runtime. How would you suggest I change values directly, lets say if there was an event that happened, like a ball hit the wall, and i wanted to change the color of that ball. How would I gain access to that property dynamicly lets say i want to set that up in the editor. – Chris Watts Jul 23 '10 at 17:03
  • @Andrew: In principle, I can think of a few cases where this sort of architecture would be useful. Think about strategy games: some object/entity properties might be modified by external factors (i.e. structures providing bonuses to various units). In such cases, you may not want to modify the effective value directly: instead, you would want want to recompute the effective value any time the base value changes or a modifier is added, removed, or invalidated. However, you would want the modifier to accept the base value and current effective value, and return the modified effective value. – Mike Strobel Aug 09 '10 at 22:38
  • @Mike: In that case you'd still be better off making each bonus-giving object hold a delegate that defines the bonus behaviour. Rather than having an "additive bonus class" and a "multiplicative bonus class" and an "exponential bonus class" and so on. – Andrew Russell Aug 10 '10 at 01:45
  • @Andrew: Well, one may want some additional metadata exposed, as well as an 'Invalidated' event to signal that a modifier's effect has changed and the affected property should be reevaluated. Therefore, it could be appropriate to have a Modifier class. However, instead of writing several different implementation classes, the Modifier class could simply accept a value modifier delegate (like what you described) and invoke as required. That's essentially what I have done in the past. – Mike Strobel Aug 10 '10 at 04:57
  • @Mike so lets say you do that. What would the delegate take in as a parameter. just a system.object? and then cast it? or should it be a generic delegate or what? This sounds like it could be going somewhere good thanks! – Chris Watts Aug 10 '10 at 18:23
  • @Chris: The most straightforward delegate would be generic on the modified property type and would have parameters for the base value and current effective value. It would return the modified effective value. public delegate T ValueModifierCallback(T baseValue, T currentValue) – Mike Strobel Aug 10 '10 at 21:12
  • @Chris: However, if you're building a particle system, this sort of OO abstraction is probably overkill. You'd probably be better off going with something more straightforward like what Andrew described below. Working with events and delegates should consume a smaller memory footprint and be more performant. – Mike Strobel Aug 10 '10 at 21:25
  • @Mike this confuses me a little more, couldnt you just have it be like.
    public delegate void ValueModifierCallBack(ref T value)
    
    – Chris Watts Aug 10 '10 at 21:30
  • I agree with the particle system idea for modifying particles, you are right this is overkill for that. I am going to be using this for bigger functions in the game. – Chris Watts Aug 10 '10 at 21:36
  • @Chris: Depends on what your needs are. My design was based on the requirement that objects had properties that could be modified by various external entities, and modifiers might be added/removed in response to various triggers. Some of those modifiers might be compounded, but others might not. For example, some effects might be additive (i.e. +25 to some property) while others might be multiplicative (i.e. +25% to some property). The former would simply return currentValue + 25 while the latter might return currentValue + (0.25 * baseValue). – Mike Strobel Aug 10 '10 at 21:37
  • Your requirements sound exactly the same as mine.

    That makes a lot more sense thank you very much.

    Oh another requirement was that I wanted to be able to create custom ones in an editor and be able to serialize them. Were you able to do that with yours?

    – Chris Watts Aug 11 '10 at 00:56
  • @Chris: Yes. Effects are persisted in XML format. The effect values are written in a custom expression language based on C# (i.e. $currentValue + (0.25 * $baseValue)). The language is basically just the subset of all C# expressions that are representable as LINQ expression trees, but with a few differences (system parameters prefixed with '$', user parameters prefixed with '#', expression-scoped namespace imports, etc.). The expressions are parsed at runtime and rewritten to be observable/reactive when possible (runtime value changes are automatically propagated). – Mike Strobel Aug 11 '10 at 03:01
  • Are you talking about serializing it with the default .net serializer or just with the XmlWriter class? and just calling WriteObject(object)? – Chris Watts Aug 11 '10 at 03:23
  • Actually, I don't remember if I've started writing the effect serialization code just yet. If I have, I probably just used XLinq as a stand-in. I still have some work to do in the expression parser/compiler before I can really put my effect system to good use (lambda support isn't finished yet).

    Lately, I've been using Xaml as a persistence format whenever possible. They've really improved the Xaml API in .NET 4, and it generally beats the hell out of the standard XML serialization mechanisms in .NET.

    – Mike Strobel Aug 11 '10 at 04:51
  • Alright I think you have lost me. What do you mean by your effect system and how does XLinq tie into that. Also what are you talking about your expression parser/compiler, is that for parsing xml? – Chris Watts Aug 11 '10 at 06:37
  • @Chris: Sorry, I started to stray a bit too far into my own project's details. I'd be glad to discuss this further, but the comments section here isn't exactly conducive to such a lengthy discussion. Feel free to drop me an e-mail if you like; first name dot last name at gmail. – Mike Strobel Aug 11 '10 at 14:24

4 Answers4

8

You really shouldn't need to worry about this unless you are doing this millions and millions of times per frame. This graph shows some empirical data about the speed of casting:

Casting Speeds
Source: "Type casting impact over execution performance in C#" by Emilio Guijarro, 2004

Keep in mind that there are about 17 milliseconds per frame at 60 frames per second, and about 33 milliseconds at 30 frames per second.

Ilmari Karonen
  • 8,347
  • 1
  • 29
  • 39
Sean James
  • 3,478
  • 2
  • 29
  • 24
6

To expand on my comment from earlier and give you a full answer:

A good way to modify values on the fly is to modify them directly using C#

It sounds like what you're trying to implement is something like Klik & Play or Unreal Kismet (and to a lesser extent the level-entity-triggering system from Unreal and some other engines).

What you need to keep in mind is that these systems make sense for companies like Epic because they (and their licensees) are creating enormous volumes of content - and it's cheaper to enable a non-programmer to make it dynamic, than it is to require a programmer to do it. (With volumes like Epic's - even a entry-level programmer working in a managed language like C# or UnrealScript is expensive enough to justify such a system.)

For the rest of us who are not making AAA middleware - making a system like that is just cargo-cult engineering. (And if you are making that kind of middleware, this is not how I'd approach it anyway.)

Basically you're adding a layer of indirection to your programming. Every time you want some new functionality you're going to have to program it anyway. Except you'll also have to wrap it in a new Modifier class (including, presumably: serialization, editable properties, considerations of re-usability, etc), and then add it to your level and link it up. And you lose the magic of version control, refactoring support, debugging, etc. And heaven help you if you want to modify a Modifier - have fun checking all your levels still work!

(By the way: I learned all this the hard way - which is why I'm being so opinionated about it.)

So that's my little rant about your architecture out of the way. What should you do?

Well you already have a fantastic editor: it's called Visual C#. In a very basic sense you should just give each level in your game a class/.cs file - and dynamically instance that class when the level starts (or whatever makes sense for your game).

And all the time you save not making your Modifer system, you could spend adding nice features like on-the-fly recompilation. (By the way: if you're concerned about end-user modding - remember that Visual C# Express is free, and the C# compiler comes with the .NET runtime.)

As to particle systems, which you mentioned in your comment: Are you nuts? Where did you see that? Particle systems need fast throughput - and I don't see that happening with a virtual function call and a cast, and using reference types for each particle!

In the ideal CPU performance case, a particle system should have a tight loop that moves through an array of structs containing a position and a velocity vector. Actually - better yet: a single draw call that gets the GPU to handle it all in a vertex shader.


Just to give you an idea of what I'm talking about, here's what your ball example might look like:

public void LevelStart()
{
    this.Walls["magicWall"].OnTouch += (touchedBy) =>
    {
        Ball ball = touchedBy as Ball;
        if(ball != null)
            ball.Color = Color.Blue;
    };
}
Andrew Russell
  • 21,268
  • 7
  • 56
  • 103
  • About the particle system, I didnt mean to say it was just like I had explained before. It used structs in a pretty tight loop but in that it would also pass those structs through a modifier class which would make the particles do things like change color or position or w/e

    Other than that I do think you have a good point about the AAA game titles, im still not sure if thats the way i want to go about it though

    – Chris Watts Jul 24 '10 at 17:36
  • @Chris: Rather than making a messy system with "modifiers", just code each "modifier" directly as a C# function and pass them to your particle system as delegates. This gives you a nice lightweight architecture and surprisingly good performance. (Of course you should do the benchmark if it matters, like this gent has done: http://www.gamedev.net/community/forums/topic.asp?topic_id=494239) – Andrew Russell Jul 25 '10 at 00:36
  • thanks for the example, Im not that familiar with lambda expressions in C#. Could you explain a little more? – Chris Watts Aug 10 '10 at 18:24
  • @Chris: Do you understand delegates? It's just a shorter syntax (when used in this way). (touchedBy) => is the same as delegate(GameObject touchedBy). For the lambda the compiler will infer the type GameObject (or whatever) from what OnTouch is expecting. – Andrew Russell Aug 11 '10 at 02:08
1

While I agree with the above that I wouldn't do it this way, and that you should measure actual performance hit, it's beside the discussion.

To me the cast kinda tells me your setup is wrong. For one, what if the cast fails?

I'd definitely advise you to look into interfaces and/or 'observer/visitor pattern'.

Alternatively wonder about a class Modifiable, where then that vector2 can derive from (instead of entity), so you know you're type-safe. Have your entity have a ModifiableVector2 as a member and work on that?

Kaj
  • 3,246
  • 21
  • 18
0

Speed it up:

internal override void Apply(Entity entity)
{
    Entity2D entity2d = entity as Entity2D;
    if (entity2d)
    {
        entity2d.X += this.Position.X;
        entity2d.Y += this.Position.Y;
    }
}

The "as" cast is faster and returns null if the object can't be casted. You also avoid the second cast but don't ask me if the if statement is faster, slower or equal than the cast. Honestly, I couldn't care less unless you know this to be an issue. Which it won't. That I'm 99.9% sure of.

Anyway, the "as" cast with null-pointer check is a C# best practice. It won't crash if at one time the entity really is just an Entity, not a Entity2D.

You could also write a Modifier class which runs Apply only on Entity2D objects if you're that worried about performance. Or change all your game entities to be of Entity2D and also change the Modifier class. It's a matter of trading off memory vs. speed.

CodeSmile
  • 1,734
  • 2
  • 15
  • 16