How to change one object into another object? - actionscript-3

This is mostly a question about code design. What you see here is a very condensed version of the original code.
An example of the code is:
player.rest();
This makes the player sleep.
I have other methods such as walk, run, talk etc. which all work great. The one that is a problem is polymorph. It changes the player from a human object to another object. The solution I came up with is this:
class main
{
var human:Human = new Human;
var alien:Alien = new Alien;
var cow:Cow = new Cow;
var player = human;
enterframe loop{
//other code
if (player does something)
player.polymorph = "alien";
switch (player.polymorph)
{
case "alien":
player = alien;
break;
case "cow":
player = cow;
break;
//etc
}
player.update();
}
}
I want something that looks like this:
class main
{
var human:Human = new Human;
var alien:Alien = new Alien;
var player = human;
enterframe loop
{
player.polymorph(alien);
}
}
I know my original solution is the wrong way to go about things as it encourages spaghetti code. How could I do it differently? I don't mind a whole rewrite, but need an example to help push me in the right direction. I hope this makes sense and thanks for the help.
If the second one can work, what would the polymorph function look like?
I thought of making a class called player and changing what that extends, but to my knowledge that can't be done? Plus I would like to change the character to something already in game rather than a new object.

One solution to your problem would be using a single class, in this case, your Player class, and a finite state machine. You'd have a Player class, which can be set to different states, such as HUMAN, ALIEN, COW, etc. When a polymorph event occurs you update the Player's state, perhaps by calling an initState() method, and handle the logic for being a human, alien, cow, accordingly in whatever method updates your player.
Assuming the player has an update() method it could contain the following:
switch (state) {
case ALIEN:
// do alien stuff
case COW:
// do cow stuff
case HUMAN:
// do human stuff
}
Next, instead of handling the various polymorph states in a switch statement, your Player class could have a polyMorph method that takes a state as a parameter:
public function polymorph(newState:Int) {
state = newState;
initState(state); // You could even just call the initState method instead, and completely omit the polymorph method
}
Using a finite state machine here would eliminate the need for numerous objects.

Related

AS3 Simple way to check if sound is finished

I have a sound that plays whenever I rollover an object. To make it less annoying I want the sound to play only if it's not playing already. Since I need this with a couple of different sounds I don't want to use a timer (if not absolutely necessary). I found this:
var channel:SoundChannel = snd.play();
channel.addEventListener(Event.SOUND_COMPLETE, onPlaybackComplete);
public function onPlaybackComplete(event:Event)
{
trace("The sound has finished playing.");
}
but I'm not sure I can use it since I have a background music as well. Any tips?
You can use such a trick selectively. Still, if you plan to have a lot of such objects that trigger sounds on mouse-overs, you might decide to have a manager class or data table that would have an entry per such an object, and its field would have a true if the sound is played, and a listener assigned as such would clear the value in that field, deriving the correct entry from registered set of SoundChannels. A premade sound manager would do, but you'd better make a tailored one. An example:
public class SoundManager {
private var _fhOPO:Dictionary;
private var _bhOPO:Dictionary;
// going sophisticated. Names stand for "forward hash once per object" and "backward"
// forward hash stores links to SoundChannel object, backward stores link to object from
// a SoundChannel object.
// initialization code skipped
public static function psOPO(snd:Sound,ob:Object):void
{
// platy sound once per object
if (_fhOPO[ob]) return; // sound is being played
var sc:SoundChannel=snd.play();
_fhOPO[ob]=sc;
_bhOPO[sc]=ob;
sc.addEventListener(Event.SOUND_COMPLETE, _cleanup);
}
private static function _cleanup(e:Event):void
{
var sc:SoundChannel=event.target as SoundChannel;
if (!sc) return; // error handling
var ob:Object=_bhOPO[sc];
_bhOPO[sc]=null;
_fhOPO[ob]=null; // clean hashes off now obsolete references
sc.removeEventListener(Event.SOUND_COMPLETE, _cleanup);
// and clean the listener to let GC collect the SoundChannel object
}
}
Then, whenever you need to play a sound but limit to one instance of channel per object, you call SoundManager.psOPO(theSound,this); providing a button reference instead of this if need be. You can use normal Sound.play() alongside this kind of sound management should you need to, or use another type of management for BGM or other types of sounds should you need to.

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)

How can I track all of my Box2D collisions in a clean, manageable manner?

I am using Box2D for the first time seriously in a medium sized Flash Game that I am working on. My current experience with Box2D is limited to creating a world, bodies and adding those bodies to the world in a functional manner.
I'm finding it easy enough to integrate Box2D into my game environment, maintaining well-written code and have completed a few tutorials that walk through dealing with collisions. The issue that I'm facing now is that my game will have many bodies, each interacting with other bodies in different ways, and I'm finding it hard to write my own b2ContactListener subclass without it getting extremely messy.
Based off a tutorial I used, I have created my own subclass of b2ContactListener and added an override of the BeginContact() method. The argument that BeginContact() receives when it is called will reference an instance of b2Contact, through which I can access two b2Fixture instances (the two instances that have collided). I am then able to access the b2Body instance associated with each of those b2Fixtures.
Problem: Currently I have a roundabout way of finding out what two things collided (i.e. whether they're a wall and a missile, or the player and a tree, etc) which uses GetUserData() and looks like this as an example:
var f1Player:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Player
var f2Player:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Player
var f1Tree:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Tree
var f2Tree:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Tree
// ... continutes with all possible combinations.
// Example of managing a collision:
if(f1Player && f2Tree)
{
// Player (FixtureA) and Tree (FixtureB)
}
if(f2Player && f1Tree)
{
// Player (FixtureB) and Tree (FixtureA)
}
As you can see, this is going to end up extremely long and unmanageable. I also have to write each set of actions to perform twice to cater for a certain element being FixtureA or FixtureB, or vice versa (obviously in the form of a function call with the parameters swapped around rather than literally re-written).
This is clearly not the correct approach, but I haven't been able to locate resources that more thoroughly explain collision detection management.
Does anyone have experience with collision detection management using Box2D that they can share? Also, is using SetUserData( entityThatOwnsTheBody ); the correct way to be using that method?
Yeah, it's a bit of a nuisance indeed. Actually I think the way you have it is quite typical.
fwiw Box2D itself has to deal with a similar problem when testing whether fixtures overlap. There are a bunch of functions such as b2CollideCircles, b2CollidePolygonAndCircle, b2CollidePolygons etc, and when two fixtures come near each other the engine chooses which of these functions should be used.
It does this by putting the function pointers in a 2-dimensional array, then looks up the appropriate function in this array by using the two shape types as index. See the first three functions in b2Contact.cpp for details.
Of course, if you can't pass around function references like this in AS3 then I guess this answer doesn't help much, but I thought I would post anyway as C/C++/JS users might come by.
I've used c++ version of Box2d, but I think the same approach will work in actionscript. I create a class Object, that contain a b2Body *_body pointer and a pointer to graphical representation. _body's UserData was set to point to Object *. class Object had the following methods:
virtual bool acceptsContacts ();
virtual void onContactBegin (const ContactData &data);
virtual void onContactEnded (const ContactData &data);
virtual void onContactPreSolve (const ContactData &data);
virtual void onContactPostSolve (const ContactData &data);
When collision was detected in b2ContactListener subclass, it checked if collided bodies have user data. If so, it casted their user data to Object* and if any of the collided objects accepted contacts - it created ContactData ( a class with all required information about collision) and put it in it's internal list to deliver later.
When b2World::update method returned, ContactListener delivers all contact information to objects to process. Delivery was delayed in order you could create new bodies, joints and so on, right when processing collision (which is not allowed while update is executing)
Also you must notify ContactListener (just put a pointer to it inside ContactData) if one of the collided body was deleted during collision processing, so it can invalidate appropriate contacts and not deliver them
I've come up with something much nicer than the original.
Firstly, I just have my Being class (which owns a b2Body) set itself as its bodies' UserData. This class will also contain an onContact() method and look similar to the below:
public class Being
{
private var _body:b2Body;
public function Being()
{
// Define the body here.
// ...
_body.SetUserData(this);
}
public function onCollision(being:Being = null):void
{
//
}
}
Then in my own b2ContactListener implementation, I simply pass the colliding Being (or null, if there is no Being assigned to the colliding b2Body's UserData) to the opposing Being's onCollision():
override public function BeginContact(contact:b2Contact):void
{
var bodyA:b2Body = contact.GetFixtureA().GetBody();
var bodyB:b2Body = contact.GetFixtureB().GetBody();
var beingA:Being = bodyA.GetUserData() as Being || null;
var beingB:Being = bodyB.GetUserData() as Being || null;
beingA && beingA.onCollision(beingB);
beingB && beingB.onCollision(beingA);
}
And finally in each of my subclasses of Being, I can easily prepare logic appropriate for a collision between other Beings of a certain type:
class Zombie extends Being
{
override public function onCollision(being:Being = null):void
{
if(being && being is Bullet)
{
// Damage this Zombie and remove the bullet.
// ...
}
}
}

ActionScript - Forced Garbage Collection Not Working In ADL?

when launching the following code in ADL, why does the square continue to rotate?
var square:Sprite = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);
addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);
function rotateSquare(evt:Event):void
{
square.rotation += 2;
}
System.gc();
Update
the following display object has a weak referenced ENTER_FRAME event listener. however, calling:
removeChild(testInstance);
testInstance = null;
doesn't stop the ENTER_FRAME event:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Test extends Sprite
{
private var square:Sprite;
public function Test()
{
addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(evt:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
square = new Sprite();
square.graphics.beginFill(0xFF0000);
square.graphics.drawRect(-25, -25, 50, 50);
square.x = square.y = 100;
addChild(square);
addEventListener(Event.ENTER_FRAME, rotateSquare, false, 0, true);
// //Current Solution - only works on display objects
// addEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
}
private function rotateSquare(evt:Event):void
{
trace("square is rotating");
square.rotation += 2;
}
// private function removeHandler(evt:Event):void
// {
// removeEventListener(Event.REMOVED_FROM_STAGE, removeHandler);
// removeEventListener(Event.ENTER_FRAME, rotateSquare);
// }
}
}
i have added a REMOVED_FROM_STAGE event listener, but this will only work on display objects.
is this problem specific to ENTER_FRAME event?
Regarding your update, I think you are misunderstanding how GC works. The basic idea is rather simple.
When you create an object, flash allocates some memory in a storage called the heap. A reference to this object is returned. This reference is what you store in a variable. What is a reference? A means to access this object. Think of it as link to the object.
var foo:Bar = new Bar();
Now, in some languages, at some point you have to release the memory allocated for this object when you're done with it, or you have a memory leak.
In a GC environment, this is done automatically. Of course, you need some rules. This rules vary depending on the concrete GC, but in general terms, you could say the GC determines that an object is collectable if it's no longer reachable. This makes sense, because if you can't reach an object, you can't use it. You've lost your link to it. So, it's considered garbage and will be eventually collected.
The specifics on how reachability is determined vary, but in flash it's a mix of reference counting and a mark and sweep algorithm.
(The following is just a high level overview, the details might not be exact)
One method is reference counting: it's easy and fast but it doesn't work in all situations. Basically, each object has a reference count. Each time you assign this object to a variable (i.e. you store a reference to the object), the reference count is incremented. Each time you lost this reference (for instance, you null out your var), this count is decremented. If the count reaches 0, it means the object is unreachable and so it's collectable.
This works fine in some cases, but no others. Specially when there are crossed references.
var foo1:Bar = new Bar(); // let's call this object Bar_1
var foo2:Bar = new Bar(); // let's call this one Bar_2
// at this point, Bar_1 has reference count of 1 (foo1) and Bar_2 has a reference of 1 (foo2)
foo1.theOtherFoo = foo2;
// now Bar_2 has a RC of 2: foo2 and foo1.theOtherFoo
foo2.theOtherFoo = foo1;
// now Bar_1 has a RC of 2: foo1 and foo2.theOtherFoo
foo1 = null;
// foo1 no longer references Bar_1, so its RC is decremented.
foo2 = null;
// foo2 no longer references Bar_2, so its RC is decremented.
// but still both Bar_1 and Bar_2 have a RC of 1.
As you can see, both Bar_1 and Bar_2 have a RC of 1, but are unreachable. This is one of the cases where reference counting doesn't work. Because for all intents and purposes, both objects are unreachable and yet won't be collected.
That's why there's a mark/sweep algorithm. From a high level point of view, what it does is traversing your objects graph, starting from some root objects and analize its relationships to determine whether an object is reachable or not. This algorithm will determine that even though Bar_1 and Bar_2 have a RC of 1, they're not reachable and thus should be considered garbage and be collected at some point.
Events, listeners and dispatchers work the same way. They're not a special case. When you do:
function test():void {
foo1.addEventListener("someEvent",someHandler);
}
function someHandler(e:Event):void {
}
It's the same as doing:
function test():void {
foo1.someProperty = this;
}
The effect is that foo1 now has a reference to this. You'd normally call removeEventListener when you're done for 2 reasons:
1) You no longer want foo1 to have a reference to this.
2) You no longer want to listener for "someEvent" events.
Lots of people insist on using weak references, because they think that then you can pretend you don't have to call removeEventListener (which is apparently too hard...). This is wrong. removeEventListener does two things and both are important. If you want to stop receiving notifications for some event, you have to tell the dispatcher. It's really that simple. In my opinion, weak references are innecesary in most cases. Some advocate to use them by default; but in my experience, in practice this is a bad service to them, as it confuses people further, encourages them to write sloppy code and gives them the impression that you can ignore how this very basic feature of the language (which is not that hard to graps) works.
Now, after this rather long (but hopefuly constructive) rant, let's look at your code:
Your sprite is not going to be collected, because it has 2 references:
1) the square variable
2) the stage.
The first follows the rules outline above. The second too, but it might not be so obvious at first sight. But it makes sense if you think about it for a second. When you did this:
addChild(square);
square got added to the Test instance, which is in turn added to the stage. The stage is always alive, so if something can be reached from the stage, it's reachable. As long as square remains added to the stage, you can be sure it won't be collected.
So, if you at some point do what Sean Thayne suggested:
removeChild(square)
square = null;
your Sprite will be collectable. That doesn't affect the fact that you told your Test object that you wanted to be called whenever a frame is entered. And that's exactly what's happening. Until you don't tell it you don't want to receive this event anymore (calling removeEventListener), it will call you back.
Flash's garbage collection only clears out elements/objects/variables that have either a zero reference count or have only weak references.
This means you would need to do this. For it to truly be gc'd.
removeChild(square)
square = null;
System.gc()

How to think "Tell, don't ask" in this simple example?

How would you adhere to the "Tell, don't ask" principle (henceforth "the principle") in the following simple scenario? In a Tetris game, I have Board, BlockGrid and Piece classes relevant to the following example:
public class Board
{
private var fallingPiece:Piece;
private var blockGrid:BlockGrid;
...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
}
Once fallingPiece is placed in the bottom row of BlockGrid, it should no longer be the "fallingPiece". Am I right in that I'm not violating the principle with the following?
if(blockGrid.getPiecePosition(piece).y == 0)
{
fallingPiece = null;
}
But is that really different from this, which I think clearly violates the principle?
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
if(blockGrid.getPiecePosition(piece).y > 0)
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
else
{
fallingPiece = null;
}
}
I'm not assuming that I've designed these class relationships in the proper way to work with the principle. Please advice on an alternate design if that's what I'm missing.
EDIT, Proposed solution:
I went with the answers proposing "command feedback" via events. Board tells BlockGrid to move a piece. BlockGrid's movePiece method dispatches MOVED_TO or MOVE_FAILED events depending on the result, which Board can listen to and use to determine whether a piece has stopped falling. Please don't hesitate to provide feedback on this solution.
public class Board
{
...
public function Board()
{
...
blockGrid.addEventListener(PieceMoveEvent.MOVE_FAILED, onPieceMoveFailed);
...
}
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.movePiece(fallingPiece, xDirection, yDirection);
}
public function onPieceMoveFailed(event:MovePieceEvent):void
{
if(event.instance == currentlyFallingPiece && event.fromPosition.y != event.toPosition.y)
{
currentlyFallingPiece = null;
}
}
I think, to better follow the Tell, Don't Ask principle, you should have blockGrid notifying your Board class when fallingPiece has reaches it's resting point. In both scenarios above, you are asking blockGrid if the piece's position.y == 0 in order to determine whether or not fallingPiece should be null. Instead, you want blockGrid to tell the Board class that fallingPiece.y has hit 0.
What you are looking for is Event driven programming. You need a Listener interface with a method called .event() and an Event interface to represent the events. Objects will register with other objects ( callbacks ) to the Listener interface.
when you create a Piece and Board they should implement the Listener interface. Then you can set the Board with registerListener(board); Then when things happen inside Piece it will loop thru all the registered listeners and call .event(event) on each. Same with the Board, call board.registerListener(piece) each time you create a new piece, as it decides things are happening it can tell all the registered listeners what has happened. Then you can tell a piece it is no longer falling by the Board object deciding this. Here is the obligitory Wikipedia entry.
I would expect a class representing each shape (without position information), a controller containing a shape, position and orientation, and another class representing the current resulting grid of "landed" shapes. The landed-grid would have a
testLanded(shape, shapePosition, orientation)
method which would be called before/after each move operation to decide if the shape is to join the landed grid or should move and stay as the falling piece.
I'm going on the idea of not giving data to objects that shouldn't really own that data - but I've never implemented Tetris...
You may need to rethink your design. Does Board really need to track the falling piece or should that belong to BlockGrid? Iron out who owns what behavior.
Keep position information on your Piece class and possibly have your Piece class hold an instance of the BlockGrid.
You can then try something like this in your Board class...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
blockGrid.moveFallingPiece(xDirection, yDirection);
}
Then in BlockGrid's moveFallingPiece method...
public function moveFallingPiece(xDirection:int, yDirection:int):void
{
fallingPiece.move(xDirection, yDirection);
}
In Piece's move method, add your logic...
public function move(xDirection:int, yDirection:int):void
{
setPosition(xDirection, yDirection);
if (getPosition().y <= 0)
{
blockGrid.setFallingPiece(null);
// this can bubble up to Board if need be
}
}
Not sure of all the power of AS3, but it would make sense to use abstractions here. (i.e., have your Piece class depend on ITrackFallingPieces instead of BlockGrid and have BlockGrid implement ITrackFallingPieces).
Good luck!