Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
Actually, imp2 raises an interesting point. You can program a function to accept an array as its parameter instead of a number of variables. That way, you can store all the parameters you need in the array and call the function directly instead of invoking the function’s apply method. The question is, which will execute faster and how much faster will it run? Well, there’s only one way to find out, and that is to run a test.
My test program consists of two classes, a Main class and a Tester class. The latter contains two versions of what is essentially the same function. The first function accepts three integer parameters and stores them in three private variables. The second function accepts an array of three integers and stores them in the aforementioned private variables. I created function objects for both functions to test how fast they run, but I also compare their execution speed with a direct call to the first function, which serves as a benchmark.
Here are the two classes I wrote:
package
{
import flash.display.Sprite;
public class Main extends Sprite
{
public function Main():void
{
var tester:Tester = new Tester();
addChild(tester);
} // public function Main
} // public class Main
} // package
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.getTimer;
public class Tester extends Sprite
{
private const REPS:uint = 50000000;
private var _logger:TextField;
private var testVar1:int;
private var testVar2:int;
private var testVar3:int;
public function Tester()
{
_logger = new TextField();
var functionObject1:Function = testFunction1;
var functionObject2:Function = testFunction2;
var i:uint;
var beforeTime:int;
var afterTime:int;
var myTestInt1:int = 1;
var myTestInt2:int = 2;
var myTestInt3:int = 3;
var myTestArr:Array = new Array(myTestInt1, myTestInt2, myTestInt3);
_logger.autoSize = TextFieldAutoSize.LEFT;
addChild(_logger);
log("testFunction1(myTestInt1, myTestInt2, myTestInt3)");
beforeTime = getTimer();
for (i = 0; i < REPS; i++)
{
functionObject1(myTestInt1, myTestInt2, myTestInt3);
}
afterTime = getTimer();
log("\ttime: " + (afterTime - beforeTime));
log("");
log("functionObject1.apply(this, myTestArr)");
beforeTime = getTimer();
for (i = 0; i < REPS; i++)
{
functionObject1.apply(this, myTestArr);
}
afterTime = getTimer();
log("\ttime: " + (afterTime - beforeTime));
log("");
log("functionObject2(myTestArr)");
beforeTime = getTimer();
for (i = 0; i < REPS; i++)
{
functionObject2(myTestArr);
}
afterTime = getTimer();
log("\ttime: " + (afterTime - beforeTime));
} // public function Tester
private function log(msg:String):void
{
_logger.appendText(msg + "\n");
} // private function log
private function testFunction1(x:int, y:int, z:int):void
{
testVar1 = x;
testVar2 = y;
testVar3 = z;
} // private function testFunction1
private function testFunction2(arr:Array):void
{
testVar1 = arr[0];
testVar2 = arr[1];
testVar3 = arr[2];
} // private function testFunction2
} // public class Tester
} // package
I compiled the program in Release mode and executed it. Here are the results I got on my computer:
testFunction1(myTestInt1, myTestInt2, myTestInt3)
time: 2544
functionObject1.apply(this, myTestArr)
time: 6960
functionObject2(myTestArr)
time: 2764
Using the apply method is 174% slower on my computer than the direct method call. By comparison, passing an array to the function object and calling it in the usual manner is only 8% slower than the direct function call. That’s great. It’s almost as fast as calling the original function directly.
If you need to pass a number of parameters to your functions, but you aren’t sure which function will receive them at run time, design your functions to accept an array instead so you can pass their parameters in an array. The only reason I can think of for calling the apply method in this case is if you are trying to call a function that you didn’t write, but even that can be worked around by creating a wrapper function that accepts an array.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
Originally posted by Ace_Blue:
I don’t think the act of constructing a dozen bullets or so (the only part that requires function variables and apply()) in a given frame (and then again not even every frame) is what’s going to bring the CPU to its knees. Maybe I’m wrong, maybe not, which is why I’m trying it out: to find out.
Do keep us posted. I’m also interested in the results of your test.
Originally posted by Drakim:
The classes aren’t really dynamic, they are created by the compiler macro so that I don’t have to do it by hand.
Lol. Cool.
You can’t inline your function calls in AS3 (well you can with Apparat but it’s much more limited). You also can’t do generics, which helps avoid a lot of casts.
Not being able to inline functions in AS3 is mildly irritating, but I can live with it I guess. Regarding casting, something magical seems to happen when you cast variables, especially when storing them in arrays and dictionaries. (Or more likely, the Flex compiler isn’t as smart as we would want it to be, so we have to resort to tricks like that to optimize our code.)
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
Dynamic classes? Whoa, I’m not sure that calling their methods is any faster than calling function references. I could run some tests, but hearing about the good performance you achieve with your own bullet hell test is enough for me. Whatever optimizations Haxe makes are ultimately compiled into AS3 opcodes and still utilize many built-in AS3 data structures such as arrays and function objects. If you can make components work well enough in Haxe, I can probably do the same in AS3.
One good thing about component-based programming is that it opens my eyes to thinking about how classes can be made less “blobby.” How I’ve been programming until now is akin to making a world by creating roads, buildings, people, and cars, but I haven’t really put in much thought about what goes into those objects. I realize now that I should be thinking more about what components make up these things, such as carburetors, engines, and whatnot for cars. Even if I stick with building classes out of other classes at compile time and calling methods directly instead of using event-driven calls, I’ll probably be designing my classes in a more component-oriented manner.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
I tested function calls in AS3 and Haxe using both direct method calls and function objects/pointers, and the results don’t look good for dynamic function references. I wrote two classes in both languages, a Main class and a Tester class. Main creates a Tester object, which then ran the actual test. I didn’t run the test in Main directly because in Haxe, the functions in the Main class have to be static, but I wanted to test access to non-static methods. Here are my two AS3 classes:
package
{
import flash.display.Sprite;
public class Main extends Sprite
{
public function Main():void
{
var tester:Tester = new Tester();
addChild(tester);
} // public function Main
} // public class Main
} // package
package
{
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.utils.getTimer;
public class Tester extends Sprite
{
private const REPS:uint = 50000000;
private var _logger:TextField;
public function Tester()
{
_logger = new TextField();
var functionObject:Function = testFunction;
var i:uint;
var beforeTime:int;
var afterTime:int;
var myTestInt:int = 0;
_logger.autoSize = TextFieldAutoSize.LEFT;
addChild(_logger);
log("Direct Function Call");
beforeTime = getTimer();
for (i = 0; i < REPS; i++)
{
testFunction(myTestInt);
}
afterTime = getTimer();
log("\ttestFunction(int): " + (afterTime - beforeTime));
log("");
log("Function Object Call");
beforeTime = getTimer();
for (i = 0; i < REPS; i++)
{
functionObject(myTestInt);
}
afterTime = getTimer();
log("\tfunctionObject(int): " + (afterTime - beforeTime));
} // public function Tester
private function log(msg:*):void
{
_logger.appendText(msg + "\n");
} // private function log
private function testFunction(testInt:int):int
{
return testInt + 1;
} // private function testFunction
} // public class Tester
} // package
And here are my two Haxe classes:
package ;
class Main
{
static function main()
{
var tester:Tester = new Tester();
} //static function main
} // class Main
package ;
import flash.Lib;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
class Tester
{
inline static var REPS:UInt = 50000000;
private var _logger:TextField;
public function new()
{
_logger = new TextField();
var functionPointer:Int->Int = testFunction;
var i:UInt;
var beforeTime:Int;
var afterTime:Int;
var myTestInt:Int = 0;
_logger.autoSize = TextFieldAutoSize.LEFT;
Lib.current.addChild(_logger);
log("Direct Function Call");
beforeTime = Lib.getTimer();
for (i in 0 ... REPS)
{
testFunction(myTestInt);
}
afterTime = Lib.getTimer();
log("\ttestFunction(int): " + (afterTime - beforeTime));
log("");
log("Function Pointer Call");
beforeTime = Lib.getTimer();
for (i in 0 ... REPS)
{
functionPointer(myTestInt);
}
afterTime = Lib.getTimer();
log("\tfunctionPointer(int): " + (afterTime - beforeTime));
} // public function new
private function log(msg:String):Void
{
_logger.appendText(msg + "\n");
} // private function log
private function testFunction(testInt:Int):Int
{
return testInt + 1;
} // private function testFunction
} // class Tester
I compiled everything in Release mode and ran both programs. Here are the test results:
AS3
Direct Function Call
testFunction(int): 1617
Function Object Call
functionObject(int): 2238
HAXE
Direct Function Call
testFunction(int): 1590
Function Pointer Call
functionPointer(int): 2250
In both cases, calls to dynamic function references are approximately 1.4 times slower than direct method calls. There doesn’t seem to be anything particularly magical about Haxe in terms of calling dynamic function references.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
Originally posted by Drakim:
Once my bullet hell test ran a few thousand objects at once at a solid 60fps, I stopped benchmarking
That certainly speaks well of Haxe and your abilities. I don’t understand how you can use macros to handle callbacks with your components, however. Macros aren’t real functions, so there shouldn’t be any kind of function pointer that you can store in, say, a linked list. From the way you word your pseudocode in this thread, it seems like you’re using either function pointers or Haxe callbacks. Am I right?
I don’t know if callbacks and function pointers in Haxe are eventually compiled as AS3 function objects. I guess some performance testing is in order.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
The Runnable design pattern sounds like it might work, but wow, that would mean having an extra class for each event that each component is supposed to handle.
Ace provided a link to a component-based Asteroid game in AS3, the first part of which is here. It looks promising because the entity’s components are statically assigned, but it uses Robert Penner’s Signals, which calls function objects through their apply methods. Damn and double damn.
I don’t know enough of Haxe to say whether programming components with it will result in a tolerable performance hit. I’ve noticed, though, that its built-in List and FastList data structures won’t allow you to insert objects in the middle of the list. Strangely, List is a FIFO collection, but FastList is a LIFO collection. It’s little things like those that make me wonder just how good Haxe is.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
[AS3] Getting function arguments from an array [Solved!]
Some components are bound to have callback functions that are called in an enter-frame event handler, so using the apply method with components’ callback functions may not be a good idea. For that matter, using function objects may not be a good idea either because they are about four times slower than calling a function directly. To make matters worse, calling the apply method on a function object is 6.5 times slower than calling a standard function directly. I got all my figures from Jackson Dunstan.
Like Ace, I’ve also been looking into component-based programming, but from all indications, it’s clear that dynamically binding components to entities would result in a significant performance hit. Whether that performance hit is acceptable would depend on the type of game being developed. Trying to make a bullet hell game with components may result in hellishly slow bullets. On the other hand, the performance hit would probably not be noticeable on games that don’t have lots of animated objects on screen at once.
Component-based programming would probably work in C++, but I’ve given up on trying to bind components to entities dynamically in AS3. Perhaps statically binding them at compile time might work better.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
AS3 Get and Set Methods
I use them often. If all you need to do is to give read and write access to a variable of a class, you’re better off making that variable public because getters and setters come with some performance overhead. If you want to provide read-only access to a variable of a class, you can give it a getter but no setter. In theory, you could also make that variable publicly accessible and promise yourself that all other classes will not write to it, but if you’re like me, you might forget the promises you make to yourself.
Setters are especially good if you want to do more than just write to a variable. Here’s an example:
public function set image(value:Bitmap):void
{
_imageClass = null;
if (_image)
removeChild(_image);
_image = value;
if (_image)
{
_imageClass = getDefinitionByName(getQualifiedClassName(value)) as Class;
// Set the registration point of the bitmap to its center.
_image.transform.matrix = new Matrix(1, 0, 0, 1, -(_image.width / 2.0), -(_image.height / 2.0));
addChildAt(_image, 0);
}
} // public function set image
|
|
|
Elyzius
235 posts
|
Topic: Game Design /
[Feedback] Control scheme
Well, it’s certainly different from the way the usual shmups are controlled. The control scheme reminds me a bit of BadEgg’s Klecter, but I’m guessing that your enemy ships won’t be actively pursuing the player and will move along their pre-programmed flight plan instead.
The limited area where the avatar can move is reminiscent of Space Invaders. In my opinion, it would probably be better to design your game as a multidirectional shooter instead of a fixed shooter like Space Invaders. Also, you may want the avatar’s speed to vary with the speed of the mouse.
With just one enemy to aim at, shooting it doesn’t seem so difficult, but that’s probably because players can stop their ships and wait for the enemy to approach the bullet. I wonder what it’s like, however, when there are many enemies coming from three possible directions, moving along different trajectories, and firing bullets in quick succession. I suspect that players may find it more difficult to shoot then. It might be better to just let the player shoot straight up if you want the mouse to control the avatar’s movement. If you want the player to shoot in any direction, using the keyboard for movement and the mouse to aim and shoot would probably work better.
These are all just suggestions of course. The problem with following my last suggestion is that it would make your game no different from most other shmups.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
GiTD [#32] Voting finished! [imp2]
Originally posted by UnknownGuardian:
x = first place votes
y = second place votes
z = after original deadline votes.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Originally posted by Ace_Blue:
Regarding velocity, you don’t need to set myEntity to null in the constructor, it’s already null. In fact, you don’t need myEntity at all in this component, you are not using it anywhere (just pass the entity as an argument in detach() as you do in attach().)
That could work as long as I make sure to pass the right entity to detach every time. I prefer to keep things as idiot-proof as possible though, knowing full well the capabilities and limitations of the programmer. :P
Other than that I just can’t decide if it’s better to use velocity as a simple repository like position and handle motion in a separate module or to combine value storage and linear uniform motion in one package like you do. I guess if you don’t need anything more complex than a straight trajectory at constant speed your way saves a component. If you do need more complex motion though you have an extra function call compared to handling all motion in a separate component.
Haven’t quite figured how to do that yet. Some entities, such as the bullets in a bullet hell game, have some pretty fancy trajectories. I’m guessing that there should be a component for each type of trajectory in the game and that it should be possible to combine components to make some pretty complex motions.
…but I’m still wondering what you’ll do if you want to allow WASD as an alternate control scheme and let the player configure to their keyboard preferences (IJKL, ZQSD, etc…).
I guess a fully configurable player component should be able to read any key pressed and interpret what it means in the game. I haven’t actually programmed anything like that yet, but those are my initial thoughts on the matter.
So far, my attempts to learn component-based programming feels kind of like trying to learn how to ride a bike without using my feet. I know I can code a lot faster right now with the usual OOP method, but the promise of long-term benefits is what’s making me persevere with the new method.
Edit: It turns out that making configurable keyboard controls is very easy. You need to store key presses as booleans in a vector of 222 elements, each of which represents a key code. Depending on your game’s needs, you may want to also have another vector of 222 booleans to indicate if a particular key had just been pressed, as opposed to having already been pressed since the last call to onKeyPress. To configure the keys, you’ll need public variables or properties for each type of key that you want to be able to configure. Something like this:
public var keyDown:uint = Keyboard.DOWN;
public var keyUp:uint = Keyboard.UP;
public var keyLeft:uint = Keyboard.LEFT;
public var keyRight:uint = Keyboard.RIGHT;
Since they are publicly accessible, you can change the default values at any time.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
And here’s my player component:
package
{
import elyzius.component.Entity;
import elyzius.component.IComponent;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
/**
* ...
* @author Frank Perez
* @since 18 May 2013
*/
public class ComponentPlayer implements IComponent
{
public var moveRate:Number;
private var _myEntity:Entity;
private var _velocityComponent:ComponentVelocity;
private var _keyLeftPressed:Boolean = false;
private var _keyRightPressed:Boolean = false;
private var _keyUpPressed:Boolean = false;
private var _keyDownPressed:Boolean = false;
public function ComponentPlayer()
{
_myEntity = null;
_velocityComponent = null;
moveRate = 0.0;
} // public function ComponentPlayer
/* INTERFACE elyzius.component.IComponent */
public function attach(entity:Entity):void
{
_myEntity = entity;
_velocityComponent = _myEntity.getComponent(ComponentVelocity) as ComponentVelocity;
_myEntity.addEvent(EventsCustom.EVENT_UPDATE, update, 1);
} // public function attach
public function detach():void
{
_myEntity.removeEvent(EventsCustom.EVENT_UPDATE, update, 1);
_myEntity = null;
} // public function detach
public function onKeyPress(e:KeyboardEvent):void
{
switch (e.keyCode)
{
case Keyboard.LEFT:
_keyLeftPressed = true;
break;
case Keyboard.RIGHT:
_keyRightPressed = true;
break;
case Keyboard.DOWN:
_keyDownPressed = true;
break;
case Keyboard.UP:
_keyUpPressed = true;
break;
}
} // private function onKeyPress
public function onKeyRelease(e:KeyboardEvent):void
{
switch (e.keyCode)
{
case Keyboard.LEFT:
_keyLeftPressed = false;
break;
case Keyboard.RIGHT:
_keyRightPressed = false;
break;
case Keyboard.DOWN:
_keyDownPressed = false;
break;
case Keyboard.UP:
_keyUpPressed = false;
break;
}
} // private function onKeyRelease
private function update(eventData:Object):void
{
if (_keyLeftPressed)
_velocityComponent.deltaX = -moveRate;
else if (_keyRightPressed)
_velocityComponent.deltaX = moveRate;
else
_velocityComponent.deltaX = 0.0;
if (_keyUpPressed)
_velocityComponent.deltaY = -moveRate;
else if (_keyDownPressed)
_velocityComponent.deltaY = moveRate;
else
_velocityComponent.deltaY = 0.0;
} // private function update
}
} // package
Am I coding these components right, or is there still room for improvement?
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Here’s my velocity component:
package
{
import elyzius.component.Entity;
import elyzius.component.IComponent;
/**
* ...
* @author Frank Perez
* @since 18 May 2013
*/
public class ComponentVelocity implements IComponent
{
public var deltaX:Number;
public var deltaY:Number;
private var _myEntity:Entity;
private var _positionComponent:ComponentPosition;
public function ComponentVelocity()
{
_myEntity = null;
_positionComponent = null;
deltaX = 0.0;
deltaY = 0.0;
} // public function ComponentVelocity
/* INTERFACE elyzius.component.IComponent */
public function attach(entity:Entity):void
{
_myEntity = entity;
_positionComponent = _myEntity.getComponent(ComponentPosition) as ComponentPosition;
_myEntity.addEvent(EventsCustom.EVENT_UPDATE, update);
} // public function attach
public function detach():void
{
_myEntity.removeEvent(EventsCustom.EVENT_UPDATE, update);
_myEntity = null;
_positionComponent = null;
} // public function detach
private function update(eventData:Object):void
{
_positionComponent.x += deltaX;
_positionComponent.y += deltaY;
} // private function update
} // public class ComponentVelocity
} // package
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Originally posted by Drakim:
Then way I do it (and you don’t necessarily have to mimic me) is by separating it into 3 components, Position, Velocity and Player.
Cool. I finally got it to work. Here’s the relevant section of my Main class that has to do with components:
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
[Embed(source="../lib/player.png")]
var imagePlayerClass:Class;
_player = new Entity();
var positionComponent:ComponentPosition = new ComponentPosition();
positionComponent.x = stage.stageWidth / 2.0;
positionComponent.y = stage.stageHeight / 2.0;
_player.attachComponent(positionComponent);
var velocityComponent:ComponentVelocity = new ComponentVelocity();
_player.attachComponent(velocityComponent);
var imageComponent:ComponentBitmap = new ComponentBitmap();
imageComponent.image = new imagePlayerClass();
_player.attachComponent(imageComponent);
var playerComponent:ComponentPlayer = new ComponentPlayer();
playerComponent.moveRate = PLAYER_MOVE_RATE;
_player.attachComponent(playerComponent);
_player.dispatchEvent(EventsCustom.EVENT_ADD_CHILD, this);
_player.dispatchEvent(EventsCustom.EVENT_DRAW);
stage.addEventListener(KeyboardEvent.KEY_DOWN, playerComponent.onKeyPress);
stage.addEventListener(KeyboardEvent.KEY_UP, playerComponent.onKeyRelease);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
} // private function init
private function onEnterFrame(e:Event):void
{
_player.dispatchEvent(EventsCustom.EVENT_UPDATE);
_player.dispatchEvent(EventsCustom.EVENT_DRAW);
} // private function onEnterFrame
Edit: It took me a few tries, but I was finally able to post the code of my velocity and player components without screwing up the text formatting. I posted my code in three separate posts, including this one, just to play safe.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
GiTD [#32] Voting finished! [imp2]
Congrats, imp2, and thanks to everyone who voted and/or gave feedback. Loved the feedback especially.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
GiTD [#32] Voting finished! [imp2]
Originally posted by UnknownGuardian:
OK, I tallied twice, imp2 tallied and you tallied. We all got different results :D
Or someone is changing their votes
Too late for extended deadline changes. The winner of the contest will be one of the three (imp2, nutter666 or Russpuppy) no matter what, however.
All right. I got 5 votes though, 2 points each from Ace_Blue and hok003 and 1 point from Ivach. Can we make that my official count? This is the most number of votes that I got in any GitD, so I’m kind of proud of how I did this time around. :P
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
GiTD [#32] Voting finished! [imp2]
Whoa, my tally shows that there is only a two-way tie between imp2 and nutter666. To compare with the official count, I haven’t included saybox’s votes yet. Here’s what I got:
1st 2nd Total
imp2 4 2 10
Elyzius 2 1 5
VBCPP 0 0 0
gobologna 0 1 1
I_love_you_lots 0 3 3
Sushin 2 3 7
nutter666 3 4 10
Kewry 1 0 2
BadEgg 3 1 7
Russpuppy 3 3 9
FadiLoutf 0 0 0
MrHasuu 1 1 3
Including saybox’s votes would bring the total votes of RussPuppy to 11 and Sushin to 8.
Can someone else please verify my tally? We programmers tend to be awful at tallying figures because we always start counting from zero. :P
Btw, do we seriously want to extend the deadline? It seems kind of unfair for the guys contending for first place if someone else shoots past them. (I’m not trying to put anyone down. I’m just trying to find a way to keep things fair.) A simple way to break the tie would be to count the number of first place votes of the tied contenders.
Edit: Ninja’d by Kewry, whose votes would tip the balance back toward imp2.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Can you give me an example of how to handle key presses, Drakim? I’d like to see how it’s handled in both the component and the World/Main class.
Edit: This is how I coded the component:
package
{
import elyzius.component.Entity;
import elyzius.component.IComponent;
import flash.geom.Point;
/**
* ...
* @author Frank Perez
* @since 17 May 2013
*/
public class ComponentPosition implements IComponent
{
public var x:Number = 0.0;
public var y:Number = 0.0;
private var _myEntity:Entity;
public function ComponentPosition()
{
_myEntity = null;
} // public function ComponentPosition
/* INTERFACE elyzius.component.IComponent */
public function attach(entity:Entity):void
{
_myEntity = entity;
_myEntity.addEvent(EventsCustom.EVENT_MOVE_TO, moveTo);
_myEntity.addEvent(EventsCustom.EVENT_MOVE, move);
} // public function attach
private function moveTo(point:Point):void
{
x = point.x;
y = point.y;
}
private function move(point:Point):void
{
x += point.x;
y += point.y;
}
} // public class ComponentPosition
} // package
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
I’ve been trying to learn how to work with components, but I’m not sure if I’m doing it right. Below is a section of the code that I wrote to experiment with them.
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
[Embed(source="../lib/player.png")]
var imagePlayerClass:Class;
var entity:Entity = new Entity();
var positionComponent:ComponentPosition = new ComponentPosition();
entity.attachComponent(positionComponent);
var imageComponent:ComponentBitmap = new ComponentBitmap();
imageComponent.image = new imagePlayerClass();
entity.attachComponent(imageComponent);
entity.dispatchEvent(EventsCustom.EVENT_ADD_CHILD, this);
entity.dispatchEvent(EventsCustom.EVENT_MOVE_TO, new Point(stage.stageWidth / 2.0, stage.stageHeight / 2.0));
entity.dispatchEvent(EventsCustom.EVENT_DRAW);
_player = entity;
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyPress);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyRelease);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
} // private function init
private function onEnterFrame(e:Event):void
{
if (_keyLeftPressed)
_player.dispatchEvent(EventsCustom.EVENT_MOVE, new Point(-PLAYER_MOVE_RATE, 0));
if (_keyRightPressed)
_player.dispatchEvent(EventsCustom.EVENT_MOVE, new Point(PLAYER_MOVE_RATE, 0));
if (_keyUpPressed)
_player.dispatchEvent(EventsCustom.EVENT_MOVE, new Point(0, -PLAYER_MOVE_RATE));
if (_keyDownPressed)
_player.dispatchEvent(EventsCustom.EVENT_MOVE, new Point(0, PLAYER_MOVE_RATE));
_player.dispatchEvent(EventsCustom.EVENT_DRAW);
} // private function onEnterFrame
Am I doing it right, or is there a way I could have done things better? I have to ask because I can almost hear Jackson Dunstan screaming in horror at the sight of my code.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Hmm, I may have misunderstood. Is the removeEvent function supposed to be similar to removeEventListener? If so, maybe I can write the function this way instead:
public function removeEvent(eventCode:uint, callback:Function):void
{
var eventList:LinkedList = _events[eventCode];
if (eventList)
{
var event:EventCBD = eventList.getFirst();
if (event)
{
do
{
if (event.callback == callback)
{
eventList.removeCurrent();
event = eventList.getCurrent();
}
else
event = eventList.getNext();
} while (event);
}
}
} // public function removeEvent
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
If I needed to remove events of a particular event code, I was thinking of coding something like this:
public function removeEvent(eventCode:uint):void
{
var eventList:LinkedList = _events[eventCode];
if (eventList)
eventList.removeAll();
} // public function removeEvent
It isn’t memory-efficient, but it accesses events pretty fast, which makes for a good trade-off, I think.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Yeah, that’s one of the nuances of AS3 programming that one eventually gets used to. I ought to learn Haxe someday.
Btw, I realize that since my version of the Entity class identifies events by an integer event code, it would be better to store the events in an array instead of a dictionary. The event code would then be the index in the array that references the specific event.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Deeper Topic (part 3): Events to the rescue!
Originally posted by Ace_Blue:
In case anyone would like to get started but isn’t quite sure how to setup their Entity class, here’s mine. Feel free to suggest improvements/optimizations for it.
Here’s my take on the Entity class. The salient differences between my version and Ace_Blue’s are that I use a custom-built LinkedList to store events in the Dictionary, and I also use a Dictionary to store components. I figure that retrieving components from a Dictionary may be faster than searching a vector or array, especially if the Entity is already holding a lot of components. If my code needs correction or improvement, please post the new code in this thread.
package elyzius.component
{
import elyzius.collection.LinkedList;
import flash.utils.Dictionary;
/**
* ...
* @author Frank Perez
* @since 17 May 2013
*/
public class Entity
{
private var _components:Dictionary;
private var _events:Dictionary;
public function Entity()
{
_components = new Dictionary();
_events = new Dictionary();
} // public function Entity
public function attachComponent(component:IComponent):void
{
_components[Object(component).constructor] = component;
component.attached(this);
} // public function attachComponent
public function attachEvent(eventCode:uint, callback:Function, priority:int = 0):void
{
var event:EventCBD = new EventCBD();
event.priority = priority;
event.callback = callback;
// Retrieve the list of events having the given eventCode.
var eventList:LinkedList = _events[eventCode];
if (!eventList)
{
// The list of events does not yet exist. Create it and store it.
eventList = new LinkedList();
_events[eventCode] = eventList;
}
// Insert event into the eventList in order from highest to lowest priority.
var inserted:Boolean = false;
var eventCurrent:EventCBD = eventList.getFirst();
if (eventCurrent)
{
do
{
if (event.priority >= eventCurrent.priority)
{
eventList.insert(event);
inserted = true;
}
else
eventCurrent = eventList.getNext();
} while (eventCurrent && !inserted);
}
if (!inserted)
eventList.push(event);
} // public function attachEvent
public function dispatchEvent(eventCode:uint, data:Object = null):void
{
// Check if we have a list of events of the given eventCode.
var eventList:LinkedList = _events[eventCode];
if (eventList)
{
var event:EventCBD = eventList.getFirst();
if (event)
{
do
{
event.callback(data);
event = eventList.getNext();
} while (event);
}
}
} // public function dispatchEvent
public function getComponent(type:Class):IComponent
{
return _components[type];
} // public function getComponent
} // public class Entity
} // package elyzius.component
internal class EventCBD
{
internal var priority:int;
internal var callback:Function;
} // internal class EventCBD
Incidentally, IComponent is essentially identical to ComponentInterface, but I use the standard naming convention. :P
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
GiTD [#32] Voting finished! [imp2]
Solsund, I can still run Photo Collection from my browser. I’m using Chrome, btw.
|
|
|
Elyzius
235 posts
|
Topic: Game Programming /
Logical OR help
Looks like you came across an error in the book. I hope for your sake that this isn’t a precursor of things to come.
|