AS3 collision with an eventual object - actionscript-3

I still did not understand what to do in situations like this...
I'm trying to develop a simple maze game where you can't touch the walls or you'll lose.
I've created a player class and added a child of it using stage.addChild(player) in a Main class.
I have also put a walls object graphically on the stage... When i run the game, it says of course that walls is an undefined property. Then how should I explain this "EVENTUAL" walls presence in the class Player --> this.hitTestObject(XXX)?

You may check collision in your main class
var player:Player;
var walls:Array;
function checkCollistion():void
{
for each (var wall:YourWallClass in walls)
{
//here is the simplest wall to do check hitTestObject
//you can optimize it
if (player.hitTestObject(wall)
{
}
}
}
Or you could keep a walls reference in your player class. So when you create a playe instance with the walls define in the main class.
public class Player
{
private var walls:Array;
public function Player($walls:Array)
{
walls = $walls;
}
}

Related

Error with hitTestObject

I am trying to make a game similar to the world's hardest game, but I have trouble with the hitTestObject block. This is my code for the enemy mi:
package {
import flash.display.MovieClip;
import flash.events.Event;
public class enemys extends MovieClip {
public function enemys() {
stage.addEventListener(Event.ENTER_FRAME, hittrue)
}
public function hittrue(event:Event) {
if (this.hitTestObject(?)) {
while (numChildren > 0) {
removeChildAt(0)
}
gotoAndStop(2)
}
}
}
}
I don't know what to put into the question mark. When I put the instance name of my player, it says that it is undefined.
You're getting an error because enemys (sic) doesn't appear to have access to any sort of player instance.
You should move the hit testing out of the enemys class to somewhere you have access to both enemys and the instance of player. A good place for this would be some kind of GameEngine class.
How do you put the instance name of player? Do you pass the instance through the constructor?
From you main class you need to send a instance of your player to the enemys class.
(by the way plural for enemy is enemies)
public class Enemies extends Sprite{
private var player:PlayerClass;
public function Enemies(p:PlayerClass) {
stage.addEventListener(Event.ENTER_FRAME, hittrue);
player = p;
}
Then in you can put 'player' where the ? is. And in yout main class you would have something like:
var enemies:Enemies = new Enemies(player);
I changed MovieClip to Sprite. This is your choice but it is sometimes better to use Sprites because it will be faster then MovieClip. You may want to look into them especially if you are going to have multiple enemies on the stage at a time.
Another thing is the design of you ENTER_FRAME event.
You do not want multiple Enter_Frame events going on in multiple classes. A good design is to have one in your main class. Then from classes that need a clock cycle, call update methods on these objects in the main class's ENTER_FRAME event.
so in your main class's ENTER_FRAME event you would call:
enemies.hittrue();
Instead of having an EnterFrame event in you enemies class. This will also make it much easier to pause your game.
And as The other answer suggests. Your Collision detection should really be outside of your Enemies Class. But, this is how you would pass a reference of you player to another class.

universal collision detection for action script 3

I'm writing a game where you have to go through a maze. I want this game to have different levels. But for each level, the maze is going to be different. So I drew other walls. But I do not want to write my collision detection method 50 times if I have 50 different levels.
I thought of a way of fixing it, but it's not working. I created a new symbol with nothing in it and named it wall. I think that I can make my wall = wall1 (another symbol I converted, and exported for as), and just do stage.addChild(wall). But I can't find a way to do that. So I need help!
Make a generic class e.g. Wall and make your library symbols use that for their base class. You won't need to create them at runtime using ActionScript for this inheritance to work, you can still just place your MovieClips on the stage.
The next thing you need to do is store these Walls somewhere. Because you seem inexperienced with ActionScript, and want to avoid writing code for new levels, you can automate this process using a manager type class. We will call this class WallManager and it will look like this:
public class WallManager
{
private static var _walls:Vector.<Wall> = new <Wall>[];
internal static function register(wall:Wall):void
{
_walls.push(wall);
}
public static function reset():void
{
_walls = new <Wall>[];
}
public static function get walls():Vector.<Wall>{ return _walls; }
}
Then we'll create your Wall class. Within the constructor for this class, we will automatically have the Wall add itself into the WallManager listing:
public class Wall extends Sprite
{
public function Wall()
{
WallManager.register(this);
}
public function touchingMouse(mouseX:int, mouseY:int):Boolean
{
// For this example I am checking for collisions with the
// mouse pointer. Replace this function with your own collision
// logic for whatever it is that is supposed to collide with
// these walls.
if(parent === null) return false;
var bounds:Rectangle = getBounds(parent);
return bounds.contains(mouseX, mouseY);
}
}
This setup is not 'best practice', but it is suitable in your situation because your project seems small, you appear to be working on it alone, it's simple and it gets the job done.
At the end of each level, use WallManager.reset() to remove the walls from the previous level. For checking collisions across all walls, just use a loop like this:
for each(var i:Wall in WallManager.walls)
{
var collision:Boolean = i.touchingMouse(mouseX, mouseY);
if(collision)
{
// There was a collision.
//
//
}
}
You can make one MovieClip with 50 frames saying stop() on the first frame and do your code like this:
private var wallnum:int;
public function Main()
{
stop();
wallnum = 1;
var wallobj = new Wall();
addChild(wallobj);
wallobj.gotoAndStop(wallnum);
}
For collision detection, I recommend Pixel Perfect Collision Detection (https://code.google.com/p/master-air-controller/source/browse/trunk/master-air-controller/src/PixelPerfectCollisionDetection.as?spec=svn6&r=6)

Game stucture design

Ok. I have a question about AS3 game structure. How to structure simple shooter game.
I have a main Hero and ships that shoot bulets. I want to add two levels but I am not sure how to structure them properly or which way is properly.
I have main class that holds everything and should switch trough levels. So I separate each level as a separate class.
Sample code
package
{
// imports
public class Main extends Sprite
{
// properties
private var testLevel:Level1;
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
testLevel = new Level1();
addChild(testLevel);
}
}
}
// Level1 code
package Levels
{
// imports
public class Level1 extends Sprite
{
// properties
private var ship:Ship;
public function Level1(stage:Object)
{
// do some stuff
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
public function onEnterFrame(e:Event):void
{
// do some stuff
}
}
}
So According to this code my question is: Should I add ship bullets using separate ENTER_FRAME function inside Ship or should I add them in the level loop?
I can do both ways but which is better for performance and for mintenance, because I plan to add some actions when the bulet reaches the end of the sceen or when hits the Hero.
This is a though compromise:
For performance is better to have one single ENTER_FRAME listener.
But for maintenance it should be better to have separate update functions inside the Ship class and inside each object (for example, Enemy class, Bullet class etc) in the game.
So usually the preferred method to deal with this and get the best of both options is to have one main ENTER_FRAME listener on your main class, which is usually referred to as the Main Game Loop. This should be the only ENTER_FRAME listener running in the game. From this Game Loop you should invoke an update function for each object currently in the game, which is the responsible for updating the object's position, status, etc inside the game.
From my opinion - make a level engine and describe a level with xml instead of createing a class for each level.
Make a shoot engine that holds a bulletes and update tham. Make a collision enginge to check the colisions.
A good example is code of Waste Invaders. link
Check src / com / shooty / engine code this will help you a lot.

Pan class in Flash AS3

I have a movieclip and I am going to add a pan feature to it. I want to add the pan as it's own class that the movieclip can reference.
When I create the pan class, I send the movieclip to it so I have it's position properties at all times.
What is the best way to access the stage in the pan class? I will need to be able to get the stage mouseX, mouseY, stageWidth, and stageHeight.
Right now, I have the pan class extend a sprite object and actually add it as a child of the movieclip I want to pan.
Would it be better to just send the stage itself into the pan class as well or is there a better way than this?
Create a singleton class that manages changes to the stage called StageManager and initialize it by passing it a reference to the stage:
//StageManager.as
import flash.display.Stage;
public class StageManager()
{
//publicly accessible singleton instance
public static var instance:StageManager = new StageManager();
private var m_stage:Stage;
//I'm using a custom getter and setter in just in case you need perform some other
//initialization when the stage gets set...
public function set stage(stg:Stage):void
{
m_stage = stg;
}
public function get stage():Stage
{
return m_stage;
}
}
Then somewhere else, like you main controller class:
StageManager.instance.stage = this.stage;
Now you can access the stage and its properties globally through the StageManager:
var stageW:int = StageManager.instance.stage.stageWidth;
This way, not just you Pan class, but anything else down the road can access the global stage any time it needs to. Pretty cool, huh?
As for how to design your Pan class, I agree with #The_asMan - extend MovieClip. Unless, that is, one Pan instance will be controlling several MovieClip instances, then it would probably make more sense to have it as its own thing (the way you describe above).

Stage and classes

I am new to AS3 and am trying to lean its OOP ways. What I am having problems with is understanding how to access the stage with separate classes.
Here is an example of what I am trying to do:
package game{
import flash.display.*;
public class Main extends MovieClip{
function Main(){
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
}
package game{
import flash.display.*;
public class Bullet extends Main // also tried with MovieClip and Sprite{
function Bullet(){
// empty
}
function blah(){
var someSprite = new someSprite();
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}
}
}
I have Omitted another class which calls the blah method as I feel it is not relevant.
Basically what I want to know is how to add things to the stage in classes as it lookes like I am missing something crucial.
*EDIT TO INCLUDE ERROR*
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at game::Bullet/blah()
at game::Player/fire()
You shouldn't necessarily be extending main to create something like a bullet class, this can be it's own class that extends Sprite or MovieClip. The stage object is considered a global object, as it is a singleton (except in the case of Adobe AIR where you can have one stage per NativeWindow that you spawn). So any object that extends DisplayObject or has DisplayObject in it's inheritance chain will by default have a reference to the stage via a getter, which is populated automatically when a displayObject is added to the display list. This can happen by either adding a clip directly to the root stage object or by adding a clip as a child of another clip, that eventually connects to the stage. For example:
var clip1:MovieClip = new MovieClip();
stage.addChild(clip1); //Clip 1 can now access the stage reference internally.
ver clip2:MovieClip = new MovieClip(); //Right now, clip2 cannot access the stage reference interally.
clip1.addChild(clip2); //Now clip2 can access the internal stage reference because it has been connected to the display list through clip1.
The other mistake people make is accessing stage within a DisplayObject typed class (such as your Main class) without first ensuring that the object itself has been added to the stage. You do this by listening for the Event.ADDED_TO_STAGE event within the constructor of the class, like so:
public class Main extends MovieClip{
function Main(){
if(stage){
//The stage reference is present, so we're already added to the stage
init();
}else{
addEventListener(Event.ADDED_TO_STAGE, init);
}
var player = new Player();
var playerBullets = new playerBullet();
addChild(player.players);
}
private function init(e:Event = null)
{
trace("Added to stage, the stage reference is now populated and stage can be accessed");
}
}
This could be the problem you're having, but it's hard to say since you have not specified any errors. However, this is likely an issue or will be for you, since it's quite common. Inside the init() method you can then set a flag so that when external classes call your Main.blah() method, you can ensure that the stage reference exists before attempting to add something to the stage. Take note however that within your Main class when you simply say:
addChild(someChild);
or
this.addChild(someChild);
you're not adding that child to the stage, but rather to the Main object, which is a MovieClip or Sprite based object that is itself attached to the stage automatically when you set it as the Document class. Hope this info helps.
Update
To explain the display list a little more:
Think of all your movieclips as dishes, and the stage as the table. You can only access the table from the dish, if the dish is placed directly on the table, or if a dish is stacked on top of another dish that touches the table. If you have 10 plates stacked on top of each other, they all touch the table eventually, via their connection to each other. This is essentially a visualization of the flash display list. The way you put dishes on the table is by using addChild(dish). If you have not placed an object somewhere on the table, and try to access the table from that object, you're going to fail. You're getting the "access to undefined" error because you're calling the "blah()" method, which accesses the stage (table) before the bullet (dish) has been added to the stage (table). So you must first either directly add the bullet to the stage, or add it to another object that has already been added to the stage. Change your code like so:
var myBullet:Bullet = new Bullet();
stage.addChild(myBullet);
//Or, if this class, assuming it's the player class, has already been added to the stage, just do this:
this.addChild(myBullet);
myBullet.blah();
Even so, you should still have some error checking within your "blah" method to ensure that the stage is available:
function blah(){
var someSprite = new someSprite();
if(stage){
Main.addChild(someSprite);
stage.addChild(someSprite);
root.addChild(someSprite);
}else{
trace("error, stage not present");
}
}
However you should also note that by adding this child to Main, then stage, then root all in sequence, this does not duplicate the someSprite object. When you add a display object to a new parent object, the object is automatically pulled from it's current parent and moved to the new one. So all this code will do is eventually add someSprite to root, which I believe will fail because root is not a display object, but rather a global reference mainly used to access global objects such as the stage and the Loader object used to load the SWF.
You shouldn't ever be calling stage.addChild. There should be only one child of the Stage, and that's the document class.
You make a MovieClip display on the screen by adding it to the stage's display list.
Stage
+ Main Timeline
+Anything
+Else
+You
+Want
So assuming that Main is your document class for the main timeline...
// inside of Main's constructor...
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
}
Then in order to add children even lower, for example if you want something to be a child of "Anything" such that you have....
Stage
+ Main Timeline
+Anything
+And
+Everything
+Else
+You
+Want
public function Main(){
var anything:MovieClip = new MovieClip();
var Else:TextField = new TextField();
var you:SimpleButton = new SimpleButton();
var want:Sprite = new Sprite();
this.addChild(anything);
this.addChild(Else);
this.addChild(you);
this.addChild(want);
var And:Sprite = new Sprite();
var everything:Sprite = new Sprite();
anything.addChild(And);
anything.addChild(everything);
}
EDIT: Ascension Systems asks why you should never add any display object directly as a child of the stage. The simplest answer is that you can't ever guarantee that what you believe you're creating as a document class, or as a main timeline in fact actually is going to be used as such. Your use of the stage may later preclude your swf from being loaded as a child of a larger application depending on what it is you've done, exactly. Relying directly on the stage can mean that you're making some assumptions about the nature of the display list that may not hold in the future. That's the way in which it breaks modularity (which is not the same as breaking oop).
Why add to the stage when you could just create your entire application as a MovieClip that is completely self-contained with no reliance on the concept of a "stage" beyond that which is required for learning world coordinates? That way you can be much more modular in your design and you sacrifice nothing.
In some people's work this may be considered an edge case. In my work this has happened both to me when I've created applications that I thought at the time were purely stand-alone that ended up being repurposed later to be a module, and also to swfs that other people created that were intended to be strictly stand-alone, but that I was then to integrate as a module into a larger application. In all cases there were some nasty side effects to contend with. That's where I learned not to rely too closely on the stage for much beyond world coordinates.
Every display object has a property called stage, which is null until that object is added to the display tree.
When you are unsure if an object has been added to the stage, there is a listener you can employ for that purpose:
public class Main extends MovieClip
{
import flash.events.Event;
public function Main():void
{
if(stage) {
init();
} else {
this.addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private function init(evt:Event = null):void
{
this.removeEventListener(Event.ADDED_TO_STAGE,init);
//object is now definitely on the display tree
}
}
I'm gonna take a wild stab in the dark here.
stage is a property implemented something like so:
public function get stage():Stage {
var s:DisplayObject = this;
while(s.parent) s = s.parent;
return s as Stage;
}
root is very similar but stops a level below stage (root is a child of stage).
These properties only work when the object you're calling them on is on the stage somewhere. Doesn't matter where, because the while loop will walk up the hierarchy to get to the stage node at the top. But if it's not on the stage, then parent will be null.
So if your movieclip is not on the stage, then its reference to stage will be null. Same goes for root.
I'm guessing that you're calling blah before the bullets are added to the stage? In which case your call stage.addChild(someSprite) will be a Null Reference error (stage is null).
So you either need to add the bullets to stage first, or you need to pass stage in as a parameter:
function blah(s:Stage){
var someSprite = new someSprite();
s.addChild(someSprite);
}