playMaker

Author Topic: Get info from plugin to a global variable  (Read 4293 times)

zombie_farm

  • Playmaker Newbie
  • *
  • Posts: 39
    • View Profile
Get info from plugin to a global variable
« on: February 05, 2014, 02:24:41 PM »
Hi , So i have a plugin called "media player" and Im trying to get the current time into a global variable called "MovieTime"

I don't know any code but I do have the API guide it says to use

public int GetSeeklPosition() // //return miliseconds
 
Can someone point me in the right direction to track this down .
Im also learning c++ but its slow going.

I attached an images that explains it.

sorry its gigantic ...



jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14819
  • Official Playmaker Support
    • View Profile
Re: Get info from plugin to a global variable
« Reply #1 on: February 06, 2014, 01:01:42 AM »
Hi

 you are not far. What you need is declare a global FsmFloat in your custom actiona dn feed the result of your call in there.

 typically, you should see how other actions are doing this, for example: RandomFloat.cs


Open that file and see how float can be stored in a fsmFloat.

bye,

 Jean

zombie_farm

  • Playmaker Newbie
  • *
  • Posts: 39
    • View Profile
Re: Get info from plugin to a global variable
« Reply #2 on: February 15, 2014, 03:16:19 PM »
Ok I have been attempting to hack at this. I understand for someone who understands scripting this is totally simple.. at least I think it is.

Would anyone out there be willing to take this script on as a paid job? The amount of time I've spent it would be worth it to me. Just email me at samgebhardt at gmail dot     com

thanks

zombie_farm

  • Playmaker Newbie
  • *
  • Posts: 39
    • View Profile
Re: Get info from plugin to a global variable
« Reply #3 on: February 18, 2014, 11:28:42 AM »
ok I think I'm getting closer. But its telling me " An object reference is required to access non-static member 'MediaPlayerCtrl.GetSeekPostion()'.. do I need to tell it what object the movie is playing on?


using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
   [ActionCategory("MediaPlayerCtrl")]
   [Tooltip("Time MediaPlayerCtrl Instance")]
   public class MediaPlayerCtrlGetSeeklPosition : FsmStateAction
   
   {

      [RequiredField]
      [UIHint(UIHint.Variable)]
      public FsmFloat storeResult;

      public override void OnEnter()
      
      {

         MediaPlayerCtrl.GetSeekPosition();

         storeResult.Value = MediaPlayerCtrl.GetSeekPosition();



         Finish();
      }
   }
}

jeanfabre

  • Administrator
  • Hero Member
  • *****
  • Posts: 14819
  • Official Playmaker Support
    • View Profile
Re: Get info from plugin to a global variable
« Reply #4 on: February 20, 2014, 09:36:51 PM »
Hi,

 Can you reference the asset plugin you are using? is it the prim31 one? It does not seem to have this getSeekPosition()

 try MediaPlayerCtrl.instance.GetSeekPosition();

bye,

 Jean

kiriri

  • Hero Member
  • *****
  • Posts: 506
    • View Profile
Re: Get info from plugin to a global variable
« Reply #5 on: February 24, 2014, 06:49:57 AM »
Hi,
I've been working for zombie_farm already, so I took this small contract and he kindly agreed to my putting the answer on the forums as well. I didn't know how much he knew about programming, so I started pretty much at square one, but I guess it may still be useful for some people :)

So what I'm doing is I start off of the RandomFloat.cs script. I create a new file, name it GetSeekIPosition and paste the entire code from RandomFloat inside. Then I change the name of the class (look for "public class XXX" in the script) from RandomFloat to GetSeekIPosition. Make sure that it is the same as the filename. Now it should look like this :

Code: [Select]
// (c) Copyright HutongGames, LLC 2010-2013. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Math)]
[Tooltip("Sets a Float Variable to a random value between Min/Max.")]
public class GetSeekIPosition : FsmStateAction
{
[RequiredField]
public FsmFloat min;
[RequiredField]
public FsmFloat max;
[RequiredField]
[UIHint(UIHint.Variable)]
public FsmFloat storeResult;

public override void Reset()
{
min = 0f;
max = 1f;
storeResult = null;
}

public override void OnEnter()
{
storeResult.Value = Random.Range(min.Value, max.Value);

Finish();
}
}
}


Ok, so now let's make it do the stuff we want. You said the function in your plugin is defined as
Code: [Select]
public int GetSeeklPosition() // //return milisecondPublic means it can be used from any other script(as opposed to for example private, which means that only the script the function is in can actually use it) , int means it returns an int variable, and the empty brackets mean it does not need any input. What this means, is that we just need one single variable for the output, and that variable has to be an int variable. However, for playmaker to be able to offer the "plug-in-a-variable" feature you're used to, you need to use an FsmInt instead of and int variable.

So now I deleted the unnecessary variables and changed the Type of the storeResult variable from FsmFloat to FsmInt. I also remove the min and max variables from the Reset function.

Code: [Select]
// (c) Copyright HutongGames, LLC 2010-2013. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Math)]
[Tooltip("Sets a Float Variable to a random value between Min/Max.")]
public class GetSeekIPosition : FsmStateAction
{
[RequiredField]
[UIHint(UIHint.Variable)]
public FsmInt storeResult;

public override void Reset()
{
storeResult = null;
}

public override void OnEnter()
{
storeResult.Value = Random.Range(min.Value, max.Value);

Finish();
}
}
}

Now, when Playmaker goes through your states it calls the OnEnter function on the first action. Then it waits until that action sends the global event (that is not programming lingo :D ) "Finish". Then and only then it goes to the next action in the state and does the same thing again. So obviously we want our storeResult variable to get filled with a value when the action is called. So what we do is we first get the result of our function, which we do by calling it like this :
Code: [Select]
GetSeeklPosition();and since we want to save the int value this gets us, we say that our variable "storeResult" is equal to the result of the function. This looks like this:
Code: [Select]
storeResult = GetSeekIPosition();
Now, if you remember, the definition of the GetSeekIPosition function was
Code: [Select]
public int GetSeekIPosition();while our storeResult was defined as
Code: [Select]
public FsmInt storeResult;The difference between int and FsmInt is like apples and the apple bowl to Unity. However, our apple bowl has apple inside, which enables us to equal the apples in the bowl to the apples we get. In actual code our FsmInt has an int variable inside. This int variable is called Value. To use it, we can just type
storeResult.Value
And in turn, this allows us to rewrite our previous statement as:
Code: [Select]
storeResult.Value = GetSeekIPosition();
Awesome. We're almost there. But if you were to compile the script right now, there would be one error left. It would, in everyday terms say : "What do you mean by GetSeekIPosition? There is no function like that in your script!". And that is true. So what we need to do, is make Unity understand where the GetSeekIPosition is coming from.

In this case it's a tad tricky, because I don't know whether there's a "global" (called static) class that has a the component we need as a variable.
Let's create a new input variable called "EasyMovieTextureComponent" which is of the type of the class the component is:
Code: [Select]
public EasyMovieTexture EasyMovieTextureComponent;Adding this to the script will allow you to drag and drop your comonent from the Inspector into your playmaker action, provided that it is called EasyMovieTexture (which I doubt, but I couldn't find any documentation online).

This way we can use the same trick we used with the FsmInt to define where the GetSeekIPosition is. So if we type :
Code: [Select]
storeResult.Value = EasyMovieTextureComponent.GetSeekIPosition();Unity should be able to understand it.

Another thing you will often see in actions are null checks. This is a fancy term for checking, whether a variable has a value assigned to it. Imagine you were the UnityEngine, and you were told to use the EasyMovieTextureComponent variable to call GetSeekIPosition. Now, what would you do, if the EasyMovieTextureComponent was empty. How would you call the GetSeekIPosition? Well, you couldn't, you'd have a midlife crysis and throw a tantrum. Unity does exactly that, but it has no way of knowing what to do next and instead of a tantrum, it throws an error. Unless you specifically expect the error, this is fatal and your entire FSM will stop. To avoid this, we have null checks :D In our case, it looks like this :
Code: [Select]
if(EasyMovieTextureComponent == null){
    Finish();
    return;
}
This makes your action stop before it even runs into the fatal error, by detecting that the variable has no value. If it does have one, it will just be skipped.

And this is it really. All you need to do is replace "EasyMovieTexture" with whatever it is the component is really called , and your done. Here's the entire class :

Code: [Select]
// (c) Copyright HutongGames, LLC 2010-2013. All rights reserved.

using UnityEngine;

namespace HutongGames.PlayMaker.Actions
{
[ActionCategory(ActionCategory.Math)]
[Tooltip("Sets a Float Variable to a random value between Min/Max.")]
public class GetSeekIPosition : FsmStateAction
{
[RequiredField]
[UIHint(UIHint.Variable)]
public FsmInt storeResult;

                [RequiredField]
                public EasyMovieTexture EasyMovieTextureComponent;

public override void Reset()
{
storeResult = null;
                        EasyMovieTextureComponent = null;
                }

public override void OnEnter()
{

                                                      if(EasyMovieTextureComponent == null){
                                                            Finish();
                                                            return;
                                                      }

                        storeResult.Value = EasyMovieTextureComponent.GetSeekIPosition();

Finish();
}
}
}

One thing that I just realized is that you probably have to add a so called namespace reference to your script.[EDIT: In this case, you apparently do not need to do this. This is very rare and actually very bad code imo.] In c#, they are nothing more than

Code: [Select]
using XXX;
at the top of your script. You can see that UnityEngine is already implemented that way. Namespaces are kind of like categories you can put your script in. For example, in your script you are defining your namespace as

Code: [Select]
namespace HutongGames.PlayMaker.Actions
{
   ....
}

So if you want to use your class in another script, you would have to add

Code: [Select]
using HutongGames.PlayMaker.Actions;

...to the top of the script so that Unity understands what class you mean.

[This is all you need to do in your case. Just find the right namespace and add a "using RightNamespace;" to the top of the script. The rest of this mail is an explanation and a tad dry I fear :D ]

If you don't put your class into a namespace, then Unity can always find it just by its' name, but most plugins use namespaces because it might just be that you have another plugin that defines a different class with the same name.
Eg. if I create a new Terrain plugin, I'd probably create a Terrain class. But since Unity already has a Terrain class, it would not know whether to use it's own class or my new one if I used it in a script.
If however, I put my Terrain class in a namespace X, I could start my script with "using X " instead of "using UnityEngine", and now Unity would always use the Terrain that is in the namespace X.

However, this depicts a problem, because what if I wanted to use Terrain from the namespace X, but everything else from UnityEngine (it contains all kinds of important stuff). I can't just remove the "using UnityEngine", because then  it would no longer understand where all the classes like "Vector3" etc are. Well, in this case I have two options :

1) I can use a namespace just like any other normal variable. What I mean with this is, if you remember the fruit bowl example, I can get the content of a class like this :
Code: [Select]
FruitBowl.Banana

This is what we used on the FsmInt (FruitBowl) variable to get the int (Banana):

FsmInt.Value


Therefore it is totally acceptable for Unity if you define a variable like this :
Code: [Select]
public ThisIsMyCustomNamespace.EasyMovieTexture easyMovieTextureComponent;
What you'd be doing here is, you'd take the namespace and look in it for the correct variable type.
With this, you would no longer have to use the additional "using XXX".

2) This is a tad more advanced :
Code: [Select]
using EasyMovieTexture = ThisIsMyCustomNamespace.EasyMovieTexture;
This is a very powerful other use of "using". You can tell Unity to replace any word in your script with another one before analysing/compiling the script.

This way, each time you write EasyMovieTexture, Unity will actually receive it as ThisIsMyCustomNamespace.EasyMovieTexture. You can do this with anything, including long namespaces. For example, if you had a namespace with a very long name, you could be lazy and replace it with a short one. Like this:

using vlnspc = VeryLongNameSpaceWellNowItsJustSixLettersLong;

Obviously this can save you a lot of typing when you use it a lot in your script.

And that's it. I know it's a lot, which is why you just need to know that adding the "using" directive at the top of your script will suffice in your case. Unity will tell you when this doesn't work, but it doesn't happen very often. That should take care of all.

Now lastly, let's have a look at why your previous script didn't work. What Unity actually means by " An object reference is required to access non-static member 'MediaPlayerCtrl.GetSeekPostion()' " is that Unity needs some "spawned object" (an instance) to use the function.

Let's have a real-use example. What are GameObjects? Well, just like pretty much everything else in programming, they are classes. Now, imagine you went and asked Unity what the position of GameObject is:
Code: [Select]
GameObject.Transform.position;Obviously, Unity can't give you a proper answer, because there may be a thousand GameObjects in the scene, or none. Which do you mean?
This is why in this case, you would always declare a GameObject public variable in your script, where people or other scripts can plugin a "spawned"(instanced) GameObject:
Code: [Select]
public GameObject go;
...
go.Transform.position;
Now you can compare your initial action with the one here and see that you did not use an instance. That is all you seem to have done wrong.

There are also cases where you do not need an instance. They are called static functions/classes/variables. An example of a static function would be
Code: [Select]
GameObject.CreatePrimitive(PrimitiveType.Plane);Since this creates a new GameObject, it does not need anything from any other GameObject. Therefore it can be static.

Also, do not hesitate to ask lots of questions. Programming is a huge discipline with a very specific vocabulary. No one who just started learning it can google his questions.
Best,
Sven