How to refactor commonalities among InputActionMaps in Unity - unity3d

Suppose I have 2 (or more) types of objects that the user can control:
public class Runner : MonoBehaviour
{
//...
public void Run() { //... }
}
and
public class Jumper : Runner
{
//...
public void Jump() { //... }
}
Furthermore, we have the following InputActionMaps in our InputActionAsset:
runner action map
jumper action map
My current solution defines the control-to-action mappings using separate "Controls" classes.
Controls.cs:
public abstract class Controls<T>
{
protected T controlled;
public Controls(InputActionMap actionMap, T controlled)
{
SetupInputCallbacks(actionMap);
this.controlled = controlled;
}
protected abstract void SetupInputCallbacks(InputActionMap actionMap);
}
RunnerControls.cs
public class RunnerControls : Controls<Runner>
{
public RunnerControls(InputActionMap actionMap, Runner controlled)
: base(actionMap, controlled) { }
protected override void SetupInputCallbacks(InputActionMap actionMap)
{
InputAction runAction = actionMap.FindAction("Run");
runAction.perform += controlled.Run();
}
}
Jumper.cs
public class JumperControls : Controls<Jumper>
{
public JumperControls(InputActionMap actionMap, Runner controlled)
: base(actionMap, controlled) { }
protected override void SetupInputCallbacks(InputActionMap actionMap)
{
InputAction runAction = actionMap.FindAction("Run");
runAction.perform += controlled.Run();
InputAction jumpAction = actionMap.FindAction("Jump");
jumpAction.perform += controlled.Jump();
}
}
The code for the "run action" is duplicated, and although this may seem minor here, my actual code does this to a much greater extent. I tried something like public class JumperControls : RunnerControls, but then the controlled.Jump() would fail since the T in the Controls class evaluates to Runner. If anyone can help me come up with a solution to this that'd be a huge help, even if that means changing the architecture. Thanks!

You can make Runner and Jumper separate interfaces with their respective Run and Jump methods. The actual monobehaviours will be implementing the interfaces and having their own Run and Jump logic. So in the Controls<T> you can check whether the T implements the respective interface:
public class Controls<T>
{
protected T controlled;
public Controls(InputActionMap actionMap, T controlled)
{
SetupInputCallbacks(actionMap);
this.controlled = controlled;
}
protected virtual void SetupInputCallbacks(InputActionMap actionMap)
{
if (T is Runner) ....
if (T is Jumper) ....
}
}

Related

Roslyn codefix and fixall action not executed properly under unit test

I've 'successfully' written a CodeFix and FixAllProvider, BUT...
The diagnostic I'm trying to handle can occur multiple times in the same document on the same line. However, the behavior under unit test (CSharpCodeFixTest) stumps me.
If a test generates only one instance of the diagnostic, CSharpCodeFix<> calls the CodeFix initially then calls the FixAllProvider multiple times during Verification. The test succeeds.
If a test generates more than one diagnostic, CSharpCodeFix<> calls the CodeFix once. CSharpCodeFix<> never calls the FixAllProvider, and since the CodeFix cannot fix all instances. the test fails the before/after document comparison.
Note that in these samples, namespaces (not shown) disambiguate the classes from their bases. I've removed the fix implementations because I believe them irrelevant to the problem.
First the CodeFix
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CodeFixProvider)), Shared]
public class CodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(EGNT0003NoInlineInstantiationAnalyzer.DiagnosticId); }
}
public sealed override Microsoft.CodeAnalysis.CodeFixes.FixAllProvider GetFixAllProvider()
{
Microsoft.CodeAnalysis.CodeFixes.FixAllProvider provider = FixAllProvider.Instance;
return provider;
}
public static readonly string EquivalenceKey = "EG0003CodeFixProvider";
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (Diagnostic diagnostic in context.Diagnostics.Where(d => FixableDiagnosticIds.Contains(d.Id)))
{
context.RegisterCodeFix(CodeAction.Create(title: "Introduce local variable",
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
equivalenceKey: EquivalenceKey), diagnostic);
}
return Task.CompletedTask;
}
;
Here is the FixAllProvider
public sealed class FixAllProvider : Microsoft.CodeAnalysis.CodeFixes.FixAllProvider
{
private FixAllProvider()
{
}
private static readonly Lazy<FixAllProvider> lazy = new Lazy<FixAllProvider>(() => new FixAllProvider());
public static FixAllProvider Instance
{
get
{
return lazy.Value;
}
}
public override IEnumerable<string> GetSupportedFixAllDiagnosticIds(Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider originalCodeFixProvider)
{
string[] diagnosticIds = new[]
{
EGNT0003NoInlineInstantiationAnalyzer.DiagnosticId,
};
return diagnosticIds;
}
public override async Task<CodeAction> GetFixAsync(FixAllContext fixAllContext)
{
:
Finally here is the CodeAction invoked by the FixAllProvider.
public class FixAllCodeAction : CodeAction
{
private readonly List<KeyValuePair<Document, ImmutableArray<Diagnostic>>> _diagnosticsToFix;
private readonly Solution _solution;
public FixAllCodeAction(string title, Solution solution, List<KeyValuePair<Document, ImmutableArray<Diagnostic>>> diagnosticsToFix)
{
this.Title = title;
_solution = solution;
_diagnosticsToFix = diagnosticsToFix;
}
public override string Title { get; }
public override string EquivalenceKey => "EG0003CodeFixProvider";
protected override async Task<Solution> GetChangedSolutionAsync(CancellationToken cancellationToken)
{
Solution newSolution = _solution;
:
I've debugged through CSharpCodeFixTest<> and continue to do so. I'm hoping someone has seen this issue before and can see my mistake.
I expected to see the code fix tests to complete successfully. I verified through other means that the documents produced by the CodeFix and the FixAllProvider are valid and correct.

Autofac: registering hierarchy of classes

I am struggling to register with Autofac a hierarchy of classes and interfaces.
I have an interface IMyService defined as below:
public interface IMyService
{
void DoMyService();
}
And I have two abstract classes with implement this interface and called MyServiceA, and MyServiceB:
public abstract class MyServiceA : IMyService
{
public abstract DoMyService();
}
public abstract class MyServiceB : IMyService
{
public abstract DoMyService();
}
Moreover I have a second-level hierarchy for each of the two aforementioned services: MyServiceA1, MyServiceA2, MyServiceB1 and MyServiceB2:
public class MyServiceA1 : MyServiceA
{
public MyServiceA1() : base() {}
public void DoMyService()
{
// Implementation goes here
}
}
public class MyServiceA2 : MyServiceA
{
public MyServiceA2() : base() {}
public void DoMyService()
{
// Implementation goes here
}
}
public class MyServiceB1 : MyServiceB
{
public MyServiceB1() : base() {}
public void DoMyService()
{
// Implementation goes here
}
}
public class MyServiceB2 : MyServiceB
{
public MyServiceB2() : base() {}
public void DoMyService()
{
// Implementation goes here
}
}
I have in input two enums FirstEnum and SecondEnum used to select which of the four concrete types to instantiate:
public enum FirstEnum
{
SvcA,
SvcB
}
public enum SecondEnum
{
Svc1,
Svc2
}
I want to register IMyService and by providing two enums, It will automatically instantiate the good concrete type.
For instance, if I want to resolve an IMyService and I provide FirstEnum.SvcB and SecondEnum.Svc2, it should instantiate the concrete type MyServiceB2 class. Moreover this hierarchy might be updated by adding some other concrete types etc, so I need a generic way of doing it
Does anyone have a clue to help me?
Thanks
If you want to create a specific service dynamically depending on a set of parameters, this is a good use case for an abstract factory:
public interface IMyServiceFactory
{
IMyService Create(FirstEnum e1, SecondEnum e2);
}
public class MyServiceFactory : IMyServiceFactory
{
private readonly ILifetimeScope scope;
public MyServiceFactory(ILifetimeScope scope)
{
if (scope == null)
throw new ArgumentNullException("scope");
this.scope = scope;
}
public IMyService Create(FirstEnum e1, SecondEnum e2)
{
if (e1 == FirstEnum.SvcA)
{
if (e2 == SecondEnum.Svc1)
{
return scope.Resolve<MyServiceA1>();
}
else //svc2
{
return scope.Resolve<MyServiceA2>();
}
}
else //B
{
if (e2 == SecondEnum.Svc1)
{
return scope.Resolve<MyServiceB1>();
}
else //svc2
{
return scope.Resolve<MyServiceB2>();
}
}
}
}
And now your consumer need to get the factory injected instead of the service:
public class MyServiceConsumer
{
private readonly IMyServiceFactory factory;
public MyServiceConsumer(IMyServiceFactory factory)
{
this.factory = factory;
}
public void Do()
{
//var service = this.factory.Create
}
}
Registration :
Autofac.ContainerBuilder builder = new Autofac.ContainerBuilder();
builder.RegisterType<MyServiceA1>().AsSelf().AsImplementedInterfaces();
builder.RegisterType<MyServiceA2>().AsSelf().AsImplementedInterfaces();
builder.RegisterType<MyServiceB1>().AsSelf().AsImplementedInterfaces();
builder.RegisterType<MyServiceB2>().AsSelf().AsImplementedInterfaces();
builder.RegisterType<MyServiceFactory>().As<IMyServiceFactory>();
builder.RegisterType<MyServiceConsumer>();
//and quick test resolve
var container = builder.Build();
var test = container.Resolve<MyServiceConsumer>();

Make a gameObject follow another gameObject's behavior?

I am controlling a text GameObject. I am performing operations such as SetActive() and changing the text of the gameObject. Now I am duplicating the same object and I want the duplicated object to follow the behaviors of its main gameObject. That is ObjA is parent and ObjB is a clone. If I change the text UI through code of objA, I want objB to automatically change its component. How do I achieve this behavior?
There are multiple ways to do that, but it will always be by code, there is no "authomatism" to do that.
So by code you can make a relation child to parent or parent to child.
One way could be that one:
public class Parent : MonoBehaviour
{
public Child child = null;
public void DoSomething()
{
this.gameObject.SetActive(true);
child.DoSomething();
}
}
public class Child : MonoBehaviour
{
public void DoSomething()
{
this.gameObject.SetActive(true);
}
}
Another fancy way to do that is using delegates, or Actions:
public class Parent : MonoBehaviour
{
public Action OnDoSomething = null;
public Action OnDoSomethingElse = null;
public void DoSomething()
{
this.gameObject.SetActive(false);
OnDoSomething();
}
public void DoSomethingElse()
{
this.gameObject.SetActive(true);
OnDoSomethingElse();
}
}
public class Child : MonoBehaviour
{
public Parent parent = null;
public void Awake()
{
parent.OnDoSomething += ChildDoSometing;
parent.OnDoSomethingElse += ChildDoSometingElse;
}
public void OnDestroy()
{
parent.OnDoSomething -= ChildDoSometing;
parent.OnDoSomethingElse -= ChildDoSometingElse;
}
public void ChildDoSometing()
{
this.gameObject.SetActive(false);
}
public void ChildDoSometingElse()
{
this.gameObject.SetActive(false);
}
}
You can be tempted to pass your own method as Action parameter like Action<Action> but remember that child will call parent method, won't operate on his own. So in this case if you do something like:
public class Parent : MonoBehaviour
{
public Action<Action> OnDoSomething = null;
[ContextMenu("A")]
public void DoSomething()
{
this.gameObject.SetActive(true);
OnDoSomething(this.DoSomething);
}
public void DoSomethingElse()
{
print("Hello");
}
}
public class Child : MonoBehaviour
{
public Parent parent = null;
public void Awake()
{
parent.OnDoSomething += RepeatedAction;
}
public void OnDestroy()
{
parent.OnDoSomething -= RepeatedAction;
}
public void RepeatedAction(Action actionToRepeat)
{
actionToRepeat?.Invoke();
}
}
Will result on StackOverflow exception, cause child will call parent, who calls child, who calls again parent...you can see the problem.
Anyway would be nice to declare an abstract base class that have all the methods, and let both classes inherit from that class, and implement those methods.

MvvmCross: IoC with Decorator pattern, two implementations of the same interface

I'd like to implement the Decorator pattern in one of my Mvx projects. That is, I'd like to have two implementations of the same interface: one implementation that is available to all of the calling code, and another implementation that is injected into the first implementation.
public interface IExample
{
void DoStuff();
}
public class DecoratorImplementation : IExample
{
private IExample _innerExample;
public Implementation1(IExample innerExample)
{
_innerExample = innerExample;
}
public void DoStuff()
{
// Do other stuff...
_innerExample.DoStuff();
}
}
public class RegularImplementation : IExample
{
public void DoStuff()
{
// Do some stuff...
}
}
Is it possible to wire up the MvvmCross IoC container to register IExample with a DecoratorImplementation containing a RegularImplementation?
It depends.
If DecoratorImplementation is a Singleton, then you could do something like:
Mvx.RegisterSingleton<IExample>(new DecoratorImplementation(new RegularImplementation()));
Then calls to Mvx.Resolve<IExample>() will return the instance of DecoratorImplementation.
However, if you need a new instance, unfortunately the MvvmCross IoC Container doesn't support that. It would be nice if you could do something like:
Mvx.RegisterType<IExample>(() => new DecoratorImplementation(new RegularImplementation()));
Where you'd pass in a lambda expression to create a new instance, similar to StructureMap's ConstructedBy.
Anyway, you may need to create a Factory class to return an instance.
public interface IExampleFactory
{
IExample CreateExample();
}
public class ExampleFactory : IExampleFactory
{
public IExample CreateExample()
{
return new DecoratorImplementation(new RegularImplementation());
}
}
Mvx.RegisterSingleton<IExampleFactory>(new ExampleFactory());
public class SomeClass
{
private IExample _example;
public SomeClass(IExampleFactory factory)
{
_example = factory.CreateExample();
}
}

Testing mocked objects rhino mocks

I am new to RhinoMocks, and I am trying to write a test as shown
I have classes like these
public class A
{
public void methodA(){}
}
public class B
{
public void methodB(A a)
{
a.methodA();
}
}
And i am trying to test it like this
A a = MockRepository.GenerateMock<A>();
public void ShouldTest()
{
B b = new B();
b.methodB(a);
a.AssertWasCalled(x=>x.methodA());
a.VerifyAllExpectations();
}
But it is giving the error as shown:
System.InvalidOperationException : No expectations were setup to be verified, ensure that the method call in the action is a virtual (C#) / overridable (VB.Net) method call.
How do I test methodB then?? Can someone help??
Rhino mock creates proxy class when you call MockRepository.Generate *** method. This means that it extends your type. If you don't declare any abstraction you cannot make any derivation which is essential in any mocking framework.
You can do two things
Create an interface (better design)
Make the member virtual (this will allow RhinoMocks to derive from your type and create a proxy for the virtual member
Sample code
public interface IA { void methodA();}
public class A:IA{public void methodA() { }}
public class B
{
public void methodB(IA a)
{
a.methodA();
}
}
[TestFixture]
public class Bar
{
[Test]
public void BarTest()
{
//Arrange
var repo = MockRepository.GenerateMock<IA>();
//Act
B b = new B();
b.methodB(repo);
//Assert
repo.AssertWasCalled(a => a.methodA());
repo.VerifyAllExpectations();
}
}
You have concrete classes with no virtual methods and no interfaces. You can't mock anything.
Update:
Here's one way to do it:
public interface IA
{
void methodA();
}
public class A : IA
{
public void methodA(){}
}
public class B
{
public void methodB(IA a)
{
a.methodA();
}
}
Then use
IA a = MockRepository.GenerateMock<IA>();