研究一种模仿棋盘的概念验证面向对象的javascript项目。目前,我已经设置了四个画布,每个画布设置为两个不同的电路板和两个"侧边栏"显示当前回合的画布和为相关游戏拍摄的任何作品的列表。这是目前的截图:
http://i.imgur.com/GPoVkK2.png
问题是,第二个侧边栏中的元素是出于任何原因在第一个侧边栏中绘制的,我还没有找到原因。
此处列出的所有项目代码文件都已尽力解释并解释:
的index.html
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="scripts/Marker.js"></script>
<script type="text/javascript" src="scripts/TurnMarker.js"></script>
<script type="text/javascript" src="scripts/GameToken.js"></script>
<script type="text/javascript" src="scripts/GameBoard.js"></script>
<script type="text/javascript" src="scripts/Validation.js"></script>
<script type="text/javascript" src="scripts/Main.js"></script>
</head>
<body onLoad=initialize()>
<canvas id="gameBoard" width="400" height="400" style="border:1px solid #000000; position=relative;">
Your browser doesn't support HTML5 canvas.
</canvas>
<canvas id="sideBar" width="50" height="400" style="border:1px solid #000000; position=relative;">
</canvas>
<canvas id="gameBoardWizard" width="400" height="400" style="border:1px solid #000000; position=relative;">
Your browser doesn't support HTML5 canvas.
</canvas>
<canvas id="sideBarWizard" width="50" height="400" style="border:1px solid #000000; position=relative;">
</canvas>
</body>
</html>
Main.js
/*** Chess Board Program
* Author: Alex Jensen
* CS 3160: Concepts of Programming Languages
* 12/5/14
*/
var gameBoards = [];
/** Initialize is called when the page loads (set up in the HTML below)
*
*/
function initialize()
{
createBoard("gameBoard", "sideBar" ,8 , 8);
createBoard("gameBoardWizard", "sideBarWizard" ,8, 8);
// setInterval is a super awesome javascript function and I love it.
setInterval(function() {draw()}, 100);
}
/** CreateBoard is a helper function for Initialize that is responsible for loading a game board into the list of game boards.
* boardID= String title of the HTML5 table within the HTML index file.
*/
function createBoard(boardID, sideBarID, numSquaresRows, numSquaresColumns)
{
var initBoardValidator = [];
for (var i=0;i<numSquaresColumns;i++)
{
initBoardValidator[i] = [];
for (var j=0;j<numSquaresRows;j++)
{
initBoardValidator[i][j] = null;
}
}
var gameBoard = new GameBoard(boardID, sideBarID, numSquaresRows, numSquaresColumns, initBoardValidator, [], []);
gameBoard.tokens = createDefaultTokens(gameBoard);
gameBoard.takenTokens = createDefaultTakeMarkers(gameBoard);
gameBoards[gameBoards.length] = gameBoard;
}
/** Helper function for initialize which creates all the tokens and stores them in appropriate locations.
*
*/
function createDefaultTokens(game)
{
tokens = [];
// Create Pawns
for (var i = 0; i < 8; i++)
{
tokens[tokens.length] = new GameToken(game, "WP", 1, 'images/wp.png', i * game.squareWidth, game.squareHeight, pawnValidator);
tokens[tokens.length] = new GameToken(game, "BP", 2, 'images/bp.png', i * game.squareWidth, game.squareHeight * 6, pawnValidator);
}
// Create other pieces
tokens[tokens.length] = new GameToken(game, "WR", 1, 'images/wr.png', 0, 0, rookValidator);
tokens[tokens.length] = new GameToken(game, "WN", 1, 'images/wn.png', game.squareWidth, 0, knightValidator);
tokens[tokens.length] = new GameToken(game, "WB", 1, 'images/wb.png', game.squareWidth * 2, 0, bishopValidator);
tokens[tokens.length] = new GameToken(game, "WQ", 1, 'images/wq.png', game.squareWidth * 3, 0, queenValidator);
tokens[tokens.length] = new GameToken(game, "WK", 1, 'images/wk.png', game.squareWidth * 4, 0, kingValidator);
tokens[tokens.length] = new GameToken(game, "WB", 1, 'images/wb.png', game.squareWidth * 5, 0, bishopValidator);
tokens[tokens.length] = new GameToken(game, "WN", 1, 'images/wn.png', game.squareWidth * 6, 0, knightValidator);
tokens[tokens.length] = new GameToken(game, "WR", 1, 'images/wr.png', game.squareWidth * 7, 0, rookValidator);
tokens[tokens.length] = new GameToken(game, "BR", 2, 'images/br.png', 0, game.squareWidth * 7, rookValidator);
tokens[tokens.length] = new GameToken(game, "BN", 2, 'images/bn.png', game.squareWidth, game.squareHeight * 7, knightValidator);
tokens[tokens.length] = new GameToken(game, "BB", 2, 'images/bb.png', game.squareWidth * 2, game.squareHeight * 7, bishopValidator);
tokens[tokens.length] = new GameToken(game, "BQ", 2, 'images/bq.png', game.squareWidth * 3, game.squareHeight * 7, queenValidator);
tokens[tokens.length] = new GameToken(game, "BK", 2, 'images/bk.png', game.squareWidth * 4, game.squareHeight * 7, kingValidator);
tokens[tokens.length] = new GameToken(game, "BB", 2, 'images/bb.png', game.squareWidth * 5, game.squareHeight * 7, bishopValidator);
tokens[tokens.length] = new GameToken(game, "BN", 2, 'images/bn.png', game.squareWidth * 6, game.squareHeight * 7, knightValidator);
tokens[tokens.length] = new GameToken(game, "BR", 2, 'images/br.png', game.squareWidth * 7, game.squareHeight * 7, rookValidator);
return tokens;
}
function createDefaultTakeMarkers(game)
{
var takenTokens = [];
// Create Pawns
for (var i = 0; i < 8; i++)
{
takenTokens[takenTokens.length] = new Marker("WP", 1, 'images/wp.png', 5, (i * 20) + 5);
takenTokens[takenTokens.length] = new Marker("BP", 1, 'images/bp.png', 5, game.sideBar.height - ((i * 20) + 25));
}
// Create other pieces
takenTokens[takenTokens.length] = new Marker("WR", 1, 'images/wr.png', 25, 5);
takenTokens[takenTokens.length] = new Marker("WN", 1, 'images/wn.png', 25, 25);
takenTokens[takenTokens.length] = new Marker("WB", 1, 'images/wb.png', 25, 45);
takenTokens[takenTokens.length] = new Marker("WQ", 1, 'images/wq.png', 25, 65);
takenTokens[takenTokens.length] = new Marker("WK", 1, 'images/wk.png', 25, 85);
takenTokens[takenTokens.length] = new Marker("WB", 1, 'images/wb.png', 25, 105);
takenTokens[takenTokens.length] = new Marker("WN", 1, 'images/wn.png', 25, 125);
takenTokens[takenTokens.length] = new Marker("WR", 1, 'images/wr.png', 25, 145);
takenTokens[takenTokens.length] = new Marker("BR", 1, 'images/br.png', 25, game.sideBar.height - 25);
takenTokens[takenTokens.length] = new Marker("BN", 1, 'images/bn.png', 25, game.sideBar.height - 45);
takenTokens[takenTokens.length] = new Marker("BB", 1, 'images/bb.png', 25, game.sideBar.height - 65);
takenTokens[takenTokens.length] = new Marker("BQ", 1, 'images/bq.png', 25, game.sideBar.height - 85);
takenTokens[takenTokens.length] = new Marker("BK", 1, 'images/bk.png', 25, game.sideBar.height - 105);
takenTokens[takenTokens.length] = new Marker("BB", 1, 'images/bb.png', 25, game.sideBar.height - 125);
takenTokens[takenTokens.length] = new Marker("BN", 1, 'images/bn.png', 25, game.sideBar.height - 145);
takenTokens[takenTokens.length] = new Marker("BR", 1, 'images/br.png', 25, game.sideBar.height - 165);
console.log(takenTokens);
return takenTokens;
}
/** Helper function for draw responsible for drawing each gameBoard
*
*/
function draw()
{
for (var i = 0; i < gameBoards.length; i++)
{
gameBoards[i].draw();
}
}
GameBoard.js
function bind(scope, fn) {
return function() {
return fn.apply(scope, arguments);
}
}
function GameBoard(boardID, sideBarID, numSquareRows, numSquareColumns, validator, tokens, takenTokens)
{
this.game = document.getElementById(boardID);
this.gameContext = this.game.getContext("2d");
var gamerect = this.game.getBoundingClientRect();
//this.gameContext.translate(gamerect.left, gamerect.top);
this.sideBar = document.getElementById(sideBarID);
this.sideBarContext = sideBar.getContext("2d");
var siderect = this.sideBar.getBoundingClientRect();
//this.sideBarContext.translate(siderect.left, siderect.top);
this.boardWidth = this.game.width;
this.boardHeight = this.game.height;
this.squareWidth = this.boardWidth / numSquareColumns;
this.squareHeight = this.boardHeight / numSquareRows;
if (this.squareHeight % 1 != 0) alert("WARNING: squareHeight is not a solid number, the program might not work correctly! Always ensure that the board height divided by the number of rows comes out as a whole number.");
if (this.squareWidth % 1 != 0) alert("WARNING: squareWidth is not a solid number, the program might not work correctly! Always ensure that the board width divided by the number of columns comes out as a whole number.");
this.validator = validator;
this.tokens = tokens;
this.takenTokens = takenTokens;
this.turnOrderToken = new TurnMarker('images/wturn.png', 'images/bturn.png', siderect.width / 2 - 20, siderect.height / 2 - 20, 40, 40);
this.activePlayer = 1; // Whose turn is it?
this.selectedToken = null; // What token is currently being dragged around?
this.takePiece = null;
// Event listeners function nearly identically to how they are handled in C#.
this.game.addEventListener("mousedown", bind(this, this.onMouseDown), false);
this.game.addEventListener("mousemove", bind(this, this.onMouseMove), false);
this.game.addEventListener("mouseup", bind(this, this.onMouseUp), false);
/** Helper function for drawBoard responsible for swapping between two colors whenever it is called.
*
*/
this.swapColor = function swapColor()
{
if (this.gameContext.fillStyle != '#0000ff')
{
this.gameContext.fillStyle = '#0000ff';
} else {
this.gameContext.fillStyle = '#ffffff';
}
}
/** Responsible for drawing all the tokens
*
*/
this.drawTokens = function drawTokens()
{
for (var i = 0; i < this.tokens.length; i++)
{
var token = this.tokens[i];
this.gameContext.drawImage(token.image, token.x, token.y, this.squareWidth, this.squareHeight);
}
}
/** Responsible for drawing the checkerboard.
*
*/
this.drawBoard = function drawBoard()
{
this.gameContext.clearRect(0, 0, this.boardWidth, this.boardHeight);
for (var i = 0; i < this.boardWidth; i += this.squareWidth)
{
for (var j = 0; j < this.boardHeight; j += this.squareHeight)
{
this.swapColor();
this.gameContext.fillRect(i,j,this.squareWidth,this.squareHeight);
}
this.swapColor();
}
}
this.drawMarkers = function drawMarkers()
{
for (var i = 0; i < this.takenTokens.length; i++)
{
var marker = this.takenTokens[i];
console.log(marker.image + " " + marker.x + " " + marker.y + " " + marker.width + " " + marker.height);
if (marker.visible)
{
this.sideBarContext.drawImage(marker.image, marker.x, marker.y, marker.width, marker.height);
}
}
}
this.drawTurnMarker = function drawTurnMarker()
{
if (this.activePlayer == 1)
{
console.log(this.turnOrderToken.player1Image + " " + this.turnOrderToken.x + " " + this.turnOrderToken.y + " " + this.turnOrderToken.width + " " + this.turnOrderToken.height);
this.sideBarContext.drawImage(this.turnOrderToken.player1Image, this.turnOrderToken.x, this.turnOrderToken.y, this.turnOrderToken.width, this.turnOrderToken.height);
}
else
{
this.sideBarContext.drawImage(this.turnOrderToken.player2Image, this.turnOrderToken.x, this.turnOrderToken.y, this.turnOrderToken.width, this.turnOrderToken.height);
}
}
/** Container method which runs all draw functions on the board.
*
*/
this.draw = function draw()
{
this.drawBoard();
this.drawTokens();
this.drawMarkers();
this.drawTurnMarker();
}
/** Removes tokens from the board and adds them to the list of captured pieces in the sidebar
*
*/
this.capture = function capture(token)
{
for (var i = 0; i < this.tokens.length; i++)
{
var takenToken = this.tokens[i];
if (takenToken.x == token.x && takenToken.y == token.y)
{
this.tokens.splice(i, 1);
break;
}
}
for (var i = 0; i < this.takenTokens.length; i++)
{
var takenToken = this.takenTokens[i];
if (takenToken.name == token.name && takenToken.visible == false)
{
console.log(takenToken);
takenToken.visible = true;
break;
}
}
}
}
/** Event that fires when the mouse button is released
* Listeners in gameBoard
*/
GameBoard.prototype.onMouseUp = function (event)
{
if (this.selectedToken != null)
{
var gridx = Math.round(this.selectedToken.x / this.squareWidth);
var gridy = Math.round(this.selectedToken.y / this.squareHeight);
// Snap to the nearest tile
this.selectedToken.x = (gridx * this.squareWidth);
this.selectedToken.y = (gridy * this.squareHeight);
// Check to see if the move that was made is legal
this.takePiece = this.validator[gridx][gridy];
if (this.selectedToken.movementValidator())
{
// If it was, then advance the turn
if (this.activePlayer == 1)
{
this.activePlayer = 2;
}
else
{
this.activePlayer = 1;
}
}
else
{
// Otherwise move the token back to where it was
this.selectedToken.x = this.selectedToken.initX;
this.selectedToken.y = this.selectedToken.initY;
}
// Wherever the token ends up, update the grid to reflect that.
this.validator[this.selectedToken.initX / this.squareWidth][this.selectedToken.initY / this.squareHeight] = null;
this.validator[this.selectedToken.x / this.squareWidth][this.selectedToken.y / this.squareHeight] = this.selectedToken;
this.selectedToken = null;
}
}
/**
*
*/
GameBoard.prototype.onMouseDown = function(event)
{
var rect = this.game.getBoundingClientRect();
var mousePos = {x:event.clientX - rect.left, y:event.clientY - rect.top};
for (var i = 0; i < this.tokens.length; i++)
{
token = this.tokens[i];
// if you clicked this token and it's your turn
if (mousePos.x > token.x && mousePos.y > token.y && mousePos.x < token.x + this.squareWidth && mousePos.y < token.y + this.squareHeight && token.player == this.activePlayer)
{
this.selectedToken = token;
// Store where the token was before we picked it up. That way if we make an illegal move we can restore it to its initial location
this.selectedToken.initX = token.x;
this.selectedToken.initY = token.y;
}
}
}
/** Event that fires when the mouse position is updated
* Listeners in gameBoard
*/
GameBoard.prototype.onMouseMove = function(event)
{
if (this.selectedToken != null)
{
var rect = this.game.getBoundingClientRect();
var mousePos = {x:event.clientX - rect.left, y:event.clientY - rect.top};
this.selectedToken.x = mousePos.x - (this.squareWidth / 2);
this.selectedToken.y = mousePos.y - (this.squareHeight / 2);
}
}
Marker.js
/** Marker is a visual widget used to show taken pieces.
* player= The player associated with the marker
* tokenImagePath= The valid path to the location of the marker texture
* x,y= location of marker on the sidebar
*/
function Marker(name, player, markerPath, x, y)
{
this.name = name;
this.image = new Image();
this.x = x;
this.y = y;
this.width = 20;
this.height = 20;
this.player = player;
this.image.src = markerPath;
this.visible = true;
}
GameToken.js
/** GameToken represents a chess piece.
* player= The player the chess piece belongs to
* tokenImagePath= The valid path to the location of the chess piece texture
* x,y= location of token on the board
* movementValidator= Function to determine whether a move made by this token is legal or not.
* Validators are different for different types of tokens.
*/
function GameToken(game, name, player, tokenImagePath, x, y, movementValidator)
{
this.game = game;
this.name = name;
this.image = new Image();
this.x = x;
this.y = y;
game.validator[x / game.squareWidth][y / game.squareHeight] = this;
this.player = player;
this.image.src = tokenImagePath;
this.movementValidator = movementValidator;
}
最后是用于检查合法移动的WIP验证脚本
/** Specific validation code for Pawns.
*
*/
function pawnValidator()
{
// Pawns are tricky to validate because they can move one square directly forward, but can't take the square directly
// in front of them, and can only move diagonally when they can capture. In addition, they can move two squares
// forward as long as they're in starting position.
if (this.takePiece != null)
{
// If the square we moved to has an enemy in it and we've made a legal move with the pawn to take that piece
if ((this.takePiece.player != this.player &&
(this.x == this.initX + this.squareWidth || this.x == this.initX - this.squareWidth) &&
((this.player == 1 && this.y == this.initY + this.squareHeight) ||
(this.player == 2 && this.y == this.initY - this.squareHeight))))
{
// We're allowed to remove the token here because we've validated that the pawn has made the correct movement to take the piece.
capture(takePiece);
takePiece = null;
return true;
}
else
{
takePiece = null;
return false;
}
}
// The pawn is not capturing, so check to see that the move it is making is legal
else if (this.x == this.initX)
{
if (this.player == 1)
{
if ((this.y == this.initY + this.game.squareHeight) || (this.y == this.initY + (2*this.game.squareHeight)) && this.initY == (this.game.squareHeight))
{
return true;
}
}
else if (this.y == this.initY - this.game.squareHeight || (this.y == this.initY - (2*this.game.squareHeight)) && this.initY == (6*this.game.squareHeight))
{
{
return true;
}
}
}
return false;
}
/** Specific validation code for Rooks.
*
*/
function rookValidator()
{
// First check if the movement made was legal for a rook (straight line)
if (this.x == this.initX || this.y == this.initY)
{
// Next check if the movement made went through any other pieces
if (lineValidation(this.initX, this.initY, this.x, this.y))
{
if (this.game.takePiece != null)
{
this.game.capture(this.game.takePiece);
}
this.game.takePiece = null;
return true;
}
}
return false;
}
/** Specific validation code for Knights.
*
*/
function knightValidator()
{
// First check if the movement made was legal for a knight using relative positioning
var relativeX = Math.abs(this.x - this.initX) / this.game.squareWidth;
var relativeY = Math.abs(this.y - this.initY) / this.game.squareHeight;
if ((relativeX == 1 && relativeY == 2) || (relativeX == 2 && relativeY == 1))
{
// Knights can jump, so we don't need to validate the movement further
if (this.game.takePiece != null)
{
this.game.capture(this.game.takePiece);
}
takePiece = null;
return true;
}
}
/** Specific validation code for Bishops.
*
*/
function bishopValidator()
{
// First check if the movement made was legal for a bishop (diagonal line)
if (Math.abs(this.x - this.initX) == Math.abs(this.y - this.initY))
{
// Next check if the movement made went through any other pieces
if (lineValidation(this.initX, this.initY, this.x, this.y))
{
if (takePiece != null)
{
capture(takePiece);
}
takePiece = null;
return true;
}
}
}
/** Specific validation code for Kings.
*
*/
function kingValidator()
{
// First check if the movement made was legal for a king using relative positioning
var relativeX = Math.abs(this.x - this.initX) / squareSize;
var relativeY = Math.abs(this.y - this.initY) / squareSize;
if ((relativeX == 1 && relativeY == 1) || (relativeX == 1 && relativeY == 0) || (relativeX == 0 && relativeY == 1))
{
// TODO: Check to see if the move puts the king in check. That's a little past the scope of this project but would make for a nice addition.
if (takePiece != null)
{
capture(takePiece);
}
takePiece = null;
return true;
}
}
/** Specific validation code for Queens.
*
*/
function queenValidator()
{
// First check if the movement made was legal for a queen (diagonal line or straight line)
if ((Math.abs(this.x - this.initX) == Math.abs(this.y - this.initY)) ||
(this.x == this.initX || this.y == this.initY))
{
// Next check if the movement made went through any other pieces
if (lineValidation(this.initX, this.initY, this.x, this.y))
{
if (takePiece != null)
{
capture(takePiece);
}
takePiece = null;
return true;
}
}
}
/** Checks each square traveled over a line to see if it has traveled through another piece
* IMPORTANT: This function only works if the move is legal! If the move made is impossible in chess this function
* will not work correctly!
*/
function lineValidation(startX, startY, endX, endY)
{
while (startX != endX || startY != endY)
{
if (startX < endX) startX += squareSize;
if (startY < endY) startY += squareSize;
if (startX > endX) startX -= squareSize;
if (startY > endY) startY -= squareSize;
var checkTake = gameBoardValidator[startX / squareSize][startY / squareSize];
if (checkTake != null && (startX != endX || startY != endY))
{
return false;
}
}
return true;
}
两个游戏都有效,但第二个侧面画布中的UI元素似乎总是在第一个画布点中绘制。有人看到我搞砸了吗?
答案 0 :(得分:0)
在您的GameBoard
对象中,您尝试从window.sideBar
而不是this.sideBar
获取上下文(它应该在控制台中产生错误):
this.sideBarContext = sideBar.getContext("2d");
将该行更改为:
this.sideBarContext = this.sideBar.getContext("2d");
^^^^
它应该有用。