Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I want to make objects in a side project interatible with each other. For this, I want to be able to create rules so "Collisions" (Interactions) can be recognised by testing the rules. The rules should be able to reference what object does the player is trying use, in what state the object is, with which object does he try to interact with (could be nothing) and the state of this object. Maybe the rules will need to consider the state of the game as well.

With this in mind, here is my question. How could I create a rule-based system that I could use ? What design patterns could I use to create something scalable and that will allow me to add, remove and modify rules without having to regenerate the project ?

share|improve this question
    
can you give a couple of examples of what rules your ideal system would allow? –  lathomas64 Oct 26 '10 at 18:58
    
Absolutly ! For exemple, the player could take an object an make it interact with another one (ex: dropping it on a surface of combining objects). He could also "Activate" an object by clicking on it. –  Frédérick Imbeault Oct 26 '10 at 19:02
add comment

3 Answers 3

up vote 2 down vote accepted

There are Objects and Actions (Interactions). Callback for interacting can be selected via multiple dispatch (multimethods).

For example, Action: "Look at A" is selected by Action "Look at" and Object A (double dispatch).

Action: "Mix A and B" is selected by Action "Look at" and Object A and Object B (3 arguments).

Read Item 31 of Scott Meyers's book "More Effective C++" and "Modern C++ design" by Andrei Alexandrescu, Chapter 11, for different double dispatcher implementations in C++. Another article.

Example of usage:

Dispatcher2 action_mix;
action_mix.registerInteraction<ObjectType1, ObjectType2>(callback_1_2);
action_mix.registerDefaultInteraction(callback_default); // for unregistered pairs
//...
action_mix.call(object1, object2);

Another example:

Dispatcher2<BaseActionType, BaseObjectType> any_action_to_any_obj;
any_action_to_any_obj.registerInteraction<ActionType1, ObjectType1>(callback_a1_t1);
//...
any_action_to_any_obj.call(action, object);
share|improve this answer
    
I like your method, I love books and got plenty for a student but unfortunately I don't have "Effective C++" serie yet (It's in my "To Buy" list). I don't know about "Modern C++ design" but I will take a look a it, thank you for the suggestion. Concerning your exemples, I think that a mix of the two could maybe suit my needs just fine. I will try to take the second exemple, but send it another object that could be NULL so it can be generic enouph for every possible actions in the game. –  Frédérick Imbeault Oct 27 '10 at 13:50
add comment

It depends on the requirements of your design. For example, do you want one unique/global "Activate" action, or do you want discrete activation actions for different types of objects? Is the "Activate" action for a light switch the same as the "Activate" action for a coffee maker, but with different behavior? Or are they completely different actions with identical names?

If you want to be able to bind actions to input gestures (e.g. keystrokes), then you'll probably want to go with the first option. In that case, you probably don't want the action to know every object on which it can be performed. You might not want a rigid set of actions supported by each object, either. Instead, your design could enable you to bind (or attach) action handlers to potential targets, delegating the behavior out to a callback. Action bindings could be bound to object types (metadata) or to actual object instances (or both).

Let's say I have a light switch, and I want it to support the Activate action. My code might look something like this:

lightSwitch.ActionBindings.Add(
    new ActionBinding(
        StandardActions.Activate,
        delegate (object target)
        {
            var lightSwitch = (LightSwitch)target;
            if (lightSwitch.IsDestroyed)
                return false;
            lightSwitch.IsActivated = !lightSwitch.IsActivated;
            return true;
        }));

This is simplified a bit, but the idea is that the behavior is injected via a callback, and the callback could return a value indicating success. In practice, you would probably want a second "can execute" callback in addition to the default "execute" callback.

Now, when the player hits the key bound to the "Activate" action, the target object should be queried to see if it has an ActionBinding for the "Activate" action. If so, the "can execute" callback should be checked (if one exists), and on successful evaluation, the "execute" callback should be invoked. If the action succeeds, stop. Otherwise, walk up the object tree until you find an object which will accept the action. That last step is only necessary if your objects are grouped in a hierarchy, i.e. the object is part of a larger composite object which might be the "true" action target.

Your callbacks could easily query the target object or game state as necessary. You might consider exposing services or state using a Service Locator pattern.

share|improve this answer
    
Your answer is well explained, I like the callback method of doing, but I would like if you can answer this; The activations are made by cliking an object, as well as interactions. The kind of action depends on the "PlayerHand" object's current state. Would this cause a problem ? –  Frédérick Imbeault Oct 26 '10 at 23:13
    
That should not be a problem. It sounds like the PlayerHand state would be a condition of the input binding rather than the action binding (where the bound input gesture triggers the bound action). –  Mike Strobel Oct 28 '10 at 16:07
add comment

You can use Prolog (as a logic, declarative scripting language) as it provides model of rule-based deduction process. One my friend has used his own Prolog-like language in games with success.

The program logic is expressed in terms of relations, represented as facts and rules. A computation is initiated by running a query over these relations.

If your host program is in C++, here some links to bind C++ and Prolog:

http://www.amzi.com/articles/prolog_cpp.htm
http://www.google.lv/search?q=Prolog+C%2B%2B

share|improve this answer
    
I Can't use prolog, the project already is under a specific engine so I'm limited –  Frédérick Imbeault Oct 26 '10 at 23:14
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.