diff --git a/Flocking/Boid.cs b/Flocking/Boid.cs
index f85cf47..147ce20 100644
--- a/Flocking/Boid.cs
+++ b/Flocking/Boid.cs
@@ -28,6 +28,7 @@ using System;
using System.Collections.Generic;
using log4net;
using OpenMetaverse;
+using Utils = OpenSim.Framework.Util;
namespace Flocking
{
@@ -74,7 +75,7 @@ namespace Flocking
public Vector3 Velocity {
get { return m_vel;}
}
-
+
public String Id {
get {return m_id;}
}
@@ -85,10 +86,11 @@ namespace Flocking
///
/// Boids. all the other chaps in the scene
///
- public void MoveInSceneRelativeToFlock (List boids)
+ public void MoveInSceneRelativeToFlock ()
{
+ List neighbours = m_model.GetNeighbours(this);
// we would like to stay with our mates
- Flock (boids);
+ Flock (neighbours);
// our first priority is to not hurt ourselves
// so adjust where we would like to go to avoid hitting things
@@ -102,10 +104,9 @@ namespace Flocking
UpdatePositionInScene ();
}
-
+
///
- /// Move within our flock
- ///
+ /// Move within our local flock
/// We accumulate a new acceleration each time based on three rules
/// these are:
/// our separation from our closest neighbours,
@@ -113,28 +114,22 @@ namespace Flocking
/// our desire to move towards the flock centre
///
///
- void Flock (List boids)
+ void Flock (List neighbours)
{
// calc the force vectors on this boid
- Vector3 sep = Separate (boids); // Separation
- Vector3 ali = Align (boids); // Alignment
- Vector3 coh = Cohesion (boids); // Cohesion
+ Vector3 sep = Separate (neighbours); // Separation
+ Vector3 ali = Align (neighbours); // Alignment
+ Vector3 coh = Cohesion (neighbours); // Cohesion
// Arbitrarily weight these forces
- //TODO: expose these consts
- sep *= 1.5f; //.mult(1.5);
- //ali.mult(1.0);
- ali *= 1.0f;
- //coh.mult(1.0);
- coh *= 1.0f;
+ sep *= m_model.SeparationWeighting;
+ ali *= m_model.AlignmentWeighting;
+ coh *= m_model.CohesionWeighting;
// Add the force vectors to the current acceleration of the boid
- //acc.add(sep);
m_acc += sep;
- //acc.add(ali);
m_acc += ali;
- //acc.add(coh);
m_acc += coh;
}
@@ -149,10 +144,8 @@ namespace Flocking
void UpdatePositionInScene ()
{
// Update velocity
- //vel.add(acc);
m_vel += m_acc;
// Limit speed
- //m_vel.limit(maxspeed);
m_vel = Util.Limit (m_vel, m_model.MaxSpeed);
m_loc += m_vel;
// Reset accelertion to 0 each cycle
@@ -186,27 +179,24 @@ namespace Flocking
/// Takes a second argument, if true, it slows down as it approaches the target
Vector3 Steer (Vector3 target, bool slowdown)
{
- Vector3 steer; // The steering vector
- Vector3 desired = Vector3.Subtract(target, m_loc); // A vector pointing from the location to the target
- float d = desired.Length (); // Distance from the target is the magnitude of the vector
+ Vector3 steer = Vector3.Zero; // The steering vector
+ Vector3 desired = target - m_loc; // A vector pointing from the location to the target
+ float distance = desired.Length (); // Distance from the target is the magnitude of the vector
// If the distance is greater than 0, calc steering (otherwise return zero vector)
- if (d > 0) {
+ if (distance > 0) {
// Normalize desired
desired.Normalize ();
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
- if ((slowdown) && (d < 100.0f)) {
- desired *= (m_model.MaxSpeed * (d / 100.0f)); // This damping is somewhat arbitrary
+ if ((slowdown) && (distance < m_model.LookaheadDistance )) {
+ desired *= (m_model.MaxSpeed * (distance / m_model.LookaheadDistance)); // This damping is somewhat arbitrary
} else {
desired *= m_model.MaxSpeed;
}
// Steering = Desired minus Velocity
- //steer = target.sub(desired,m_vel);
- steer = Vector3.Subtract (desired, m_vel);
+ steer = desired - m_vel;
//steer.limit(maxforce); // Limit to maximum steering force
steer = Util.Limit (steer, m_model.MaxForce);
- } else {
- steer = Vector3.Zero;
- }
+ }
return steer;
}
@@ -226,40 +216,37 @@ namespace Flocking
/// keeps us a respectable distance from our closest neighbours whilst still
/// being part of our local flock
///
- ///
+ ///
/// Boids. all the boids in the scene
///
- Vector3 Separate (List boids)
+ Vector3 Separate (List neighbours)
{
- Vector3 steer = new Vector3 (0, 0, 0);
- int count = 0;
// For every boid in the system, check if it's too close
- foreach (Boid other in boids) {
- float d = Vector3.Distance (m_loc, other.Location);
- // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
- if ((d > 0) && (d < m_model.DesiredSeparation)) {
- // Calculate vector pointing away from neighbor
- Vector3 diff = Vector3.Subtract (m_loc, other.Location);
- diff.Normalize ();
- diff = Vector3.Divide (diff, d);
- steer = Vector3.Add (steer, diff);
- count++; // Keep track of how many
- }
- }
- // Average -- divide by how many
- if (count > 0) {
- steer /= (float)count;
- }
-
- // As long as the vector is greater than 0
- if (steer.Length () > 0) {
+ float desired = m_model.DesiredSeparation;
+ //who are we too close to at the moment
+ List tooCloseNeighbours = neighbours.FindAll( delegate(Boid neighbour) {
+ // Is the distance is less than an arbitrary amount
+ return Utils.GetDistanceTo (m_loc, neighbour.Location) < desired;
+ });
+
+ // move a bit away from them
+ Vector3 steer = Vector3.Zero;
+ tooCloseNeighbours.ForEach( delegate(Boid neighbour) {
+ // Calculate vector pointing away from neighbor
+ Vector3 diff = m_loc - neighbour.Location;
+ steer += Utils.GetNormalizedVector(diff) / (float)Utils.GetDistanceTo (m_loc, neighbour.Location);
+ });
+
+ if( steer.Length () > 0 ) {
+ // Average -- divide by how many
+ steer /= (float)tooCloseNeighbours.Count;
// Implement Reynolds: Steering = Desired - Velocity
steer.Normalize ();
steer *= m_model.MaxSpeed;
steer -= m_vel;
- //steer.limit(maxforce);
+ //don't go too fast;
steer = Util.Limit (steer, m_model.MaxForce);
- }
+ }
return steer;
}
@@ -273,15 +260,14 @@ namespace Flocking
///
Vector3 Align (List boids)
{
- Vector3 steer = new Vector3 (0, 0, 0);
- int count = 0;
- foreach (Boid other in boids) {
- float d = Vector3.Distance (m_loc, other.Location);
- if ((d > 0) && (d < m_model.NeighbourDistance)) {
- steer += other.Velocity;
- count++;
- }
- }
+ Vector3 steer = Vector3.Zero;
+
+ boids.ForEach( delegate( Boid other ) {
+ steer += other.Velocity;
+ });
+
+ int count = boids.Count;
+
if (count > 0) {
steer /= (float)count;
}
@@ -303,22 +289,19 @@ namespace Flocking
/// MAintain the cohesion of our local flock
/// For the average location (i.e. center) of all nearby boids, calculate our steering vector towards that location
///
- ///
+ ///
/// Boids. the boids in the scene
///
- Vector3 Cohesion (List boids)
+ Vector3 Cohesion (List neighbours)
{
Vector3 sum = Vector3.Zero; // Start with empty vector to accumulate all locations
- int count = 0;
- foreach (Boid other in boids) {
- float d = Vector3.Distance (m_loc, other.Location);
- if ((d > 0) && (d < m_model.NeighbourDistance)) {
- sum += other.Location; // Add location
- count++;
- }
- }
+ neighbours.ForEach( delegate(Boid other) {
+ sum += other.Location; // Add location
+ });
+
+ int count = neighbours.Count;
if (count > 0) {
sum /= (float)count;
return Steer (sum, false); // Steer towards the location
diff --git a/Flocking/ChatCommandParser.cs b/Flocking/ChatCommandParser.cs
new file mode 100644
index 0000000..935cf26
--- /dev/null
+++ b/Flocking/ChatCommandParser.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using log4net;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Framework.Console;
+using OpenSim.Region.Framework.Interfaces;
+
+namespace Flocking
+{
+ public delegate void BoidCmdDelegate (string module,string[] args);
+
+ public class BoidCmdDefn
+ {
+ public string Help = "";
+ public string Args = "";
+ public int NumParams = 0;
+ string m_name;
+
+ public BoidCmdDefn (string name, string args, string help)
+ {
+ Help = help;
+ Args = args;
+ m_name = name;
+
+ if (args.Trim ().Length > 0) {
+ NumParams = args.Split (", ".ToCharArray ()).Length;
+ } else {
+ NumParams = 0;
+ }
+ }
+
+ public string GetSyntax ()
+ {
+ return m_name + " " + Args + " (" + Help + ")";
+ }
+ }
+
+ public class ChatCommandParser
+ {
+ private static readonly ILog m_log = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod ().DeclaringType);
+ private string m_name;
+ private Scene m_scene;
+ private int m_chatChannel;
+ private Dictionary m_commandMap = new Dictionary ();
+ private Dictionary m_syntaxMap = new Dictionary ();
+
+ public ChatCommandParser (IRegionModuleBase module, Scene scene, int channel)
+ {
+ m_name = module.Name;
+ m_scene = scene;
+ m_chatChannel = channel;
+ }
+
+ public void AddCommand (string cmd, string args, string help, CommandDelegate fn)
+ {
+ m_commandMap.Add (cmd, new BoidCmdDelegate (fn));
+ m_syntaxMap.Add (cmd, new BoidCmdDefn (cmd, args, help));
+ }
+
+ public void SimChatSent (Object x, OSChatMessage msg)
+ {
+ if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel)
+ return; // not for us
+
+ // try and parse a valid cmd from this msg
+ string cmd = msg.Message.ToLower ();
+
+ //stick ui in the args so we know to respond in world
+ //bit of a hack - but lets us use CommandDelegate inWorld
+ string[] args = (cmd + " ").Split (" ".ToCharArray ());
+
+ BoidCmdDefn defn = null;
+ if (m_syntaxMap.TryGetValue (args [0], out defn)) {
+ if (CorrectSignature (args, defn)) {
+
+ // we got the signature of the command right
+ BoidCmdDelegate del = null;
+ if (m_commandMap.TryGetValue (args [0], out del)) {
+ del (m_name, args);
+ } else {
+ // we don't understand this command
+ // shouldn't happen
+ m_log.ErrorFormat ("Unable to invoke command {0}", args [0]);
+ RespondToMessage (msg, "Unable to invoke command " + args [0]);
+ }
+
+ } else {
+ // we recognise the command, but we got the call wrong
+ RespondToMessage (msg, "wrong syntax: " + defn.GetSyntax ());
+ }
+ } else {
+ // this is not a command we recognise
+ RespondToMessage (msg, args [0] + " is not a valid command for osboids");
+ }
+
+ }
+
+ private bool CorrectSignature (string[] args, BoidCmdDefn defn)
+ {
+ // args contain cmd name at 0 and tagged in last pos
+ return args.Length - 2 == defn.NumParams;
+ }
+
+ public void RespondToMessage (OSChatMessage msg, string message)
+ {
+ m_log.Debug ("sending response -> " + message);
+ IClientAPI sender = msg.Sender;
+ sender.SendChatMessage (message, (byte)ChatTypeEnum.Say, msg.Position, "osboids", msg.SenderUUID, (byte)ChatSourceType.Agent, (byte)ChatAudibleLevel.Fully);
+ }
+
+ public void SendMessage (ScenePresence recipient, string message)
+ {
+ IClientAPI ownerAPI = recipient.ControllingClient;
+ ownerAPI.SendChatMessage (message,
+ (byte)ChatTypeEnum.Say,
+ recipient.AbsolutePosition,
+ "osboids",
+ recipient.UUID,
+ (byte)ChatSourceType.Agent,
+ (byte)ChatAudibleLevel.Fully
+ );
+ }
+ }
+}
+
diff --git a/Flocking/Flocking.dll.build b/Flocking/Flocking.dll.build
index 1705ee0..339ea86 100644
--- a/Flocking/Flocking.dll.build
+++ b/Flocking/Flocking.dll.build
@@ -24,6 +24,7 @@
+
diff --git a/Flocking/FlockingModel.cs b/Flocking/FlockingModel.cs
index d14f8dc..6f425c6 100644
--- a/Flocking/FlockingModel.cs
+++ b/Flocking/FlockingModel.cs
@@ -27,6 +27,7 @@
using System;
using System.Collections.Generic;
using OpenMetaverse;
+using Utils = OpenSim.Framework.Util;
namespace Flocking
{
@@ -39,6 +40,11 @@ namespace Flocking
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
+ private float m_separationWeighting = 1.5f;
+ private float m_alignmentWeighting = 1f;
+ private float m_cohesionWeighting = 1f;
+ private float m_lookaheadDistance = 100f;
+
private Random m_rnd = new Random(Environment.TickCount);
@@ -95,6 +101,26 @@ namespace Flocking
get {return m_tolerance;}
}
+ public float SeparationWeighting {
+ get{ return m_separationWeighting; }
+ set{ m_separationWeighting = value;}
+ }
+
+ public float AlignmentWeighting {
+ get{ return m_alignmentWeighting; }
+ set{ m_alignmentWeighting = value;}
+ }
+
+ public float CohesionWeighting {
+ get{ return m_cohesionWeighting; }
+ set{ m_cohesionWeighting = value;}
+ }
+
+ public float LookaheadDistance {
+ get { return m_lookaheadDistance; }
+ set { m_lookaheadDistance = value;}
+ }
+
public void Initialise (int num, FlowField flowField)
{
@@ -103,12 +129,19 @@ namespace Flocking
AddBoid ("boid"+i );
}
}
+
+ public List GetNeighbours(Boid boid) {
+ return m_flock.FindAll(delegate(Boid other) {
+ return (boid != other) && (Utils.GetDistanceTo (boid.Location, other.Location) < m_neighbourDistance);
+ });
+ }
+
public List UpdateFlockPos ()
{
- foreach (Boid b in m_flock) {
- b.MoveInSceneRelativeToFlock(m_flock); // Passing the entire list of boids to each boid individually
- }
+ m_flock.ForEach( delegate(Boid boid) {
+ boid.MoveInSceneRelativeToFlock();
+ } );
return m_flock;
}
diff --git a/Flocking/FlockingModule.cs b/Flocking/FlockingModule.cs
index f14637d..3c2079c 100644
--- a/Flocking/FlockingModule.cs
+++ b/Flocking/FlockingModule.cs
@@ -60,6 +60,11 @@ namespace Flocking
private float m_neighbourDistance;
private float m_desiredSeparation;
private float m_tolerance;
+ private float m_separationWeighting = 1.5f;
+ private float m_alignmentWeighting = 1f;
+ private float m_cohesionWeighting = 1f;
+ private float m_lookaheadDistance = 100f;
+ private ChatCommandParser m_chatCommandParser;
private UUID m_owner;
@@ -81,6 +86,10 @@ namespace Flocking
m_neighbourDistance = config.GetFloat("neighbour-dist", 25f);
m_desiredSeparation = config.GetFloat("desired-separation", 20f);
m_tolerance = config.GetFloat("tolerance", 5f);
+ m_separationWeighting = config.GetFloat("separation-weighting", 1.5f);
+ m_alignmentWeighting = config.GetFloat("alignment-weighting", 1f);
+ m_cohesionWeighting = config.GetFloat("cohesion-weighting", 1f);
+ m_lookaheadDistance = config.GetFloat("lookahead-dist",100f);
// we're in the config - so turn on this module
@@ -94,27 +103,33 @@ namespace Flocking
m_scene = scene;
if (m_enabled) {
//register commands
+ m_chatCommandParser = new ChatCommandParser(this, scene, m_chatChannel);
RegisterCommands ();
//register handlers
m_scene.EventManager.OnFrame += FlockUpdate;
- m_scene.EventManager.OnChatFromClient += SimChatSent; //listen for commands sent from the client
+ m_scene.EventManager.OnChatFromClient += m_chatCommandParser.SimChatSent; //listen for commands sent from the client
// init module
m_model = new FlockingModel (m_maxSpeed, m_maxForce, m_neighbourDistance, m_desiredSeparation, m_tolerance);
+ m_model.SeparationWeighting = m_separationWeighting;
+ m_model.AlignmentWeighting = m_alignmentWeighting;
+ m_model.CohesionWeighting = m_cohesionWeighting;
+ m_model.LookaheadDistance = m_lookaheadDistance;
m_view = new FlockingView (m_scene);
m_view.BoidPrim = m_boidPrim;
}
}
+ void chatCom (object sender, OSChatMessage chat)
+ {
+
+ }
+
public void RegionLoaded (Scene scene)
{
if (m_enabled) {
- //make a flow map for this scene
- //FlowMap flowMap = new FlowMap(scene );
- //flowMap.Initialise();
-
//build a proper flow field based on the scene
FlowField field = new FlowField(scene, new Vector3(128f, 128f, 128f), 200, 200, 200);
@@ -134,7 +149,7 @@ namespace Flocking
{
if (m_enabled) {
m_scene.EventManager.OnFrame -= FlockUpdate;
- m_scene.EventManager.OnChatFromClient -= SimChatSent;
+ m_scene.EventManager.OnChatFromClient -= m_chatCommandParser.SimChatSent;
}
}
@@ -155,9 +170,6 @@ namespace Flocking
if (((m_frame++ % m_frameUpdateRate) != 0) || !m_ready || !m_enabled) {
return;
}
-
- //m_log.InfoFormat("update my boids");
-
// work out where everyone has moved to
// and tell the scene to render the new positions
lock( m_sync ) {
@@ -166,34 +178,6 @@ namespace Flocking
}
}
- protected void SimChatSent (Object x, OSChatMessage msg)
- {
- if (m_scene.ConsoleScene () != m_scene || msg.Channel != m_chatChannel)
- return; // not for us
-
- // try and parse a valid cmd from this msg
- string cmd = msg.Message.ToLower ();
-
- //stick ui in the args so we know to respond in world
- //bit of a hack - but lets us use CommandDelegate inWorld
- string[] args = (cmd + " ").Split (" ".ToCharArray ());
-
- if (cmd.StartsWith ("stop")) {
- HandleStopCmd ("flock", args);
- } else if (cmd.StartsWith ("start")) {
- HandleStartCmd ("flock", args);
- } else if (cmd.StartsWith ("size")) {
- HandleSetSizeCmd ("flock", args);
- } else if (cmd.StartsWith ("stats")) {
- HandleShowStatsCmd ("flock", args);
- } else if (cmd.StartsWith ("prim")) {
- HandleSetPrimCmd ("flock", args);
- } else if (cmd.StartsWith ("framerate")) {
- HandleSetFrameRateCmd ("flock", args);
- }
-
- }
-
#endregion
#region Command Handling
@@ -205,6 +189,7 @@ namespace Flocking
argStr = " <" + args + "> ";
}
m_scene.AddCommand (this, "flock-" + cmd, "flock-" + cmd + argStr, help, fn);
+ m_chatCommandParser.AddCommand(cmd, args, help, fn);
}
private void RegisterCommands ()
@@ -215,6 +200,7 @@ namespace Flocking
AddCommand ("stats", "", "show flocking stats", HandleShowStatsCmd);
AddCommand ("prim", "name", "set the prim used for each boid to that passed in", HandleSetPrimCmd);
AddCommand ("framerate", "num", "[debugging] only update boids every frames", HandleSetFrameRateCmd);
+ AddCommand ("set", "name, value", "change the flock dynamics", HandleSetParameterCmd);
}
private bool ShouldHandleCmd ()
@@ -235,15 +221,19 @@ namespace Flocking
private void ShowResponse (string response, bool inWorld)
{
if (inWorld) {
- IClientAPI ownerAPI = null;
- if (m_scene.TryGetClient (m_owner, out ownerAPI)) {
- ownerAPI.SendBlueBoxMessage (m_owner, "osboids", response);
- }
+ ScenePresence owner = m_scene.GetScenePresence(m_owner);
+ m_chatCommandParser.SendMessage(owner, response);
} else {
MainConsole.Instance.Output (response);
}
}
+ public void HandleSetParameterCmd(string module, string[] args)
+ {
+ if (ShouldHandleCmd ()) {
+ }
+ }
+
public void HandleStopCmd (string module, string[] args)
{
if (ShouldHandleCmd ()) {
diff --git a/Flocking/FlockingView.cs b/Flocking/FlockingView.cs
index 5db8f33..20f9816 100644
--- a/Flocking/FlockingView.cs
+++ b/Flocking/FlockingView.cs
@@ -57,25 +57,24 @@ namespace Flocking
public void Clear ()
{
//trash everything we have
- foreach (string name in m_sogMap.Keys)
- {
- RemoveSOGFromScene(name);
- }
+ List current = new List (m_sogMap.Keys);
+ current.ForEach (delegate(string name) {
+ RemoveSOGFromScene(name);
+ });
m_sogMap.Clear();
}
public void Render (List boids)
{
- foreach (Boid boid in boids) {
- DrawBoid (boid);
- }
+ boids.ForEach(delegate( Boid boid ) {
+ DrawBoid (boid);
+ });
}
private void DrawBoid (Boid boid)
{
SceneObjectPart existing = m_scene.GetSceneObjectPart (boid.Id);
-
SceneObjectGroup sog;
if (existing == null) {
SceneObjectGroup group = findByName (m_boidPrim);
@@ -86,20 +85,24 @@ namespace Flocking
sog = existing.ParentGroup;
}
- Quaternion rotation = CalcRotationToEndpoint (sog, sog.AbsolutePosition, boid.Location);
+ Quaternion rotation = CalcRotationToEndpoint (sog, boid.Location);
sog.UpdateGroupRotationPR( boid.Location, rotation);
}
- private static Quaternion CalcRotationToEndpoint (SceneObjectGroup copy, Vector3 sv, Vector3 ev)
+ private static Quaternion CalcRotationToEndpoint (SceneObjectGroup sog, Vector3 ev)
{
//llSetRot(llRotBetween(<1,0,0>,llVecNorm(targetPosition - llGetPos())));
// boid wil fly x forwards and Z up
+ Vector3 sv = sog.AbsolutePosition;
Vector3 currDirVec = Vector3.UnitX;
Vector3 desiredDirVec = Vector3.Subtract (ev, sv);
desiredDirVec.Normalize ();
Quaternion rot = Vector3.RotationBetween (currDirVec, desiredDirVec);
+
+ //TODO: if we turn upside down, flip us over by rotating along Y
+
return rot;
}
@@ -113,13 +116,9 @@ namespace Flocking
private SceneObjectGroup findByName (string name)
{
- SceneObjectGroup retVal = null;
- foreach (EntityBase e in m_scene.GetEntities()) {
- if (e.Name == name) {
- retVal = (SceneObjectGroup)e;
- break;
- }
- }
+ SceneObjectGroup retVal = (SceneObjectGroup)m_scene.Entities.Find (delegate( EntityBase e ) {
+ return (e.Name == name) && (e is SceneObjectGroup);
+ });
// can't find it so make a default one
if (retVal == null) {
diff --git a/Flocking/FlowField.cs b/Flocking/FlowField.cs
index be2a774..04cf214 100644
--- a/Flocking/FlowField.cs
+++ b/Flocking/FlowField.cs
@@ -64,11 +64,9 @@ namespace Flocking
for( int x = 0; x < size.X; x++ ) {
for( int y = 0; y < size.Y; y++ ) {
for( int z = 0; z < size.Z; z++ ) {
- if( IsWithinFlowField(
}
}
}
-
}
}
diff --git a/Flocking/VectorTest.cs b/Flocking/VectorTest.cs
new file mode 100644
index 0000000..5f0872e
--- /dev/null
+++ b/Flocking/VectorTest.cs
@@ -0,0 +1,23 @@
+using System;
+using OpenMetaverse;
+using NUnit.Framework;
+
+namespace Flocking
+{
+ [TestFixture()]
+ public class VectorTest
+ {
+ [Test()]
+ public void TestCase ()
+ {
+ Vector3 [,] field = new Vector3[3,3];
+ Vector2 start = new Vector2(0f, 0f);
+ Assert.That( field[1,1].Z == 0 );
+
+ field[1,0] = Vector3.UnitZ;
+
+
+ }
+ }
+}
+