The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,639,015
since: 19 Jan 2005

Ulysses Agenda - Using Workflow to Script Enemy Behavior

posted Thu 02 Nov 06

If you have read enough of my blog entries, you know that when I encounter a new technology, the first thing I ask is, "That's great, but can I game with it?". The answer to WF is of course, yes

The basic idea is that NPCs will be instances of workflows managed by a central workflow runtime within the Universe Server. The Universe Server is the server-side portion of UA that drives all of the central state management as well as manages all NPCs and player-persisted data. Each of these NPC workflows will be a state machine. I would have had figures, but my blog software made the "upload" button dissappear on my file space. So, if I had a picture, imagine that in this picture, you saw an initial state. From this initial state, the NPC immediately drops into a state called Patrolling. There's also an end state and possibly a couple other states like Combat and Corpse. The Corpse state is awesome because what I can do is have the hull wreckage still visible (still broadcasting radar pings), but then after a set delay like 30 seconds, I can go to the workflow termination state - at which point the instance dissappears, the memory goes away, and the NPC stops broadcasting radar pings because the workflow isn't running. All of this without having to clog up the main server itself.

I have a state initialization activity on the Patrolling state. As soon as the NPC enters this state, I have an activity that I coded called LaunchWFActivity (should probably call it LaunchPatrolWorkflowActivity now that I think about it... that's what I get for starting with an unrelated proof of concept) that is invoked. This activity launches a sequential workflow called Patrol (stored in Patrol.xoml). Here's the code for the launching activity (again, the naming is poor):

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace NPCWorkflowTest
{
public partial class LaunchWFActivity: SequenceActivity
{
public LaunchWFActivity()
{
    InitializeComponent();
}

public static DependencyProperty LaunchedWFGuidProperty =
    System.Workflow.ComponentModel.DependencyProperty.Register(
    "LaunchedWFGuid", typeof(Guid), typeof(LaunchWFActivity));

[Description("")]
[Category("")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Guid LaunchedWFGuid
{
    get
    {
        return ((Guid)(base.GetValue(LaunchWFActivity.LaunchedWFGuidProperty)));
    }
    set
    {
        base.SetValue(LaunchWFActivity.LaunchedWFGuidProperty, value);
    }
}

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
    IStartWorkflow startWorkflow =
executionContext.GetService(typeof(IStartWorkflow)) as IStartWorkflow;
    LaunchedWFGuid = startWorkflow.StartWorkflow(typeof(Patrol), null);
    Console.WriteLine("Launched patrol WF with Guid {0}", LaunchedWFGuid.ToString());
    return ActivityExecutionStatus.Closed;
}
}
}

Something that will prove to be crucial is that I hit the "Promote Bindable Properties" hotlink in the quick tasks panel that shows up below the activity properties when looking at the LaunchWFActivity item in the designer. This makes it so that I can bind the accompanying ShutDownWFActivity (which, of course, should also be renamed to ShutdownPatrolWorkflowActivity) so that it knows what GUID to shut down.

I also need a service that performs the actual shutdown. The model here is that the shutdown activity will grab the (not a) instance of the shutdown service from the execution context and then use the shutdown service to kill the (data-bound) appropriate GUID. Here's the code for the shutdown service (I cut out the using directives, they're the same as the previous code sample):

namespace NPCWorkflowTest
{
public class WFShutDownService : WorkflowRuntimeService
{
public void ShutDownWorkflow(Guid workflowGuidtoShutdown)
{
    WorkflowInstance wi = this.Runtime.GetWorkflow(workflowGuidtoShutdown);
    wi.Terminate("normal shutdown");
}
}
}

And finally, here's the code from the shutdown activity (which should be called ShutdownPatrolWorkflowActivity):

namespace NPCWorkflowTest
{
public partial class ShutdownWFActivity : Activity
{
public static DependencyProperty WFToShutDownGuidProperty =
    System.Workflow.ComponentModel.DependencyProperty.Register("WFToShutDownGuid", typeof(Guid),
    typeof(ShutdownWFActivity));

[Description("")]
[Category("")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Guid WFToShutDownGuid
{
    get
    {
        return ((Guid)base.GetValue(ShutdownWFActivity.WFToShutDownGuidProperty));
    }
    set
    {
        base.SetValue(ShutdownWFActivity.WFToShutDownGuidProperty, value);
    }
}

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
    WFShutDownService shutdownService = executionContext.GetService<WFShutDownService>();
    shutdownService.ShutDownWorkflow(this.WFToShutDownGuid);
    return ActivityExecutionStatus.Closed;
}

}
}

In order for this to work, you have to make absolutely sure that you bind the WFToShutDownGuid property to the promoted property fed by the LaunchedWFGuid property from the launch activity. You can have as many child workflows running as you want, that can be interrupted by external events. For example, my implementation uses the ShutdownWFActivity activity to shut down the patrol activity when the state machine workflow as a whole (the NPC) is attacked.

So let's review: I've illustrated by example of NPC behavorial simulation that you can create state machine workflows that spawn sequential workflows when certain states are entered or even when certain events are signalled into the workflow by outside stimuli. You can also dynamically shut down these workflows in response to additional stimuli.

Where am I going with this?:

  • I could use the System.IO.Packaging namespace as well as self-host a WF designer to allow anyone running a Universe Server to actually edit the behavior of all classes of NPCs, or maybe even create new classes of NPCS! - all packaged up in a nice neat package like Enemies.pak or something.
  • The entire system of NPC interaction could be built into workflows. For example, the NPC could have an infinitely looping sequential workflow that broadcasts a rader ping into the galaxy in which the NPC is currently located. This ping workflow could be toggled on an off based on whether the NPC activates a stealth ability or dies.
  • The persistence feature of the workflow system could be used to maintain state for each instance of all running NPCs - completely independent of the Universe Server application. In other words - nearly all of the work involved with maintaining, manipulating, tracking, and persisting NPCs in the game can be managed entirely by the Workflow foundation with the Universe Server just being responsible for instantiating new NPCs on demand.

I'd love to hear comments about this system from WF enthusiasts or even WF critics.

p.s. I also want to thank Serge for a lot of help with the syntax. Now that I look at how its implemented, it seems only natural. But staring at a blank workflow surface trying to figure this out with no samples (that I could find) on the web was daunting.

tags:            

links: digg this    del.icio.us    technorati    reddit




1. Rob left...
Thu 02 Nov 06 9:56 pm

Great ideas! Bennage and I have implemented similar concepts for controlling our NPCs in Empyrean. We have rehosted the designer to allow an Admin to create NPCs out of "personality components". Some of the things you are doing are slightly different than us, but I like the direction you are going. I think you are on your way to having a great game. Count me in when you are ready to beta test.


Tag Related Posts

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags:              

Ulysses Agenda : First Cut Networking Design

Thu 14 Sep 06 12:46 A GMT-05
tags:                  

First Impressions of Windows Vista RC1

Thu 07 Sep 06 1:30 P GMT-05
tags:                      

Is Windows Workflow Foundation Too Complex?

Fri 18 Aug 06 12:15 P GMT-05