How to delay an action (or) How to control program flow to wait for a particular action to complete - cocos2d-x

I have code something like this.
I have to move a sprite to particular position and after completion of that action, i want to remove it.
But it is removing it without performing the action. How, to achieve this.
sp is a Sprite
sp->runAction(MoveTo::create(1.0, Vec2(visibleSize.width/2, visibleSize.height/2)));
this->removeChild(sp);

auto move = MoveTo::create(1.0, Vec2(visibleSize.width/2, visibleSize.height/2));
auto moveDone = CallFuncN( CC_CALLBACk_1(ClassName::removeSprite, this) );
sp->runAction( Sequence::create( move, moveDone, NULL ) );
//create a function removeSprite, it will be called after action move is finished
void ClassName::removeSprite(Node* pNode) {
pNode->removeFromParent();
}

Related

What's code sequence in the below cocos2dx code?

I'm new to cocos2dx. In the below code, _nextProjectile is added to the children of "this" in the callback function which is executed after _nextProjectile->runaction (since "finish shoot" is always printed after "run action"). However, I print the position of _nextProjectile in finishShoot(), which is exact the same with that I set when I created the _nextProjectile.
So my question when does the _nextProjectile->runAction is executed actually?
Thank you.
{
_player->runAction(Sequence::create(RotateTo::create(rotateDuration, cocosAngle), CallFuncN::create(CC_CALLBACK_0(HelloWorld::finishShoot, this)), NULL));
// Move projectile to actual endpoint
printf("run action\n");
_nextProjectile->runAction(Sequence::create(MoveTo::create(realMoveDuration, realDest), CallFuncN::create(CC_CALLBACK_1(HelloWorld::spriteMoveFinished, this)), NULL));
}
void HelloWorld::finishShoot()
{
// Ok to add now - we've finished rotation!
printf("finish shoot\n");
this->addChild(_nextProjectile);
// Add to projectiles vector
_projectiles.pushBack(_nextProjectile);
_nextProjectile = NULL;
}
Both actions run simultaneously. The player rotates for rotateDuration before calling the callback, at the same time nextprojectile (if not null) already starts moving.
It seems like you may want to move the nextprojectile runaction in the finishShoot method instead.

How to remove this unrelated block of code, that checks child's availability in the container

This is my code on button click. It adds myView to the container. And takes care of fact that all previous instances get removed before adding again. I wanna know, if there is some better way of making this "check" ? I especially wanna have some way to remove the 1s 7 lines of this code ( within comments). It seems like an unrelated block of code in the normal code-flow.
function button_CLICK(e:Event)
{
///////////////////////////////////////////////////
if ( myView!= null)
{
if ( contains(myView))
{
removeChild(myView) ;
}
}
/////////////////////////////////////////////////////
myView = new myView("hello")
addChild(myView);
}
it would depend on what the object myView consists of. if, for example, it has its own event listeners you would want to use the condition check to clean up the object before disposing of it so there are no memory leaks.
function button_CLICK(e:Event):void
{
if (myView && contains(myView))
{
//clean up myView
removeChild(myView);
}
myView = new myView("hello");
addChild(myView);
}
however, if myView is a simple display object that doesn't contain any leakable references or event listeners than you could simply reassign the variable to overwrite the previous.
keep in mind that addChild() places the added display object on top above all others. but regardless if that's something you'd like to avoid or not, i believe it would be better to assign a default or empty view once instead of addChild(myView) each time the button is clicked.
function button_CLICK(e:Event):void
{
myView = new myView("hello");
}
i assume your code is for for demonstration because new myView("hello") is going to always be the same object? unless perhaps this object relies on dates or timers to make instances different from previous constructs.
The checking code you have could create a memory leak as you don't null the previous myView before creating a new instance. NB. It's good practice to begin the name of your classes with an uppercase letter.
Your code also seems to allow for the button_CLICK method to be called when myView has been added. Perhaps consider hiding the button when the view has been added.
You could just have one instance of the MyView Class instantiated. Then rather than having to null and create a new instance each time, just add the same one and add an init function that will accept the initialising parameters. e.g.
var myView = new MyView();
function button_CLICK(e:Event)
{
if ( contains(myView))
{
removeChild(myView) ;
}
}
myView.init("hello");
addChild(myView);
}
I don't think you have much of an option, based on the (IMO) flawed implementation of DisplayObjectContainer. If you're feeling feisty, you can "fix" removeChild to see if the child is there before calling the super method, rather than simply throwing an error, as Adobe thought was the appropriate solution.
Your code above will still run, but it will be in the removeChild override.
Your commented bloc of code is necessary only for the first call to button_CLICK (you sould rename this method : buttonClick).
After the firts call, this bloc of code is unnecessary.
In order to remove this code myView should be not null, and an instance of MyView should be added on the display list.
var myView : MyView;
function init(){
myView = new MyView("FAKE");
myView.visible = false;
addChild(myView);
addEventListener("click",click);
}
function click(e:MouseEvent):void{
removeChild(myView);
myView = new Child("REAL");
addChild(myView);
}
Try:
if(myView != null)
myView.parent && myView.parent.removeChild(myView);

Timeline instances not on first frame

It's been a while since I've had to write Actionscript that really needs to integrate with the timeline (in this case, controlling a series of frames that must happen in a certain sequence) and I am trying to figure out what to do.
In the first few frames, I have a button "next_1".
At frame 10, I need to have another button "next_2". I really really need this button to not be on frame one (I could possibly just make it invisible, but that's going to create a clickable area that I don't want).
The problem is, anything I don't put on "frame_1" renders as null in my Document class.
Is there any solution to this? I would rather not have to write my script on the timeline if possible (it seems easier in the long run to keep it in a document class)...
Items on the timeline are created on the fly, so if the playhead has not reached frame 10, next_2 is not created.
Easiest Document-class solution:
Create an array of frame labels like ["label1", "label2"]
Create sectionIndex var and set it to 0
Create a next button on its own layer so it is always showing.
When the next button is clicked, increment sectionIndex, then gotoAndPlay(myLabels[sectionIndex])
Okay, directly lifted from "Real World Flash Game Development":
/**************************************************
* FRAME LABELS *
**************************************************/
private function enumerateFrameLabels():void {
for each (var label:FrameLabel in currentLabels) {
addFrameScript(label.frame-1, dispatchFrameEvent);
}
}
private function dispatchFrameEvent():void {
dispatchEvent(new Event(currentLabel, true));
}
This dispatches an event at each frame label on the timeline.
Then you can just add event listeners for each frame:
addEventListener("name_of_my_framelabel", frameHandler);
addEventListener("another_framelabel", frameHandler);
And write a switch statement to add event listeners for the buttons when they actually show up on the timeline.
private function frameHandler(e:Event):void {
switch(e.type) {
case 'screen_2':
stop();
next_2.addEventListener(MouseEvent.CLICK, click2, false, 0, true)
break;
}
}

ComboBox Bug in ActionScript

I was trying to filter a combo box dataprovider based on the values in the text boxes . When the contents of the dataprovider changes Combo box automatically calls change event method . Please find the sample code below.
Filter Utility Function:
private function filterLocations(event:FocusEvent):void {
locationsList1.filterFunction = filterUtility;
locationsList1.refresh();
}
public function filterUtility(item:Object):Boolean {
// pass back whether the location square foot is with in the range specified
if((item.SQUARE_FOOTAGE >= rangeText1.text) && (item.SQUARE_FOOTAGE rangeText2.text))
return item.SQUARE_FOOTAGE;
}
// THIS WOULD BE CALLED WHEN COMBO BOX SELECTION IS DONE
private function selectLocationsReports(event:ListEvent):void {
selectedItem =(event.currentTarget as ComboBox).selectedItem.LOCATION_ID;
}
When the DataProvider gets refreshed its automatically calls change method and was throwing Null Pointer function because its prematurely calling the above selectLocationsReports method and its throwing error.
Can somebody let me know how to stop the CHANGE event from propogation when the dataprovider is refreshed.
You can't stop a CHANGE event, just don't add an event listener unless you are prepared to get the event. I don't see where your event listener for Event.CHANGE is in the code above.
Just be sure that you don't addEventListener(Event.CHANGE, selectLocationsReports) until your ComboBox is ready for it.
The other thing to do (on top of Kekoa's response) is put an if statement in the event handler, and check to make sure the data is there before you begin working with it.
A handy syntax I use frequently for this is
if(dataprovidername) {
}

Flex DRAG_DROP Event - Is it possible to access the Image Being Dragged?

When you start a Flex drag action, you pass in a proxy image to be displayed when you drag across the screen. When the drop occurs, I want to be able to grab this proxy but I can't find a way to from the DragEvent object.
Is it possible? What I want is to actually drop the dragged image when the mouse button is released... Flex automatically does a nice shrinking animation on the proxy but I don't want that.
The Flex examples show what I don't want - the proxy is removed and a new image added but not in exactly the right place...
More info: I tried adding my Proxy Image as a data item to the DragSource. I was able to access this when the drop occurred and saw there is a class mx.managers.dragClasses.DragProxy which seems to have all the info I need... but this class is not documented?
So there's two questions really... how to get the proxy and find out the position of the mouse cursor within the proxy, and how to disable the Flex drop animation.
The dragProxy is a static getter on the DragManager and is scoped to mx_internal. So to reference it, you'd have to do something like this:
import mx_internal;
And in a drag event handler:
var p:* = DragManager.mx_internal::dragProxy;
I'm not sure how you could prevent the animation. If I find out, I'll let you know.
For disabling the animation in the s:List, in a dragCompleteHandler override, you can 'hack' into the DragManager to get the dragProxy and hide it.
override protected function dragCompleteHandler(e:DragEvent):void
{
DragManager.mx_internal::dragProxy.visible = false; // <- MAGIC!
super.dragCompleteHandler(e);
}
Probably applicable in other situations.
Only way to prevent the animation:
-You have to monkey patch the DragProxy class (i.e. create a new class with identical name, code, and package structure), and remove the effects code from the mouseUpHandler().
No other way to override it as far as I know, though the issue was been submitted as a bug to Adobe over a year ago.
As far as getting the mouse coords for the proxy to drop it in the correct location try this:
assuming you are initiating the drag on mouseDown get the coords using e.currentTarget.contentMouseX and e.currentTarget.contentMouseY in your handler. then add these to the dragSource ( I did it as an object ) like:
var drgSrc:DragSource = new DragSource();
drgSrc.addData( { x:e.currentTarget.contentMouseX, y:e.currentTarget.contentMouseY }, 'drgXY' );
then in your drop handler ( assuming you are dropping it into a Canvas named drpCvs ):
var newImg:Image = new Image();
newImg.x = drpCvs.contentMouseX - e.dragSource.dataForFormat( 'drgXY' ).x;
newImg.y = drpCvs.contentMouseY - e.dragSource.dataForFormat( 'drgXY' ).y;
I found this while looking for a way to get rid of the shrink animation, so thanks for that. Thought I'd RTF.
If you just want to prevent the animation, the easiest (hackiest) way is this: create you're own proxy and add a MOUSE_UP handler to the stage that when triggered sets the visible property of your proxy to false. It won't actually stop the animation, it will just hide the proxy while the animation is happening. Something like this:
var proxy:UIComponent = new UIComponent();
proxy.graphics.lineStyle(1);
proxy.graphics.beginFill(0xccddff);
proxy.graphics.drawRect(0, 0, main.width, main.height);
stage.addEventListener(MouseEvent.MOUSE_UP, function (e:MouseEvent):void {
proxy.visible = false;
});
#ykessler: Thank you, the monkey patch worked like a charm. SDK: DragProxy.as
#Alvaro: I believe this approach results in a race condition. I tried it, and it only worked sometimes.
Setting
event.dragInitiator.visible = false;
in the drag drop handler works for me!
My solution to turn off the animation, was to set visible=0 onMouseUp in my custom ListItemDragProxy component.
My solution is to remove the MouseUp-Handler on SandboxRoot and attach an own MouseUp-Handler in dragEnterHandler of the target like this:
protected function dragEnterHandler(event:DragEvent):void{
DragManager.acceptDragDrop(this);
this.dragProxy = DragManager.mx_internal::dragProxy;// get drag proxy
var sm:ISystemManager = event.dragInitiator.systemManager.topLevelSystemManager as ISystemManager;
var ed:IEventDispatcher = sm.getSandboxRoot();
this.sandboxRoot = sm.getSandboxRoot();
//remove
ed.removeEventListener(MouseEvent.MOUSE_UP, dragProxy.mouseUpHandler, true);
//attach own
ed.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, true);
ed.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
this.dragInitiator = event.dragInitiator;}
In mouseUpHandler I've implemented the copy of function mouseUpHandler from original DragProxy.as and removed the Drop-Effect.