ActionScript3, moving objects of type movieclip - actionscript-3

Here i am trying to create a new movieclip type object, which is moved when function mvBall is called. When i run the code i get this err: implicit coercion of a value with static type object to a possibly unrelated type flash.display:MovieClip. Later on i want to be able to make the ball bounce back when it colides with another object. I'm new to action script and don't really know how things work so any help would be appreciated. Here's the code:
private function frame(x:Event):void {
var ball:MovieClip = new MovieClip();
ball.addEventListener(Event.ENTER_FRAME, animate);
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(100, 100, 15);
ball.graphics.endFill();
stage.addChild(ball);
}
private function animate(ev:Event):void {
mvBall(ev.target);
}
private function mvBall(mc:MovieClip) {
mc.x += 10;
}

You need to cast the target to MovieClip
private function animate(ev:Event):void {
mvBall(ev.target as MovieClip);
}
With that said it is better to just have one ENTER_FRAME handler and animate your objects in there.
stage.addEventListener(Event.ENTER_FRAME, animate);
private function animate(ev:Event):void
{
mvBall(myBall);
//other object animations
}

You are getting this error because the target property of the Event class is of type object.
In order to not throw the error, you need to cast it as a MovieClip:
mvBall(ev.target as MovieClip);
or
myBall(MovieClip(ev.target));
Something else to consider, is the difference between an Events target and currentTarget properties. If you ball had multiple layers/object inside it (sprites or other movieClips), the target would be whichever one of those sub-elements had the mouse over it during the click. currentTarget refers to the object that you've attached the listener to. In your case they may be the same (if your ball doesn't have any movie clips inside it), but your code could have unexpected results if you have sub-movieClips inside your ball.

Related

Removing a child from the stage with active event listeners

I have an enemy object that fires missile objects, like so;
public function fire():void {
var missile:Bullet = new Bullet();
stage.addChild(missile);
}
No problem there.
Now, when any missile objects leave the stage area, I want to remove them. Seems simple enough. So in my Bullet class, I've done the following;
I've used the standard method of calling this in my constructor;
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
Which then triggers init();
public function init(e:Event = null):void {
trace("init");
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, actions);
}
So actions(); is triggered by Event.ENTER_FRAME;
public function actions(e:Event):void {
this.move();
trace(stage.stageWidth); // this works fine
stage.removeChild(this); // this throws Error #1009:
}
I'm just using this is an example. My actions(); function will eventually contain code to remove the object when it leaves the stage, not just when the function is called. I can trace the stageWidth property, no problem. So why is stage.removeChild(this); throwing this error?
[Fault] exception, information=TypeError: Error #1009: Cannot access a
property or method of a null object reference.
Now, this is where it gets really baffling.
If I move stage.removeChild(this); into init();, it works perfectly. So why would it work there, and not in actions(); which is always called after init();?
I never thought to check for this, as I assumed once an object was removed from the stage, all of its event listeners would be as well.
Removing a child from the stage, however, does not mean the object has been deleted, so stage.removeChild(this); was working within actions();, but even though I'd removed the bullet object from the stage, it was still calling actions();, which was then calling stage.removeChild(this); again, which was trying to reference the object when it no longer existed on the stage.
if (this.y > stage.stageHeight) {
stage.removeChild(this);
removeEventListener(Event.ENTER_FRAME, actions);
}
All I had to do was remove the event listener so actions(); would stop being called after I removed the bullet object from the stage.
Hopefully someone else can learn from my mistake, here. Still, I'm a bit surprised that Event.ENTER_FRAME still triggers even when you remove the child from the stage.

Replacing a movieclip with another movieclip, keeping the same instance name

I'm new to actionscript so this question might be a stupid one.
I'm trying to replace a movieclip with another movieclip, while keeping the instance name of the previous.
I have a menu with a selection of buttons, each leading to the same screen with a movieclip and a scrubber bar. I tried defining the movieclip through a variable, then tried redefining it through an event listener function, but I'm guessing I can't do like this:
var MC: movieclipsymbol1 = new movieclipsymbol1;
private function selectionscreen(): void {
selectionscreenbutton1.addEventListener(MouseEvent.CLICK, screenbutton1);
selectionscreenbutton2.addEventListener(MouseEvent.CLICK, screenbutton2);
private function screenbutton1(event: MouseEvent): void {
var MC: movieclipsymbol1 = new movieclipsymbol1;
movieclipscreen();
}
private function screenbutton2(event: MouseEvent): void {
var MC: movieclipsymbol2 = new movieclipsymbol2;
movieclipscreen();
}
}
public function movieclipscreen(): void {
stage.addChild(MC);
}
Because of the scrubber bar code I did, I need to keep the instance for the movieclips the same. Is the approach I'm using completely off?
You have to remove var MC from both handlers, as you want your new MC to be accessible from outside of the handlers. But also you need to change the type of class variable MC so that it could hold either movieclipsymbol1 or movieclipsymbol2. The most common choice for the type in there is MovieClip. So, you have to change your functions like this:
var MC:MovieClip = new movieclipsymbol1();
private function screenbutton1(event: MouseEvent): void {
clearOldMC();
MC = new movieclipsymbol1();
movieclipscreen();
}
private function screenbutton2(event: MouseEvent): void {
clearOldMC();
MC = new movieclipsymbol2();
movieclipscreen();
}
private function clearOldMC():void {
if (MC.parent) MC.parent.removeChild(MC);
}
The new function removes the previously displayed movie clip, regardless of its type.
Use "name" property of display object to give the instance name to movieclip.

addEventListener of random MovieClips

I'm making a game in AS3.
I've got in my code :
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
public function apparitionDechet(event : TimerEvent):void{
_movieClips.push(new _classes[Math.floor(Math.random() * _classes.length)]());
stageRef.addChild(_movieClips[_movieClips.length-1]);
I'm trying to put an addEventListener on the MovieClips.
The player should be able to click on a MovieClip when it's appearing or he can wait. Few will appears, and he can click on them at any moments.
Each clicks will make the MoviClip disapear..
So I've put :
_movieClips[1].addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
if(e.target=="_movieClips[0]"){
trace("ok1");
}
if(e.target=="_movieClips[1]"){
trace("ok2");
}
if(e.target=="_movieClips[2]"){
trace("ok3");
}
but it's not that...
Do you know how I can do that ?
It's my first time that I'm using the randomly apparition of MovieClips...
Thank you very much,
EDIT
So I've followed your tips and did this :
public function apparitionDechet(event : TimerEvent):void{
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
public function removePoubelle(e:MouseEvent):void{
var mc:DisplayObject = e.target;
var i:int=_movieClips.indexOf(mc);
if (i>=0){
_movieClips.splice(i,1);
mc.parent.removeChild(mc);
}
}
But I've got the error 1118 Implicit coercion of a value with static type Object to a possibly unrelated type flash.display:DisplayObject
EDIT 2
Quick question though, is it possible to do :
if(stageRef.contains(poubelle1)) {
trace("poubelle1détécté");
}
if(stageRef.contains(poubelle2)) {
trace("poubelle2 détécté");
}
?
movieClips poubelle1 and poubelle 2 are defined like this
public var _classes:Array = new Array(poubelle1, poubelle2, poubelle3);
public var _movieClips:Array = new Array();
it doesn't seem to work if I do that.(error 1027 Contrainte implicite d'une valeur du type Class vers un type sans rapport flash.display:DisplayObject) Any idea why ?
Do you want me to create a new post ?
Thank you
If you are to remove the movieclip that was clicked, you already have it as the event's target. So you get its parent and call removeChild(). Don't forget to remove the event listener off the target.
public function removePoubelle(e:MouseEvent):void {
var mc:DisplayObject = e.target as DisplayObject;
if (!mc) return; // typecast failed
mc.parent.removeChild(mc);
// mc.removeEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
// the line above might not be needed as the listener weakly references the mc
}
And you put the listener as soon as you create your new movie clip.
public function apparitionDechet(event : TimerEvent):void {
var mc:DisplayObject = new _classes[Math.floor(Math.random() * _classes.length)]();
_movieClips.push(mc);
stageRef.addChild(mc);
mc.addEventListener(MouseEvent.CLICK, removePoubelle, false, 0, true);
}
See, how you can avoid referencing the newly created movie clip without ugly _movieClips[_movieClips.length-1] construction? You just make a local variable which is then instantiated for your random MC out of _classes, and you then use the variable to do everything else that's needed at the time of creation.
But, this is still not enough - your "poubelle" is still inside your _movieClips array, so it'll grow. You need to clean up the array too. So, add this code to removePoubelle:
var i:int=_movieClips.indexOf(mc);
if (i>=0) _movieClips.splice(i,1);
This gets the position of the clicked movie clip inside your array, and if it's a valid one (zero or more) the array is told to remove that element.
You shouldn't be using just _movieClips[1]. That refers specifically to the SECOND object in your _movieClips Array.
You should add your eventListener as soon as the MovieClip is added to the _movieClips Array. You can add it to the most recently '.push'ed MovieClip, like this:
_movieClips[_movieClips.length-1].addEventListener(MouseEvent.CLICK, removePoubelle);
Do that on the next line after the line where MovieClip is pushed into the _movieClips Array.
Your event handler (the removePoubelle function) will be passed a MouseEvent and you can refer to the .target of this event to isolate WHICH MovieClip has been clicked:
private function removePoubelle(e:MouseEvent):void {
var mcToRemove:DisplayObject = e.target;
removeChild(mcToRemove); // note there is no need to refer to .parent as the MovieClip was added in this Class
// more code to come - see below
}
Also note: Because each MovieClip has an eventListener added WHEN IT IS CREATED, e.target will ALWAYS refer to whichever MovieClip was clicked.
The only other thing you may want to implement is removing the MovieClip from the _movieClips Array. This can be done in the removePoubelle function too:
var removalIndex:int = _movieClips.indexOf(MovieClip(e.target)); // here I am 'casting' the e.target to the type MovieClip. That basically just means I'm changing it's type from DisplayObject to MovieClip (which is a subclass of DisplayObject)
if (removalIndex>-1) {
_movieClips.splice(removalIndex, 1); // this line removes one item at the index returned from the _movieClips.indexOf... line above.
}
Let me know if any of this doesn't make sense.
}

referencing objects in different functions as3

I have an object in my mouse event functions that I want to reference in my time function.
Example, I basically created tiles and have mouse events:
var cell:MovieClip = new Tile();
cell.gotoAndStop(floor1[i][u]);
cell.x = ((u-i)*tileh)+365;
cell.y = ((u+i)*tileh/2)+70;
addChild(cell);
cell.addEventListener(MouseEvent.ROLL_OVER, mouseover);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseout);
cell.addEventListener(MouseEvent.CLICK, mouseclick);
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener);
In the mouse events, I have something called event.currentTarget. Since I have tiles lined up with each other, I wanted to differentiate each individual tile. Thus creating that event.currentTarget. I wanted to use this in my time event, but it isn't recognizing event.currentTarget as an object, rather it's own timer. Is there any way to have the time function recognize the event.currentTarget from the mouse events?
Event.currentTarget is the last object to dispatch that specific event (and Event.target is the original object to dispatch the event). It can be absolutely anything that extends EventDispatcher.
The only way to do what you want is like this:
var currentTile:Tile;
cell.addEventListener(MouseEvent.ROLL_OVER, mouseEventsHandler);
cell.addEventListener(MouseEvent.ROLL_OUT, mouseEventsHandler);
cell.addEventListener(MouseEvent.CLICK, mouseEventsHandler);
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener);
function mouseEventsHandler( e:MouseEvent ):void {
this.currentTile = e.currentTarget as Tile;
}
function timeListener( e:TimerEvent ):void {
this.currentTile.blah.blah();
}
Basically you save the tile that most recently was interacted with into currentTile and then that is what you access in your timeListener.
You should really look through the LiveDocs to get a basic understanding of how events work and possibly look into how scope works as well.
Some explanation:
Your Event-Listener receives an Event-Object. Always. What kind of Event-Object it is depends on the Event. In your MouseListener you receive a MouseEvent, in you TimerListener a TimerEvent and so on.
EVERY Event-Object has two specific attributes.
event.currentTarget
is the Object, which binds the event listener (in your case "cell")
event.target
is the object, which "caused" the Event. If e.g. in you "cell"-MovieClip is another MovieClip called "nucleus" and you click on the the it, event.currentTarget would be "cell", but even.target would be "nucleus".
Since the event-object is a passed as a function parameter, it is destroyed, once the function is finished. If you wand to use parts of it in another function, you need to do it like this
var myCell:MovieClip;
function mouseClick(event:MouseEvent):void{
myCell = event.currentTarget as MovieClip;
}
function timeListener(event:TimerEvent):void{
if(myCell){
///what ever you want to do with it
myCell = null;
}
}
I hope I explained it clearly.
Here's one way you could do this :
enemyMoveTimer.addEventListener(TimerEvent.TIMER, timerListener(cell));
enemyMoveTimer.start();
public function timerListener(cell:MovieClip):Function
{
var doStuffToCell:Function = new Function(e:TimerEvent)
{
trace (cell.x + " : " + cell.y);
// do whatever you want with cell
}
return doStuffToCell;
}
I should note that I don't think it's a good idea to call your handler timerListener, as it's a handler.
Listeners do just that, they listen for an event. A handler, handles an event. The second parameter in the addEventListener method specifies a function to handle the event. Naming your handler timerListener is not a good idea in my opinion, as it's really not that.

How to hitTestObject MC items of an array with a MC target?

I have items in an array, each is a movieclip, which play at delayed intervals of 1 second and a target movieclip (carTarget) controlled by arrow keys where the aim is to avoid a collision with the array items (obstacleArray[i]). I have been attempting to create a hitTestObject statement to notify when there is a collision. However I have received errors such as parameter hitTestObject most be non-null and cannot figure out how to resolve it.
//obstacles
function randomSort(a:*, b:*):Number
{
if (Math.random() < 0.5) return -1;
else return 1;
}
var obstacleArray:Array = [obstacleCar,obstacleCar2,obstacleCar3];
obstacleArray.sort(randomSort);
trace(obstacleArray);
trace(obstacleArray.length);
//OBSTACLE START DELAY
var timerPlay:Timer = new Timer(1000,1);
timerPlay.addEventListener(TimerEvent.TIMER, ontimerPlay);
timerPlay.start();
var i:int = 0;
var timerDelay:Timer = new Timer(1000, 3);
function ontimerPlay(evt:TimerEvent):void{
obstacleArray[i].addEventListener(Event.ENTER_FRAME,checkHitTest);
//FOR EACH OBSTACLE
timerDelay.addEventListener(TimerEvent.TIMER, ontimerDelay);
timerDelay.start();
}
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(obstacleArray[i]))
{
trace("HIT!");
}
}
function ontimerDelay(evt:TimerEvent):void{
obstacleArray[i].play();
trace(obstacleArray[i]);
i++;
}
Most likely, the problem is in this block of code:
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(obstacleArray[i]))
{
trace("HIT!");
}
}
After i changes with the timer calls, then obstacleArray[i] will be null or worse - another object other than the one you need to check.
You should change this to:
function checkHitTest(event:Event):void{
if(carTarget.hitTestObject(event.target))
{
trace("HIT!");
}
}
event.target will get the current obstacle calling the checkHitTest from Enterframe event
It is very likely that hitTestObject does not exist at the moment you're trying to use it. From the code you shared, I do not see where you create it in your code. So, assuming carTarget is an object on the stage and that it has the property or object hitTestObject initialized when you want to use it -assuming those things, then you need to make sure that when this code is running, that object still exists.