Object Undefined but it seems to be Defined actionscript - actionscript-3

First I am still new to coding so take it easy on me. Bit of back tracking before I show the code. I got a book on how to make games with ActionScript 3, only thing is it mainly works under the assumption your working with Photoshop & Flash Builder together rather than adobe animate/flash. So it has you do things like embed images, and I'm not sure how to change the code to define the images, which is exactly the problem. However at the same time, the stage is coming up as undefined access! I really, I mean how can the stage be undefined... Well any way, Heres an example of one of the Undefined Properties:
///
class BetterScrolling extends Sprite
var ForegroundImage: Class;
var foregroundImage: DisplayObject = ForegroundImage();
var foreground = ForegroundImage;
///
Theres some code in-between.
///
function BetterScrolling(){
foreground.x = -((foreground.width - stage.width) / 2);
foreground.y = -((foreground.height - stage.height) / 2);
}
///
The point of this code is to create a scrolling Background. However I'm kinda complicating it because I have an X and an Y variable, yet I also have a slowed background in addition to my foreground. I will now link to the complete code as is currently, http://pastebin.com/WHg9DGsB Every change in that code I made with a reason in mind, even if it is a stupid one... so please don't be distracted by the other errors that I'm sure are in there... for now just the defining of my images.

There a number of issues that will keep your code from compiling.
Stage is not available when your class loads. Any code that is not inside a function will run when the application is initialized (before anything is even seen on the screen). Your error is because of these lines:
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
Those lines are floating in your class declaration when stage does not yet exist.
What you want to do is move that code (and any code that isn't a variable or function definition) into a function. If you want it to run immediately, put it in your constructor (the function named the same as your class). The stage however is also not always available yet in your constructor, so to be safe, you should do the following:
function BetterScrolling() {
if(!stage){
this.addEventListener(Event.ADDED_TO_STAGE, stageReady);
}else{
stageReady();
}
}
function stageReady(e:Event = null):void {
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
//any other code that needs the stage and should run as soon as possible
}
You have a bunch of brackets and code that don't seem to belong anywhere. Lines 45 - 71 will not compile properly. Most likely, you want that code in your constructor/stage ready method as well, so adding that in, you would have something like this:
function BetterScrolling() {
if(!stage){
this.addEventListener(Event.ADDED_TO_STAGE, stageReady);
}else{
stageReady();
}
}
function stageReady(e:Event = null):void {
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandHandler);
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
foreground.x = -((foreground.width - stage.width) / 2);
foreground.y = -((foreground.height - stage.height) / 2);
background.x = -((background.width - stage.width) / 2);
background.y = -((background.height - stage.height) / 2);
cloud.x = -((cloud.width - stage.width) / 2);
cloud.y = -((cloud.height - stage.height) / 2);
character.x = 370
character.y = 320
rightInnerBoundary = (stage.stageWidth / 2) + (stage.stageWidth / 4)
leftInnerBoundary = (stage.stageWidth / 2) + (stage.stageWidth / 4)
topInnerBoundary = (stage.stageHeight / 2) + (stage.stageHeight / 4)
bottomInnerBoundary = (stage.stageHeight / 2) + (stage.stageHeight / 4)
}
You have vars that are blank classes, then you try to instantiate them
var BackgroundImage: Class;
var backgroundImage: DisplayObject = BackgroundImage();
This will cause a runtime error because BackgroundImage is null/undefined. Most likely, you want to export a library object in FlashPro as a class? To do that, right/alt click the object in question in the library, and go to it's properties. Check off 'export for actionscript' and give it a class name. In this case, call it BackgroundImage. Then you replace the above code with:
var backgroundImage:BackgroundImage = new BackgroundImage();
As an aside, it safer to only declare you class vars that reference other objects/classes and instantiate them in the constructor. To change it like that, change the above line to this:
var backgroundImage:BackgroundImage;
Then in your constructor, do:
backgroundImage = new BackgroundImage();

First, DisplayObject.stage property is defined only when the DisplayObject in question is attached to display list. As a general policy, never try to access stage in the constructors. You need to subscribe a certain event that will tell you when stage is really available.
public function IWannaStage()
{
// WRONG
x = stage.stageWidth >> 1;
y = stage.stageHeight >> 1;
// RIGHT
if (stage) onStage();
else addEventListener(Event.ADDED_TO_STAGE, onStage);
}
private function onStage(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, onStage);
// Stage is really available from this point on.
x = stage.stageWidth >> 1;
y = stage.stageHeight >> 1;
}
Second, I think you're trying to create instances incorrectly. If ForegroundImage is a class, then:
A = ForegroundImage; // Wrong, you assign the reference to the class.
B = ForegroundImage(); // Wrong, this actually is type casting.
C = new ForegroundImage(); // Correct way to create a new instance of a class.
D = new ForegroundImage; // If constructor does not need any arguments you can omit brackets.

Related

AS3 tracking and using coordinates of a rotated object

How does one track and use the coordinates of an object that is rotated on initialization?
Let's say I have a sword that is put on the stage in Main init(); and rotated (adjusted) so that it would look ok together with the character perspective. In another class however, I am making the sword rotate some more on a keypress timer event so to create a 'swing' animation.
All this is done through flashdevelop. I only used CS6 to create the symbols. And as this 'swing' is happening, I want to add another symbol onto the tip of the sword which is a collision point object. It's being added to the stage when the swing starts and removed after every swing. I want this object to follow the very tip of the sword, yet it seems like I can only achieve that it follows the coordinates of the original sword object, as if I hadn't initially modified the rotation of the said sword. I tried to implement GlobalToLocal() and LocalToGlobal() methods, but I don't think I fully understand what is happening with that.
I hope I'm being clear enough of what I'm trying to do. Thank you. This is the relevant code in question. The code is as was before I tried the two mentioned methods and the issue currently is exactly as described before that. Do I want any of those methods or am I just doing something else wrong?
Main initialization:
sword = new Sword();
sword.x = 53;
sword.y = 90;
addChild(sword);
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
Coll_Point = new coll_point();
The class that deals with the swing has a method like this:
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Main.MazeNr1.addChild(Main.Coll_Point);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
Edit:
A more holistic version of the code:
public class Main extends MovieClip
{
public static var from_point:Point = null;
public static var to_point:Point = new Point();
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Puts everything on the stage here.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
PlayerInst = new Dorf();
PlayerInst.x = 45;
PlayerInst.y = 51;
addChild(PlayerInst);
sword = new Sword();
sword.x = 53;
sword.y = 90;
sword.rotationZ = -150;
sword.rotationY = 25;
sword.rotationX = -15;
from_point = new Point (Main.sword.width, Main.sword.height);
to_point = sword.localToGlobal(from_point);
addChild(sword);
swordBD = new BitmapData(32, 32, true, 0x0000000000);
swordBD.draw(sword);
Coll_Point = new coll_point();
Coll_PointBD = new BitmapData(2, 2, true, 0x0000000000);
Coll_PointBD.draw(Coll_Point);
}
}
This is how the Main looks like and literally every single object instantiation is added onto the stage this way. Including collision points, background, characters, gradient fills of line of sight radius, etc. And the relevant symbol class goes somewhat like this:
public class Creature extends MovieClip
{
protected var Swing:Boolean;
private var SwingTimer:Timer = new Timer (5, 0);
private var SwingBckTimer:Timer = new Timer (150, 1);
// Constructor.
public function Creature()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
// Initializer.
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
SwingTimer.addEventListener(TimerEvent.TIMER, SwingTime);
SwingBckTimer.addEventListener(TimerEvent.TIMER, SwingBack);
}
private function SwingAction():void
{
if (Swing == true)
{
SwingTimer.start();
}
}
private function SwingTime(event:Event):void
{
Main.Coll_Point.x = Main.sword.localToGlobal(Main.from_point).x;
Main.Coll_Point.y = Main.sword.localToGlobal(Main.from_point).y;
Main.sword.addChild(Main.Coll_Point);
trace(Main.Coll_Point.x);
trace(Main.Coll_Point.y);
if (Main.sword.rotationZ > -330)
Main.sword.rotationZ -= 20;
if (Main.sword.rotationX < 15)
Main.sword.rotationX += 10;
if ((Main.sword.rotationZ == -330) && (Main.sword.rotationX == 15))
{
SwingTimer.stop();
SwingBckTimer.start();
}
}
private function SwingBack(event:Event):void
{
Main.sword.rotationZ = -150;
Main.sword.rotationX = -15;
//Main.MazeNr1.removeChild(Main.Coll_Point);
}
There is also a rather long update(); function that animates and moves every single object that needs moving.
I think your problem might be in
Main.Coll_Point.x = Main.sword.x + Main.sword.width;
Main.Coll_Point.y = Main.sword.y + Main.sword.height;
Coll_Point expects global coordinates.
The parts + Main.sword.width and + Main.sword.height only work as expected if the sword is not rotated so that height is aligned with the y-axis and width with the x-axis.
You should use localToGlobal() on the position that is local to Main.sword (
Main.sword.width, Main.sword.height) to get the global position that represents the swords rotated tip before you add it as a child.
There are two ways you can approach this (you seem to have somewhat combined both). You can either
Add the Coll_Point as a child to something above the sword in hierarchy (Stage, MazeNr1, ...) and update the position manually every timer callback. You would have to recalculate the position everytime, so take the localToGlobal() from init to your timer function. It won't update if it doesn't get called.
For that you should have this kind of code in the timer callback:
var local:Point = new Point(Main.sword.width, Main.sword.height);
var global:Point = Main.sword.localToGlobal(local);
Main.Coll_Point.x = global.x;
Main.Coll_Point.y = global.y;
Add the point as a child to the sword. This might be a better approach as then the position will be updated automatically. What I did not remember before was that you then give the coordinates in "local" form, so do not use localToGlobal()
Run this once where you create the Collision_Point:
Coll_Point.x = <your x offset>;
Coll_Point.y = <your y offset>;
Main.sword.attachChild(Coll_Point);
Instead of sword height and width you might want to try something like -height and width/2.
Here is a quick (and not the prettiest) picture to demonstrate the problem. Local space is rotated with the object:
The only thing I can imagine to help you with this problem is to have your collision object have the same registration point as the sword. What I mean is that the orientation points should match, and the collision graphic should be moved inside of the sprite so that it matches the position of the top of the sword.
This way you can put the collision object at the very same location of the sword and apply the very same rotation. This way it will move along with the top of the sword and still have hitTest working properly.
I cannot imagine any other way to figure this out as any code will get bounds and positions. But the real thing that matters is the registration point and the top of the sword, which is a graphic thing and cannot be dealt with coding.
I hope you can imagine what I mean - if now, just say and I will provide an image for you :)

Simple variables in AS3 not increasing properly

Alright, so i've got some code that says that if a hit test is preformed, a variable is increased by 1. However, when I run it, I get crazy numbers like 1,1,1,2,5,3,2,5,2,3,4,1,1
There's no pattern, it's just random. Here's the code:
public function loop(e:Event)
{
y += speed;
if (y > stage.stageHeight)
{
setupAsteroid(false);
}
//hittest for the asteroid
if (hitTestObject(target))
{
stageRef.addChild(new Explosion(stageRef, x, y));
trace("Hit");
kills ++;
trace(kills);
if(kills == 3){
trace("Success");
}
}
I've included all the code in the function used, and not all of it is necessary to the hit test as you can see. also, the variable is declared correctly as
private var kills:Number = 0;
inside the same .as file and class.
You add an Explosion to the stage but there is no sign of you removing anything from the stage, or at least stopping the check for hitTest. Also I'm guessing that your function loop(e:Event) listens for an ENTER_FRAME event and in that case if you're not removing anything from the stage that hitTest is going to be returning true every frame and creating a new Explosion every frame. Why not just add a public variable to target, like public var destroyed:boolean = false; and reconstruct your hitTest to read if(hitTestObject(target) && !target.destroyed) and in the body of your conditional just add target.destroyed = true;
More code would definitely help though.

Creating Multiple Levels AS3

Hey Guys so I'm having a little trouble creating Multiple levels. I'm not so sure if im creating them the right way but i have a player and goal_1, goal_2, etc.. Basically when the player hitTestObject the goal_1 i want it to go to a new function called level_2 then level_3 after that hitTest. so Level_1 works just fine the hitTest works and it initializes level_2 but when i try to hitTest the player and goal_2 or even goal_1 again it just goes through it and doesnt do anything.
I understand now that level_2 isnt being called every frame like level_1 since its not part of the Enter_Frame listener. But i cant figure out how to have multiple Enter Frame events and not have them run simultaneously. If thats even the right way to create multiple levels.
Can you see what i could do in order to make it work?
private function gameLoop(e:Event):void
{
playerShoot();
playerControl();
playerStageBoundaries();
checkEndGameCondition();
checkPlayerOffScreen();
level_1();
}
private function level_1():void
{
if(player.hitTestObject(mGoal_1))
{
trace("Goal_1 Collision");
//Remove button for constant movement
btnShootPlayer = false;
mGoal_1.destroyGoal_1();
player.destroyPlayer();
//Update High Score text
nScore += 10;
updateHighScore();
stage.removeEventListener(Event.ENTER_FRAME, gameLoop);
//Update level
nLevel++;
updatePlayerLevel();
level_2();
}else
{
checkEndGameCondition();
}
}
public function level_2():void
{
stage.addEventListener(Event.ENTER_FRAME, gameLoop);
TweenMax.to(mGoal_1, 1, {y:40, repeat:-1, yoyo:true, ease:Power0.easeInOut});
trace("Level_2 Initiated");
//Keep Text Scores initiated
updateHighScore();
updatePlayerLives();
player = new mPlayer();
stage.addChild(player);
player.x = (stage.stageWidth / 2) - 280;
player.y = (stage.stageHeight / 2);
mGoal_1 = new goal_1();
stage.addChild(mGoal_1);
mGoal_1.x = (stage.stageWidth / 2) + 300;
mGoal_1.y = (stage.stageHeight) - 35;
if (player.hitTestObject(mGoal_1))
{
trace("Level 2 Hit test works!");
nScore += 10;
updateHighScore();
}
}
I didn't read too carefully all the code, but I guess you can use a function variable. Declare it on class level (outside any function):
var _doFunction:Function;
than, instead calling level1 function, pass the reference and call the _doFunction:
_doFunction = level1;
_doFunction();//or _doFunction.call(); - see Adobes documentation
when you are done with the level1, than pass the next level:
_doFunction = level2;
P.S. don't forget to accept the answer if it helped to solve your problem.

Why doesn't my custom FlxSprite appear on the stage?

I created a custom FlxSprite for a fireball by creating a new class(Fireball.as) and typing "extends FlxSprite" after the class name. In the constructor, it calls a function called "shoot()", which directs it to the target and plays a sound:
private function shoot():void {
dx = target.x - x;
dy = target.y - y;
var norm:int = Math.sqrt(dx * dx + dy * dy);
if (norm != 0) {
dx *= (10 / norm);
dy *= (10 / norm);
}
FlxG.play(soundShoot);
}
Then, I created an array to hold all the fireballs in the game state(PlayState.as):
public var fire:Array = new Array();
Then in my input() function, when someone pushes the mouse button, it pushes a new Fireball in the array:
if (FlxG.mouse.justPressed()) {
fire.push(new Fireball(x, y, FlxG.mouse.x, FlxG.mouse.y));
}
But when I test it, it only plays the sound, but the fireball doesn't show up. I'm guessing that the fireball is somewhere offstage, but how do I fix it?
If it helps, here's the constructor for Fireball.as:
public function Fireball(x:Number, y:Number, tar_x:Number, tar_y:Number) {
dx = 0;
dy = 0;
target = new FlxPoint(tar_x, tar_y);
super(x, y, artFireball);
shoot();
}
I think you must add them to stage.
if (FlxG.mouse.justPressed()) {
var fireball:Fireball = new Fireball(x, y, FlxG.mouse.x, FlxG.mouse.y);
fire.push(fireball);
// This line needs to be changed if you add the fireball to a group,
// or if you add it to the stage from somewhere else
add(fireball);
}
Let me know if it worked for you.
As stated before, the problem was due to not adding the FlxSprite to the Flixel display tree. But also I would advise you to use FlxObject groups instead of a simple Array.
FlxGroups are way better data structure than arrays. For instance they inherit all the methods from a FlxObject, so you can check for collisions directly against the group, also they have specific methods to handle the collection of objects, such as countDead and countAlive (if you make you of the alive attribute), recycle, getFirstDead and so on.
Check it out, you won't be disappointed. :)

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.