I have made an interface called IHero i implement in my hero.as3 class.
the hero class is written so it can be inheritted in a movieclip class to handle movement etc etc. But somehow i can't figure out how to code this with a good practice.
Maybe i am in the wrong direction.
I want to have a movieclip subclass, which will be a hero for instance.
Should i just implement the IHero in the hero class with the following methods, or is this to overkill? - I guess I am looking for an answer upon what should be in an interface and what should not. Here is the interface.
package com.interfaces
{
public interface IHero
{
//movement
function MoveLeft():void;
function MoveRight():void;
function MoveUp():void;
function MoveDown():void;
//in battle
function DoDamage(isCasting:Boolean):void;
function DoHeal():void;
function Flee():void;
function TakeDamage():void;
function IsAlive():Boolean;
function CheckDeath():void;
function Die():void;
}
}
I think you are on a right track, whether its the right one or the wrong one is always subjective. But if you do want to go down this road, I suggest you read this article by Mick West. Its a few years old, but its still very applicable.
I think you really have two distinct interfaces, but probably more
public interface IMoveable {
function moveLeft(obj:DisplayObject):void;
function moveRight(obj:DisplayObject):void;
function moveUp(obj:DisplayObject):void;
function moveDown(obj:DisplayObject):void;
function Flee(obj:DisplayObject);
}
public interface IFightable {
function doDamage(withWeapon:IEquipableWeapon);
function takeDamage(fromWeapon:IEquipableWeapon);
function get isAlive():Boolean;
function checkDeath():void;
function Die():void;
function doHeal();
function get health():Number;
}
Then....
public class SimpleMover implements IMoveable {
// The movement implementation
// for example:
public funciton moveLeft(obj:DisplayObject) {
obj.x = obj.x -= 10;
}
}
public class SimpleFighter implements IFightable {
// The fighting implementation
private var _health:Number = 100;
function doDamage(withWeapon:IEquipableWeapon) {
_health -= withWeapon.damage;
}
}
Then inject those into your subclass MovieClip for your Hero.
public class Hero extends MovieClip {
private var _mover:IMoveable;
private var _fighter:IFightable;
public function Hero(mover:IMoveable, fighter:IFightable) {
_mover = move;
_fighter = fighter;
}
}
Here you are using the Hero class as both Component Manager and Render component, which goes slightly against what West is talking about in the article, but I digress. But the idea is that your Manager (the Hero) becomes more or less an orchestrator, proxying calls back through to which ever component is applicable; calling methods on _mover and fighter to do your actual work.
There are several advantages using an approach like this, and some disadvantages. First, its more complex to set up; it requires you to really like about components and what each logical chunck of code is going to do. But on the other hand, it decouples your implementations from each other, makes it more testable, and reuseable. You can also swap out components at any time (compile-time or run-time for that matter), which gives you some flexability when creating new Entities.
But anyway, its just a suggestion of a slightly different paradigm that you seem to be flirting with. Maybe you'll get some mileage out of it. But definitely give the article a read, if you haven't already.
Also look into (like check out the API) for the Unity Engine which has a similar aggregation vs inheritance model where interfaces are key to abstraction.
Usually you will want a regular class definition that extends movieclip, then set your hero movieclip to use that class in the export for actionscript options. The interface is most likely not needed at all for your game.
Just to clarify - interfaces are implemented, not inherited. Therefore it does not matter what type of class you create that implements IHero. So to make a type of library class that implements Ihero, extend a display object like Sprite or MovieClip, and then use the implements keyword to indicate the class implements Ihero.
Add the Ihero methods, and you now have a display object class that implements iHero. It might look something like this (note no constructor function supplied)
package
{
import flash.display.Sprite;
import com.interfaces.IHero;
/**
* ...
* #author Zachary Foley
*/
public class MyDisplayObkect extends Sprite implements IHero
{
public function MoveLeft():void
{
// Add Implementation here;
}
public function MoveRight():void
{
// Add Implementation here;
}
public function MoveUp():void
{
// Add Implementation here;
}
public function MoveDown():void
{
// Add Implementation here;
}
//in battle
public function DoDamage(isCasting:Boolean):void
{
// Add Implementation here;
}
public function DoHeal():void
{
// Add Implementation here;
}
public function Flee():void
{
// Add Implementation here;
}
public function TakeDamage():void
{
// Add Implementation here;
}
public function IsAlive():Boolean
{
// Add Implementation here;
}
public function CheckDeath():void
{
// Add Implementation here;
}
public function Die():void
{
// Add Implementation here;
}
}
}
Related
I have a rather simple theoretical question regarding OOP (in AS3) that I don't know how to google:
I need something like an abstract class, which would require that dependant class implements some interface, like this:
Interface ISomething
{
public function somethingize(otherThing:type):void;
}
abstract public class AbstractSomething implements ISomething
{
public function AbstractSomething()
{
// ...
}
public function doSomething():void
{
//code here
// ...
this.somethingize();
// ...
}
}
Is the only way to achieve such a thing is to drop an "abstract" keyword, and move somethingize to SomethingWrapper (with an implementation of throwing an "unimplemented exception"), or is there some better way to model it?
ActionScript doesnt support Abstract classes (unfortunately).
I think there are a few techniques out there to try and mimic abstracts, but my way is too just throw errors in my abstract classes to stop them being used directly, eg:
public class AbstractSomething implements ISomething
{
public function AbstractSomething()
{
throw new Error("this is an abstract class. override constructor in subclass");
}
public function doSomething():void
{
throw new Error("this is an abstract class. override doSomething in subclass");
}
}
Without more information about the specific implementation, I would prefer composition over inheritance in this case, specifically dependency injection.
public interface ISomething {
function somethingize(thing:*):void;
}
public class SomeWorker {
private var _something:ISomething;
public function SomeWorker(something:ISomething) {
this._something = something;
}
public function doSomething():void {
// work
this._something.somethingize(obj);
// more work
}
}
Inherrited classes of SomeWorker could inject the correct implementation of ISomething for the work they need to do, or that dependency could be resolved somewhere else.
I've found myself needing to refer to certain animations in some unknown frame in my classes. What would be the best way to do this? Should I be creating a custum name and specify that all instances of the class should label a certain animation the same thing. For instance:
public class CanBeHurt{
public CanBeHurt() extends MovieClip{
// constructor code here
}
public function hurt():void{
gotoAndPlay("hurt");
}
}
Any instance of this class would then be forced to label the start of the hurt animation "hurt". Alternatively I could take in strings in the constructor that specify the frame, like so:
public class CanBeHurt{
private var hurtAnimationLabel: String;
public CanBeHurt(hurtAnimationLabel: String) extends MovieClip{
this.hurtAnimationLabel = hurtAnimationLabel;
}
public function hurt():void{
gotoAndPlay(hurtAnimationLabel);
}
}
But unfortunately with more complex objects, I already have a bunch of other input arguments for the constructor and with so many animations, I really don't want to add anymore to it. And lastly, the last option I came up with is:
public class CanBeHurt{
private var hurtAnimationLabel: String;
public CanBeHurt() extends MovieClip{
this.hurtAnimationLabel = this.currentLabels[0];
}
public function hurt():void{
gotoAndPlay(hurtAnimationLabel);
}
}
Unfortunately, with multiple animations, now I'm forced to have my animation labels in a certain order to be able to refer to them properly. Of the 3 methods, I've found the first one to be the most satisfying, but is there a better way?
You don't need to store label names, only if you want the efficient way.
Example:
public class CanBeHurt{
private var label: String;
public CanBeHurt() extends MovieClip{
label= "hurtLabel";
}
public function hurt():void{
currentFrameLabel != "hurtLabel" ? gotoAndPlay("hurtLabel") : null;
}
}
You should check the current frame's label and then play the animation to avoid frame stucking. (The animation is always at the first frame)
For games, most of the developers use a function to control the animations, it's the handleAnimation function.
Example:
public class Example{
private var label:String = "someLabel1";
public Example() extends MovieClip{
//constructor code here
}
private function handleAnimation(){
if(condition){
label = "someLabel1";
}
if(condition2){
label = "someLabel2";
}
gotoAndPlay(label);
}
}
I usually don't extend movieclip for my code. I create some kind of class that accepts a MovieClip in constructor and stores a reference to this and then acts upon it on function calls.
When it comes to label names I usually place them as a static variable in the appropriate class.
This has worked well for me throughout the years :)
I read a question on stackoverflow (couldn't find it now) about how variables in a method can be only accessed in that method, but the code still works with the answer being an analogy of a hotel room. In AS3, I believe everything that's not primitive gets passed as a reference. So, the following code would be the same as that question and isn't guaranteed to work?
public class Testy {
private var foo:Array;
public function Testy(input:Array) {
// Allow the whole class to access it
foo = input;
}
public function traceFoo(){
trace(foo);
}
}
Now, foo would be a reference to the input argument in the class' constructor. Is this safe code/good practice? Thanks!
Yes this is safe/good code practice as long as you don't want to manipulate the original Array. If you want to manipulate the original array, allow public access to the array by making it a public var or using a public getter/setter.
What you've described is a property, and is inline with encapsulation of object oriented programming.
This would expose a getter and setter:
package
{
import flash.display.Sprite;
public class Testy extends Sprite
{
private var _foo:Array;
public function get foo():Array
{
return _foo;
}
public function set foo(value:Array):void
{
_foo = value;
}
public function Testy()
{
super();
}
}
}
Also it's better to return _foo.concat() in getter not to break encapsulation.
What I am trying to do is kind of odd, but I am wondering if anyone can come up with a clever way to do what I want to do. Basically, I want to re-define a named function at runtime. I can do this with anonymous functions, but I can't figure out a way to do it for named functions. I want to do this so that I can implement a "spy" functionality on an object for a testing framework (a port of Jasmine to Flex).
Take, for instance, this class:
public class TestClass
{
public var anonymous:Function = function():void {
trace("original anonymous");
};
public function named():void {
trace("original named");
}
}
I can easily re-define the anonymous function because it is just a variable. Javascript uses this idiom a lot.
var testClass:TestClass = new TestClass();
testClass.anonymous = function():void { trace("overridden anonymous"); }
BUT, when I do the same thing for named functions, you get a compile-time error:
// Does not compile
testClass.named = function():void { trace("overridden named"); }
I tried to make it a bit more "squishy" but this leads to a runtime failure "Cannot assign to a method named on TestClass".
// Compiles with runtime failure
testClass["named"] = function():void { trace("overridden named"); }
Can anyone more clever than I come up with a way to hack this? Can the bytecode be hijacked? Something?
I want to modify an object, not a
class
But object doesn't contain functions, only non-static variables. I tried to use prototype property and replace method there, but original method still gets called instead of injected one.
About "hack" bytecode, do you mean "hack" already loaded SWF in runtime? I think it's not possible. I'm sure, though, you can parse SWF with something like as3swf, find method in bytecode, replace it and save result in new SWF.
I had an idea bout making a function "cache" . This might work with what you need.
Let's say you have a class "Car" with a method you need to redefine at runtime:
public class Car extends Sprite
{
private var functionCache:Function;
public function Car()
{
super();
}
public function flexibleFunction(functionBody:*=null):void{
if(functionBody is Function){
functionBody.call();
functionCache=functionBody;
} else {
functionCache(functionBody);
}
}
}
Usage:
public class Main extends Sprite
{
private var car:Car;
public function Main()
{
car = new Car();
car.flexibleFunction(function(){trace("redefine test #1")});
car.flexibleFunction();
car.flexibleFunction(function(doParametersWork:String="let's see"){trace("redefine test #2: " + doParametersWork);});
car.flexibleFunction("yes they do");
car.flexibleFunction();
}
}
an easy way to accomplish what you want is to simply pass a new function to the original function and execute it from there:
package
{
//Imports
import flash.display.Sprite;
//Class
public class RedefineFunction extends Sprite
{
//Constructor
public function RedefineFunction()
{
originalFunction();
originalFunction(redefinedFunction);
}
//Original Function
public function originalFunction(redefinition:Function = null):void
{
if (redefinition != null)
redefinition();
else
trace("Original Function Definition");
}
//Redefined Function
private function redefinedFunction():void
{
trace("Redefined Function Definition")
}
}
}
traces:
Original Function Definition
Redefined Function Definition
Is there any non-hacky way to determine wether a class' superclass implements a particular interface?
For example, assume I've got:
class A extends EventDispatcher implements StuffHolder {
protected function get myStuff():Stuff { ... };
public function getStuff():Array {
if (super is StuffHolder) // <<< this doesn't work
return super['getStuff']().concat([myStuf]);
return [myStuff];
}
class B extends A {
override protected function get myStuff():Stuff { ... };
}
How could I perform that super is StuffHolder test in a way that, well, works? In this case, it always returns true.
In this case you might have to define StuffHolder (and have it extend EventDispatcher) as a class and have getStuff as a public/protected function. You could then overload the getStuff function in class A, but not in class B.
package {
public class StuffHolder extends EventDispatcher {
function StuffHolder() {
}
protected function getStuff():Array{
return ["Default"];
}
}
}
package {
public class A extends StuffHolder {
function A {
super();
}
protected override function getStuff():Array {
return ["A"];
}
}
}
package {
public class B extends StuffHolder {
function B {
super();
}
}
}
I don't have the full picture to figure out why you'd need that kind of (weird and possibly broken) inheritance, but could you rephrase it to if (this is actually an A instance)?
If so, you could go with...
import flash.utils.getQualifiedClassName;
if (getQualifiedClassName(this) == 'A')
The thing is, the is operator should be used on object instances. And it works all the way up to object -- I mean, A is B is true for every A ancestor or interface implementation.
I think you could come up with a better structure for your classes, though.
This isn't that pretty.
I could do something involving introspection, getting the current class, finding its parent class, then checking to see if that implements the StuffHolder interface… But that seems pretty ugly :(