# FlightChess
**Repository Path**: wangluochong/flight-chess
## Basic Information
- **Project Name**: FlightChess
- **Description**: 鸿蒙JS小游戏——飞行棋
- **Primary Language**: JavaScript
- **License**: Apache-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 2
- **Created**: 2025-07-22
- **Last Updated**: 2025-07-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# FlightChess
## 项目结构

## 页面构建
游戏的布局并不复杂,分为左边的飞行记录,中间的棋盘,右边的骰子、按钮操作区,还有游戏结束时的排行榜,共四部分。

- 左侧飞行记录:也即各个阵营的当前战绩统计,除了与游戏胜利直接相关的抵达终点的棋子数,还记录了各方击落敌机的数量,这也是游戏的趣味之一。
```
——飞行记录——
阵营 | 击落敌机 | 飞行进度
{{ $item.hit }}
{{ $item.progress }}/4
```
- 中间棋盘:背景图片为飞行棋棋盘,其上是4×4=16枚棋子,棋子的位置在游戏过程中是动态变化的,所以使用绝对定位将某一棋格的坐标赋值给棋子的left和top属性。在对应的区域棋子的朝向也会变化,否则棋子始终朝一个方向有些呆板。另外,棋子能否移动与掷出的骰子点数和飞机状态有关,disabled属性便是用于控制该棋子是否可以交互移动。
```
```
- 右侧操作区:文本提示当前回合轮到哪个阵营,点击骰子随机掷出1~6,还有重新开始按钮,为避免误触设置其触发事件为长按事件。
```
{{ roundtitle }}
```
- 排行榜:默认隐藏,当游戏结束时显示各阵营名次先后及用时。

```
———游戏排名———
{{ $item.rank }}
{{ $item.round }}
```
## 走棋准备
我们需要另外新建一个js文件作为棋盘的地图,记录每一个棋格的序号、坐标、角度、颜色等属性。文件格式如下:
```JS
// MapData.js
export let MapData = [
{
index: 0, // 格子序号
x: 85, // 格子X轴
y: 63, // 格子Y轴
angle: 270, // 棋子朝向角度(顺时针方向)
color: "blue", // 格子颜色
chess: [], // 该棋格上的棋子
},
{
index: 1,
x: 79,
y: 65,
angle: 270,
color: "red",
chess: [],
},
…………
{
index: 95,
x: 86.45,
y: 14.1,
angle: 180,
color: "blue",
chess: [],
},
]
export default MapData;
```
- 序号index对应的棋格下标,用于查找棋格;
- 坐标的x和y分别赋值给行走到该棋格的棋子的left和top属性;
- angle用于设置该棋格上的棋子的朝向角度;
- color记录棋格颜色,当棋子行走到同色的棋格时会触发位移事件;
- 棋子数组chess[]则是用来记录当前回合该棋格上有哪些棋子。若是同色的棋子则数组长度加一;若是异色的则触发踩棋事件,将原数组中的元素清空重置,写入新棋子。
由于各个阵营棋子的走棋路线是不同的,所以需要先设定好各自对应的航线,利用上面设置好的棋格下标,分别设定四条路线。
```JS
// 各色飞机的飞行航线
let Route = [
[76, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 52, 53, 54, 55, 56, 57], // 红线
[81, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 58, 59, 60, 61, 62, 63], // 绿线
[86, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 64, 65, 66, 67, 68, 69], // 黄线
[91, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17,18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 70, 71, 72, 73, 74, 75] // 蓝线
]
```
## 游戏逻辑
- 掷骰子:随机地掷出点数1~6,根据骰子点数和当前阵营的棋子状态改变对应棋子的disabled属性,以控制该棋子是否可交互移动,若无符合交互条件的棋子可操作则进行回合轮替。
```JS
todice() {
this.dice_dab = true;
this.dice_num = Math.floor(Math.random()*6+1);
switch(this.dice_num) {
case 1:
this.dice_pic = "point1";
break;
case 2:
this.dice_pic = "point2";
break;
case 3:
this.dice_pic = "point3";
break;
case 4:
this.dice_pic = "point4";
break;
case 5:
this.dice_pic = "point5";
break;
case 6:
this.dice_pic = "point6";
break;
default:
console.log("骰子意外出错");
break;
}
// 骰子点数小于6,若有飞行状态的棋子可点击,该回合可操作,否则回合轮替
if(6 > this.dice_num) {
var operable = false;
for(var i=0; i<4; i++) {
if("flying" == thetype[i].type) {
thetype[i].chess_dab = false;
operable = true;
}
}
if(false == operable) {
this.rotate();
}
else {}
}
// 骰子点数为6,除已到达的棋子都可点击
else {
for(var i=0; i<4; i++) {
if("arrive" != thetype[i].type) {
thetype[i].chess_dab = false;
}
}
}
},
```
- 选择棋子:玩家选择可移动的棋子行动,根据棋子状态移动棋子。若棋子还未“起飞”,则移动到起点;若棋子已经行走在航线上,则移动与骰子点数对应的步数,若超过终点则回退多余步数。
```JS
// 选中棋子行动
appoint(thecamp, num) {
for(var i=0; i<4; i++) {
thecamp[i].chess_dab = true;
}
// 若该棋子已进入航线
if(null != thecamp[num].step) {
for(var t=0; t= (thecamp[num].step + this.dice_num)) {
forward = temp;
}
// 超过终点,回退几步
else {
forward = 56 - thecamp[num].step;
backward = temp - forward;
}
// 0.5秒执行一次走棋方法
onestep = setInterval(()=> {
this.move(thecamp[num]);
}, 500);
},
```
- 棋子移动:重复定时器执行棋子移动方法,一步一步走完后确认落点,先后进行是否触发踩棋子判定或位移判定,之后再进行回合轮替。当有棋子行至终点时更新左侧的飞行进度,若其中三名玩家完成游戏则游戏结束,弹出排行榜,未完成的一方为最后一名。

```JS
// 移动棋子
move(thechess) {
// 若前进步数为0,且需要后退
if((0 == forward) && (0 != backward)) {
thechess.step -= 1;
backward --;
}
// 若需要前进
if(forward != 0) {
thechess.step += 1;
forward --;
}
thechess.x = MapData[Route[this.theround%4][thechess.step]].x;
thechess.y = MapData[Route[this.theround%4][thechess.step]].y;
thechess.angle = MapData[Route[this.theround%4][thechess.step]].angle;
temp -= 1;
// 若步数走完
if(0 == temp) {
clearInterval(onestep);
forward = 0;
backward = 0;
this.complex(thechess); // 踩棋子判断
this.getjump(thechess); // 位移判断
// 向棋子当前落点写入棋子信息
ruzhan = setTimeout(()=> {
MapData[Route[this.theround%4][thechess.step]].chess.push(thechess);
}, 1200);
// 延迟后进行回合轮替
changeturn = setTimeout(()=> {
// 若该棋子到达终点,更新进度
if(56 == thechess.step) {
thechess.type = "arrive";
this.flylog[this.theround%4].progress += 1;
// 若该棋子走完后刚好全部到达,计入排行榜
if(4 == this.flylog[this.theround%4].progress) {
this.allrank.push(
{
rank: this.allrank.length + 1,
chess: this.flylog[this.theround%4].camp,
round: "用时" + this.theround + "回合",
}
)
if(3 == this.allrank.length) {
for(var i=0; i<4; i++) {
if(this.flylog[i].progress < 4) {
var chesstemp = this.flylog[i].camp;
}
}
this.allrank.push(
{
rank: this.allrank.length + 1,
chess: chesstemp,
round: "未完成",
}
)
this.dice_dab = true;
this.result = true;
return;
}
}
}
this.rotate();
}, 1500);
}
},
```
- 踩棋事件判定:当棋子落点处已有其它棋子时判断是否异色,若为同方阵营的棋子则共处一格;若为其它阵营的棋子则会被击落回到起点。

```JS
// 落点是否有棋子
complex(thechess) {
if(52 > MapData[Route[this.theround%4][thechess.step]].index) {
if(0 != MapData[Route[this.theround%4][thechess.step]].chess.length) {
// 我方棋子
if(thechess.color == MapData[Route[this.theround%4][thechess.step]].chess[0].color) {
}
// 敌方棋子,踩回起点
else {
for(var i=0; i= thechess.step) {
if(thechess.color == MapData[Route[this.theround%4][thechess.step]].color) {
if(18 == thechess.step) {
thechess.step += 12;
}
else {
thechess.step += 4;
}
jump1 = setTimeout(()=> {
thechess.x = MapData[Route[this.theround%4][thechess.step]].x;
thechess.y = MapData[Route[this.theround%4][thechess.step]].y;
thechess.angle = MapData[Route[this.theround%4][thechess.step]].angle;
// 第二次踩棋子
this.complex(thechess);
if(18 == thechess.step) {
jump2 = setTimeout(()=> {
thechess.step += 12;
thechess.x = MapData[Route[this.theround%4][thechess.step]].x;
thechess.y = MapData[Route[this.theround%4][thechess.step]].y;
thechess.angle = MapData[Route[this.theround%4][thechess.step]].angle;
// 第三次踩棋子
this.complex(thechess);
}, 500);
}
}, 500);
}
}
},
```
- 回合轮替:以回合数%4的方式进行回合轮替,若玩家掷出点数6则追加一次掷骰子机会。
```JS
// 回合轮替
rotate() {
// 刚刚是否投出6,是则再来一次,否则回合数加一,进行轮替
if(6 == this.dice_num) {
if(4 == this.flylog[this.theround%4].progress) {
this.theround += 1;
}
}
else {
this.theround += 1;
}
this.dice_num = 0;
this.dice_pic = "dice";
this.dice_dab = false;
switch(this.theround % 4) {
case 0: // 红的回合
thetype = this.RED;
this.roundtitle = "红色方的回合";
break;
case 1: // 绿的回合
thetype = this.GREEN;
this.roundtitle = "绿色方的回合";
break;
case 2: // 黄的回合
thetype = this.YELLOW;
this.roundtitle = "黄色方的回合";
break;
case 3: // 蓝的回合
thetype = this.BLUE;
this.roundtitle = "蓝色方的回合";
break;
default:
console.log("意外出错");
break;
}
// 若该颜色的4枚棋子都已到达终点,直接进行回合轮替
var win = 0;
for(var i=0; i<4; i++) {
if("arrive" == thetype[i].type) {
win += 1;
}
}
if(4 == win) {
this.rotate();
}
},
```
- 重新开始游戏:为了避免误触,将按钮事件设定为长按触发,长按后重置游戏各个变量为初始值。

```JS
// 重新开始游戏
restart() {
// 重置游戏其它变量
clearInterval(onestep);
temp = 0;
forward = 0;
backward = 0;
clearTimeout(jump1);
clearTimeout(jump2);
clearTimeout(ruzhan);
clearTimeout(changeturn);
this.roundtitle = "";
this.theround = 0;
this.dice_pic = "dice";
this.dice_num = 0;
this.dice_dab = false;
this.result = false;
// 重置地图
for(var i=0; i