I have two interfaces, IDrawable and IUpdateable. Some concrete drawables are updateable, but not all. A sprite with animations, for example, implements both IDrawable and IUpdateable but a static image just implements IDrawable.
The problem is obvious... how could I possibly update animations with just an IDrawable reference? If I pass a Sprite to a Button to use as it's graphic representation, the Button just remains static because it can't advance it's sprite animations.
Ideas?
A solution is to define a new abstraction:
public interface IXXXObject {
IDrawable Drawable { get; } // return null if not support IDrawable
IUpdatable Updatable { get; } // return null if not support IUpdatable
}
All your objects should implement this interface.
Now you should pass objects as IXXXObject and you can call a feature if it is supporting.
Related
I'm working on a 2d Topdown game for my senior project, and I'm coming across a bit of a design issue. Currently, the plan is for the player to be able to hold 1 weapon for each of three classes (melee, ranged, magic). They can pick these items up on the map, and it will be added to their inventory. These items carry 2 attacks each, and whichever weapon is active in their inventory gives the player their two attacks they can use.
Here's the base class for the Item ScriptableObject:
public class BaseItemSO : ScriptableObject
{
[SerializeField] private string itemName;
[SerializeField] private string description;
[SerializeField] GameObject model;
[SerializeField] Sprite inventoryIcon;
public string getName() { return itemName; }
public string getDescription() { return description; }
public GameObject getModel() { return model; }
public Sprite getIcon() { return inventoryIcon; }
}
The GameModel holds the item's overworld sprite, its collider, the usual stuff.
Then we have the WeaponItemSO. This inherits from Item, and it meant for the weapons that the player can pick up. It has a weapon class (same melee, ranged, magic) and I'm attempting to give it two references to some sort of Attack script.
{
[SerializeField] private WeaponClass weaponClass;
[SerializeField] private BaseAttackController attack1;
[SerializeField] private BaseAttackController attack2;
public enum WeaponClass
{
Melee,
Ranged,
Magic
}
}
The problem I'm running into is that each attack has a different script. I haven't implemented baseAttackController, but the plan was for each to have an OnAttack() and dealDamage() and whatever else is necessary, and these attacks could be read from the WeaponSO that the player holds. Unfortunately, Unity can't serialize these abstract classes or interfaces. What might be a pattern I should go about here?
I haven't implemented further yet, but my ideas have included simply putting those attack controllers on the GameModel that is referenced. Then the player could read that, but it certainly doesn't feel like the right approach, the attack class should be able to exist with or without a player or gameobject.
The code to play animation on trigger button does not seem to work. I saw a video on Youtube and with a simple animation.Play(); it worked on that video but yet, I couldn't get it to work on my computer. What did I do wrong or did unity change it? please help I cant find solution on the net. All the "solution not working either".
This is the error I got:
Type UnityEngine.Component does not contain a definition for play
and no extension method Play of type UnityEngine.component could
be found
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class animationtrigger : MonoBehaviour {
void Start()
{
}
int speed = 10;
// Update is called once per frame
void Update () {
if (Input.GetKeyDown("N"))
{
animation.Play("Cube|moving side");
//transform.Translate(1 * Vector3.forward * Time.deltaTime * speed);
//Animator anim = GetComponent<Animator>();
//if (null != anim)
// {
// anim.Play("Cube|moving side");
// }
}
}
}
what did i do wrong or did unity change it?
Unity changed. I've seen similar questions for the past few weeks. Although I don't think they are duplicates but it would make sense to answer all these here for future questions.
The animation variable is defined under Component as a public variable which MonoBehaviour inherits from. Your code then inherits from MonoBehaviour and you have access to animation.
These are the complete list of variables from the Component class that are deprecated:
public Component animation { get; }
public Component audio { get; }
public Component camera { get; }
public Component collider { get; }
public Component collider2D { get; }
public Component constantForce { get; }
public Component guiElement { get; }
public Component guiText { get; }
public Component guiTexture { get; }
public Component hingeJoint { get; }
public Component light { get; }
public Component networkView { get; }
public Component particleEmitter { get;
public Component particleSystem { get; }
public Component renderer { get; }
public Component rigidbody { get; }
public Component rigidbody2D { get; }
New way to access component that attached to the-same script:
Use GetComponent<ComponentName>(). Capitalize the first letter of that variable to make it its component class. One exception is audio which becomes
AudioSource instead of Audio.
1.animation.Play("Cube|moving side"); becomes GetComponent<Animation>().Play("Cube|moving side");
2.rigidbody2D.velocity becomes GetComponent<Rigidbody2D>().velocity
3.rigidbody.velocity becomes GetComponent<Rigidbody>().velocity
4.renderer.material becomes GetComponent<Renderer>().material
5.particleSystem.Play() becomes GetComponent<ParticleSystem>().Play()
6.collider2D.bounds becomes GetComponent<Collider2D>().bounds
7.collider.bounds becomes GetComponent<Collider>().bounds
8.audio.Play("shoot"); becomes GetComponent<AudioSource>().Play("shoot");
9.camera.depth becomes GetComponent<Camera>().depth
I don't have to put all of them because the example below should guide anyone do this. These are the most asked and asked components on SO.
Cache Component:
You can cache component so that you don't have be GetComponent everytime.
Animation myAnimation;
void Start()
{
//Cache animation component
myAnimation = GetComponent<Animation>();
}
void Update()
{
if(somethingHappens)
{
//No GetComponent call required again
myAnimation.Play("Cube|moving side");
}
}
If you're using an animator and you want to play a specific state, then you can do:
animator.Play("stateName");
I see that in the commented code below. That should be working. If nothing is happening then make sure that the "Cube|moving side" is a STATE name and not the ANIMATION name. Also make sure that the state actually contains an animation, and that the animation actually has frame data.
(Below, I have 2 states (Move and Idle). The state name is "Move" while the animation name is "test".)
If I wanted to switch to the move state, I would call animator.Play("Move").
Also I wouldn't recommend explicitally playing states. If you can manage it's generally better to make a state tree and trigger animations using parameters. You see in the image above how I have two states and transitions between them. You can assign parameters that can make my animator switch from one to another.
Here I use the "Example Anim Trigger" to make the animator switch from the Idle to Move state. I could do this in code with:
animator.SetTrigger("ExampleAnimTrigger");
This is the more modern way of using Unity animations, so I'd highly recommend picking this up.
Here's the documentation!
I'm working on applying the MVVM pattern (and learning it in the process) for a Windows Store application.
Right now I am leaning towards having a 1:1 correspondence between View and ViewModel, where multiple ViewModels have a dependency on the same underlying Model.
For example, suppose I have an entity "Student". I have two ways to view the student: in a full-screen details page or as a list of students in a classroom. That results in the following View/ViewModel pairs:
StudentDetailsView/StudentDetailsViewModel
StudentListItemView/StudentListItemViewModel
At the moment I'm assuming my ViewModel will directly expose the Model, and my Xaml will bind to ViewModel.Model.property-name (I realize that's debatable).
Suppose I can perform some action on the Student from either View (e.g., "Graduate"). I want to have the Graduate behavior in my Model (to avoid an Anemic Domain Model), and I want to avoid duplicating behavior between ViewModels that depend on the same Model.
My intent is to have an ICommand (e.g., a RelayCommand) that I can bind a Graduate button to in the View. Here's my question:
Is there any reason not to make the ICommand a property of the Model class?
Basically that would mean something like the following (ignoring the need for a Repository):
public class Student {
public ICommand GraduateCommand { get { ... } }
void Graduate() { ... }
}
That way both StudentDetailsView and StudentListItemsView could have Xaml that binds to that command (where DataContext is StudentViewModel and Model is the public property):
<Button Command="{Binding Model.GraduateCommand}" />
Obviously I could just make Student::Graduate() public, create duplicate GraduateCommands on the two ViewModels, and have the execution delegate call Model.Graduate(). But what would be the disadvantage of exposing the behavior of the class via an ICommand rather than a method?
First of all, in many cases, it is perfectly fine to bind directly from the View to the Model, if you can implement INotifyPropertyChanged on the Model. It would still be MVVM. This prevents the ViewModel to be cluttered with a lot of "relay-directly-to-Model" code. You only include in the VM what can't be directly used by the View (need to wrap/denormalize/transform data, or Model properties don't implement INPC, or you need another validation layer...).
That said, Commands are a primary mean of communication between the View and the ViewModel.
There may be many receivers for the command (possibly on different ViewModels).
The Execute/CanExecute pattern often doesn't fit outside of the context of the VM.
Even if the real stuff is done in a method of the Model, Commands may have some logic other than just delegating to the model (validation, interaction with other VM properties/methods...).
When it comes to test your VMs, you can't stub the commands' behavior if they're outside of the VM.
For these reasons, Commands do not belong to the Model.
If you're concerned by code duplication across VMs, you can create a StudentViewModel from which both StudentDetailsViewModel and StudentListItemViewModel will inherit. StudentViewModel will define the Command and its common behavior.
If you use a model's property in your view, then you should stop calling that MVVM. You can move graduate command implementation into another class (let's say a Helper class) an share it between your ViewModels (during the initialisation).
GraduateCommand=new RelayCommand (GraduateHelper.Graduate, CanGraduate);
Wrong: put Graduate() into your entity.
EDIT
Extension methods for INotifyPropertyChanged
public static class NotifyExtension
{
public static void OnPropertyChanged(this INotifyPropertyChanged source, PropertyChangedEventHandler h, string propertyName)
{
PropertyChangedEventHandler handler = h;
if (handler != null) handler(source, new PropertyChangedEventArgs(propertyName));
}
public static bool SetProperty<T>(this INotifyPropertyChanged source,PropertyChangedEventHandler handler, ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
source.OnPropertyChanged(handler, propertyName);
return true;
}
}
And then :
public class Student:INotifyPropertyChanged
{
private string _name = "Name";
public string Name
{
get { return _name; }
set {
this.SetProperty<string>(PropertyChanged, ref _name, value, "Name"); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class MyViewModel :INotifyPropertyChanged
{
private Student _student=new Student();
public Student Student
{
get { return _student; }
set
{
this.SetProperty<Student>(PropertyChanged, ref _student, value, "Student");
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Finally Xaml:
<TextBlock Text="{Binding Path=Student.Name}"></TextBlock>
As much as it makes sense I'm trying to use interfaces when working with scripts, something like this:
public interface IConsumable
{
Sprite Icon { get; set; }
}
However when using this approach, any classes implementing the interface do not show these properties in the inspector and I end up with something like this:
public class TestConsumable : MonoBehaviour, IConsumable
{
public Sprite Icon { get { return IconSprite; } set { IconSprite = value; } }
// Hack just to show up in Editor
public Sprite IconSprite;
}
This doesn't really make sense to me and I was hoping there was a better solution.
Side-note, I'm not using getters / setters exclusively for Interfaces but also for some validation etc.
Thanks!
By default properties with get/set will not show up in the editor. You need to mark them with the "[SerializeField]" attribute in order to use them in the editor. The reason is that unity will only display types that it can save.
public class TestConsumable : MonoBehaviour, IConsumable
{
[SerializeField]
private Sprite icon;
public Sprite Icon { get { return icon; } set { icon = value; } }
}
I have already searched some tutorials and even looked pluralsite Introduction to PRISM. However, most examples based on using unity containers and the some lack of information on how to implement this feature with Mef container.
My simple helloworld module is based on web tutorial. My code is the same except I’m stuck only on HelloModule and using Mef, not Unity as tutorial shows:
The main my problem how to initialize my view with my view model. The only working way I have found via experimenting is to initialize view-model in View constructor:
HelloView.xaml.cs
namespace Hello.View
{
[Export]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
Model = new HelloViewModel(this);
}
public IHelloViewModel Model
{
//get { return DataContext as IHelloViewModel; }
get { return (IHelloViewModel)DataContext; }
set { DataContext = value; }
}
}
}
And standard module initialization code:
[ModuleExport(typeof(HelloModule), InitializationMode=InitializationMode.WhenAvailable)]
public class HelloModule : IModule
{
IRegionManager _regionManager;
[ImportingConstructor]
public HelloModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
_regionManager.Regions[RegionNames.ContentRegion].Add(ServiceLocator.Current.GetInstance<HelloView>());
}
}
However, can someone tell the correct way how to this things, I this it must be done in Module initialization section.
MatthiasG shows the way to define modules in MEF. Note that the view itself does not implement IModule. However, the interesting part of using MEF with PRISM is how to import the modules into your UI at startup.
I can only explain the system in principle here, but it might point you in the right direction. There are always numerous approaches to everything, but this is what I understood to be best practice and what I have made very good experiences with:
Bootstrapping
As with Prism and Unity, it all starts with the Bootstrapper, which is derived from MefBootstrapper in Microsoft.Practices.Prism.MefExtensions. The bootstrapper sets up the MEF container and thus imports all types, including services, views, ViewModels and models.
Exporting Views (modules)
This is the part MatthiasG is referring to. My practice is the following structure for the GUI modules:
The model exports itself as its concrete type (can be an interface too, see MatthiasG), using [Export(typeof(MyModel)] attribute. Mark with [PartCreationPolicy(CreationPolicy.Shared)] to indicate, that only one instance is created (singleton behavior).
The ViewModel exports itself as its concrete type just like the model and imports the Model via constructor injection:
[ImportingConstructor]
public class MyViewModel(MyModel model)
{
_model = model;
}
The View imports the ViewModel via constructor injection, the same way the ViewModel imports the Model
And now, this is important: The View exports itself with a specific attribute, which is derived from the 'standard' [Export] attribute. Here is an example:
[ViewExport(RegionName = RegionNames.DataStorageRegion)]
public partial class DataStorageView
{
[ImportingConstructor]
public DataStorageView(DataStorageViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
The [ViewExport] attribute
The [ViewExport] attribute does two things: Because it derives from [Export] attribute, it tells the MEF container to import the View. As what? This is hidden in it's defintion: The constructor signature looks like this:
public ViewExportAttribute() : base(typeof(UserControl)) {}
By calling the constructor of [Export] with type of UserControl, every view gets registered as UserControl in the MEF container.
Secondly, it defines a property RegionName which will later be used to decide in which Region of your Shell UI the view should be plugged. The RegionName property is the only member of the interface IViewRegionRegistration. The attribute class:
/// <summary>
/// Marks a UserControl for exporting it to a region with a specified name
/// </summary>
[Export(typeof(IViewRegionRegistration))]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[MetadataAttribute]
public sealed class ViewExportAttribute : ExportAttribute, IViewRegionRegistration
{
public ViewExportAttribute() : base(typeof(UserControl)) {}
/// <summary>
/// Name of the region to export the View to
/// </summary>
public string RegionName { get; set; }
}
Importing the Views
Now, the last crucial part of the system is a behavior, which you attach to the regions of your shell: AutoPopulateExportedViews behavior. This imports all of your module from the MEF container with this line:
[ImportMany]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
This imports all types registered as UserControl from the container, if they have a metadata attribute, which implements IViewRegionRegistration. Because your [ViewExport] attribute does, this means that you import every type marked with [ViewExport(...)].
The last step is to plug the Views into the regions, which the bahvior does in it's OnAttach() property:
/// <summary>
/// A behavior to add Views to specified regions, if the View has been exported (MEF) and provides metadata
/// of the type IViewRegionRegistration.
/// </summary>
[Export(typeof(AutoPopulateExportedViewsBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class AutoPopulateExportedViewsBehavior : RegionBehavior, IPartImportsSatisfiedNotification
{
protected override void OnAttach()
{
AddRegisteredViews();
}
public void OnImportsSatisfied()
{
AddRegisteredViews();
}
/// <summary>
/// Add View to region if requirements are met
/// </summary>
private void AddRegisteredViews()
{
if (Region == null) return;
foreach (var view in _registeredViews
.Where(v => v.Metadata.RegionName == Region.Name)
.Select(v => v.Value)
.Where(v => !Region.Views.Contains(v)))
Region.Add(view);
}
[ImportMany()]
private Lazy<UserControl, IViewRegionRegistration>[] _registeredViews;
}
Notice .Where(v => v.Metadata.RegionName == Region.Name). This uses the RegionName property of the attribute to get only those Views that are exported for the specific region, you are attaching the behavior to.
The behavior gets attached to the regions of your shell in the bootstrapper:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
ViewModelInjectionBehavior.RegionsToAttachTo.Add(RegionNames.ElementViewRegion);
var behaviorFactory = base.ConfigureDefaultRegionBehaviors();
behaviorFactory.AddIfMissing("AutoPopulateExportedViewsBehavior", typeof(AutoPopulateExportedViewsBehavior));
}
We've come full circle, I hope, this gets you an idea of how the things fall into place with MEF and PRISM.
And, if you're still not bored: This is perfect:
Mike Taulty's screencast
The way you implemented HelloView means that the View has to know the exact implementation of IHelloViewModel which is in some scenarios fine, but means that you wouldn't need this interface.
For the examples I provide I'm using property injection, but constructor injection would also be fine.
If you want to use the interface you can implement it like this:
[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public IHelloViewModel Model
{
get { return DataContext as IHelloViewModel; }
set { DataContext = value; }
}
}
[Export(typeof(IHelloViewModel))]
public class HelloViewModel : IHelloViewModel
{
}
Otherwise it would look like this:
[Export(typeof(IHelloView)]
public partial class HelloView : UserControl, IHelloView
{
public HelloView()
{
InitializeComponent();
}
[Import]
public HelloViewModel Model
{
get { return DataContext as HelloViewModel; }
set { DataContext = value; }
}
}
[Export]
public class HelloViewModel
{
}
One more thing: If you don't want to change your Views or provide several implementations of them, you don't need an interface for them.