I've been reading many articles online about how to set up a unit test, and most of all it seems pretty straight forward: you create a Test directory using Test Running in Unity. According to this post here, if you run into a namespace issue, then you create an assembly definition file in your scripts directly, reference it in your test.asmdef file, and boom you can start running tests successfully.
My problem is I've inherited a project with 34 of these Scripts directories, and the moment I add an assembly definition file to one, it creates a namespace issue with all other namespaces/objects. Logical conclusion is I create an .asmdef in each of these files and create references where they are needed. Unfortunately this program was designed in such a way that this creates a cyclical dependency among the assembly definition files. This circular dependency is not an issue in the general usage of the program. Without restructuring the code base, is there a way to make this code testable?
Simple solution would be to add the asmdef to the top folder of your 34 script folders.
If they are all across Assets folder then you can create that Script folder and move them all in there. That should not break your project as Unity will update all connections.
The long term solution you may have to go for is creating abstract/interface in assembly that current code would implement.
Say you have script Player in player.asmdef and you want to test it. But it has a dependency to Inventory which is not in any asmdef. You could move Inventory but it also has its set of dependencies and so on.
Instead of moving Inventory, you create a base inventory as abstract and interface in the manager.asmdef and add this one to player.asmdef. Assuming Player.cs uses
List<Item> Inventory.GetInventory();
void Inventory.SetItem(Item item);
Your IInventory.cs could look like so
public abstract class InventoryBase : MonoBehaviour, IInventory
{
// if those methods were self contained, meaning they don't use any outside code
// the implementation could be moved here
public abstract List<Item> GetInventory();
public abstract void SetItem(Item item);
}
public interface IInventory
{
List<Item> GetInventory();
void SetItem(Item item);
}
public class Item
{
public string id;
public int amount;
public string type;
}
Then the Inventory class
public class Inventory : InventoryBase
{
// Implementation is already there since it was used
// but requires the override on the methods
}
It may feel like adding extra useless layers but this adds a second advantage of major importance, you can mock the IInventory object in your player test:
[Test]
public void TestPlayer()
{
// Using Moq framework but NSubstitute does same with different syntax
Mock<IInventory> mockInventory = new Mock<IInventory>();
Mock<IPlayer> mockPlayer= new Mock<IPlayer>();
PlayerLogic player = new PlayerLogic(mockPlayer.Object, mockInventory.Object);
mock.Setup(m=> m.GetInventory).Returns(new List<Item>());
}
This assumes the Player class is decoupled between the MonoBehaviour and the logic:
public class Player : MonoBehaviour ,IPlayer
{
[SerializedField] private InventoryBase m_inventory;
PlayerLogic m_logic;
void Awake()
{
m_logic = new PlayerLogic(this, m_inventory);
}
}
public interface IPlayer{}
public class PlayerLogic
{
IPlayer m_player;
IInventory m_inventory
public PlayerLogic(IPlayer player, IInventory inventory)
{
m_player = player;
m_inventory = inventory;
}
// Do what you need with dependencies
// Test will use the mock objects as if they were real
}
Notice that Player uses InventoryBase since it cannot see Inventory not being in an assembly. But as you drop in the Inventory object, the compiler will use the code down there even if Player type is not aware of Inventory type.
If you were to use another method from Inventory into Player, then you'd need to add the abstract to the base class and the declaration in the interface for testing.
PlayerLogic uses the interface instead of the base type to make the testing possible.
Related
I am developing a small nuclear reactor simulator game. I have a bunch of reactor component classes: HeatVent, HeatExchanger, UraniumCell etc. They are not deriving from MonoBehaviour since they don't have any Unity logic, but they do implement a shared interface IReactorComponent. What I want to do is to be able to create prefabs of such components (simple heat vent, advanced heat vent, doubled uranium cell etc.) The prefabs would have different sprites and something like that, but the main issue is to define what reactor component class the prefab is related to, because I can't just drag'n'drop a non-MonoBehaviour script on inspector. Also, I want to be able to set settings in the inspector (for example, HeatVent has CoolAmount and HeatCapacity properties, UraniumCell has FuelAmount, HeatProduce and PowerProduce properties).
I have read about factory method pattern and as I understood, I have to create a fabric class that derives from MonoBehaviour for each reactor component class like HeatVentBehaviour, HeatExchangerBehaviour etc. Yes, that completely solves my issue with prefabs but is there any way to not create an additional MonoBehaviour wrap for each class? If I had 15 IReactorComponent classes, I would need to create 15 fabrics which feels like not the greatest solution.
Sounds like what you are looking for is ScriptableObject!
Instances of those are assets so they don't live in a scene but in the Assets folder and basically behave a little bit like prefabs except: They already exist and do not need to be instantiated anymore.
Mostly they are used as just configurable data containers. They have an Inspector so you can easily fill them with your desired data and references to other assets (e.g. the related prefab in your case).
But in addition you can as well let them implement behavior like your interface and thereby change the behavior of your scene objects by using different implementations of a method from different ScriptableObjects!
For the factory you then only need to figure out for which method to use which ScriptableObject instance e.g. either by having different methods or by having a Dictionary where you fill in your SO references.
Just as an example how this might look like (make sure each MonoBehaviour and ScriptableObject has its individual script file with matching name)
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
[SerializeField] private ReactorComponentBehaviour _behaviourPrefab;
[SerializeField] private BaseReactorComponent[] _components;
public bool TrySpawn<T>(out T component, out ReactorComponentBehaviour componentBehaviour) where T : IReactorComponent
{
component = default(T);
componentBehaviour = default;
var foundComponent = components.FirstOrDefault(c => c.GetType() == typeof(T));
if(foundComponent == null)
{
Debug.LogError($"No component found of type {T.GetType().Name}!");
return false;
}
// Here Instantiate doesn't spawn anything into the scene but
// rather creates a copy of the ScriptableObject asset
// This is just to avoid that any changes in the fields during the game
// would change the original ScriptableObject asset and thereby ALL related behavior instances
component = Instantiate ( (T) foundComponent);
// This now indeed spawns the related MonoBehaviour + GameOver
componentBehaviour = Instantiate (behaviourPrefab);
componentBehaviour.Init(component);
return true;
}
}
BaseReactorComponent.cs
public abstract class BaseReactorComponent : ScriptableObject, IReactorComponent
{
public abstract void WhateverIReactorComponentNeeds();
// Common fields and methods e.g.
public Sprite Icon;
}
HeatVent.cs
[CreateAssetMenu]
public class HeatVent : BaseReactorComponent
{
public int CoolAmount;
public int HeatCapacity;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
UraniumCell.cs
[CreateAssetMenu]
public class UraniumCell : BaseReactorComponent
{
public int FuelAmount;
public int HeatProduce;
public int PowerProduce;
public override void WhateverIReactorComponentNeeds ()
{
// Do something
}
}
And finally you need only one base prefab with the
ReactorComponentBehavior.cs
public class ReactorComponentBehavior : MonoBehaviour
{
[SerializeField] private Image _image;
private IReactorComponent _component;
public void Init(IReactorComponent component)
{
_componemt = component;
// Do other stuff like e.g. adjust visuals according to the component etc
_image.sprite = component.Icon;
}
// And then use whatever this behavior should do with the assigned component
}
So in the end you would use that like e.g.
if(spawManagerReference.TrySpawn<HeatVent>(out var component, out var componentBehaviour)
{
// Do something with the behavior e.g. set its position, parent etc
}
else
{
Debug.LogError($"Failed to get a {nameof(HeatVent)}!");
}
If then at some point you still want different additional behaviours you could let them inherit from the common ReactorComponentBehavior and rather reference the prefabs inside the BaseReactorComponent itself .. then every component can bring its own prefab but still have a common core behaviour
So I'm working on a game and I'd like some recommendations for how to sort items for easy, legible, coding reference in Unity. I know my current method is flawed, and really I'd just like some guidance.
So what I'm doing is using classes to separate item categories and their items.
For example, here's an idea of a setup for a script :
public class Items {
public class Equipment {
public class Weapons {
public Item Sword = new Item();
Sword.name = "Sword";
Sword.cost = 1;
}
}
}
then a basic Item class for example
public class Item {
public string name;
public int cost;
}
I can tell this is terrible practice, especially based on the problems I've been having, but I like the idea of using a reference like Items.Equipment.Weapons.Sword and I've grown accustomed to using that from an API I previously used.
I'm open to completely changing everything, I just want some tips. Thanks.
I guess my main question is (was), what's the best way to organize nested classes so they can be references from other scripts easily?
The answer I found was that instead of nesting classes, in my case, it's better to use namespaces to separate items into categories. Thanks a million.
I recommend using ScriptableObjects to create your items, armor, and weapons. You'll have to spend an hour or two learning them, but I think you'll be much happier with your design if you go that route.
Think of a ScriptableObject as a set of properties (item name, cost, attack power, defense power, etc.). For each item you have in your game, you create an instance of a ScriptableObject. Those ScriptableObject instances then become assets in your Unity project, just like a prefab or a sprite. That means you can drag them around in your project, and assign them to the fields on your MonoBehaviours. That'll result in you being able to assign equipment to a character by dragging it from your Project view into the Inspector.
Here's an example of how it'll look
Item.cs
public class Item : ScriptableObject
{
public string name;
public int cost;
public Sprite image;
}
Equipment.cs
public class Equipment : Item
{
public Slots slot;
}
public enum Slots
{
Body,
DoubleHanded,
Hands,
Head,
Feet,
Legs,
LeftHand,
RightHand
}
Weapon.cs
// CreateAssetMenu is what lets you create an instance of a Weapon in your Project
// view. Make a folder for your weapons, then right click inside that folder (in the
// Unity project view) and there should be a menu option for Equipment -> Create Weapon
[CreateAssetMenu(menuName = "Equipment/Create Weapon")]
public class Weapon : Equipment
{
public int attackPower;
public int attackSpeed;
public WeaponTypes weaponType;
}
public enum WeaponTypes
{
Axe,
Bow,
Sword
}
Armor.cs
[CreateAssetMenu(menuName = "Equipment/Create Armor")]
public class Armor : Equipment
{
public int defensePower;
}
Now create a bunch of weapons and armor in your project.
One thing that makes ScriptableObjects nice is you can edit them in your Inspector, rather than having to do it through code (although you can do that too).
Now on your "character" MonoBehaviour, add some properties for that character's equipment.
public class Character : MonoBehaviour
{
public Armor bodyArmor;
public Armor headArmor;
public Weapon weapon;
}
Now you can assign your weapons and armor to your character in the Inspector
You'll probably want something more customized to your needs than my example, but those are the basics. I recommend spending some time looking at ScriptableObjects. Read the Unity docs I linked earlier, or watch some videos on YouTube.
One of Unity's strengths is that it lets you do a lot of design and configuration through the editor rather than through code, and ScriptableObjects reinforce that.
The need :
To have the possibility to interact with Component instances of different type attached on GameObject instances through an interface.
For exemple, if I have a game with soldiers, and assuming that medics and snipers both are soldiers, I want to be able to get the Soldier component attached to a soldier GameObject, regardless of whether that soldier is actually a Sniper or a Medic. Then, I could do something as follows : soldier.GetComponent<Soldier>().Respawn(); which would end up calling either Medic.Respawn() or Sniper.Respawn(), depending on the actual type of the soldier.
Possible but dirty solution 1 :
A first naive approach would be to have the Sniper and Medic components implement a Soldier interface. However, this causes several problems.
For example, if you want to check whether a GameObject has a component implementing Soldier, you can't, because Soldier is only an interface, not an actual Unity Component. Thus, calling GetComponent<Soldier>() on a GameObject having, for exemple, a Medic component would not return that Medic component, even if Medic does implement Soldier.
(Actually you could check this by iterating over all the components and using the is operator, but that would be dirty and slow).
Possible but dirty solution 2 :
A second approach is to create a base Component class Soldier from which the Medic and Sniper classes would inherit.
But this also poses several problems.
First, the Unity events (Awake(), Start(), etc) are only going to be called on the leaf classes of the hierachy, forcing you to manually call the same functions on the parent class. Anyone who has tried that knows that it's easy to forget calling something, which results in improperly initialized objects, for example.
And second, the usual problems of inheritance are here too. For exemple, if I want my Medic and Sniper componenents to not only be Soldier, but also be Explodable or VehicleDriver or whatever, I can't, because C# does not support multiple inheritance.
The approach I'm thinking about :
I've thought about a way to design my code so that the issues listed above are solved.
The idea is to have a Component class that acts as the interface and have that interface component coexist with the acutal component on the same GameObject. In other words, let two game objects. One of them would have both a Soldier and a Medic component and the other one would have both a Soldier and a Sniper component. All three component classes, i.e Soldier, Medic and Sniper would be completely separate and all inherit from MonoBehaviour.
The other parts of the code would only interact with the Soldier component. In this case you would be able to do : soldier.GetComponent<Soldier>().Respawn();.
Then, it would be the resposibility of the "interface" component (i.e Soldier) to use the actual component (i.e Medic or Sniper) in order to perform the specific action.
However, since Soldier does not known anything about Medic, Sniper or whatever implementation might be added in the future, the Soldier component exposes an actual interface that the Medic and Soldier have to implement.
Since it is possible to implement multiple interfaces, using this solution, it would be possible to use more than one "interface" component. For exemple, a soldier game object could have the following "interface" components : Soldier and Explodable, and the following "actual" componenent : Medic which would implement both interfaces Soldier.ISolder and Explodable.IExplodable.
What do you think about this solution ? Thx !
EDIT :
I coded what I had in mind and it seems to work nicely. I've also created an editor script allowing to have the "interface" component reference the "actual" component without having public fields, but properties instead. I'll post the code, just in case someone wants it :
WaterComponent.cs - The "interface" component for water objects :
using System;
using UnityEngine;
public class WaterComponent : MonoBehaviour
{
#region Interface
public interface IWater
{
bool IsPointSubmerged(Vector3 worldPoint);
Vector3 GetNormalAtPoint(Vector3 worldPoint);
}
#endregion Interface
#region Properties
public IWater Water
{
get
{
return waterImplementation;
}
set
{
Component asComponent = value as Component;
if (null != value && null == waterComponent)
{
throw new ArgumentException($"The given {typeof(IWater).Name} is not a {typeof(Component).Name}.");
}
waterComponent = asComponent;
waterImplementation = value;
}
}
#endregion Properties
#region Fields
[SerializeField]
private Component waterComponent;
private IWater waterImplementation;
#endregion Fields
#region Public methods
public bool IsPointSubmerged(Vector3 worldPoint)
{
return waterImplementation.IsPointSubmerged(worldPoint);
}
public Vector3 GetNormalAtPoint(Vector3 worldPoint)
{
return waterImplementation.GetNormalAtPoint(worldPoint);
}
#endregion Public methods
#region Unity events
private void Awake()
{
waterImplementation = waterComponent as IWater;
}
#endregion Unity events
}
RealWater.cs - The "actual" component implementing the "interface" component :
using UnityEngine;
public class RealWater : MonoBehaviour, WaterComponent.IWater
{
#region WaterComponent.IWater implementation
public bool IsPointSubmerged(Vector3 worldPoint)
{
return SpecificIsPointSubmerged(worldPoint);
}
public Vector3 GetNormalAtPoint(Vector3 worldPoint)
{
return SpecificGetWaterAtPoint(worldPoint);
}
#endregion WaterComponent.IWater implementation
#region Non-public methods
private bool SpecificIsPointSubmerged(Vector3 worldPoint)
{
return true;
}
private Vector3 SpecificGetWaterAtPoint(Vector3 worldPoint)
{
return transform.up;
}
#endregion Non-public methods
}
WaterComponentEditor.cs - The custom editor allowing not to have naked fields exposed :
using UnityEditor;
[CustomEditor(typeof(WaterComponent))]
[CanEditMultipleObjects]
public class WaterComponentEditor : Editor
{
#region Serialized properties
private SerializedProperty waterProperty;
#endregion Serialized properties
#region Overridden methods
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(waterProperty);
if (EditorGUI.EndChangeCheck())
{
((WaterComponent) target).Water = waterProperty.exposedReferenceValue as WaterComponent.IWater;
}
serializedObject.ApplyModifiedProperties();
}
#endregion Overridden methods
#region Unity events
private void OnEnable()
{
waterProperty = serializedObject.FindProperty("waterComponent");
}
#endregion Unity events
}
Feel free to reuse, unless you see a flaw with this, in which case I'd would really like to know about it !!
EDIT : Well the problem with that custom editor is that you can have the "interface" component reference any Component even if the latter does not implement the real interface exposed by the "interface" component. It is still possible to do some run time checks in the custom editor script, but that's not so clean. However I think the advantages remain good enough in comparison to that issue.
Well...
GetComponent family of functions now supports interfaces as generic argument.
Unity 5.0 release notes : https://unity3d.com/fr/unity/whats-new/unity-5.0
Whatever...
So basically I want to end with several ingame objects, each having few .cs scripts with monobehaviour. (different set of scripts on each object)
party of characters with their skills
Those scripts on each character can vary over time
characters learning new skills/abandoning old
So when the game starts, I want to attach the scripts to objects dynamically at runtime
based on player decision in skill tree
Is there any way how can I do this without using reflection?
EDIT: It seems I found solution how to make it work without reflection
public class TestSkill : MonoBehaviour {}
public class TestFireball : TestSkill {}
public class TestMeleeAttack : TestSkill {}
public class TestSkillBook : MonoBehaviour {
public MonoScript script;
void Start () {
System.Type t = script.GetClass();
TestSkill skill = gameObject.AddComponent(t) as TestSkill;
}
}
I want to attach the scripts to objects dynamically at run-time.
he AddComponent function is used to attach scripts to GameObjects.
Your Object
public GameObject yourgameObject;
Attach script to it:
yourgameObject.AddComponent<YourScript>();
EDIT:
The problem is, I dont know if it will be MyScript1 or MyScript2. I
dont want it to be hardcoded, but modifiable via editor/ingame UI.
I think that you are looking for AddComponent that can take string as param.
There used to be one like this:
public Component AddComponent(string className);
but it was deprecated years ago. I made a new one called AddComponentExt as extension method last year and you can get it here. It can be used like this:
yourgameObject.AddComponentExt<"YourScript">();
You can add script even if it doesn't exist yet. You will get run-time error instead of compile-time error in that case.
Is there any way how can I do this without using reflection?
No, you can't do this without reflection since it doesn't exist yet. That's what reflection is used for.
Since this doesn't fit into the comments of Programmers answer, some example and to clarify on what you can/must do:
// The component to add
public class B : MonoBehaviour
{
public void TestCall()
{
Debug.Log("Success");
}
}
public class A : MonoBehaviour
{
public string ToAddName = "B";
private void Start()
{
System.Type t = System.Type.GetType(ToAddName);
AddComponent(t); // This does successfully add the component (assuming it exists)
Debug.Log(t.GetType()); // This will give out "System.MonoType"
// This doesn't work since it is not known that this is actually "B", not "MonoScript"
GetComponent(t).TestCall();
// What is possible is this, requires hardcoding the method name though:
System.Reflection.MethodInfo mI = t.GetMethod("TestCall");
var c = GetComponent(t);
mI.Invoke(c, null); // null because "TestCall" doesn't take params
}
}
This is not meant to be an actual solution, I'd rather say that there is probably a (better) way to set up your whole construct so that you don't have this problem at all.
Edit 1:If anyone has a better Title for that, feel free to tell me or edit it yourself.
Edit 2: Thanks for your contribution guys, the answer given is almost what I need with some tweaks and I'm thankful for the small stuff here. Really learnt much today!
Little stuff here that I'm banging my head on for a while now.
I want to create a Slideshow and want the logic in the Image objects itself.
The programm should be able to set a desired transition or just a random one so
I wanted to create a Transition Superclass with the general stuff and spezialize
it in the subclasses. So I have Transitions.cs (with no Code currently inside it)
and no derived class. I want it to be in the way of adding a single .cs file
extending Transitions.cs and not change any other code to implement a new Transition.
The Code I currently have looks something like this but I guess my
description is more helpful than the code
public class SlideImages : MonoBehaviour {
Image image;
Image nextImage;
int tracker;
private void Transition(int ID)
{
/*Something to choose a transition based on the ID
*Transitions.cs is the superclass of all different transitions
*f.e. Ken-Burns Effect, or scattered Transition which all extend from it
*/
}
~SlideImages()
{
//TODO: Pop and Push
}
}
I had the idea of something along the lines of static stuff to workaround that
looks like this but it doesn't work I suppose
public class Transitions : MonoBehaviour {
public static int TransitionID;
protected static int SubclassCount;
protected static void SetID()
{
TransitionID = Transitions.SubclassCount;
Transitions.SubclassCount++;
}
}
I did look into the state design pattern but I don't want to implement it as I just need the state to be chosen once and shortlived. The Image Objects themself only have a lifetime of around a few seconds. I don't want to do the usual if-nesting or just put all the code inside the SlideImages.cs. Is there any good guidance to it or stuff that goes very indepth into inheritance and such stuff?
Appreciate all the input.
There are two straight-forward solutions to what you want to do. Your basic problem is that you want to be able to dynamically add functionality to your program but you don't want to have knowledge of what has been added in order to use it. The most hack-ish way of doing it is to use Actions instead of subclassing. When you want to add another transition you just update an actions list like this:
public static class Transitions
{
private static Action[] TransitionStrategies = new Action[]
{
() => { /* One way of performing a transition */ },
() => { /* Another way of performing a transition */ },
() => { /* Keep adding methods and calling them here for each transition type */ }
}
public static void PerformTransition(int? transitionIndex = null)
{
int effectiveIndex;
// if the transition index is null, use a random one
if (transitionIndex == null)
{
effectiveIndex = new Random().Next(0, TransitionStrategies.Length);
}
else
{
effectiveIndex = transitionIndex.Value;
}
// perform the transition
TransitionStrategies[effectiveIndex]();
}
}
The above approach is simple but all of the logic (or at least references to the logic depending on where you implement the actual work for the transitions) is in one place. It also has the potential to get quite messy depending on how many transitions you are adding and how many developers are touching this codebase. It also requires all functionality to be added by someone with access to the full codebase and requires a recompilation each time new transitions are added.
A more complex but much more maintainable and flexible approach in the long term is to use modules (or plugins for our purposes). Each transition in that approach is provided by either a shared module or a specific module and is a subclass of a base AbstractTransition class or an implementation of an ITransition interface depending on how you want to go about doing that. Use post-build tasks to place all of your module dlls in a single directory accessible to your main program (anyone else given permission can put transition module dlls there too). When your program launches, it dynamically loads all dlls from that directory (no recompilation required when new transitions are added as long as the right dlls are in that directory) and pulls out all of the classes implementing that interface. Each of those interface implementations are instantiated and placed into a data structure after which you can use a similar strategy to the above's PerformTransition method to perform a random one or one based on an ID instead of an index. I can edit this question with an example of that structure if you would like.
Edit: You didn't ask for it yet, but here's an example with plugins/modules.
First, create a project to load and run the transitions. This example will use a project called ModuleDemo. Give it a main method like this:
static void Main(string[] args)
{
// create a list to hold the transitions we load
List<AbstractTransition> transitions = new List<AbstractTransition>();
// load each module we find in the modules directory
foreach (string dllFilepath in Directory.EnumerateFiles("Modules", "*.dll"))
// this should really read from the app config to get the module directory
{
Assembly dllAssembly = Assembly.LoadFrom(dllFilepath);
transitions.AddRange(dllAssembly.GetTypes()
.Where(type => typeof(AbstractTransition).IsAssignableFrom(type))
.Select(type => (AbstractTransition) Activator.CreateInstance(type)));
}
// show what's been loaded
foreach (AbstractTransition transition in transitions)
{
Console.WriteLine("Loaded transition with id {0}", transition.TransitionId);
// execute just to show how it's done
transition.PerformTransition();
}
Console.Read(); // pause
}
You'll notice that the method references an AbstractTransition class. Let's create a separate TransitionModule project for that now. This is the project that the modules will reference:
namespace TransitionModule
{
public abstract class AbstractTransition
{
public readonly int TransitionId;
public abstract void PerformTransition();
protected AbstractTransition(int transitionId)
{
TransitionId = transitionId;
}
// you can add functionality here as you see fit
}
}
Now that we have an abstract transition class for the plugins to implement and a functioning plugin loader, we can go ahead and create a few transition plugins.
I created a Modules folder for this in my solution but it doesn't really matter.
First module in a FlipTransition project:
using System;
using TransitionModule;
namespace FlipTransition
{
public class FlipTransition : AbstractTransition
{
public FlipTransition() : base(2)
{
}
public override void PerformTransition()
{
Console.WriteLine("Performing flip transition");
}
}
}
Second module in a SlideTransition project:
using System;
using TransitionModule;
namespace SlideTransition
{
public class SlideTransition : AbstractTransition
{
public SlideTransition() : base(1)
{
}
public override void PerformTransition()
{
Console.WriteLine("Performing slide transition");
}
}
}
Note that each of those projects needs to reference the TransitionModule project but the main project doesn't need to know about any of the other projects.
Now we have 2 transition plugins and a plugin loader. Since the plugin loader is going to load the modules from a Modules directory, go to the /bin/Debug directory of the main project and make a Modules directory. Copy all of the dlls from the /bin/Debug directories of the transition plugin projects into that directory as well. All of this can be automated with post-build tasks later on.
Go ahead and run the program. You should get output like this:
Loaded transition with id 2
Performing flip transition
Loaded transition with id 1
Performing slide transition
There's a lot you can do to make this more elegant, but this is at least a simple example of how you can use a plugin-based architecture to provide what you need.
You could try playing around with abstract classes, create a base abstract class for image transitioning;
public abstract class ImageTransition
{
protected int imageId { get; set; }
public Dictionary<int, Image> ImageDictionary { get; set; }
protected abstract void TransitionToNextImageId();
public Image GetNextImage()
{
TransitionToNextImageId();
return ImageDictionary[imageId];
}
}
You then create new Transition types that inherit from this base class and have their own implementation of the TransitionToNextImageId method;
public class InTurnImageTransition : ImageTransition
{
protected override void TransitionToNextImageId()
{
if(this.imageId < ImageDictionary.Count)
this.imageId ++;
}
}
public class RandomImageTransition : ImageTransition
{
protected override void TransitionToNextImageId()
{
imageId = new Random().Next(0, ImageDictionary.Count);
}
}
This allows you to build up some custom transitions however you wish.
-Edit-
You would of course fill the dictionary ImageDictionary before calling the GetNextImage method.