Why am I unable to access the methods of an object via is ObjectContainer - actionscript-3

first of all, i'm not a native english speaker but, still, i'll try my best to be understandable and as clear as possible.
So, in my programming class, I need to make a Tile based game (like zelda, for exemple) with animate cc (flash). On a map, I want to make a dance floor with tiles that changes on the rhythm of a music. these tiles are movieclip with two frame, one white and one red.
This is how the tiles are generated:
private function createGrid(): void {
grid = new MovieClip();
addChild(grid);
for (var r: int = 0; r < nbRow; r++) {
for (var c: int = 0; c < nbCol; c++) {
var t: Tiles = new Tiles();
t.x = t.width * c;
t.y = t.height * r;
grid.addChild(t);
}
}
grid.x = 15; //center the grid on x
grid.y = 35; //center the grid on y
}
This is the Tiles Class :
package {
import flash.display.MovieClip;
import flash.events.*;
public class Tiles extends MovieClip {
private var rand:int;
public function Tiles() {
// constructor code
getTiles();
}
public function getTiles():void {
random();
setColor();
}
private function random() : void{
rand = Math.floor(Math.random()*100)+1;
}
private function setColor() : void{
if(rand<=30){
gotoAndStop(8); //red frame
}else{
gotoAndStop(7); //white frame
}
}
}
}
createGrid() place the tiles as soon as the map is placed on the stage and stock every tiles in the MovieClip grid. Now, I want the tiles to change randomly between red and white on the beat of a streamed music (and keep the ratio of 30% red tiles and 70% white tiles)
var s: Sound = new Sound();
var sc: SoundChannel;
s.load(new URLRequest("GameSong_mixdown.mp3"));
sc = s.play(0, 1000);
I know i need the leftpeek properties of my soundchannel to achieve that but,for now, I do my test with a button that trigger this function:
private function setTiles(e: Event): void {
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++) {
grid.getChildAt(i).getTiles();
}
}
Right now, the problem is : I'm unable to acces my Tiles method. I did a trace on grid,getChildAt(i), and saw all instances of my tiles in the console. So, i know for sure that every instances of my tiles are stored in grid. But, I don't know why, grid.getChildAt(i).getTiles(); doesn't work (and every other method from Tiles). The error message is: Call to a possibly udefined method getTiles through a reference with static type flash.display:DisplayObject
Does someone know what i'm doing wrong ?
ps: I translated all my class name, var name, etc from french to
english to make the code clearer.

Your mistake is that getChildAt(...) method has a return type of DisplayObject which is neither dynamic (will not let you access random properties) nor it have DisplayObject.getTiles() method.
All you need is to tell the program that this object is actually of Tiles class:
private function setTiles(e:Event):void
{
// loop through all child element of a movieclip
for (var i: int = 0; i < grid.numChildren; i++)
{
// Cast display objects to Tiles class.
var aTiles:Tiles = grid.getChildAt(i) as Tiles;
// Call the method.
aTiles.getTiles();
}
}

Related

HitTest for objects not yet on Stage

I need to add a MovieClip to stage, the limitation being that it should only be added to an empty area on the stage. The stage itself either contains complex shapes or is manipulable by the user i.e. he can drag/move objects to change the empty area. The hitTest and hitTestObject methods need DisplayObject already available on the stage. What is the right way to go - the only solution I can imagine is having added my object on the stage and then repeatedly doing hit tests?
[Imagine it to something like adding sprites in a video game - they must spawn in empty regions; if they pop out from inside of each other, then it'll look really odd.]
Well, when you create a new class, just turn it off with a variable and set the visibility to false, then loop until there is no hitTest.
A silly example:
public class someClass extends Sprite
{
private var objectsOnStage:Array;
public function someClass(objectsArray:Array) {
objectsOnStage = objectsArray;
visible = false;
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event){
removeEventListener(Event.ADDED_TO_STAGE, init);
addEventListener(Event.ENTER_FRAME, SEARCH);
}
private function SEARCH(e:Event) {
var doesHit:Boolean = false;
x = Math.round(Math.random() * (550 - 0)) + 0;
y = Math.round(Math.random() * (400 - 0)) + 0;
for (var i:int = 0; i < objectsOnStage; i++) {
if (doesHit) break;
if (this.hitTestObject(objectsOnStage[i])) {
doesHit = true;
}
}
if (doesHit) return;
placedInit();
}
private function placedInit() {
visible = true;
removeEventListener(Event.ENTER_FRAME, SEARCH);
//now init the stuff you want.
}
}
You just check if bounding boxes of both clips overlaps. Like this:
import flash.geom.Rectangle;
import flash.display.MovieClip;
// create simple movie clips that has a rectangle shape inside
var sym1 : MovieClip = new Sym1();
var sym2 : MovieClip = new Sym2();
// get a rectanle of both clipt
var boundingBox1 : Rectangle = sym1.getBounds(this);
var boundingBox2 : Rectangle = sym2.getBounds(this);
// check if bounding boxes of both movie clips overlaps
// so it works like hitTestObject() method
trace( boundingBox1.intersects( boundingBox2) )
I know this post is super old, but in case it helps anybody --
If you need to do a hit test on a movieclip that isn't on the stage. A workaround is to rasterize it to a bitmap first.
var bitmapData:BitmapData = new BitmapData(mc.width, mc.height, true, 0x0000000);
bitmapData.draw(mc);
if (bitmapData.getPixel32(x, y) > 0) {
// Hit true.
}

collision between a particle array and a box

Im working on a school project and I need to get collision between an array of particles and a box, here is my current code:
for(var i : int = 0; i < particles.length; i++)
{
particles[i].update();
if(Particle.hitTestPoint(Square))
{
particle = particles.shift();
particle.destroy();
}
}
I get an error telling me that im trying to call an undefined method (hittestpoint) through a reference with static type class.
Any help would be greatly appreciated, thank!
Here is the code for the Particle.
package
{
import flash.display.*;
public class Particle
{
public var clip : DisplayObject;
public var xVel : Number = 0;
public var yVel : Number = 0;
public var drag : Number = 1;
public var gravity : Number = 0.0;
public var shrink : Number = 1;
public var fade : Number = 0;
public function Particle(symbolclass : Class, target : DisplayObjectContainer, xpos : Number, ypos : Number)
{
clip = new symbolclass();
target.addChild(clip);
clip.x= xpos;
clip.y= ypos;
}
public function update() : void
{
clip.x += xVel;
clip.y += yVel;
xVel *= drag;
yVel *= drag;
yVel+=gravity;
clip.scaleX *= shrink;
clip.scaleY *= shrink;
clip.alpha -= fade;
}
public function destroy() :void
{
clip.parent.removeChild(clip);
}
}
}
I used this tutorial for the particles, and my goal is to add collision to them
The Particle class must extend MovieClip to have the hitTestPoint method. Try changing the class to:
import flash.display.MovieClip;
public class Particle extends MovieClip
{
//code here
and import specific classes in your code (see MovieClip class as that's the one you are using and it's methods) for better results as good practice.
I'm not sure what particle refers to in that code since it's not defined in what you posted, but particles[i] is the particle you want to test.
Your Particle class doesn't have a hitTestPoint function- but it contains a DisplayObject that does. So with those two things in mind, you can call hit tests using particles[i].clip.hitTestPoint.
Next problem: you're trying to pass the class Square to the hitTest rather than an instance. And still, hitTestPoint takes a single point as its argument, not an object like what a square would represent. So you will probably want to use hitTestObject instead.
Edit: Updated my answer now that Particle code was posted.
Your Particle class doesn't define hitTestPoint. In addition to that, you are calling a static method which I don't think you want here... you want an instance method.
Make sure you define hitTestPoint in your Particle class. Then, instead of Particle.hitTestPoint, try particle.hitTestPoint (note upper case, which refers to the class is changed to lower-case which refers to the instance.
Try this:
for(var i : int = 0; i < particles.length; i++)
{
var particle:Particle = particles[i];
particles.update();
if(particle.hitTestPoint(Square))
{
particle = particles.shift();
particle.destroy();
}
}

AS3 draws bigger Sprite than i have set (padding/margin ?)

i created my own Button which simply extends from Sprite.
Its able to show 3 different text fading in and out using a timer.
In my draw function i first draw a rectangle with the Sprites graphics object representing the background of the button.
I added a function to set the button width. This property is used to draw the rectangle. I know that the sprites's size is updated after the rectangle drawing. But for some reason the sprite's width is always more than what i have set via my "setButtonWidth" function.
Now i have a simple sprite acting as a button having a graphics.drawRectangle part drawing a simple rect. with lets say 500px width. But when i trace the width of that button it is always about 10% more. Where are these 10% coming from?
I read about calling validateNow(). But this is only for Labels, or Checkboxes. For some reason i cannot access the library for Label. This must work somehow with TextFields. But how?
// this function is part of MyButtonClass.as file
function drawButton()
{
this.graphics.clear();
this.graphics.beginFill( currentBackColor);
this.graphics.drawRoundRect(0, 0, int(this.buttonWidth)+20, 30, 10, 10);
this.graphics.endFill();
}
// this code is in main action code frame
// the buttonWidth is set this way
stage.addEventListener( Event.RESIZE, updateBtn);
function updateBtn( event:Event)
{
// access the container in which the buttons are in like ...
for( var i = 0; i < buttonContainer.numChildren; i++) {
var btn = buttonContainer.getChildAt( i) as MyButtonClass;
// MyButtonClass is the class which extends from Sprite drawing my button
// and using buttonWidth to draw the backgrounds width.
btn.setButtonWidth( (stage.stageWidth / 2) - 20);
// i set half of stageWidth because i have two columns with a list of buttons
// after setButtonWidth is called in MyButtonClass, the draw function is called
// which does the graphics stuff above.
}
}
this.buttonWidth is set to 500. There is nothing special done on that sprite. No children to be added, only this drawing stuff. But this sets the sprite's width to 550 or something.
Define Button like this
package {
import flash.display.Sprite;
public class Button extends Sprite {
private var _buttonWith: int = 0;
private var _buttonHeight: int = 0;
public function Button() {
// constructor code
}
public function set buttonWidth(w: int): void {
_buttonWith = w;
updateButton();
}
public function get buttonWidth(): int {
return _buttonWith;
}
public function set buttonHeight(h: int): void {
_buttonHeight = h;
updateButton();
}
public function get buttonHeight(): int {
return _buttonHeight;
}
protected function updateButton() {
graphics.clear();
graphics.beginFill(0xff00ff);
graphics.drawRoundRect(0, 0, buttonWidth, buttonHeight, 10, 10);
graphics.endFill();
}
}
}
And create instance like this
var button:Button = new Button();
addChild(button);
button.buttonWidth = 100;
button.buttonHeight = 30;

AS 3.0 Dynamic Instance Names

Hi i made a custom class where i would like to create x instances of a movieclip. But the following doesn't work:
package {
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
public function CustomClass(amount:uint) {
var Collector:Array = new Array();
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
Collector.push(newMovieClip);
}
addChild(Collector);
}
}
}
I would like to position them on the timeline with
var customClass_mc:CustomClass = new CustomClass(10);
addChild(customClass_mc);
//try to trace the x position of one of the instances.
trace(customClass_mc.Collector[5].x);
I keep getting the error: Scene 1, Layer 'Layer 1', Frame 1, Line 5 1119: Access of possibly undefined property Collector through a reference with static type CustomClass.
Firstly, you need to declare Collector as public:
public var Collector:Array = new Array();
Your Collector is an array, not a display object, and so it can't be added to the display tree. Instead you would push each newMovieClip onto the display of Custom class and position them inside your for loop. Then you don't need the collector at all, because you can target the movieclips using getChildAt():
trace(customClass_mc.getChildAt(5).x);
I found another answer myself which i think is even better!
You don't need the container at all.
when you use the following
package {
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
public function CustomClass(amount:uint) {
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
newMovieClip.name = "clip"+i;
addChild(newMovieClip);
}
}
}
}
No i can acces the movieclips by using:
var customClass_mc:CustomClass = new CustomClass(10);
addChild(customClass_mc);
//try to trace the x position of the fifth instance.
trace(customClass_mc.getChildByName("child5").y);
The variable 'Collector' is only available inside the constructor the way you have it. Collector has to be made public to be accessible from outside the timeline. The best thing to do would be to make a public getter method to access this. So something like:
import flash.display.MovieClip;
public class CustomClass extends MovieClip {
private var Collector:Array = new Array();
public function get Collector():Array
{
return Collector;
}
public function CustomClass(amount:uint) {
//Add and position Tiles to stage.
for (var i:uint = 1; i <= amount; i++){
var newMovieClip:MovieClip = new MovieClip;
newMovieClip.y = amount * 10;
Collector.push(newMovieClip);
}
addChild(Collector);
}
}

How can I use Action Script 3.0 to make random placed Symbols fly by?

I'm trying to make a simple animation with Flash CS4 and Action Script 3.0 to make a number of Symbols fly by from right to left constantly. What I want is that once a symbol has reached the end of the screen it is destroyed and another one is placed at the start position.
I intend to give each symbol a random speed and create a random symbol each time one is 'destroyed'. Any clues where I can start?
As you seem new to flash as a platform I would think writing classes shouldn't be your first port of call when learning ActionScript. Definitely just play about on the timeline for now and learn the basics. As very simple solution to this, I would suggest creating a MovieClip in the library with a class name like 'MyBall'... then paste this onto the first frame of the main timeline et voila.
// Create some variables to store data
var numberOfBalls : int = 20;
var myBalls : Array = [];
var xVelocities : Array = [];
var maxXVelocitySpeed : Number = 5;
var minXVelocitySpeed : Number = 2;
// Add your orginal balls to the stage
for (var i : int = 0; i < numberOfBalls; i++)
{
var myBall : MyBall = new MyBall();
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
var xVelocity : Number = minXVelocitySpeed + (Math.random() * (maxXVelocitySpeed - minXVelocitySpeed));
myBalls.push(myBall);
xVelocities.push(xVelocity);
addChild(myBall);
}
// Add a listener for enter frame events
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//Run this code on every frame to move the balls and reposition them if they are off the stage
function enterFrameHandler(event : Event) : void
{
for each( var myBall : MyBall in myBalls)
{
var ballIndex : int = myBalls.indexOf(myBall);
myBall.x += xVelocity[ballIndex];
if (myBall.x > stage.stageWidth)
{
myBall.x = -(Math.random() * stage.stageWidth);
myBall.y = Math.random() * stage.stageHeight;
}
}
}
First, turn your symbols into MovieClips. Then create a base class MySymbol.as for your symbols, something like:
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class MySymbol extends MovieClip
{
public var speed:Number; // Pixels moved per frame
public function MySymbol(speed:Number, startPosition:Point)
{
this.speed = speed;
this.addEventListener(Event.ENTER_FRAME, update);
this.x = startPosition.x;
this.y = startPosition.y;
}
private function update():void
{
this.x -= this.speed;
if (this.x < 0 - this.width) { // We're at the left edge
this.removeEventListener(Event.ENTER_FRAME, update);
this.dispatchEvent(new Event(Event.COMPLETE));
}
}
}
}
Then make sure your movie clips are exported for AS3 (the "linkage" option on the item in the library). Make the class name for each item unique (e.g. MySymbol1, MySymbol2), and set the base class to MySymbol.
Your document class might look something like this:
package {
import flash.display.MovieClip;
import flash.events.Event;
import MySymbol; // Not strictly needed
public class DocumentClass extends flash.display.MovieClip
{
private static var SYMBOLS:Array = new Array(MySymbol1, MySymbol2);
public function DocumentClass()
{
// Create five symbols:
for (var i:int = 0; i < 5; i++) {
makeSymbol();
}
}
private function makeSymbol():void
{
// Pick a random symbol from the array:
var symType:Class = SYMBOLS[Math.random() * SYMBOLS.length];
// Construct the new symbol:
var loc:Point = new Point(stage.stageWidth, Math.random() * stage.stageHeight);
var sym:MySymbol = new symType(1 + Math.random() * 30, loc);
// Listen for the object hitting the left edge:
sym.addEventListener(Event.COMPLETE, remakeObject);
this.addChild(sym);
}
private function remakeObject(e:Event):void
{
e.target.removeEventListener(Event.COMPLETE, remakeObject);
this.removeChild(e.target);
// Replace the dead symbol:
makeSymbol();
}
}
}
It is a lot more efficient if instead of destroying and re-creating an object that flies off-stage you re-use the existing one and move it back to the right. But this is an optimization you can implement later, if things become slow.
Note that all the code above is UNTESTED and I have not coded AS3 in a while, so there's likely at least a few bugs in it. Hopefully it will serve as a good enough starting point.
Define a Circle (symbol) class that extends Sprite/Shape and has a velocity variable
Draw a circle (or whatever) with a random color
Math.floor(Math.random() * 0xffffff)
Assign a random value to velocity
minVelocity + Math.floor(Math.random() * velocityRange)
Create a start() method inside the Circle class that registers an enter frame handler
Increment this.y inside the enter frame handler, and dispatch a 'recycleMe' event if y is more than the max value.
Create N instances of Circle, addChild them, and call their start() methods.
listen to 'recycleMe' events on each of them, and reset the value of y from the handler.
Here's a few prompts to get you started.
MovieClips have an x and y property. If you were to add to these numbers over time you would see the MovieClip move along the x and/or y axis of the stage. Look into doing this using the Event.ENTER_FRAME which will allow you to change the values every time the screen is going to update.
Your stage will have a given width (a stageWidth property). You probably want to monitor when your MovieClip's x property is greater than the width of your stage. If it is remove (removeChild) it and add a new one (addChild) and place it back at the start x/y position.