这是我最近几天做着玩的,这篇文章仅为了记录下实现的过程,欢迎大家共同探讨
最终的效果图
预览地址(云服务配置贼差,耐心等会,用电脑看哈,没做适配)
描述
用的vue框架(任何框架都能做,不用框架也可以,只是这个我用的多)。
一开始想到的时候,有考虑过两种实现方式,一种是画一堆div,一种是用canvas实现,我最终选择了用canvas实现。
目前已经实现了游戏的大部分规则,game over还没做,我怕做着做着忘记前面的实现了,先记录一下
- 目标样式
绘制棋盘和棋盘上的格子
- 思考
整个棋盘是横着9个竖着10个,我们将canvas的宽度设置为450,高度设置为500,边距为25,每个格子的宽度为50,这样在循环后就能用一个循环画出来棋盘的格子了
<template>
<div class="chess_test">
<canvas
id="chess"
:width="450"
:height="height"
:style="{ background: primary }"
></canvas>
</div>
</template>
<script>
export default {
data() {
return {
canvas: null, // 画布
ctx: null,
primary: "#E4B97F", // 主题色
height: 500,
width: 450,
locationScale: 50, // 一格的物理尺寸
padding: 25, // 左右边距
};
},
mounted() {
this.initData();
},
methods: {
// 初始化数据
initData() {
const canvas = (this.canvas = document.getElementById("chess"));
const ctx = (this.ctx = canvas.getContext("2d"));
this.drawChessContainer();
},
// 绘制棋盘
drawChessContainer() {
const { ctx } = this;
// 每次绘制棋盘前,先清空画布
ctx.rect(0, 0, this.width, this.height);
// 设置颜色
ctx.fillStyle = this.primary;
ctx.strokeStyle = "#000";
// 循环画出来会少一条横线,我们手动加上
ctx.beginPath();
ctx.moveTo(this.padding, 475);
ctx.lineTo(425, 475);
// 绘制线条
ctx.stroke();
for (var index = 1; index <= 9; index++) {
// 横线
ctx.beginPath();
ctx.moveTo(this.padding, index * this.locationScale - this.padding);
ctx.lineTo(425, index * this.locationScale - this.padding);
// 绘制线条
ctx.stroke();
// 竖线
ctx.beginPath();
ctx.moveTo(this.locationScale * index - this.padding, this.padding);
ctx.lineTo(this.locationScale * index - this.padding, 475);
// 绘制线条
ctx.stroke();
}
},
},
};
</script>
<style lang="scss" scoped>
.chess_test {
background: #555;
width: 100%;
height: 100%;
position: relative;
.action {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
#chess {
background: #fff;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>
复制代码
- 效果
绘制河道和将帅区域的斜线
- 继续思考
中间有河道,我们可以绘制一个矩形放到河道的位置覆盖,再往河道的位置画文字。双方有将帅区域,斜线也需要我们手动去绘制。下面的代码将只会出现发生变化的部分,否则太占篇幅了
// 绘制棋盘
drawChessContainer() {
// ....循环绘制格子部分
// 双方的将军区域
// 上方
ctx.beginPath();
ctx.moveTo(175, this.padding);
ctx.lineTo(275, 125);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(275, this.padding);
ctx.lineTo(175, 125);
ctx.stroke();
// 下方
ctx.beginPath();
ctx.moveTo(175, 375);
ctx.lineTo(275, 475);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(175, 475);
ctx.lineTo(275, 375);
ctx.stroke();
// 楚河,汉界
ctx.fillStyle = this.primary;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(26, 225, 398, this.locationScale);
ctx.fill();
ctx.font = '28px "报隶-简"';
ctx.fillStyle = "#000";
ctx.textBaseline = "middle";
ctx.fillText("楚河", 75, 250);
ctx.fillText("汉界", 325, 250);
},
复制代码
- 效果
绘制炮和兵下面的十字线
- 思考
我们定义一个虚拟坐标轴,把棋盘左上角定义为0的位置,往右是x轴,往下是y轴,一个格子的物理距离50等于虚拟坐标轴一格的距离。双方的炮,和兵下面都有 十字线(我自己起的名字,原谅我不知道怎么这个怎么称呼),x轴为0时没有左边部分,x轴为最大时,没有右边部分,这样我们就可以先定义好会有十字线的位置的坐标,再通过一个循环搞定这些,免去一个一个手动画的辛苦
- 效果
data(){
return {
crossList: [
// 上部分
{ x: 1, y: 2 },
{ x: 7, y: 2 },
{ x: 0, y: 3 },
{ x: 2, y: 3 },
{ x: 4, y: 3 },
{ x: 6, y: 3 },
{ x: 8, y: 3 },
// 下部分
{ x: 0, y: 6 },
{ x: 2, y: 6 },
{ x: 4, y: 6 },
{ x: 6, y: 6 },
{ x: 8, y: 6 },
{ x: 1, y: 7 },
{ x: 7, y: 7 },
], // 十字数组
}
},
methods:{
// 绘制棋盘
drawChessContainer(){
// ......绘制棋盘的代码
this.drawCross()
},
// 绘制十字线
drawCross(x, y) {
const { ctx, crossList, locationScale } = this;
ctx.lineWidth = 1;
// 画 炮和兵 下面的十字
crossList.forEach((item) => {
if (item.x !== 0) {
// 左上
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 10, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 20);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 20, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 10);
ctx.stroke();
// 左下
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 10, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 30);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 20, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 40);
ctx.stroke();
}
if (item.x !== 8) {
// 右上
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 40, item.y * locationScale + 20);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 30, item.y * locationScale + 10);
ctx.stroke();
// 右下
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 40, item.y * locationScale + 30);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 30, item.y * locationScale + 40);
ctx.stroke();
}
});
},
}
复制代码
绘制棋子
- 思考
我们需要一个数组,里面记着双方棋子的位置的配置项,包含位置,类型,分组等等 这个数组需要自己去定义,下面是我定义的某一项
{
y: 0, // 位置
x: 0, // 位置
text: "車", // 显示的文字
show: true, // 表示棋子是否显示
type: "car", // 是什么棋子 car既代表車
camp: "black", // 属于红方
active: false, // 是否激活,后边做逻辑的时候用到
},
复制代码
双反各有16个棋子,各个棋子的位置不同。怎么确定棋子的位置呢,我们之前设置了一个虚拟坐标就起作用了,例如 将 在x:4 y:0 位置,我们将 4*50(每一格的物理大小)+25(边距大小)=arcx arcx就是圆心的x轴,y也这样计算就能得到 圆心的x,y的实际位置,可以用于canvas绘制
绘制棋子的代码直接放下面吧,免得占篇幅。第一篇就先写到这里,有空再写怎么让棋子动起来,让游戏跑起来的逻辑
为自己带盐
广州有没有好的坑位,求介绍
绘制棋盘棋子的完整代码
<template>
<div class="chess_test">
<canvas
id="chess"
:width="450"
:height="height"
:style="{ background: primary }"
></canvas>
</div>
</template>
<script>
export default {
data() {
return {
canvas: null, // 画布
ctx: null,
primary: "#E4B97F", // 主题色
height: 500,
width: 450,
locationScale: 50, // 一格的物理尺寸
padding: 25, // 左右边距
crossList: [
// 上部分
{ x: 1, y: 2 },
{ x: 7, y: 2 },
{ x: 0, y: 3 },
{ x: 2, y: 3 },
{ x: 4, y: 3 },
{ x: 6, y: 3 },
{ x: 8, y: 3 },
// 下部分
{ x: 0, y: 6 },
{ x: 2, y: 6 },
{ x: 4, y: 6 },
{ x: 6, y: 6 },
{ x: 8, y: 6 },
{ x: 1, y: 7 },
{ x: 7, y: 7 },
], // 十字数组
chessFontSize: 28, // 棋子中字的大小
chessWidth: 15, // 棋子半径
chessList: [
// 黑方
{
y: 0,
x: 0,
text: "車",
show: true, // 表示棋子是否显示
type: "car",
camp: "black",
active: false,
},
{
y: 0,
x: 1,
text: "馬",
show: true,
type: "horse",
camp: "black",
active: false,
},
{
y: 0,
x: 2,
text: "象",
show: true,
type: "like",
camp: "black",
active: false,
},
{
y: 0,
x: 3,
text: "士",
show: true,
type: "shi",
camp: "black",
active: false,
},
{
y: 0,
x: 4,
text: "帅",
show: true,
type: "will",
camp: "black",
active: false,
},
{
y: 0,
x: 5,
text: "士",
show: true,
type: "shi",
camp: "black",
active: false,
},
{
y: 0,
x: 6,
text: "象",
show: true,
type: "like",
camp: "black",
active: false,
},
{
y: 0,
x: 7,
text: "馬",
show: true,
type: "horse",
camp: "black",
active: false,
},
{
y: 0,
x: 8,
text: "車",
show: true,
type: "car",
camp: "black",
active: false,
},
{
y: 2,
x: 1,
text: "炮",
show: true,
type: "gun",
camp: "black",
active: false,
},
{
y: 2,
x: 7,
text: "炮",
show: true,
type: "gun",
camp: "black",
active: false,
},
{
y: 3,
x: 0,
text: "卒",
show: true,
type: "pawn",
camp: "black",
active: false,
},
{
y: 3,
x: 2,
text: "卒",
show: true,
type: "pawn",
camp: "black",
active: false,
},
{
y: 3,
x: 4,
text: "卒",
show: true,
type: "pawn",
camp: "black",
active: false,
},
{
y: 3,
x: 6,
text: "卒",
show: true,
type: "pawn",
camp: "black",
active: false,
},
{
y: 3,
x: 8,
text: "卒",
show: true,
type: "pawn",
camp: "black",
active: false,
},
// 红方
{
y: 9,
x: 0,
text: "車",
show: true,
type: "car",
camp: "red",
active: false,
},
{
y: 9,
x: 1,
text: "馬",
show: true,
type: "horse",
camp: "red",
active: false,
},
{
y: 9,
x: 2,
text: "象",
show: true,
type: "like",
camp: "red",
active: false,
},
{
y: 9,
x: 3,
text: "士",
show: true,
type: "shi",
camp: "red",
active: false,
},
{
y: 9,
x: 4,
text: "将",
show: true,
type: "will",
camp: "red",
active: false,
},
{
y: 9,
x: 5,
text: "士",
show: true,
type: "shi",
camp: "red",
active: false,
},
{
y: 9,
x: 6,
text: "象",
show: true,
type: "like",
camp: "red",
active: false,
},
{
y: 9,
x: 7,
text: "馬",
show: true,
type: "horse",
camp: "red",
active: false,
},
{
y: 9,
x: 8,
text: "車",
show: true,
type: "car",
camp: "red",
active: false,
},
{
y: 7,
x: 1,
text: "炮",
show: true,
type: "gun",
camp: "red",
active: false,
},
{
y: 7,
x: 7,
text: "炮",
show: true,
type: "gun",
camp: "red",
active: false,
},
{
y: 6,
x: 0,
text: "兵",
show: true,
type: "pawn",
camp: "red",
active: false,
},
{
y: 6,
x: 2,
text: "兵",
show: true,
type: "pawn",
camp: "red",
active: false,
},
{
y: 6,
x: 4,
text: "兵",
show: true,
type: "pawn",
camp: "red",
active: false,
},
{
y: 6,
x: 6,
text: "兵",
show: true,
type: "pawn",
camp: "red",
active: false,
},
{
y: 6,
x: 8,
text: "兵",
show: true,
type: "pawn",
camp: "red",
active: false,
},
],
};
},
mounted() {
this.initData();
},
methods: {
// 初始化数据
initData() {
const canvas = (this.canvas = document.getElementById("chess"));
const ctx = (this.ctx = canvas.getContext("2d"));
this.drawChessContainer();
this.drawChess();
},
// 绘制棋盘
drawChessContainer() {
const { ctx } = this;
// 每次绘制棋盘前,先清空画布
ctx.rect(0, 0, this.width, this.height);
// 设置颜色
ctx.fillStyle = this.primary;
ctx.strokeStyle = "#000";
// 循环画出来会少一条横线,我们手动加上
ctx.beginPath();
ctx.moveTo(this.padding, 475);
ctx.lineTo(425, 475);
// 绘制线条
ctx.stroke();
for (var index = 1; index <= 9; index++) {
// 横线
ctx.beginPath();
ctx.moveTo(this.padding, index * this.locationScale - this.padding);
ctx.lineTo(425, index * this.locationScale - this.padding);
// 绘制线条
ctx.stroke();
// 竖线
ctx.beginPath();
ctx.moveTo(this.locationScale * index - this.padding, this.padding);
ctx.lineTo(this.locationScale * index - this.padding, 475);
// 绘制线条
ctx.stroke();
}
// 双方的将军区域
// 上方
ctx.beginPath();
ctx.moveTo(175, this.padding);
ctx.lineTo(275, 125);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(275, this.padding);
ctx.lineTo(175, 125);
ctx.stroke();
// 下方
ctx.beginPath();
ctx.moveTo(175, 375);
ctx.lineTo(275, 475);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(175, 475);
ctx.lineTo(275, 375);
ctx.stroke();
// 楚河,汉界
ctx.fillStyle = this.primary;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(26, 225, 398, this.locationScale);
ctx.fill();
ctx.font = '28px "报隶-简"';
ctx.fillStyle = "#000";
ctx.textBaseline = "middle";
ctx.fillText("楚河", 75, 250);
ctx.fillText("汉界", 325, 250);
this.drawCross();
},
// 绘制单个棋子
drawChessSingle(chess, { chessWidth } = {}) {
if (!chess.show) return;
const { ctx, chessFontSize, locationScale, padding } = this;
chessWidth = chessWidth || this.chessWidth;
let x = chess.x * locationScale + padding;
let y = chess.y * locationScale + padding;
// 画棋
ctx.beginPath();
ctx.lineWidth = "5";
ctx.strokeStyle = "#898341";
ctx.arc(x, y, chessWidth, 0, 2 * Math.PI);
ctx.stroke();
ctx.closePath();
ctx.fillStyle = chess.camp;
ctx.fill();
ctx.lineWidth = "1";
// 画字
ctx.font = chessFontSize + 'px "报隶-简"';
ctx.fillStyle = "#fff";
ctx.textBaseline = "middle";
ctx.fillText(chess.text, x - chessFontSize / 2, y);
},
// 绘制棋子
drawChess() {
this.chessList.forEach((item) => {
this.drawChessSingle(item);
});
},
// 绘制十字线
drawCross(x, y) {
const { ctx, crossList, locationScale } = this;
ctx.lineWidth = 1;
// 画 炮和兵 下面的十字
crossList.forEach((item) => {
if (item.x !== 0) {
// 左上
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 10, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 20);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 20, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 10);
ctx.stroke();
// 左下
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 10, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 30);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 20, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 20, item.y * locationScale + 40);
ctx.stroke();
}
if (item.x !== 8) {
// 右上
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 40, item.y * locationScale + 20);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 20);
ctx.lineTo(item.x * locationScale + 30, item.y * locationScale + 10);
ctx.stroke();
// 右下
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 40, item.y * locationScale + 30);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(item.x * locationScale + 30, item.y * locationScale + 30);
ctx.lineTo(item.x * locationScale + 30, item.y * locationScale + 40);
ctx.stroke();
}
});
},
},
};
</script>
<style lang="scss" scoped>
.chess_test {
background: #555;
width: 100%;
height: 100%;
position: relative;
.action {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
#chess {
background: #fff;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
</style>
复制代码