PDA

View Full Version : Passing Object Reference Through an Event?


Jofu
07-13-2009, 09:34 AM
Hey all,
I'm new to AS3 and I was making a game of chess. Basically whenever you click a square with a chess piece, I assumed it would be like java, and you'd be able to create an "ActionListener" that would pass a reference to a square that was clicked.

My question is this: How can I access this square object that was clicked in my array?

board[x1][y1].addEventListener(MouseEvent.CLICK, onClick);

because I can make a function "onClick" but I will have no access to the object board[x1][y1] (the square object).

I'd like to do something like this...where my x's and y's would be the x1 and y1 of the board[x1][y1]

public function onClick(ev:MouseEvent): void
{
var x:Number = ev.THE X OF THE OBJECT?
var y:Number = ev.THE Y OF THE OBJECT?
trace(x);

//If there's a piece that is clicked.
if (board[x][y].piece != null)
{
//If an ally piece is clicked,
if (game.currentPlayer().white && board[x][y].piece.isWhite)
{
selected = board[x][y];
trace(selected.name);
}
}
//Moving to an empty spot
if (board[x][y].piece == null && selected != null)
{
selected.moveToSquare(board[x][y]);
//Switch players
game.playerOne.active = !game.playerOne.active;
game.playerTwo.active = !game.playerTwo.active;
}
//Kill enemy
if (board[x][y].piece != null && selected.piece != null)
{
if (board[x][y].piece.isWhite && !selected.piece.isWhite)
{
selected.moveToSquare(board[x][y]);
//Switch players
game.playerOne.active = !game.playerOne.active;
game.playerTwo.active = !game.playerTwo.active;
}
}
//Deselecting a piece
if (board[x][y] == selected)
{
selected = null;
}

}



If you could maybe point me in the right direction, I'd appreciate it :) This has me pretty frustrated. If you need additional information, I'd be glad to share more of my code.

Amn
07-13-2009, 10:28 AM
There are tons of ways to do this :-)

First of all, remember that dispatchEvent (function which sends events) and addEventListener implementation are part of EventDispatcher class. This means that all objects of EventDispatcher class or its subclasses (includes most of flash.display.* classes) can send events and register "listeners" (functions that are called when event happens).

In your case, I don't see which of your objects, if any, implements event dispatching or extends EventDispatcher class, or includes a property that is an event dispatcher.

I assume, since you say your board squares can be clicked, they are visual objects. If they are, each of them can send events and register Listeners. You can choose if you want 8x8=64 event dispatchers, or you can do another thing - for your board class, which I assume is a display object too, create an "event_dispatcher: EventDispatcher" object, or inherit the whole class from EventDispatcher. Then, relying on the three event phases - capturing/target/bubbling, and an extended BoardSquare class, do something like:

//Each of your board squares now can have x and y values, which you assign when you create the board.


class ChessBoardSquare extends Sprite //event dispatcher too, but not used now
{
var x: Int;
var y: Int;
}

class ChessBoard extends Sprite //this is an event dispatcher, because Sprite is
{
function new()
{
build();
init();
}

function build()
{
//Add those squares

for(var x = 0; x < 8; x++)
for(var y = 0; y < 8; y++)
{
var square = new ChessBoardSquare();

square.x = x;
square.y = y;

//Make sure the square is also of the right color :-)

addChild(square);
}
}

function init()
{
//board is display object and will respond to "clicks" from any display object below it - including, but not limited to, squares
addEventListener("click", on_click);
}

//Might happen for any object below the board (even the board itself), not just a ChessBoardSquare
function on_click_event(e: Event)
{
if(e.target instanceof ChessBoardSquare)
{
//obviously a square was clicked, not anything else (which does send events too!)
on_click_square(e);
}
}

//Sending e just in case you wanted some more event properties, like some mouse properties f.e.
function on_click_square(e: Event)
{
var square = e.target;
var x = square.x;
var y = square.y;

//That code you posted that wanted to work...
}
}


There are many other ways to do this, and MANY optimizations you can do. All depending on what you want and what you weigh as important.

I found out yesterday that an event dispatcher is 48 bytes in memory. Not of any importance for chess games and squares that dispatch events, but for a fluid dynamics simulation it may be :-) Don't optimize until your game works.

Amn
07-13-2009, 10:34 AM
In case you wanted a more professional reply, remember that you can extend Event class and dispatch your own event objects, of your Event subclass, around. These objects can have their own properties, naturally. Why not x, and y?

Event.target already carries an object reference. The problem is you cannot add properties to "sealed" classes (you get a runtime exception error), which all classes are, unless specified otherwise. And unless your event target is an object that actually has x and y properties, it is more difficult to extract such values just from a Sprite.

Jofu
07-13-2009, 10:54 AM
Hey, thanks for the quick replies!

I was doing just that with the target earlier on. Except when I said ev.target, it was not returning a square object. And my square object contains and x and y that I wish to extract.

So I'm thinking that a good alternative would be just to go ahead and create my own Event by extending MouseEvent and assigning x and y variables which would be stored as instance variables.

That way whenever I say like board[x][y].addEventListener();

I can use that new MouseEvent object of mine that contains the x's and y's to extract those values directly.

In theory, is this the right idea? I think this is what you had in mind. Would it work?

Jofu
07-13-2009, 11:19 AM
Huh, well extending the class and assigning instance variables was making it unhappy. "Access of possibly undefined property xClicked through a reference with static type clickedPieceEvent." I guess because it could be called as a static class.

Either way, I'm confused why I wasn't able to obtain the square object (using event.target) that contained the x and y values from my original MouseEvent?

gclx
07-13-2009, 11:26 AM
var x:Number = ev.currentTarget.x;
var y:Number = ev.currentTarget.y;

Jofu
07-13-2009, 11:29 AM
var x:Number = ev.currentTarget.x;
var y:Number = ev.currentTarget.y;


Yeah, I had tried that as well, and it gives me a

"TypeError: Error #1010: A term is undefined and has no properties.
at Board/onClick()"

whenever I click on the chess piece :eek: Thanks, though.

Amn
07-13-2009, 11:30 AM
As far as I understand, "currentTarget" would not be valid, as it references the object processing the event, in case of my proposal the board.

You can create a custom event class. It sounds logical.

But, in case the "e.target" IS an object of ChessBoardSquare class, you SHOULD NOT be getting any errors regarding accessing its properties.

Amn
07-13-2009, 11:31 AM
Post your board and square classes, if you have made them.

Jofu
07-13-2009, 11:34 AM
Here's my board class:

public class Board extends Sprite
{
var board:Array = new Array(8);
var selected:Square;
var game:Game;

public function Board(currentGame:Game)
{
this.game = currentGame;

for (var y: Number = 0; y < 8; y++)
{
board[y] = new Array(8);
}


board[0][0] = new Square(new Piece(this, "rook", false), 0, 0, this);
board[1][0] = new Square(new Piece(this, "knight", false), 1, 0, this);
board[2][0] = new Square(new Piece(this, "bishop", false), 2, 0, this);
board[3][0] = new Square(new Piece(this, "queen", false), 3, 0, this);
board[4][0] = new Square(new Piece(this, "king", false), 4, 0, this);
board[5][0] = new Square(new Piece(this, "bishop", false), 5, 0, this);
board[6][0] = new Square(new Piece(this, "knight", false), 6, 0, this);
board[7][0] = new Square(new Piece(this, "rook", false), 7, 0, this);

board[0][1] = new Square(new Piece(this, "pawn", false), 0, 1, this);
board[1][1] = new Square(new Piece(this, "pawn", false), 1, 1, this);
board[2][1] = new Square(new Piece(this, "pawn", false), 2, 1, this);
board[3][1] = new Square(new Piece(this, "pawn", false), 3, 1, this);
board[4][1] = new Square(new Piece(this, "pawn", false), 4, 1, this);
board[5][1] = new Square(new Piece(this, "pawn", false), 5, 1, this);
board[6][1] = new Square(new Piece(this, "pawn", false), 6, 1, this);
board[7][1] = new Square(new Piece(this, "pawn", false), 7, 1, this);

board[0][7] = new Square(new Piece(this, "rook", false), 0, 7, this);
board[1][7] = new Square(new Piece(this, "knight", false), 1, 7, this);
board[2][7] = new Square(new Piece(this, "bishop", false), 2, 7, this);
board[3][7] = new Square(new Piece(this, "queen", false), 3, 7, this);
board[4][7] = new Square(new Piece(this, "king", false), 4, 7, this);
board[5][7] = new Square(new Piece(this, "bishop", false), 5, 7, this);
board[6][7] = new Square(new Piece(this, "knight", false), 6, 7, this);
board[7][7] = new Square(new Piece(this, "rook", false), 7, 7, this);

board[0][6] = new Square(new Piece(this, "pawn", false), 0, 6, this);
board[1][6] = new Square(new Piece(this, "pawn", false), 1, 6, this);
board[2][6] = new Square(new Piece(this, "pawn", false), 2, 6, this);
board[3][6] = new Square(new Piece(this, "pawn", false), 3, 6, this);
board[4][6] = new Square(new Piece(this, "pawn", false), 4, 6, this);
board[5][6] = new Square(new Piece(this, "pawn", false), 5, 6, this);
board[6][6] = new Square(new Piece(this, "pawn", false), 6, 6, this);
board[7][6] = new Square(new Piece(this, "pawn", false), 7, 6, this);


//white is for making the squares black or white.
var white: Boolean = true;
for (var y1:Number = 0; y1 < 8; y1++ , white = !white)
{
for (var x1:Number = 0; x1 < 8; x1++ , white = !white)
{
//creatings squares for all the squares that do not have pieces initially
if (board[x1][y1] == null )
board[x1][y1] = new Square(null, x1, y1, this);

//Drawing all the pictures and adding them to the board. This is so messy...
board[x1][y1].addToBoard(white);


//Assigning mouse events

//event:clickedPieceEvent = new clickedPieceEvent();
board[x1][y1].addEventListener(MouseEvent.CLICK, onClick);
}
}
}

public function onClick(ev:MouseEvent): void
{
var x:Number;
var y:Number;
if (ev.target instanceof Square)
{
x = ev.currentTarget.xS;
y = ev.currentTarget.yS;
}

//If there's a piece that is clicked.
if (board[x][y].piece != null)
{
//If an ally piece is clicked,
if (game.currentPlayer().white && board[x][y].piece.isWhite)
{
selected = board[x][y];
trace(selected.name);
}
}
//Moving to an empty spot
if (board[x][y].piece == null && selected != null)
{
selected.moveToSquare(board[x][y]);
//Switch players
game.playerOne.active = !game.playerOne.active;
game.playerTwo.active = !game.playerTwo.active;
}
//Kill enemy
if (board[x][y].piece != null && selected.piece != null)
{
if (board[x][y].piece.isWhite && !selected.piece.isWhite)
{
selected.moveToSquare(board[x][y]);
//Switch players
game.playerOne.active = !game.playerOne.active;
game.playerTwo.active = !game.playerTwo.active;
}
}
//Deselecting a piece
if (board[x][y] == selected)
{
selected = null;
}

}

}


Here's my square class:

public class Square extends Sprite
{
var piece:Piece;
//white = true. black = false.
var color:Boolean;
var xS:Number;
var yS:Number;
var board:Board;
public function Square(piece:Piece, x1:Number, y1:Number, board:Board)
{
this.board = board;
this.piece = piece;
this.xS = x1;
this.yS = y1;

this.x = 50*xS;
this.y = 50 * yS;
//trace(x1);
// adding displayobject to the display list
}

function addToBoard(color:Boolean):void
{
//if color == white
if (color == true)
this.graphics.beginFill( 0x995500 , 1 );
else
this.graphics.beginFill( 0xFFDD11 , 1 );

//making the square
this.graphics.drawRect(0, 0, 50, 50);

if (piece != null)
addChild(this.piece.draw());
board.addChild(this);

}

function moveToSquare(square:Square)
{
//moving to an empty space
if (square == null)
{
square.piece = this.piece;
square.addChild(square.piece.draw() );
this.removeChild(this.piece);
this.piece = null;
}
//moving to a space where you capture a piece
else
{
//adding the captured piece to the players stash.
board.game.currentPlayer().captured.push(square.pi ece);
square.removeChild(square.piece);
square.piece = this.piece;
this.removeChild(this.piece);
this.piece = null;
}
}

}


Sorry if it's a bit messy.

Amn
07-13-2009, 11:54 AM
Obvious errors in your code:

Board class:

1. You use squares as event dispachers, not the board, which is what I wrote. If you use squares as event dispatchers, you DO NOT NEED to check whether event target is a Square. Squares (and any other display object, for that matter) only dispatch events when either they or their descendants in the display list are event targets. Since your squares will probably not contain any advanced event-dispatching children, you can safely ignore checking whether "e.target" is a Square.

2. I don't know why you have "currentTarget" when assigning "x" and "y" variables, but "currentTarget" indicates the object CURRENTLY processing the event, in this case I believe the board. Board does not have "x" and "y" properties, Squares do. The Square object you are trying to find is "e.target". If you REALLY still want to make sure you are dealing with the correct object, by all means, ignore .1 and check whether "e.target" is a Square.

3. If you do need to check whether "e.target" is a Square, and you are afraid that onClick is called for other type of objects (which it will if you handle events during "capture" or "bubble" phase), you have to "return" in case they are not. You, however, proceed with game logic right after checking "e.target" class, regardless of the outcome of your check.

Cannot see any useful errors in your Square class. Who knows, if you fix the above, it may work. Good luck! :-)

Jofu
07-13-2009, 12:03 PM
Hey, well thanks for all the help. I wasn't expecting to get this much feedback. You suggested a lot of good ideas.

And yeah, the checking for the event dispatchers was me messing around with the code. I guess it got left in there while testing.

I tried using e.target and it was giving me the same error as currentTarget. Granted, it is 6AM and I've been up far too late. I'll read all that you said when I wake up and go through and refine everything.

As for anyone else who comes across this, if you see anything I'm doing wrong, let me know..

Amn
07-13-2009, 12:38 PM
I am telling you, if your "ev.target" is a Square, it should work. I would bet a lot it should.

You are welcome anyway, when I have time, I try to do my best. Sometimes it can be too much :-)

henke37
07-13-2009, 12:49 PM
I am fairly sure that it's completly oposite, currentTarget is the object that you added the listener to, while target is the object that is the leaf of the displaylist that the event will get to.

Amn
07-13-2009, 01:11 PM
You may be right, I have not used "currentTarget" all that much, which could indicate I am missing out on features here. And I currently don't have the time to debug the code. Maybe in the evening I will do that.

Jofu
07-13-2009, 05:26 PM
Hm. Well the consensus seems to be that target or currentTarget should be working the way which I hoped it had. Since the square class contains the x and y values, I should have access to that. Which leads me to believe there's something wrong in my square class.

I'm going to look back over it when I get back from class in a few hours.