I'm new to passing delegates and was wondering if the following code is possible to do?
CommandQueue.AddCommand(
new CommandItem(
group.MyGroupActionMovement(
new Vector3(-1000,-1000,-1000))));
ok to elaborate I'm going to do a code dump of my bad attempt at the command pattern...
So I'm trying to create a queue of commands. Starting with just the movement command. It takes a vector3 as a parameter. Other Commands will have different parameters like enums and game objects.
This is supposed to be my interface for commands/actions but, I've been messing with it to see what I can do.
public class IGroupAction: MonoBehaviour
{
public delegate bool Del(Vector3 destination);
public virtual bool Execute()
{
return true;
}
}
This is supposed to be the command/action unit/item
public class GroupActionItem
{
public IGroupAction MyGroupAction;
public GroupActionItem(IGroupAction.Del action)
{
}
public bool PerformAction()
{
return MyGroupAction.Execute();
}
}
This is the command pattern that I've completed so far
public class GroupAction
{
public List<GroupActionItem> Actions;
public GroupAction()
{
Actions = new List<GroupActionItem>();
}
public void AddAction(GroupActionItem groupActionItem)
{
Actions.Add(groupActionItem);
}
public void ClearActions()
{
Actions.Clear();
}
public void PerformActions()
{
if (Actions.Count >= 1)
{
if(Actions[0].PerformAction())
{
Actions.Remove(Actions[0]);
}
}
}
}
This is where I execute the movement command
public class GroupActionMovement : IGroupAction
{
public override bool Execute()
{
return Movement();
}
public bool Movement(Vector3 destination)
{
return true;
}
}
public class Group : MonoBehaviour
{
public GroupActionMovement MyGroupActionMovement;
}
This is the execution of the coded behavior.
public class Player : MonoBehaviour
{
public Dictionary<Group,GroupAction> PlayerGroupsActions;
public List<Group> Groups;
void Start()
{
PlayerGroupsActions = new Dictionary<Group, GroupAction>();
Groups = GetComponentsInChildren<Group>().ToList();
foreach (Group group in Groups)
{
GroupAction playerGroupActions = new GroupAction();
PlayerGroupsActions.Add(group,playerGroupActions);
}
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Alpha1))
{
//remove the foreach
foreach (Group group in Groups)
{
//directly apply the group
PlayerGroupsActions.TryGetValue(group, out GroupAction playerGroupActions);
if (playerGroupActions != null)
{
playerGroupActions.AddAction(new GroupActionItem(group.MyGroupActionMovement.Movement));
}
}
}
foreach (Group group in Groups)
{
PlayerGroupsActions.TryGetValue(group, out GroupAction playerFormationActions);
if (playerFormationActions != null)
{
if (playerFormationActions.Actions.Count != 0)
{
playerFormationActions.PerformActions();
}
}
}
}
}
I'm sorry if this isn't the best explanation of what is going on or, that a code dump is not the best way to explain what i'm doing.
CodePudding user response:
don't pass it as a method, use something like this
CommandQueue.Command = doSomething;
void doSomething()
{
//doSomething
}
So that whenever the function CommandQueue.Command
is run in the seperate class it will also run the doSomething()
void in the main class
CodePudding user response:
A delegate is not a method call, but the method it self, for the receiver to call it, whenever its needs to, with the parameters it wants.
Here is an example:
public delegate int MyDelegate(Vector3 vec);
public class MyCallingClass
{
public void DoSomething(MyDelegate method)
{
// Do something.
int result = method(new Vector3(-1000, -1000, -1000));
// Do something else.
}
}
public class MyClass
{
public static int MyStaticMethod(Vector3 arg)
{
// Do something with arg.
return x;
}
int MyMethod(Vector3 arg)
{
// Do something with arg.
return x;
}
private void DelegateWork()
{
MyCallingClass c = // Get an instance of MyCallingClass.
c.DoSomething(this.MyMethod);
c.DoSomething(MyClass.MyStaticMethod);
}
}
Multiple things to understand here:
At line
c.DoSomething(this.MyMethod)
, thethis
is captured into the delegate you pass, meaning that whenMyCallingClass.DoSomething
will call the delegate, it will be called on the instance ofMyClass
. The linec.DoSomething(MyClass.MyStaticMethod)
will call it with no instance, because it is a static method.This is the method
MyCallingClass.DoSomething
that decides what parameters to pass, not the one that provides the delegate.If you need the caller to provide arguments and the calling part to just decide when to call but not deciding what arguments to pass, then you can capture the argument ahead of time, and pass a delegate without argument, as follow.
public delegate int MyDelegate(); // No arguments anymore.
public class MyCallingClass
{
public void DoSomething(MyDelegate method)
{
// Do something.
int result = method(); // Not passing arguments anymore.
// Do something else.
}
}
public class MyClass
{
// ...
private void DelegateWork()
{
MyCallingClass c = // Get an instance of MyCallingClass.
c.DoSomething(() => this.MyMethod(new Vector3(-1000, -1000, -1000)));
c.DoSomething(() => MyClass.MyStaticMethod(new Vector3(-1000, -1000, -1000));
}
}
If you are not familiar with the syntax () => ...
, this is a lambda expression, you can see it as an anonymous function created on-the-fly. It still respects the prototype returning an int
and taking no parameters. The lambda expression now captures the construction of the Vector3
instance, and so this value will be used when the lambda will be called. A very important aspect to understand is that the values in the lambda expressions are evaluated when the lambda is called, not when it is created (lazy evaluation).
- You do not have to use a specific
delegate
but instead you can use aFunc<Vector3, int>
, as follow:
// Not actually needed.
// public delegate int MyDelegate(Vector3 vec);
public class MyCallingClass
{
public void DoSomething(Func<Vector3, int> method)
{
...
}
}