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.
Related
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.
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)
We have an effect we like to use where we synchronize a series of slides with a sound. As the sound plays, we show each slide (which is its own frame in a MovieClip), and slowly scale the slide up to provide a little bit of movement. Our sounds tend to be equivalent to one frame on the parent timeline, so we look to see what sound is playing for that frame to calculate how long each slide should be displayed for.
The issue we have is that occasionally we need to "stretch" one of these animations across two or more slides, which means we need to look ahead and calculate the slide length based on the combined length of the sounds during the span of frames where the MovieClip that contains the slide images is displayed.
However, I haven't been able to find a property that tells me how many timeline frames a MovieClip is displayed for (note that this would be different from totalframes, the number of frames that clip contains). Is it just wishful thinking that such a property exists, or can someone point me in the right direction on this?
I'm not sure if I'm understanding the problem correctly but have you tried MovieClip.currentFrame?
Can you create a class like this, which will keep track of the amount of frames it has been present on the DisplayList:
package
{
import flash.display.MovieClip;
import flash.events.Event;
public class ExtMovieClip extends MovieClip
{
// Properties.
private var _lifetime:int = 0;
// Constructor.
public function ExtMovieClip()
{
addEventListener(Event.ADDED_TO_STAGE, _added);
}
// Was added to the DisplayList.
private function _added(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, _added);
addEventListener(Event.ENTER_FRAME, _track);
addEventListener(Event.REMOVED_FROM_STAGE, _removed);
}
// Was removed from the DisplayList.
private function _removed(e:Event):void
{
removeEventListener(Event.REMOVED_FROM_STAGE, _removed);
removeEventListener(Event.ENTER_FRAME, _track);
addEventListener(Event.ADDED_TO_STAGE, _added);
}
// Increment the lifetime of this MovieClip.
public function _track(e:Event):void
{
_lifetime ++;
}
// Retunrns the lifetime of this MovieClip.
public function get lifetime():int
{
return _lifetime;
}
}
}
Lets say we have a movieclip "Enemy" in the Flash library and a class "Enemy.as" is associated with it which listens to ENTER_FRAME event as follows,
public function Enemy():void
{
//constructor of this "Enemy.as" class
addEventListener(Event.ENTER_FRAME, move);
}
private function move(evt:Event):void
{
x += 5;
}
Now my question is if this "Enemy.as" is instantiated in other class say "Main.as" which again uses ENTER_FRAME event on the same instantiated Enemy object as follows,
public function Main():void
{
//constructor of this "Main.as" class
enemy1 = new Enemy();
enemy1.addEventListener(Event.ENTER_FRAME, checkCollision);
}
private function checkCollision(evt:Event):void
{
if(enemy1.x == mainObj.x)
{
//do something
}
}
Is this the good approach in terms of optimization? Or should not use this approach at all?
From a performance point of view, it's always best to only listen for ENTER_FRAME in a single location (e.g. in your main application class) and to then invoke custom update() methods on all the objects that need to be updated. That's a very common approach for games for instance.
One reason why this is greatly superior from a performance point of view is that no new instances of Event need to be created. If you have 100 listeners (which is not uncommon when using the approach you outline) that means 100 new instances of the Event class every frame, and instantiating classes is among the heaviest things that you can do in Flash.
You rarely actually need the Event object in ENTER_FRAME handlers, so using the update() approach instead makes a lot of sense. If you can, try centralizing other events as well in performance critical applications like games.
As Richard says, it is much more efficient to use a single update (Game Loop) method when creating games. Not only from the performance point of view, but also because you get a lot more control using a single update. Like this:
public function update(deltaTime : Number) : void
{
var gameObject : IGameObject;
for(var i : int = 0 ; i < _gameObjects.length ; i++)
{
gameObject = _gameObjects[i];
gameObject.update(deltaTime);
}
...
}
I've got a AS3 program with a Main.as custom class.
In this class I load an instance of a 'menu' movieclip which has simpleButton instances inside... How do I access the Main class public functions by the menu movieclip buttons?
I.e. Menu button -> gotoPage(5); (which is a Main public function)
If I try to access the Main function with the above statement, it gives
"1180: Call to a possibly undefined method gotoPage.
Create a static method called GetMain() on the Main class that would return the instance of Main (Main should be a singleton).
package whatever
{
public class Main
{
private static var _instance:Main = null;
public static function getMain():Main
{
return _instance;
}
// Main constructor
function Main(..):void
{
_instance = this;
}
}
}
To refer to the instance of Main() from your Menu class, you could use:
Main.getMain().gotoPage(5);
You want to do this with events. If your menu movieclip is a child of Main.as as you say, name the instance buttons inside of the menu movieclip, and set up the listeners in Main.as:
1) Put the below code in the constructor: public function Main(){...
menu.button_a.addEventListener(MouseEvent.CLICK, onButtonClick);
menu.button_b.addEventListener(MouseEvent.CLICK, onButtonClick);
2) and then write the onButtonClick function in Main.as
private function onButtonClick(e:MouseEvent):void{
switch(e.currentTarget.name){
case "button_a":
//call the Main.as function you want here
break;
case "button_b":
//call a different Main.as function
break;
}
ruedaminute's answer on dispatching events from the buttons and having main process those events is by far the best way to handle this, but there are many ways to do this in as3 - but try to use the aforementioned technique. Some of the other techniques.
Make a function in Main such as public function GotoPage(iPageNum:int):void{}
from a button - try this._parent.GotoPage(1);
but this._parent might not be main, do a trace(this._parent), and keep trying
it might end up being
this._parent._parent._parent.GotoPage(1) depending on your display tree hierachry.
Again, this is REALLY bad OOP practices, but well, it will work.
Another tecnique - use a singleton for main- looks like u already are - add that same public method, then from the button click, you could do Main.getMain().GotoPage(1);
That is a bit better, in that you can change the display tree and not have to figure out where the heck Main is in the display tree, but singletons also are discouraged for a variety of reasons, but in this case I would say it makes since.
Good Luck!
~ JT