I'm learning AS3 and creating a simple 'Asteroids' game.
I have written a simple class of linear movement:
package {
import flash.display.MovieClip;
import flash.display.Stage;
import flash.events.Event;
public class lin extends MovieClip {
private var vx:Number=0;
private var vy:Number=0;
public function lin(x:Number,y:Number,sr:Number,spd:Number)
{
this.rotation=sr;
vy+=Math.sin(degToRad(sr))*spd;
vx+=Math.cos(degToRad(sr))*spd;
this.x=x+vx;
this.y=y+vy;
addEventListener(Event.ENTER_FRAME,loop,false,0,true);
}
public function loop(evt:Event)
{
y+=vy;
x+=vx;
if (outOfBounds())
kill();
}
public function outOfBounds():Boolean
{
return (x>stage.stageWidth || x<0 || y>stage.stageHeight || y<0);
}
public function kill():void
{
if(parent)
parent.removeChild(this);
removeEventListener(Event.ENTER_FRAME,loop);
}
public function degToRad (deg:Number)
{
return deg * Math.PI / 180;
}
}
}
And I need to set this behaviour of movement to several objects (LaserBeam, Asteroids)
I created a new MovieClip with class 'LaserBeam' and wrote this:
package {
import flash.display.MovieClip;
import flash.display.Stage;
import lin;
public class LaserBeam extends MovieClip {
public var LaserBeamInstance:lin;
public var LaserSPD=15;
public function LaserBeam(x,y,r) {
LaserBeamInstance=new lin(x,y,r,LaserSPD);
}
}
}
But when I try to run my game, it says:
Line 1 1203: No default constructor found in base class lin.
What should I do to make many different MovieClips share one behaviour?
Thanks in advance!
UPD: all project files is here
Try calling super() from your lin constructor. This is because it inherits from MovieCLip. Properties such as this.rotation won't be initialized until you call the MovieClip constructor with super()
You should also make the class name 'Lin' to follow the standard naming convention.
public function Lin(x:Number,y:Number,sr:Number,spd:Number)
{
super();
this.rotation=sr;
vy+=Math.sin(degToRad(sr))*spd;
vx+=Math.cos(degToRad(sr))*spd;
this.x=x+vx;
this.y=y+vy;
addEventListener(Event.ENTER_FRAME,loop,false,0,true);
}
I think you should try:
public function lin(x:Number = 1,y:Number = 1,sr:Number = 1,spd:Number = 1)
I don't know exactly what's happening, but I think that you have pointed you class "lin" as base class for some of your symbols in your library.
So, that symbols try to construct themselves by calling "lin" constructor, but of cause without any parameters.
BTW, Golden rule:
-variable names should start from lowercase letter
-names of classes and constructors from uppercase
In answer to your question:
What should I do to make many different MovieClips share one behaviour?
You should read about OOP patterns. This book is a great introduction.
http://www.amazon.com/First-Design-Patterns-Elisabeth-Freeman/dp/0596007124
In response to your error, I think you are not giving us the whole picture. The error refers to class lin being extended, but in the code there are no classes extending lin.
Related
I have a weird problem in a game which I want to create. At first I have created a project without external classes.
On the root I have three Characters and one Level. Also there is a script for the key listeners and I have eventListeners to register the level, levelElements, coins and the characters. Then I have a CharacterControl MovieClip in the library. This MovieClip contains the character behaviour. As example walk, jump, idle, gravity if not colliding to the ground. There are also different events and eventListeners.
The scripts are on the timeline. If I call in both timelines a trace-function, the root was called before the CharacterController.
After that in my next exercise I created a document class Main. Now there are all root scripts. And for the CharacterController I also copied the timeline code and put it into an external class.
Now my problem is that the CharacterController class is called before the main class gets called. This leads to the problem that the eventListener and events can't get called in right order. There are happening a few errors. No Coin and no Character collides on the ground or a plattform. Everything is falling down.
How can I achieve that the Main gets called at first? Should I remove the characters and create them by script?
EDIT:
Ok, I give a short example which shows the basic problem without the complex code of my game.
package {
import flash.display.MovieClip;
public class Main extends MovieClip {
public function Main() {
trace("main was called");
}
}
}
package {
import flash.display.MovieClip;
public class My_Circle extends MovieClip {
public function My_Circle() {
// constructor code
trace("circle was called");
}
}
}
Here are some pictures of the configuration and structure of my project:
I need Main called as first. I think it's a basic problem in as3.
You'd make the class file of your stage Main.as in the properties pane.
Edit: Interesting. Just replicated this. I believe then that flash/air constructs elements so they're ready to be put on the stage instead of constructing the stage first and elements after. You should put the code you want to execute for your circle in some sort of init function and execute it in.
package
{
import flash.display.MovieClip;
public class Main extends MovieClip
{
public function Main()
{
super();
trace("Hello");
(circle_mc as Circle).init();
}
}
}
Circle:
package
{
import flash.display.MovieClip;
public class Circle extends MovieClip
{
public function Circle()
{
super();
}
public function init():void
{
trace("World");
}
}
}
ok, I figured out a simple solution how you can make it by code. At first I think it's a better solution to create the object (circle, character, whatever) by code.
The timeline code:
import flash.events.Event;
Main.setStageRef(this.stage); //Super-important
stop();
The Main class code:
package {
import flash.display.MovieClip;
import flash.display.Stage;
import flash.display.DisplayObject;
import flash.events.Event;
public class Main extends MovieClip {
public static var stageRef : Object = null;
var movieClipExample : My_Circle;
public function Main() {
this.addEventListener(Event.ENTER_FRAME,this.afterMainTimeLine);
stage.addEventListener("myEvent", myEventFunction);
}
public function afterMainTimeLine(e:Event) {
trace("Hello");
movieClipExample = new My_Circle;
movieClipExample.init();
stageRef.addChild(movieClipExample);
removeEventListener(Event.ENTER_FRAME, this.afterMainTimeLine);
this.addEventListener(Event.ENTER_FRAME,this.updateFunction);
}
public function updateFunction(e:Event){
movieClipExample.moveFunction();
}
public function myEventFunction(_event: Event) {
trace("myEvent was triggered");
}
//Getter/setter for stage
public static function setStageRef(_stage : Object) : void
{
stageRef = _stage;
}
public static function getStageRef() : Object
{
return stageRef;
}
}
}
The Object code (as example My_Circle):
package {
import flash.display.MovieClip;
import flash.display.Stage;
import flash.display.DisplayObject;
public class My_Circle extends MovieClip {
public function My_Circle()
{
stageRef = Main.getStageRef();
trace("World");
this.x = x;
this.y = y;
var evt = new Event("myEvent", true);
stageRef.dispatchEvent(evt);
}
public function init():void
{
trace("Created Circle");
this.x = 300;
this.y = 100;
}
function moveFunction(){
this.y += 1;
}
}
}
The output is following:
Hello
World
myEvent was triggered
Created Circle
Maybe this could be helpful for others.
Just one thing. For different objects from the same class it's better to use an array. The position (maybe) should be random.
hi I'm relatively new to as3 (this year) and I'm getting this error
typer error #1009 cannot access a property or method of a null object
reference. at FoodObject/collisionTest()
i was hoping anyone could help
package {
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.events.*
import flash.utils.*
import flash.display.Stage;
public class GameScene1 extends Scene {
//public variables
//character & scenery
public var mainChar: Character;
public var testFood: FoodObject;
//constructor is used to create all necessary objects for this scene and display them
public function GameScene1(gm_: Manager) {
//constructor
super(gm_);
trace("GameScene 1 constructor");
//character
mainChar = new Character;
addChild(mainChar);
mainChar.x = 200;
mainChar.y = 200;
testFood = new FoodObject;
addChild(testFood)
testFood.x = 50
testFood.y = 200
the food object class is here.
package {
import GameScene1
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
public class FoodObject extends MovieClip {
public var Game1:GameScene1;
public function FoodObject() {
//constructor code
this.addEventListener(Event.ENTER_FRAME, collisionTest)
}
public function collisionTest(e:Event)
{
if (this.hitTestObject(Game1.mainChar))
{
trace("it works")
}
}
}
}
game manager here:
package {
import flash.display.MovieClip;
public class Manager extends MovieClip {
//stores which scene is currently loaded
public var curScene:Scene=null;
public function Manager() {
//constructor
trace("Manager Construct")
GoToScene("menu");
}
public function GoToScene(name:String)
{
if (curScene) //there was a scene already
{
curScene.OnLeaveScene(); //call its OnLeaveScene function to remove all objects
removeChild(curScene);
}
if(name=="menu") curScene = new MenuScene(this);
if(name=="select") curScene = new SelectScene(this);
if(name=="game1") curScene = new GameScene1(this);
if(name=="game2") curScene = new GameScene2(this);
if(name=="game3") curScene = new GameScene3(this);
if(name=="credit") curScene = new CreditScene(this);
addChild(curScene);
}
}
Your problem is that the concerns of your classes are not separate:
Your Scene knows both the Character and the Food object, you instantiate both classes there, nothing wrong with that.
The problem starts when you are trying to do something in the Food object, that requires knowledge of the character. The thing is: the Food object doesn't know anything about the Character.
You can solve this by simply passing the reference of Character to your Food object. In order to do this, modify the constructor like so:
private var character:Character;
public function FoodObject(character:Character) {
//constructor code
this.addEventListener(Event.ENTER_FRAME, collisionTest)
this.character = character;
}
The usage of said constructor in your Scene changes as follows:
testFood = new FoodObject(mainCharacter);
This way, Food knows the character and can do stuff with it, for example do collision tests:
public function collisionTest(e:Event)
{
if (this.hitTestObject(character)) // ==== this line changed
{
trace("it works")
}
}
However, this raises an important issue: Why should Food know the Character at all?
Sure enough, you want to do the collision test, which requires both objects.
But why do you want to do it in the Food object?
Doing the collision check in Food is cumbersome, because you have to pass a reference to Character in order to do it there.
The much preferred way of doing this is to do the collision check where both objects participating in the check are already known.
In your case, this is the Scene.
Think about how easy it is to do the check in Scene:
testFood.hitTestObject(mainCharacter);
It's that simple, because everything you need is already there.
To recap:
The collision check requires knowledge of 2 objects that you want to
check.
In order to do the check in either one, you have to pass a reference
of the other. (Character to Food as seen above or the other way
round)
It is a lot easier to do the check in some place that already knows
both objects, because no reference have to be passed around.
Your original code failed because Game1 in FoodObject is never assigned a value and therefore remains null.
Invoking methods on null causes the error you experienced.
You forgot to take the instance of GameScene1 class using new keyword.
public var Game1:GameScene1;
public function FoodObject() {
//constructor code
var _manager:Manager = new Manager();
Game1 = new GameScene1(_manager)
this.addEventListener(Event.ENTER_FRAME, collisionTest);
}
public function collisionTest(e:Event):void{
....
}
i'm trying to create a flash game, but i'm having trouble with my classes. i'm importing the classes on the main script. they're imported okay, i've tested it all. but i dont know how to use classes inside another class
this is what i've got so far:
class Class.Player {
public static var self:MovieClip;
public static var bullet:Class.Bullet;
function controls() {
//Shoot
if (Key.isDown(Key.SPACE)) {
bullet = new Bullet(100, 100);
}
}
it loads the class and declares the variable, but it claims "Bullet" is not an existing method. it is an existing method, and it works when i call it from the main script.
also, do i need to declare the class like public static var bullet:Class.Bullet;? it's the only way that works for me but wondered if there's a better way?
It looks like you are having problems with the naming of your classes. You don't have to call your classes Class.Name. Just use Name.
In your case, you could have a file called
Player.as
With this content:
package {
import flash.display.MovieClip;
public class Player extends MovieClip {
public private var bullet:Bullet;
public function Player(){
//constructor
}
function controls() {
//Shoot
if (Key.isDown(Key.SPACE)) {
bullet = new Bullet(100, 100);
}
}
}
}
And then a file called
Bullet.as
With this content:
package {
import flash.display.MovieClip;
public class Bullet extends MovieClip {
public function Bullet(x:Number, y:Number){
//constructor
}
}
}
You can read more information about creating and using custom classes here: http://www.flashandmath.com/bridge/intro/class.html
You're putting your package information in the Class name. I have no idea how this is even compiling for you (especially given that Class is a reserved word.
Your files should look more like:
package somePackage {//somePackage instead of the reserved word Class you were using
import someOtherPackage.Bullet;
public class Player extends MovieClip {
public var bullet:Bullet;
public function Player():void {
super();
//other constructor logic here
}
}
}
I have this code:
package graphics {
import flash.display.Sprite;
import flash.events.*;
public class Ball extends Sprite {
public function Ball(_stage){
_stage.addChild(this);
drawBall();
}
private function drawBall(){
graphics.beginFill(0x0000CC);
graphics.lineStyle(2,0x000000,1);
graphics.drawCircle(0,0,10);
graphics.endFill();
}
}
}
ADDED:
and the class that I pass to mxmlc:
package {
import flash.display.Sprite;
import graphics.*;
[SWF(width='1024', height='768', backgroundColor='#FFFFFF', frameRate='30')]
public class Application extends Sprite {
public function Application(){
var ball:Ball = new Ball(this);
}
}
}
Except that when I compile, I get the following error:
ball.as(11): col: 14 Error: Call to a possibly undefined method beginFill.
graphics.beginFill(0x0000CC);
^
Along with the other three graphics.x() calls.
I am probably doing something wrong here, but I do not know what. Do you?
Just use this.graphics.method to avoid confusions created by your package name.
Edit - after comment.
After some messing around, it seems that though this.graphics traces as a correct Graphics object as expected, the this.graphics.method is looked in the Ball class. Don't know what messes this up like this, but it can be solved with a simple casting.
package graphics {
import flash.display.Graphics;
import flash.display.Sprite;
import flash.events.*;
public class Ball extends Sprite {
public function Ball(_stage) {
_stage.addChild(this);
drawBall();
}
private function drawBall() {
var g:Graphics = this.graphics as Graphics;
g.beginFill(0x0000CC);
g.lineStyle(2,0x000000,1);
g.drawCircle(0,0,10);
g.endFill();
}
}
}
Good luck
I am afraid the problem must be elsewhere in your code. If I take your class and clean it up so it compiles like so:
package
{
import flash.display.Sprite;
public class Ball extends Sprite
{
public function Ball()
{
drawBall();
}
private function drawBall():void
{
graphics.beginFill(0x0000CC);
graphics.lineStyle(2,0x000000,1);
graphics.drawCircle(0,0,10);
graphics.endFill();
}
}
}
$ mxmlc Ball.as
Loading configuration file [...]/flex-config.xml
/private/tmp/actionscript/Ball.swf (666 bytes)
It draws a blue ball in the top left corner of the screen. So your problem with graphics must be related to code which you have not shown here.
Edit: Based on the new information:
Your namespace graphics for the class Ball conflicts with the property name graphics. You need to rename the package and the directory it lives in.
Your class can not add itself to the stage. It has no reference to the stage until it has been added to the display list. Your application class needs to both create the ball and add it to the stage.
Also, it's just stage, not _stage.
Is it possible to use EventListener to Listen to a variable and detect when the value of that variable changes? Thanks.
This is quite easy to do if you wrap it all into a class. We will be using getter/setter methods. The setter method will dispatch and event whenever it is called.
(Note: Setters and Getters are treated like properties). You merely assign a value, as opposed to calling a method (e.g someVar = 5 instead of someVar(5); Even though setters / getters are functions/methods, they are treated like properties.
//The document class
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
public Class TestDocClass extends Sprite
{
private var _model:Model;
public function TestDocClass():void
{
_model = new Model();
_model.addEventListener(Model.VALUE_CHANGED, onModelChanged);
}
private function onModelChanged(e:Event):void
{
trace('The value changed');
}
}
}
//The model that holds the data (variables, etc) and dispatches events. Save in same folder as DOC Class;
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Model extends EventDispatcher
{
public static const VALUE_CHANGED:String = 'value_changed';
private var _someVar:someVarType;
public function Model():void
{
trace('The model was instantiated.');
}
public function set someVariable(newVal:someVarType):void
{
_someVar = newVal;
this.dispatchEvent(new Event(Model.VALUE_CHANGED));
}
}
}
#BrianHodge: How do you actually use your example? How do you call the set function? How do you refer to it? Where do pass the variable to be changed..?
Let's say if I want to change the wrapped variable with a button click, for example.
I have to confess that I tried some other codes and example (getter/setter) type, with dispatchEvent or without,and I can't get over it! But your example seems to be exactly what I need, just can't make it work.
I get the The model was instantiated when I set the function as document class. That's all.
I found out, at last, for people like me who are loosing time with this dispatch thing!
In my case the _someVar variable has to be data typed as a String (same thing for fornewVal).
OnceTestDocClass is set as your document class; you refer to the Model instantiated like this:
_model.someVariable="new stuff";
I was trying to change the value like this:
_model.someVariable("new stuff");
You can add some trace actions in the Model class to have a clear demo in the output panel:
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
public class Model extends EventDispatcher
{
public static const VALUE_CHANGED:String = 'value_changed';
private var _someVar:String = "default";
public function Model():void
{
trace('The model was instantiated.');
}
public function set someVariable(newVal:String):void
{
trace ("before " + _someVar);
_someVar = newVal;
trace ("after " + _someVar);
this.dispatchEvent(new Event(Model.VALUE_CHANGED));
}
}
}
It's not much, but these things can cost some people a whole lot of time =)
You used to be able to do something similar in AS2 using Object.watch. I don't see a direct equivalent, but it looks like mx.binding.utils.ChangeWatcher will give you similar functionality for any variables that are bindable.
I don't know of a way to do it in AS3 for non-bindable variables, but if the variable you want to watch is bindable (or if you can modify it to be binadable) then ChangeWatcher might give you what you want.