Events, listeners & other such cool things to know for beginners

howdy all,

this is one thing that has puzzled me in knowing. but things like events and listeners. i understand say how they work to some degree. but that is with things like keys etc, moving things around, or even doing updates, but even in some way it can become confusing still.

but what i have, and have experienced many times is this Uncaught exception - Invalid field access : addEventListener
this would be if im trying to get keys, mouse up/down etc.
im still new to openfl and the haxe language, and actually not really found some nice solid info/tutorials that i can really delve into to learn. though do have some pdfs im getting into with actionscript. so hopefully those will make sense to me in some way.

but to show what i mean - here is some code im currently working on that has mouse events using nape. the fact its using nape is pointless but its more the outcome and the error i run into. which i have ran into other times :wink:

so can someone tell me more about event, listener and hopefully some good practices to get to know them better so they do what i want when i need them to?
if you have any info or links/videos, that would also be cool

but why would this give an error like this: Uncaught exception - Invalid field access : addEventListener

many thanks to anyone that could shed some light on this, that does actually confuse me. im gonna see if i can find something that may explain :wink:

class Player extends Sprite {
private var _player:Body;
private var _playerJoint:PivotJoint;
private var _playerPoint:Vec2;

public function new(space:Space) {
	super();
	_player = new Body(BodyType.DYNAMIC);
	_player.shapes.add(new Circle(30));
	_player.position.setxy(300,300);
	_player.space = space;

	_playerJoint = new PivotJoint(space.world, null, Vec2.weak(), Vec2.weak());
	_playerJoint.space = space;
	_playerJoint.active = false;
	_playerJoint.stiff = false;

	stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
	stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
	stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

private function onEnterFrame(event:Event):Void {
	if (_playerJoint.active) {
		_playerJoint.anchor1.setxy(mouseX, mouseY);
	}
}

private function onMouseDown(event:MouseEvent):Void {
	_playerPoint = Vec2.get(mouseX, mouseY);
	_playerJoint.body2 = _player;
	_playerJoint.anchor2.set(_player.worldPointToLocal(_playerPoint, true));
	_playerJoint.active = true;
}

private function onMouseUp(event:MouseEvent):Void {
	_playerJoint.active = false;
}
}

The stage reference for a DisplayObject is null until it has been added to the display list. When you create your Player instance, it has not been added to the display list yet, so stage will be null. You have to listen to addEventListener (Event.ADDED_TO_STAGE, this_onAddedToStage); first to get a stage reference

I would recommend that you do the ENTER_FRAME, MOUSE_DOWN and MOUSE_UP event listeners at a higher level. Let that tell your player what to do. This makes it simpler to replace control schemes, restart gameplay, pause gameplay and other things that are a higher scope than what your Player class should be thinking about.

For example, in your above sample, perhaps Player could have a moveTo method, that the class above it calls? Then it handles the mouse active true/false and gives the player the coordinates (which it can use on its physics body)

1 Like

right so do all handling say in: PlayState, instead, since that is where it would be, playing :wink: makes sense actually

one thing that nilsen filc said is use instead of just stage.addEventListener - have Lib.current.stage.addEventListener, then import openfl.Lib. i mean, that works, but might guess to say its more a hack to get things working? i may be wrong completely though :wink:

thanks again

Yes, technically in Haxe projects (not in Actionscript) you are able to use the static Lib.current.stage reference to always get a pointer to the stage

Although I believe this is handy for libraries like Actuate (which should not have to ask for a display object from you), I think in general, if you are having to engineer around a null stage reference, you may be going about things in a path that (design-wise) will create trouble down the road :slight_smile:

1 Like

hmm ok. makes sense i guess.
so really in an a say Player.hx, id have a setupEvent like this:

public function setupEvent():Void {
	this.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
	this.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
	this.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

then in PlayState, id have

private function onEnterFrame(event:Event):Void {
      _player.setupEvent();
   }

is that fine?
just trying to weed this out :wink:

thanks again :wink:

If it were me:

I would listen to those events in a Game class, or something that is aware of the full context of gameplay, versus a player, which (should probably be) focused on a narrower scope.

When the top-level receives an ENTER_FRAME, or input, it decides how to use it. Most of the time (by the sound of it) it will be notifying the player to move to that location, but other times the game might be paused, or you might be showing a dialog or some other thing that causes you not to tell the player to move.

Like:

addEventListener (Event.ENTER_FRAME, this_onEnterFrame);
addEventListener (MouseEvent.MOUSE_DOWN, this_onMouseDown);

...

private function this_onEnterFrame (event:Event):Void {
    
    var currentTime = Lib.getTimer ();
    var deltaTime = currentTime - cacheTime;
    
    if (active) {
        
        player.update (deltaTime);
        
    }
    
    cacheTime = currentTime;
    
}

private function this_onMouseDown (event:MouseEvent):Void {
    
    if (active) {
        
        stage.addEventListener (MouseEvent.MOUSE_MOVE, stage_onMouseMove);
        stage.addEventListener (MouseEvent.MOUSE_UP, stage_onMouseUp);
        
    }
    
}

private function stage_onMouseMove (event:MouseEvent):Void {
    
    player.moveTo (mouseX, mouseY);
    
}

private function stage_onMouseUp (event:MouseEvent):Void {
    
    stage.removeEventListener (Event.MOUSE_MOVE, stage_onMouseMove);
    stage.removeEventListener (Event.MOUSE_UP, stage_onMouseUp);
    
}

So in a higher-level game class, you listen for ENTER_FRAME as well as mouse down. When you receive an ENTER_FRAME, you update the player (and any other game objects) with a delta time value, so that they can animate and update accordingly, based on the elapsed time.

Once you click, you listen to move and up events on the stage. I tend to do this in case the mouse drifts over some object that would cause it to not move over β€œthis” but move over something else. Once you click and drag, you want it to continue, regardless of what is under the mouse pointer, until you release, usually.

When it moves, it sends an update to the player to tell it to go to where you want it. This may be limited by certain logical conditions.

When the mouse is released, we stop handling the motion events, and go back to waiting for a mouse down before doing anything

1 Like

right okay. awesome. ill give this a look see and see what i can figure out :wink:
so really, you call this in a Game state? interesting. so movements are dealt outside of the actualy Player state itself. very interesting

many thanks again :wink:

actually i have noticed just now. is that the mouse affects everything right now. so i dont even have to click directly on the body to move it. if you can see the body in the gif
very strange. ill try and figure this out

If you want it to drag when you click only on the player, then perhaps the mouse events should be in the player class.

I would still recommend keeping updates in one place, at a high level

You can use the same idea – a local MOUSE_DOWN listener, which adds stage MOUSE_MOVE and MOUSE_UP listeners. That will work fine, because by the time someone clicks on your Player, the Player will already be on the stage, so stage will not be null

1 Like

cool cool. many thanks :wink:

actually looking at it more, with what you have said, it starting to make more sense.
anything that is directly affecting it, is good at least to try inside the class itself, anything can be done outside.
thats what im getting.

but ill look a little bit at a tutorial or something if i can find that explains as well

thanks again :wink: