PDA

View Full Version : Converting NG Movement Tutorial into a Class



xanderdavis
February 10th, 2009, 11:56 PM
I'm trying to move this:
http://www.newgrounds.com/bbs/topic/700462

...into its own class. That way, control code can be partitioned off and can separately affect the Level, another class.

However, even though the FLA compiles all code without error, any keyboard input fails to trigger any traces, let alone any movements. It needs to move the world (public static MovieClip property) in the Levels class. The Levels class successfully triggers GameControls(); here in the Control class, but the event listeners themselves seem inoperative.

What am I missing? Here's what I have:

Control.as


package engine
{
import flash.display.MovieClip;
import flash.display.*;
import flash.events.*;

public class Controls extends MovieClip
{
public static var keyMoveLeft:Boolean = false;
public static var keyMoveRight:Boolean = false;
public static var keyMoveUp:Boolean = false;
public static var keyMoveDown:Boolean = false;
public static var keyRun:Boolean = false;
public static var speed = 20;

public function Controls():void
{
trace("Controls Engaged");
}

public function checkKeys(event:KeyboardEvent):void
{

if (event.keyCode == 65) {
trace("Player is moving left");
keyMoveLeft = true;
}
if (event.keyCode == 68) {
trace("Player is moving right");
keyMoveRight = true;
}
if (event.keyCode == 87) {
trace("Player is moving up");
keyMoveUp = true;
}
if (event.keyCode == 83) {
trace("Player is moving down");
keyMoveDown = true;
}
if (event.keyCode == 16) {
trace("Player is running");
keyRun = true;
}
}

public function keyUps(event:KeyboardEvent):void
{
if (event.keyCode == 65) {
event.keyCode = 0;
keyMoveLeft = false;
trace("Player stopped moving left");
}
if (event.keyCode == 68) {
event.keyCode = 0;
keyMoveRight = false;
trace("Player stopped moving right");
}
if (event.keyCode == 87) {
event.keyCode = 0;
keyMoveUp = false;
trace("Player stopped moving up");
}
if (event.keyCode == 83) {
event.keyCode = 0;
keyMoveDown = false;
trace("Player stopped moving down");
}
if (event.keyCode == 16) {
event.keyCode = 0;
keyRun = false;
trace("Player stopped running");
}
}

public function movement(event:Event):void
{
if (keyMoveLeft == true)
{
Levels.world.x += speed;
}
if (keyMoveRight == true)
{
Levels.world.x -= speed;
}
if (keyMoveUp == true)
{
Levels.world.y += speed;
}
if (keyMoveDown == true)
{
Levels.world.y -= speed;
}
if (keyRun == true)
{
speed = 20;
}
if (keyRun == false)
{
speed = 10;
}
}

public function GameControls():void
{
addEventListener(Event.ENTER_FRAME, movement);
addEventListener(KeyboardEvent.KEY_DOWN , checkKeys);
addEventListener(KeyboardEvent.KEY_UP, keyUps);
}

} // End Class
} //End Package */

Charleh
February 11th, 2009, 09:05 AM
I'm not too sure about AS3 as I haven't got round to using it yet, but the Levels variable in the movement function - is this declared within the movieClip class that you are extending? I can't see a declaration for it here.

If that's not the case - is the target object passed in via the event argument? In that case should you not be targeting a member of the event object for modification (e.g. event.Levels.world.x) - otherwise Levels.world.x doesn't do anything as Levels does not exist in the scope of this object

xanderdavis
February 11th, 2009, 09:14 AM
Levels is actually another class, an external class file Levels.as which extends MovieClip.

In the code above and for simplicity, I removed a mouse tracking function that successfully rotated the player character to the Mouse (x,y). _charPlayer is an instance of CharPlayer.as (another external class) called into Levels, which initializes the player and loads the player graphics.

That class calls charPlayer, a MovieClip in the compiler FLA library, linked within the library to export to ActionScript with a base class of flash.display.MovieClip).

Levels calls in an instance of Level01 (also a MovieClip in the compiler FLA library, linked the same way).

Everything displays correctly. The aformentioned character rotation worked off of an identicle event listener in Controls.as that executed a function to track the mouse and rotate the player towards it. So, then, I'm not sure why keyboard movement isn't working, since it's all set up in the same way, and most importantly, it worked.

xanderdavis
February 11th, 2009, 10:49 AM
Upon placing a test in the code (in the mouse tracking) I was able to successfully access world and manipulate its x.

I now know for sure that the problem isn't access to "world". The problem seems to be that the addEventListener(KeyboardEvent.'s aren't triggering checkKeys and keyUps.

No trace is made when keys are pressed, so we may be actively accessing the "movement" method, but the Booleans aren't changing to true.

So why wouldn't "checkKeys" and "keyUps" be working?

therobot
February 11th, 2009, 10:50 AM
Ok, have you tried:
stage.focus = this;// or stage.focus = whateverIsProcessingKeyboardEvents;
?

that usually helps in capturing key presses. not sure why tho. Ensure you get a good stage reference by using Event.ADDED_TO_STAGE

xanderdavis
February 11th, 2009, 11:07 AM
In AS3, any time I ever try accessing stage from within a class, it says "Access of undefined property stage".

Also, when I tried stage.focus = checkKeys; it said "implicit coercion of a value of a type Function to an unrelated type: flash.display.InteractiveObject".

As you can see, I've already imported all displays, but it seems I can combine the two within a method.

Charleh
February 11th, 2009, 11:13 AM
I'm still confused as to how your code is working at all when you have no reference to Levels in your code. You have created a class which extends movieClip - movieClip doesn't contain a 'Levels' member and you don't pass one in or have a private member which is called that.

Your addEventListener function will be in the scope of the instance of the object you have created and is inheriting the EventDispatcher interface - hence why you can use addEventListener - but even so, there is still no reference to Levels. How is Levels accessible? Does it actually compile?

Charleh
February 11th, 2009, 11:13 AM
Aha, you can't access stage from within a class unless you pass the reference to the stage into the class. Encapsulation actually works in AS3 so you'll have to shake away your AS2 cobwebs.

Use a singleton class if needs be to get the stage instance, or pass it in by reference to the objects that require it.

xanderdavis
February 11th, 2009, 11:23 AM
I believe I'm not technically accessing Levels, but rather simply referencing where the accessed instance lives.

For example, an instance of Level01 (MovieClip in Library) is created in Levels.as as the property "world". Before this is even done, the property world is defined in the class Levels as a public static var. Because it is public and static, it can be referenced in other classes. To do that, you have to write the path in the package, in this case: Levels.world

It all compiles without error. The only thing that doesn't work so far is checkKeys and KeyUps.

Charleh
February 11th, 2009, 11:44 AM
Ah I see. Seems strange if you had a mouse event working off the same listener - you must be instantiating the Control class to be able to call GameControls - for events to fire from the Control class do you need to ensure that the Control class is added to the stage using addChild? Are you doing that?

I'm not sure when events fire but I'd imagine that the stage would be the lowest level to determine when enter frame etc was fired and bubble it up to any children - I may be wrong though.

Apart from that it doesn't look like a scoping issue since AS3 handles scoping properly over AS2.

Have you tried using your own event string and firing a completely different event instead of a registered constant e.g. "someEvent" instead of KeyboardEvent.KEYDOWN (addEventListener takes a string as the event argument) then tracing from the handler function to the output window

substance
February 11th, 2009, 09:29 PM
this.stage.addEventListener(KeyboardEvent.KEY_DOWN , checkKeys);If you get a compile error then 'Controls' hasn't been added to the display list.

On a unrelated note, the class name should be the same as the file name.

xanderdavis
February 11th, 2009, 09:40 PM
Yes, a class name should be the same as the filename and this is the case. You also need a function that is the same name as your class name. From your default function that matches your class name, you can pass off the flow of events to new functions with unique names within your class.

Yes, I've found this link:
http://www.kirupa.com/forum/showthread.php?p=1952513#post1952513

It is indeed coming down to needing to add Control to the display list, but I'm struggling with implementing that.

Charleh
February 13th, 2009, 05:43 AM
Should be quite simple - you just need to call <reference_to_stage>.addChild(<instance_of_control_class>);

You should have the reference to the stage when you instantiate the control class, but if not make sure you pass reference to the stage in either the constructor or a member function.