切斯林斯(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/chesslings-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 15 分钟阅读 - 7495 个词 阅读量 0切斯林斯(译文)
原文地址:https://www.codeproject.com/Articles/646113/Chesslings
原文作者:Marcelo Ricardo de Oliveira
译文由本站 robot-v1.0 翻译
前言
An isometric chess game using Paper JS
使用Paper JS的等距国际象棋游戏 下载Chess3D.zip(Download Chess3D.zip)
目录(Table of Contents)
- 介绍(Introduction)
- 系统要求(System Requirements)
- 纸JS(Paper JS)
- 小象棋(Tiny Chess)
- 切斯林斯(Chesslings)
- 运动检测(Movement Detection)
- 动画(Animation)
- 最后考虑(Final Considerations)
- 历史(History)
介绍(Introduction)
作为程序员,我一直认为开发象棋游戏将是一个重要的里程碑,我为此感到自豪.我在这里,向您展示国际象棋游戏.是的,我为此感到自豪.可是等等.我真的开发过象棋游戏吗?(As a programmer, I always thought that developing a chess game would be a big milestone and I would be so proud of it. And here I am, presenting you with a chess game. And yes, I’m a little proud of it. But wait. Have I really developed a chess game?)
首先,创建国际象棋游戏无非就是开发(First of all, creating a chess game means nothing less than developing the) 象棋引擎(chess engine) ,这是坐在板子后面的聪明人软件,试图提出最好的举动并假装自己是人.我一直都是一个贫穷的国际象棋选手,我的第一个想法是,开发国际象棋引擎的最佳办法就是从一些现有的且正在运行的C国际象棋引擎移植过来.目标语言将是我感到满意的少数几种语言之一:C#或JavaScript.(, which is the smarty-pants piece of software sitting behind the board, trying to come up with the best moves and pretending to be a human. I have always been a poor chess player, and my first thought was that the best thing I could do to develop a chess engine would be porting from some existing and working C chess engine. The target language would be one of the few which I feel comfortable with: C# or JavaScript.)
令我惊讶的是,最近我发现一个聪明的墨西哥人叫(Much to my surprise, recently I discovered that a brilliant Mexican guy named)奥斯卡托莱多
古铁雷斯(Óscar Toledo Gutiérrez)在开发了C和Java版本之后,已经开发了JavaScript国际象棋引擎.不仅如此:托莱多设法挤压了他的C象棋引擎,名为(had already developed a JavaScript chess engine, after having developed a C and Java versions of it. Not only that: Toledo managed to squeeze his C chess engine, named)纳米象棋(Nano Chess),在(, in) 仅1257个字符(only 1257 characters) ,成为世界上最小的C象棋引擎.托莱多进一步完善了它,他的(, becoming the world’s smallest C chess engine. Toledo perfected it further and his)笔克象棋(Pico Chess)C代码小于1Kb.他是IOCCC(国际混淆C代码竞赛)的五次冠军.经过一番思考,我决定不自己写国际象棋引擎.(C code amounts to less than 1Kb. He is five times winner of the IOCCC (International Obfuscated C Code Contest). After some thought, I decided not to write the chess engine by myself.)
Chesslings是一款基于Toledo的国际象棋游戏(Chesslings is a chess game based on Toledo’s) 小象棋(Tiny Chess) .也就是说,从字面上看(. That is, literally)**基于(based)**在上面. Tiny Chess实际上在后台默默运行,提供了我们需要的所有游戏逻辑.什么(on it. Tiny Chess actually runs silently behind the scenes, providing all the game logic we need. What)切斯林斯(Chesslings)真正的目的是通过提供不同的外观(really does is provide a different look and feel by) 等距投影(isometric projection) 和每一步的动画.(and animations for each move.)
系统要求(System Requirements)
本文包含运行除JavaScript之外不依赖任何编程语言的网站所需的代码,因此不引用程序集或C#代码,因此无需编译.您可以简单地下载源代码并在您喜欢的浏览器中打开HTML文件.(This article contains the code needed to run a website relying on no programming languages other than JavaScript, and consequently without references to assemblies or C# code, so it doesn’t need to be compiled. You can simply download the source code and open the HTML file in the browser of your preference.)
纸JS(Paper JS)
对于这个项目,我们使用(For this project, we use) 纸JS(Paper JS) 作为渲染框架. Paper.js是一个运行在HTML5 Canvas之上的开源矢量图形脚本编写框架.该项目将不会使用Paper JS最引人注目的功能,即矢量图形,而是利用Paper JS的内置渲染循环以及在html5上渲染图像的功能.(as the rendering framework. Paper.js is an open source vector graphics scripting framework that runs on top of the HTML5 Canvas. This project will not use the most noticeable feature of Paper JS, i.e. the vector graphics, but takes advantage of the Paper JS’s built-in render loop and ability to render images on the html5) canvas
.(.)
在整个代码中,您会注意到许多对Paper JS框架定义的类型的引用.这是我们在项目中如何使用它们的简短说明:(Throughout the code, you will notice many references to types defined by the Paper JS framework. Here goes a short explanation of how we use them in the project:)
- 纸张设置(paper.setup):为我们建立一个空项目.如果提供了画布,它还会为其创建一个View,两者都链接到该作用域.有问题的"项目"是(: Sets up an empty project for us. If a canvas is provided, it also creates a View for it, both linked to this scope. The “project” in question is an instance of the)
project
由Paper JS框架内部创建.可以说它接受我们页面上的画布作为参数,并管理该画布的所有输出(例如渲染图像)并控制用户交互(例如键盘击键)就足够了.(created internally by the Paper JS framework. It’s enough to say that it accepts the canvas residing on our page as a parameter, and manage all output to that canvas (like rendering images) and control user interactions such as keyboard strokes.) - 纸点(paper.Point):表示画布上一个点的二维对象.它在我们的项目中被广泛使用,以保持和计算屏幕上的位置.(: a two-dimensional object representing a point on the canvas. It is extensively employed in our project to hold and calculate positions on the screen.)
- 纸.矩形(paper.Rectangle):(:)
- paper.view.center(paper.view.center):的中心(: the center of the)
paper.view
对象,这也意味着该点位于画布的中心.(object, which also means the point at the center of our canvas.) - paper.Key.isDown(paper.Key.isDown):一个布尔函数,用于检查键盘是否(: a boolean function that checks whether the keyboard)下(Down)在该特定时刻按下键.(key is pressed at that particular moment.)
- 纸.光栅(paper.Raster):栅格是一种特殊的(: a raster is a specialized kind of)
Item
代表Paper JS项目中图像的对象.我们使用它为每个棋子创建独立的图像.(object that represents a image in the Paper JS project. We use it to create independent images for each of our chess pieces.) - paper.view.onFrame(paper.view.onFrame):要在动画的每个帧上调用的项目级处理函数.此函数为我们提供有关事件触发次数,自第一个帧事件以来经过的总时间以及自最后一个帧发生以来经过的时间的信息.它被我们的国际象棋动画广泛使用.(: Item level handler function to be called on each frame of an animation. This function gives us information about how many times the event was fired, the total amount of time passed since the first frame event, and the time passed since the last frame occurred. It is extensively used by our chess animations.)
- paper.tool.onMouseUp(paper.tool.onMouseUp):在项目上释放鼠标按钮时要调用的函数.该函数接收一个MouseEvent对象,该对象包含有关鼠标事件的信息.(: The function to be called when the mouse button is released over the item. The function receives a MouseEvent object which contains information about the mouse event.)
托莱多的小象棋(Toledo’s Tiny Chess)
正如我在文章简介中提到的那样,ÓscarToledo在获得IOCCC(国际混淆C代码竞赛)奖后不久就将C代码" Nano Chess"移植到了JavaScript中,这就是他所说的(As I mentioned in the article introduction, Óscar Toledo ported his C code “Nano Chess” to JavaScript short after he had won the IOCCC (International Obfuscated C Code Competition) prize, and this resulted in what he called) 小象棋(Tiny Chess) .不出所料,该代码被大量混淆,并且对于国际象棋引擎来说简直令人吃惊.这是Tiny Chess的完整JavaScript来源:(. As expected, the code is heavily obfuscated, and surprisingly short for a chess engine. Here is the full JavaScript source of Tiny Chess:)
//(c)2009 Oscar Toledo G.
var B,i,y,u,b,I=[],G=120,x=10,z=15,M=1e4,l=[5,3,4,6,2,4,3,5,1,1,1,1,1,1,1,1,9,9
,9,9,9,9,9,9,13,11,12,14,10,12,11,13,0,99,0,306,297,495,846,-1,0,1,2,2,1,0,-1,-
1,1,-10,10,-11,-9,9,11,10,20,-9,-11,-10,-20,-21,-19,-12,-8,8,12,19,21];function
X(w,c,h,e,S,s){var t,o,L,E,d,O=e,N=-M*M,K=78-h<<x,p,g,n,m,A,q,r,C,J,a=y?-x:x;
y^=8;G++;d=w||s&&s>=h&&X(0,0,0,21,0,0)>M;do{if(o=I[p=O]){q=o&z^y;if(q<7){A=q--&
2?8:4;C=o-9&z?[53,47,61,51,47,47][q]:57;do{r=I[p+=l[C]];if(!w|p==w){g=q|p+a-S?0
:S;if(!r&(!!q|A<3||!!g)||(r+1&z^y)>9&&q|A>2){if(m=!(r-2&7))return y^=8,I[G--]=
O,K;J=n=o&z;E=I[p-a]&z;t=q|E-7?n:(n+=2,6^y);while(n<=t){L=r?l[r&7|32]-h-q:0;if(
s)L+=(1-q?l[(p-p%x)/x+37]-l[(O-O%x)/x+37]+l[p%x+38]*(q?1:2)-l[O%x+38]+(o&16)/2:
!!m*9)+(!q?!(I[p-1]^n)+!(I[p+1]^n)+l[n&7|32]-99+!!g*99+(A<2):0)+!(E^y^9);if(s>h
||1<s&s==h&&L>z|d){I[p]=n,I[O]=m?(I[g]=I[m],I[m]=0):g?I[g]=0:0;L-=X(s>h|d?0:p,L
-N,h+1,I[G+1],J=q|A>1?0:p,s);if(!(h||s-1|B-O|i-n|p-b|L<-M))return W(),G--,u=J;
J=q-1|A<7||m||!s|d|r|o<z||X(0,0,0,21,0,0)>M;I[O]=o;I[p]=r;m?(I[m]=I[g],I[g]=0):
g?I[g]=9^y:0;}if(L>N||s>1&&L==N&&!h&&Math.random()<.5){I[G]=O;if(s>1){if(h&&c-L
<0)return y^=8,G--,L;if(!h)i=n,B=O,b=p;}N=L;}n+=J||(g=p,m=p<O?g-3:g+2,I[m]<z|I[
m+O-p]||I[p+=p-O])?1:0;}}}}while(!r&q>2||(p=O,q|A>2|o>z&!r&&++C*--A));}}}while(
++O>98?O=20:e-O);return y^=8,G--,N+M*M&&N>-K+1924|d?N:0;}B=i=y=u=0;while(B++<
120)I[B-1]=B%x?B/x%x<2|B%x<2?7:B/x&4?0:l[i++]|16:7;for(a=
"<table cellspacing=0 align=center>",i=18;i<100;a+=++i%10-9?
"<th width=40 height=40 onclick=Y("+i+") style='border:2px solid #aae' id=o"+i+
" bgcolor=#"+(i*.9&1?"9090d0>":"c0c0ff>"):(i++,"<tr>"));
a+="<th colspan=8><select id=t><option>Q<option>R<option>B";
document.write(a+"<option>N</select></table>");
function W(){B=b;for(p=21;p<99;++p)if(q=document.getElementById("o"+p)){q.
innerHTML="<img width=40 src="+(I[p]&z)+".gif>";q.
style.borderColor=p==B?"#ff0":"#aae";}}W();
function Y(s){i=(I[s]^y)&z;if(i>8){b=s;W();}else if(B&&i<9){b=s;i=I[B]&z;if((i&
7)==1&(b<29|b>90))i=14-document.getElementById("t").selectedIndex^y;X(0,0,0,21,
u,1);if(y)setTimeout("X(0,0,0,21,u,2/*ply*/),X(0,0,0,21,u,1)",250);}}
而已.这就是使用JavaScript运行完整的象棋游戏所需要的.现在,我们的Chesslings游戏会进入哪里?首先,我们创建一个专门的(That´s it. That is all you need to run a full chess game in JavaScript. Now, where does our Chesslings game enter? First, we create a dedicated) div
Tiny Chess的名称,并为其指定一个等于" toledoTable"的ID:(for Tiny Chess, and give it an id equals to “toledoTable”:)
<div id="toledoTable" style="position: absolute; top: 0; display: none;"></<div>
现在,我们对Tiny Chess代码进行了一些更改,足以在每次出现新的游戏动作时向我们的Chesslings游戏注入一些通知,并为了可读性而重命名一些函数和变量.这是必需的,因为实际上,所有游戏动作实际上都是由后台运行Tiny Chess代码进行的.(Now we change Tiny Chess code a little, just enough to inject some notifications to our Chesslings game whenever a new game movement occurs, and renaming some functions and variables for the sake of readability. This is needed because, in fact, all game movements are actually made by the background running Tiny Chess code.)
function renderChess() {
i = "<table>";
for (u = 18;
u < 98;
render()
)
B = selectedTileNum;
if (game) {
game.processMovement(I);
}
}
renderChess()
function render() {
$('#toledoTable').html(i += ++u % x - 9 ?
"<th width=30 height=30 onclick='clickTile(" + u + ")' style='font-size:25px'bgcolor=#" + (u - B ? u * .9 & 1 || 9 :
"d") + "0f0e0>&#" + (I[u] ? 9808 + l[67 + I[u]] : 160) + ";" : u++ && "<tr>");
}
function clickTile(u) {
game.onTileClick(u);
I[selectedTileNum = u] > 8 ? renderChess() : X(0, 0, 1);
}
在原始代码中进行了这些小调整之后,我们可以继续进行游戏开发(After these little tweaks in the original code, we can proceed to our game development)
切斯林斯(Chesslings)
Chesslings是一款JavaScript游戏,可对基础Tiny Chess代码所产生的动作做出反应,如前所述.但是,我们不会谈论代码的每个方面.相反,让我们关注它的关键功能. Chesslings的第一个入口是(Chesslings is a JavaScript game that reacts to the movements made by the underlying Tiny Chess code, as explained before. However, we will not be talking about each and every aspect of the code. Instead, let’s focus on the key features of it. The first entry point for the Chesslings is the) onTileClick
功能暴露(function exposed by the) game
实例:(instance:)
onTileClick: function (tileNum) {
//here goes the code that highlights the selected tile.
},
下一个入口点是(The next entry point is the) processMovement
功能,负责获取当前棋盘状态并将其与之前的棋盘状态进行比较,以便我们可以找出移动了哪些棋子以及在移动过程中可能丢失了哪些棋子.(function, responsible for acquiring and comparing the current board state with the previous board state, so that we can find out which chess piece was moved, and which one was possibly lost in the movement.)
processMovement: function (tableArray) {
//here goes the code that processes the game board's current state.
},
棋盘状态由10x12位置的矩阵表示,该矩阵的中央包含一个较小的8x8棋盘.矩阵尺寸大于常规棋盘的原因是,棋盘本身仅占据了矩阵的中心区域,并保留了边距位置,以便于进行小国际象棋的移动计算.(The board state is represented by a matrix of 10x12 positions, which contains a smaller 8x8 chess board in its center. The reason for the matrix dimensions being greater than a regular chess board is that only the central area of the matrix is occupied by the chess board itself, where the margin positions are kept so to facilitate the Tiny Chess movement calculations.)
可以看到,表数组中的每个位置都由一个棋子占据,该棋子包含代表棋子类型的不同数字,但棋子,空格和场外位置(它们具有相同的棋子编号)除外.我们将棋子类型代码移植到我们的游戏中,以便以后根据棋子的含义进行显示.(As we can see, each position in the table array is occupied by a chess piece containing a distinct number representing the piece type, except for the pawns, the blank spaces and the off-board positions (they have the same piece numbers). We ported the piece type codes to our game to later show each piece according to its meaning.)
Chess.PieceTypes = {
SPACE: 0,
BLACK_PAWN: 1,
BLACK_KING: 2,
BLACK_HORSE: 3,
BLACK_BISHOP: 4,
BLACK_ROOK: 5,
BLACK_QUEEN: 6,
OFF_BOARD: 7,
WHITE_PAWN: 9,
WHITE_KING: 10,
WHITE_HORSE: 11,
WHITE_BISHOP: 12,
WHITE_ROOK: 13,
WHITE_QUEEN: 14
};
正如预期的那样,游戏从一组32个开始.下面的代码显示了我们如何实例化它们并填充片段数组.为此,我们仅考虑顶部的两行和底部的两行(y <2 || y> 5),而忽略了电路板的其余部分.每件作品都分配有唯一的钥匙,以及名字.(As expected, the game starts with a set of 32 pieces. The code below shows how we instantiate them and populate the array of pieces. For this we consider only the top two rows and the bottom two rows (y < 2 || y > 5) and ignore the rest of the board. Each piece has a unique key assigned to it, along with the name of the)精灵表(Spritesheet)代表那一类的作品.(representing that category of pieces.)
var keys = ('BRL,BNL,BBL,BQ,BK,BBR,BNR,BRR,BP1,BP2,BP3,BP4,BP5,BP6,BP7,BP8,' +
'WP1,WP2,WP3,WP4,WP5,WP6,WP7,WP8,WRL,WNL,WBL,WQ,WK,WBR,WNR,WRR').split(',');
for (var y = 0; y <= 7; y++) {
for (var x = 0; x <= 7; x++) {
if (y < 2 || y > 5) {
var key = keys[self.pieces.length];
var ss;
if (y < 2)
ss = window['spriteSheetS_' + key[1]];
else
ss = window['spriteSheetN_' + key[1]];
self.pieces.push(new Chess.Piece(
key,
{ x: x, y: y }, ss));
}
}
}
名字(The names)" spriteShetN"(“spriteShetN”)和(and)" spriteShetS"(“spriteShetS”)分别代表"白色"和"黑色"团队,其中" N"和" S"表示"朝北的棋子"与"朝南的棋子".每个样片([P]叉,[R]钩,[B] ishop,k [N] ight,[Q] ueen,[K] ing)都有"朝北"和"朝南"两种版本,如下图所示:(represent both “white” and “black” teams respectively, where “N” and “S” mean “North-facing pieces” vs. “South-facing pieces”. Every category of piece ([P]awn, [R]ook, [B]ishop, k[N]ight, [Q]ueen, [K]ing) has a “north-facing” and “south-facing” version, as we can see in the images below:)
片(Piece) | 后视图(Back View) | 正视图(Front View) |
---|---|---|
Pawn |
|
| |Rook|
|
| |Bishop|
|
| |Knight|
|
| |Queen|
|
| |King|
|
|
注意所有棋ling的颜色如何相同.为了区分它们,我们假设背面的部分是"白色"部分,而正面的部分是"黑色"部分.这意味着,当棋子向后走时,它们也会向后走.(Notice how all chesslings have the same colors. In order to differentiate them, we assume that the back-facing pieces are “white” pieces while the front-facing pieces are the “black” ones. This means that when the pieces walk back, they will also walk backwards.) 注意(Note):" Chesslings"也是我在游戏中赋予的名称.就像地球上的人是"土拨鼠"一样,国际象棋世界中的这些看起来怪异的居民也就是"扁狮".(: “Chesslings” is also the name I gave to the pieces of the game. Just like “earthlings” are people living on Earth, “chesslings” are these weird looking inhabitants of the Chess world.)
运动检测(Movement Detection)
每次底层(Every time the underlying)小象棋(Tiny Chess)代码调用(code calls the) processMovement
函数,Chesslings代码将表数组的状态与最后处理的数组状态进行比较.需要一些循环来检查更改的位置,另一些则是要找出受影响的部分,然后(function, the Chesslings code compares the state of the table array with the last processed array state. Some loops are required to check the changed positions, others are intended to find out which pieces are affected, and then a)动画(animation)被排队(is enqueued in a) AnimationManager
一个接一个地处理待处理的动画.的(to process pending animations one after another. The) processMovement
功能结构如下图:(function structure is shown below:)
processMovement: function (tableArray) {
var self = this;
//Let's proceed only when something changed
if (self.lastTableArray != tableArray) {
var fromPosition; //the position from where some piece moved
var toPosition; //the position to where the piece moved
$(self.lastTableArray).each(function (index, item) {
//some code to set the "fromPosition" variable
});
//Let's proceed only if some movement was detected
if (fromPosition) {
$(tableArray).each(function (index, item) {
var row = parseInt(index / 10) - 2;
var col = (index % 10) - 1;
//If this position was last time empty but now is occupied by a piece,
//then the piece moved to here
if (self.lastTableArray[index] == Chess.PieceTypes.SPACE &&
tableArray[index] != Chess.PieceTypes.SPACE) {
//some code to set the "toPosition" variable
}
//If this position was last time occupied by a piece but now is replaced by another one,
//then this last piece was lost
else if (self.lastTableArray[index] != Chess.PieceTypes.SPACE &&
tableArray[index] != Chess.PieceTypes.SPACE &&
self.lastTableArray[index] != tableArray[index]) {
//some code to set the "toPosition" variable
}
});
//This loop will decide which piece moved, and take action accordingly
$(game.pieces).each(function (index, piece) {
if (piece.isActive &&
piece.currentTile.x == fromPosition.x &&
piece.currentTile.y == fromPosition.y) {
//The animation will move "piece" from "fromPosition" to "toPosition" in 1000 milliseconds,
//starting 200 milliseconds after the start of the animation
var animation = new Chess.PointAnimation(piece, fromPosition, toPosition, 200, 1000);
animation.onFinish = function () {
//When the animation ends, we inactivate (make invisible) the piece that
//may have been lost to the attacking one.
$(game.pieces).each(function (index2, piece2) {
if (piece2.isActive &&
piece2.currentTile.x == toPosition.x &&
piece2.currentTile.y == toPosition.y &&
piece2.key != piece.key) {
//setting a chess piece to "inactive" actually makes it invisible
piece2.setInactive(false);
}
});
}
//we enqueue the animation, so that it will be processed only when required
self.animationManager.enqueueAnimation(animation);
}
});
}
}
self.lastTableArray = tableArray.slice();
self.processCount++;
},
动画(Animation)
动画是代码中非常重要的方面.我们不想立即将片段从A点移动到B点.相反,我们希望在给定的T时间内将棋子从A移到B.这是通过处理动画来完成的.我们的(Animation is a very important aspect of the code. We don’t want to move the pieces from point A to point B right away. Instead, we want the piece to move from A to B in a given T time. And this is accomplished by processing animations. Our) AnimationManager
处理以下实例的队列(processes a queue of instances of the) PointAnimation
类型,这样我们就可以确保同时出现两个或更多动画.(type, and this way we can assure that two or more animations occur concurrently.)
每个实例(Each instance of the) PointAnimation
类型是使用一组参数创建的:(type is created with a set of parameters:)
- 片(piece):的实例(: an instance of the)
Chess.Piece
类型(type) - startPosition(startPosition):(: the)
Paper.Point
具有(x,y)坐标作为动画开始位置的实例(instance with the (x, y) coordinates for the animation’s start position) - endPosition(endPosition):(: the)
Paper.Point
具有动画结束位置的(x,y)坐标的实例(instance with the (x, y) coordinates for the animation’s end position) - startTimeInMS(startTimeInMS):动画开始播放前的延迟时间,以毫秒为单位(: the time delay time until the animation starts, in milliseconds)
- totalTimeInMS(totalTimeInMS):总动画持续时间(以毫秒为单位)(: the total animation duration in milliseconds)
主要功能(The main function of) PointAnimation
,(,) onFrame
,是对象的核心.在动画的生命周期中,多次调用此函数.给定一个(, is the core of the object. This function is called many times during the animation’s lifetime. Given a) deltaTimeInSec
参数,函数会考虑动画的总持续时间和经过的时间来计算相对位置,以产生增量矢量,即A点(ax,ay)和B点之间的D(dx,dy)矢量差( bx,作者).函数返回(parameter, the function calculates the relative position, taking in consideration the total animation duration and the ellapsed time, to produce the delta vector, i.e., the D (dx, dy) vector difference between point A (ax, ay) and point B (bx, by). The function returns) true
直到动画总持续时间结束为止.(until the total animation duration is completed.)
//********************
//Chess.PointAnimation
//********************
//piece: an instance of the Chess.Piece type
//startPosition: the Paper.Point instance with the (x, y) coordinates for the animation's start position
//endPosition: the Paper.Point instance with the (x, y) coordinates for the animation's end position
//startTimeInMS: the time delay time until the animation starts, in milliseconds
//totalTimeInMS: the animation duration in milliseconds
Chess.PointAnimation = function (piece, startPosition, endPosition, startTimeInMS, totalTimeInMS) {
this.init(piece, startPosition, endPosition, startTimeInMS, totalTimeInMS)
}
$.extend(Chess.PointAnimation.prototype, {
init: function (piece, startPosition, endPosition, startTimeInMS, totalTimeInMS) {
this.piece = piece;
this.startTimeInMS = startTimeInMS;
this.startPosition = startPosition;
this.endPosition = endPosition;
this.totalTimeInMS = totalTimeInMS;
this.ellapsedTimeInMS = 0; //
this.isActive = true; //
this.point = startPosition; //
},
onFrame: function (deltaTimeInSec) {
var self = this;
//the delta time, in seconds
var deltaTimeInMS = deltaTimeInSec * 1000;
//adds to the total ellapsed time for this animation instance
self.ellapsedTimeInMS += deltaTimeInMS;
if (self.ellapsedTimeInMS < self.startTimeInMS) {
//the animation is active but still not started, so do nothing
return true;
}
else if (self.ellapsedTimeInMS <= self.totalTimeInMS) {
//the current point is calculated taking in consideration the total animation duration, the ellapsed time,
//the start and the end position.
var timeFraction = self.ellapsedTimeInMS / self.totalTimeInMS;
var diffVector = new paper.Point(self.endPosition.x - self.startPosition.x, self.endPosition.y - self.startPosition.y);
self.piece.currentTile =
self.point = new paper.Point(self.startPosition.x + diffVector.x * timeFraction, self.startPosition.y + diffVector.y * timeFraction);
return true;
}
else {
//when the total animation duration is completed, we force the piece to assume the end position.
//the onFinish callback function is invoked when needed.
self.isActive = false;
self.piece.currentTile =
self.point = self.endPosition;
consoleLog('end of: ' + self.toString());
if (self.onFinish)
self.onFinish();
return false;
}
},
toString: function () {
return 'pa (from: ' + this.startPosition + ' to:' + this.endPosition + ')';
}
});
动画序列背后的逻辑在于(The logic behind the animation sequencing lies in the) Chess.AnimationManager
码.它基本上是针对以下实例的队列实现(code. It is basically a queue implementation for instances of) Chess.PointAnimation
类型:(type:)
//********************
//Chess.AnimationManager
//********************
Chess.AnimationId = 1;
Chess.AnimationManager = function () {
this.init();
}
$.extend(Chess.AnimationManager.prototype, {
currentAnimation: null,
queue: [],
init: function () {
this.id = Chess.AnimationId++;
},
enqueueAnimation: function (animation) {
consoleLog('enqueueing ' + animation.piece.key + ':' + animation.toString());
var self = this;
//positioning the animation at the end of the queue
self.queue.push(animation);
},
dequeueAnimation: function () {
var self = this;
//taking the first element of the queue
var animation = self.queue[0];
consoleLog('dequeueing ' + animation.piece.key + ':' + animation.toString());
//deleting the first element
self.queue = self.queue.slice(1, self.queue.length)
self.currentAnimation = null;
return animation;
},
getCurrentAnimation: function () {
var self = this;
if (!self.currentAnimation) {
if (self.queue.length > 0) {
self.currentAnimation = self.queue[0];
}
}
return self.currentAnimation;
}
})
回到(Back to the) processMovement
代码,我们注意到(code, we notice how the instance of) Chess.AnimationManager
被调用.首先,我们找出移动的那一块,然后从原始表格位置开始到目标位置结束,加入动画.(is invoked. First we discover which piece moved, then we enqueue an animation starting from the original table position and ending in the destination position.)
我们还订阅了(We also subscribe an) onFinish
事件,如果出现这种情况,将使丢失的片段不可见,并且此类回调事件仅在动画结束时发生.这确保了处理遵守事件的顺序.(event, that will make the lost piece invisible if it´s the case, and such callback event will take place only when the animation ends. This ensures that the processing respect the sequence of events.)
processMovement: function (tableArray) {
*** HERE WE INSTANTIATE THE fromPosition AND toPosition VARIABLES ***
//This loop will decide which piece moved, and take action accordingly
$(game.pieces).each(function (index, piece) {
if (piece.isActive &&
piece.currentTile.x == fromPosition.x &&
piece.currentTile.y == fromPosition.y) {
//The animation will move "piece" from "fromPosition" to "toPosition" in 1000 milliseconds,
//starting 200 milliseconds after the start of the animation
var animation = new Chess.PointAnimation(piece, fromPosition, toPosition, 200, 1000);
animation.onFinish = function () {
//When the animation ends, we inactivate (make invisible) the piece that
//may have been lost to the attacking one.
$(game.pieces).each(function (index2, piece2) {
if (piece2.isActive &&
piece2.currentTile.x == toPosition.x &&
piece2.currentTile.y == toPosition.y &&
piece2.key != piece.key) {
piece2.setInactive(false);
}
});
}
//we enqueue the animation, so that it will be processed only when required
self.animationManager.enqueueAnimation(animation);
}
});
*** SOME POST-PROCESSING CODE HERE ***
},
最后考虑(Final Considerations)
如您所见,如果没有ÓscarToledo及其出色的国际象棋工作,就不可能完成该项目,而不是原始工作.如果您有困难阅读这些最后几行,非常感谢您的耐心配合.我希望本文对游戏开发,等距投影和基于JavaScript的动画在某种程度上对您有所帮助.(As you have seen, rather than an original work, this project would not have been possible without Óscar Toledo and his amazing work with chess. If you had the trouble to read to these final lines, thanks very much for your patience. I hope this article may be useful for you in some way, regarding game development, isometric projection and javascript-based animation.)
历史(History)
- 2013-08-31:初始版本.(2013-08-31: Initial version.)
- 2013-09-05:新的鼠标控件.(2013-09-05: New mouse controls.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
HTML5 Javascript jQuery game 新闻 翻译