Unity, Calling a IEnumerator function from Button onClick - function

I tried to call a IEnumerator function from button click so I could yield return something, but I can't choose the function from the inspector's On Click() dropdown menu.
I tried to call the IEnumerator from another function and assign that function to the button, doesn't work!
And apparently I can't do yield return in a void function.
So could somebody please be so kind and teach me what should I do!?
Much appreciated!

There are a certain rules to hook a function to a unity event from the inspector.
1) The function must be public to be able to choose it from the inspector.
2) The return type must be void, so you won't be able to choose functions with any return type, and since your IEnumerator function returns an IEnumerator, you won't be able to choose it from the inspector.
3) The function parameters must match the event parameters unless the UnityEvent doesn't take any parameters like Button.onClick event (thanks #derHugo for correcting), for example the button onClick event doesn't take any parameters, so to choose a function for it from the inspector the function must look like
public void ChoosableTemplateForOnClickButton() { }
But you want to call an IEnumerator from the inspector, so what you can do is wrap it in a void function:
assuming your IEnumerator function looks like
public IEnumerator MyRoutine()
{
yield and enjoy...
}
wrap it in a void function:
public void RoutineWrap()
{
StartCoroutine(MyRoutine());
}
Now you can choose RoutineWrap from the inspector.

Alternative to this correct answer you can always also add the callback on runtime via script.
Still in Unity every Coroutine has to be started using StartCoroutine:
public Button button;
private void Awake()
{
button.onClick.AddListener(() => StartCoroutine(MyRoutine()));
}
private IEnumerator MyRoutine()
{
...
}

Related

Selenium Webdiver- Writing function inside a function

I would like automate a drag and drop functionality. I have written a function for that. In that function there is two activites
1. Get the drag-from and drag-to element
2. Perform the drag and drop
can i write this as two sub functions inside ?
The following is the code:
public class dunelmtest {
static WebDriver driver= new FirefoxDriver();
#Test
public void test() {
DragandDrop();
}
public static void DragandDrop(){
driver.manage().window().maximize();
driver.get("http://marcojakob.github.io/dart-dnd/basic/");
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
// Want a sub function for this
//Locate element which you wants to drag.
WebElement dragElementFrom = driver.findElement(By.xpath("//img[#class='document']"));
//Locate element where you wants to drop.
WebElement dropElementTo = driver.findElement(By.xpath("//div[#class='trash']"));
// Want another sub function fir this
//Use Actions class and Its members of WebDriver API to perform drag and drop operation.
Actions builder = new Actions(driver);
Action dragAndDrop = builder.clickAndHold(dragElementFrom)
.moveToElement(dropElementTo)
.release(dropElementTo)
.build();
dragAndDrop.perform();
}
}
So how can i create two sub function inside DragandDrop?
I'm not sure if i understand your Question clearly but from what i can understand you want to do something more of a Fluent, the snippet below shows a Class structure that should help you achieve what you want.
Hope this helps
Public class DragandDrop{
public DragandDrop LocateElement(By locator){
//Implementation
return this;
}
public DragandDrop DragElement(toElement){
//Implementation
return this;
}
}

Why is inheriting from Event not working in this case?

Given the following code:
Class CEvent:
public class CEvent extends Event
{
public static const TYPE:String = "cEvent";
private var m_strCode:String;
public function get code():String
{
return m_strCode;
}
public function CEvent(pCode:String, bubbles:Boolean=false,
cancelable:Boolean=false)
{
super(TYPE, bubbles, cancelable);
m_strCode = pCode;
}
}
Class A:
dispatchEvent(new CEvent(MY_CONST))
Class B:
m_a = new A();
m_a.addEventListener(CEvent.TYPE, onCEvent);
.
.
.
private function onCEvent(pEvent:CEvent):void
{
switch (pEvent.code)
{
case A.MY_CONST:
dispatchEvent(pEvent);
}
}
Class C:
m_b = new B();
m_b.addEventListener(CEvent.TYPE, onCEvent);
.
.
.
private function onCEvent(pEvent:CEvent):void
{ // breaks right here
}
I get this error when it breaks on class C, after dispatching it originally from Class A:
Error #1034: Type Coercion failed: cannot convert flash.events::Event#9861089 to
<path>.CEvent.
This doesn't seem to make a lot of sense, and it seems to be going completely against the way inheritance works. Even if there were code in Adobe's implementation of dispatchEvent() that specifically goes through and shaves off anything that's been added through inheritance and just dispatches a "normal" Event instance, that should cause it to break in class B, not C.
Could someone please explain? Thanks.
Edit: By the way changing class B's code to do this instead makes everything work just fine:
dispatchEvent(new CEvent(pEvent.code));
I still need to understand what the issue is though. Thanks.
The error occurs because you have not implemented the clone() method in your custom event.
When you re-dispatch an event (in your Class C), Flash clones the event instead of just re-dispatching the original event.
The event that is re-dispatched therefore is a plain old Event object, because that's what the default clone() method returns.
In general, you should always implement a clone() method for your custom events. It's pretty straight forward to do. In this case it should look something like this:
override public function clone():Event
{
return new CEvent(m_strCode, bubbles, cancelable);
}

public function access fail

I have this situation where I declare inside my main class a function that looks like this:
public class Main extends MovieClip
{
public static var instance:Main;
public function Main()
{
// constructor code
welcomeScreen();
instance = this;
}
public final function welcomeScreen():void
{
//some code in here
}
public final function startLevelOne():void
{
//some other code here
}
}
In some other class I use this statement to fire a reset:
restart.addEventListener('click', function() {
Main.instance.welcomeScreen();
});
Somehow in another class I try to use the same statement for 'startLevelOne' but it seems it is not working and gives the fallowing error:
1195: Attempted access of inaccessible method startLevelOne through a reference with static type Main.
Any ideas?
UPDATE #1
The class where I try to access the function is in full this one:
public class LevelBrief extends MovieClip
{
public function LevelBrief()
{
// constructor code
startBut.addEventListener('click', function() {
Main.instance.startLevelOne();
});
}
}
UPDATE #2
I have pasted the full code of the main definition here http://pastebin.com/s6hGv7sT
Also the other class could be found here http://pastebin.com/s6h3Pwbp
UPDATE #3
Even though the problem was solved with a workaround, I still cannot understand where was the problem.
I would recommend to leave the static instance (singleton), and work event-based. Now you make all functions public, which is not desirable. It's not that hard to use custom events. See this is how your Main class could look:
public class Main extends MovieClip
{
public function Main()
{
this.addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
}
public function handleAddedToStage(event:Event)
{
this.removeEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
this.showWelcomeScreen();
stage.addEventListener(ScreenEvent.SHOW_WELCOME_SCREEN, handleScreenEvent);
stage.addEventListener(ScreenEvent.SHOW_LEVEL, handleScreenEvent);
}
private function handleScreenEvent(event:ScreenEvent):void
{
switch (event.type)
{
case ScreenEvent.SHOW_WELCOME_SCREEN:
{
this.showWelcomeScreen()
break;
}
case ScreenEvent.SHOW_LEVEL:
{
// event.data contains level number
this.startLevel(event.data);
break;
}
default:
{
trace("Main.handleScreenEvent :: Cannot find event.type '" + event.type + "'.");
break;
}
}
}
private function showWelcomeScreen():void
{
trace("show WelcomeScreen")
//some private code in here
}
private function startLevel(level:int):void
{
trace("start level: " + level)
//some other private code here
}
}
This is how the custom event class should look (ScreenEvent.as). Note it has an optional parameter called data. You can pass any value (objects, numbers, strings etc) into this. To the example as clear as possible, I used one event-class for both actions, you can also choose to make more specific custom events for other actions with more detailed parameters, you would have names like ScreenEvent, LevelEvent, PlayerEvent, GameEvent etc etc..
At the top of the class the (static constant) types are defined. An event should only have getters.
package
{
import flash.events.Event;
public class ScreenEvent extends Event
{
public static const SHOW_WELCOME_SCREEN:String = "ScreenEvent.showWelcomeScreen";
// event.data contains level number
public static const SHOW_LEVEL:String = "ScreenEvent.showLevel";
private var _data:String;
public function ScreenEvent(type:String, data:String):void
{
super(type);
this._data = data;
}
public function get data():String
{
return this._data;
}
override public function clone():Event
{
return new ScreenEvent(this.type, this._data);
}
}
}
.. Anywhere in your code you can dispatch the event to the stage.
// dispatch event to Main (stage). Should show welcome screen in our case
stage.dispatchEvent(new ScreenEvent(ScreenEvent.SHOW_WELCOME_SCREEN));
// show level 2
stage.dispatchEvent(new ScreenEvent(ScreenEvent.SHOW_LEVEL, 2));
I know, its a bit more code, it looks more difficult at first but if the project grows, it will help a lot. The difference with events is 'this could happen, and when it happens, do this' instead of 'do this here, do that over there'
The advantage is that if you remove the event listener in the Main class, nothing will break (loosely coupled). This makes it easier to maintain, it saves a singleton, and you have the ability to extend the Main class if you want to.
I think you wrote
Main.startLevelOne();
instead of
Main.instance.startLevelOne();
Hmm. Given your code, there is only one serious question - what is PerwollGame? You have there public static var instance:PerwollGame; and you assign it an object of type Main. Perhaps that PerwollGame has a startLevelOne() function with a different signature, that obscures your function in the Main class. Also, the other people who answered you are right as well, you should never use nested functions in your code, really put that listener of yours out from inline declaration.
Judging from your coding style and the error reported, I would assume you did this.
public static function startLevelOne():void
There is a fine line between static methods and instantiated objects.
Also never use nested functions
public class LevelBrief extends MovieClip
{
public function LevelBrief()
{
// constructor code
startBut.addEventListener('click', onMyClick )
}
public functiononMyClick (e:Event) {
Main.instance.startLevelOne();
});
}
}
I assume that when you register the listener Main.instance is not already assigned.
Did you try to trace Main instance here?
public function LevelBrief()
{
// constructor code
startBut.addEventListener('click', function() {
Main.instance.startLevelOne();
});
trace(Main.instance); // I assume Main.instance is null
}
what about if you add the listener in another method in LevelBrief like :
public function registerListeners():void{
trace("Main.instance == null? -> " + (Main.instance == null)); //not null here if called later.
startBut.addEventListener('click', function() {
Main.instance.startLevelOne();
});
}

Static Stop/Play Functions

Let's say I would want to use the frameScript method to add some stop and play methods to some frames.
Normally I would declare the stop function:
private function $FUN_FrameStop():void {
stop();
return;
}
and then use it like this:
addFrameScript(47, $FUN_FrameStop, 122, $FUN_FrameStop);
My question is, how can I create the same $FUN_FrameStop as a static function?
Static functions do not allow the use of this since static members are bound to a class and are not inherited by that class' instances.
So, is there a way to create a function similar to $FUN_FrameStop, but static?
I never added any frame script dynamically but.. did you try using the instance as the parameter?
private static function $FrameStop($inst:MovieClip):void {
$inst.stop();
}
The answer is that there's no way unless I have a static reference to the class instance, but that's not what I want.
public static var staticClassRef:MovieClip;
function $FUN_FrameStop():void {
staticClassRef.stop();
return;
}
You may declare a function outside of class but in package
package
{
public function Func(): void
{
trace( "Func" );
}
}
then you may call it everywhare with Func() after including the package ( if needed )

AS3 - Can I have access to the object (or function) who call me?

I've asked this same question with Python.
Now I like to know if this can be done in AS3.
If I have something like this:
package
{
public class SomeClass
{
private function A():void { C() }
private function B():void { C() }
private function C():void
{
// who is the caller, A or B ???
}
public function SomeClass()
{
A()
B()
}
}
}
Despite the design or other issues, this is only a question of an inquiring mind.
Note: I like to have an access to an instance of the caller function so I can call that caller function (if I want to)
Note 2 : This has to be done without changing function C() signature
"Unlike previous versions of ActionScript, ActionScript 3.0 has no arguments.caller property. To get a reference to the function that called the current function, you must pass a reference to that function as an argument."
From http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/arguments.html
That's the only way you can do that, otherwise you'll need to make a global variable to tell what function is calling C
Sure it can be done. You can do something like
private function C():void
{
var e:Error = new Error();
var stack:String = e.getStackTrace();
//analyze stack and find out which function called it.
}
this is ugly but it would work.