|
@@ -2,6 +2,8 @@
|
|
|
<div class="game-container">
|
|
|
<canvas id="canvas" @mousedown="handleMouseDown" @mouseup="handleMouseUp" @touchstart="handleTouchStart"
|
|
|
@touchend="handleTouchEnd"></canvas>
|
|
|
+ <canvas ref="canvasRef" :width="clientObj.width" :height="clientObj.height"
|
|
|
+ style="position:fixed;left: 0; top: 0;"></canvas>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
@@ -12,6 +14,7 @@ import { useWebSocket } from '@/utils/bodyposeWs';
|
|
|
const { proxy } = getCurrentInstance() as any;
|
|
|
const router = useRouter();
|
|
|
const { bodyposeWs, startDevice, checkBodypose, openBodypose, terminateBodypose, suspendBodypose, resumeBodypose, getBodyposeState, closeWS } = useWebSocket();
|
|
|
+const canvasRef = ref(null);
|
|
|
|
|
|
const data = reactive<any>({
|
|
|
bodyposeData: {},//姿态信息
|
|
@@ -22,27 +25,29 @@ const data = reactive<any>({
|
|
|
againTimer: null,//定时状态
|
|
|
wsState: false,//WS状态
|
|
|
clientObj: {},//浏览器对象
|
|
|
+ boxes: [],//四个点坐标
|
|
|
+ proportion: null,//人框和屏幕比例
|
|
|
myThrow: 0,//0放下1举投
|
|
|
});
|
|
|
|
|
|
-const { bodyposeData, bodyposeState, parameter, deviceInfo, againNum, againTimer, wsState, clientObj, myThrow } = toRefs(data);
|
|
|
+const { bodyposeData, bodyposeState, parameter, deviceInfo, againNum, againTimer, wsState, clientObj, boxes, proportion, myThrow } = toRefs(data);
|
|
|
|
|
|
/**
|
|
|
* 输出犯规
|
|
|
*/
|
|
|
watch(
|
|
|
() => myThrow.value,
|
|
|
- (newData,oldData) => {
|
|
|
- console.log("ppp",oldData,newData)
|
|
|
+ (newData, oldData) => {
|
|
|
+ console.log("ppp", oldData, newData)
|
|
|
if (newData == 1) {
|
|
|
console.log("投篮")
|
|
|
handleMouseDown();
|
|
|
- setTimeout(()=>{
|
|
|
+ setTimeout(() => {
|
|
|
handleMouseUp();
|
|
|
- },100)
|
|
|
-
|
|
|
- }else{
|
|
|
-
|
|
|
+ }, 100)
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
}
|
|
|
},
|
|
|
{ immediate: true }
|
|
@@ -67,6 +72,7 @@ const getInit = async () => {
|
|
|
return false;
|
|
|
}
|
|
|
speckText("正在姿态识别");
|
|
|
+ proxy?.$modal.msgWarning(`正在姿态识别`);
|
|
|
bodyposeWs((e: any) => {
|
|
|
//console.log("bodyposeWS", e)
|
|
|
if (e?.wksid) {
|
|
@@ -135,7 +141,14 @@ const getInit = async () => {
|
|
|
}
|
|
|
//console.log("result", result)
|
|
|
bodyposeData.value = result;
|
|
|
- getCanvas(result);
|
|
|
+ if (boxes.value.length == 0) {
|
|
|
+ speckText("识别成功");
|
|
|
+ proxy?.$modal.msgWarning(`识别成功`);
|
|
|
+ let arr = e.data.result.boxes;
|
|
|
+ boxes.value = [{ x: arr[0], y: arr[3] }, { x: arr[0], y: arr[1] }, { x: arr[2], y: arr[1] }, { x: arr[2], y: arr[3] }]
|
|
|
+ proportion.value = (clientObj.value.height / (arr[3] - arr[1])).toFixed(2);
|
|
|
+ }
|
|
|
+ getCanvas();
|
|
|
}
|
|
|
if (e?.cmd == 'terminate_bodyposecontroller') {
|
|
|
if (e?.code == 0) {
|
|
@@ -175,18 +188,102 @@ const getCloseBodypose = () => {
|
|
|
};
|
|
|
|
|
|
|
|
|
-const getCanvas = (data) => {
|
|
|
- let leftA = data[6][1];
|
|
|
- let rightA = data[5][1];
|
|
|
- let leftB = data[10][1];
|
|
|
- let rightB = data[9][1];
|
|
|
+const getCanvas = () => {
|
|
|
+ //过肩或者过鼻子都算投篮
|
|
|
+ let leftA = bodyposeData.value[6][1];//右肩Y
|
|
|
+ let rightA = bodyposeData.value[5][1];//左肩Y
|
|
|
+ let leftB = bodyposeData.value[10][1];//右手Y
|
|
|
+ let rightB = bodyposeData.value[9][1];//左手Y
|
|
|
+ let bizi = bodyposeData.value[0][1];//左手Y
|
|
|
|
|
|
- if (leftB > leftA || rightB > rightA) {
|
|
|
+ if (leftB > leftA || rightB > rightA || leftB > bizi || rightB > bizi) {
|
|
|
myThrow.value = 1;
|
|
|
} else {
|
|
|
myThrow.value = 0;
|
|
|
}
|
|
|
|
|
|
+ const canvas: any = canvasRef.value;
|
|
|
+ const ctx = canvas.getContext('2d');
|
|
|
+ // 清空整个画布
|
|
|
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
+ // 保存当前状态
|
|
|
+ ctx.save();
|
|
|
+ function calculateOffset(a: any, b: any) {
|
|
|
+ return {
|
|
|
+ x: b.x - a.x,
|
|
|
+ y: b.y - a.y
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const pointA = { x: clientObj.value.width / 2, y: clientObj.value.height / 2 };
|
|
|
+ const pointB = { x: (boxes.value[2].x + boxes.value[0].x) / 2, y: (boxes.value[3].y + boxes.value[1].y) / 2 };
|
|
|
+ const offset = calculateOffset(pointA, pointB);
|
|
|
+ ctx.translate(-offset.x, -offset.y);
|
|
|
+ // console.log("Canvas分辨率", clientObj.value);
|
|
|
+ // console.log("人体图片四点坐标", boxes.value)
|
|
|
+ // console.log("Canvas中心", pointA);
|
|
|
+ // console.log("人体中心", pointB);
|
|
|
+ // console.log("offset", offset)
|
|
|
+ // console.log("proportion.value",proportion.value)
|
|
|
+ const originalPoints = bodyposeData.value;
|
|
|
+ // 计算缩放后坐标
|
|
|
+ const postData = originalPoints.map((point: any) => {
|
|
|
+ const newX = (point[0] - pointB.x) * proportion.value + pointB.x;
|
|
|
+ const newY = (point[1] - pointB.y) * proportion.value + pointB.y;
|
|
|
+ return [newX, newY];
|
|
|
+ });
|
|
|
+ // console.log("原始坐标:", originalPoints);
|
|
|
+ // console.log("缩放后坐标:", postData);
|
|
|
+
|
|
|
+ //绘制头部
|
|
|
+ const point1 = { x: postData[4][0], y: postData[4][1] };
|
|
|
+ const point2 = { x: postData[3][0], y: postData[3][1] };
|
|
|
+ // 计算椭圆参数
|
|
|
+ const centerX = (point1.x + point2.x) / 2; // 椭圆中心X
|
|
|
+ const centerY = (point1.y + point2.y) / 2; // 椭圆中心Y
|
|
|
+ const distance = Math.sqrt(
|
|
|
+ Math.pow(point2.x - point1.x, 2) +
|
|
|
+ Math.pow(point2.y - point1.y, 2)
|
|
|
+ ); // 两个焦点之间的距离
|
|
|
+ const radiusX = distance * 0.5; // 水平半径(可调整)
|
|
|
+ const radiusY = distance * 0.6; // 垂直半径(可调整)
|
|
|
+ // 1. 绘制填充椭圆
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, Math.PI * 2);
|
|
|
+ ctx.fillStyle = 'red'; // 填充颜色
|
|
|
+ ctx.fill(); // 填充
|
|
|
+ // 2. 绘制边框
|
|
|
+ ctx.strokeStyle = 'red';
|
|
|
+ ctx.lineWidth = 5;
|
|
|
+ ctx.stroke();
|
|
|
+ // 绘制每个点
|
|
|
+ postData.forEach((point: any) => {
|
|
|
+ const [x, y] = point;
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.arc(x, y, 5, 0, Math.PI * 2); // 绘制半径为5的圆点
|
|
|
+ ctx.fillStyle = 'red';
|
|
|
+ ctx.fill();
|
|
|
+ ctx.lineWidth = 1;
|
|
|
+ ctx.stroke();
|
|
|
+ });
|
|
|
+ // 根据点关系连线
|
|
|
+ const arr = [[10, 8], [8, 6], [6, 5], [5, 7], [7, 9], [6, 12], [5, 11], [12, 11], [12, 14], [14, 16], [11, 13], [13, 15]]
|
|
|
+ arr.forEach((point: any) => {
|
|
|
+ let index1 = point[0];
|
|
|
+ let index2 = point[1];
|
|
|
+ //连线
|
|
|
+ const dian1 = { x: postData[index1][0], y: postData[index1][1] };
|
|
|
+ const dian2 = { x: postData[index2][0], y: postData[index2][1] };
|
|
|
+ // 绘制连线
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(dian1.x, dian1.y); // 起点
|
|
|
+ ctx.lineTo(dian2.x, dian2.y); // 终点
|
|
|
+ ctx.strokeStyle = 'red'; // 线条颜色
|
|
|
+ ctx.lineWidth = 3; // 线条宽度
|
|
|
+ ctx.stroke(); // 描边
|
|
|
+ });
|
|
|
+ ctx.restore(); // 恢复状态
|
|
|
+
|
|
|
+
|
|
|
};
|
|
|
|
|
|
// 游戏主类的响应式状态
|
|
@@ -268,7 +365,7 @@ class Ball {
|
|
|
this.solid = false;
|
|
|
this.z = 1;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
setAngle(angle) {
|
|
|
this.angle = angle;
|
|
|
this.vx = this.speed * Math.cos(this.angle * Math.PI / 180);
|
|
@@ -384,6 +481,8 @@ const setupCanvas = () => {
|
|
|
gameState.canvas = document.getElementById('canvas');
|
|
|
gameState.canvas.width = 640;
|
|
|
gameState.canvas.height = 960;
|
|
|
+ // gameState.canvas.width = document.documentElement.clientWidth / 2;
|
|
|
+ // gameState.canvas.height = document.documentElement.clientHeight;
|
|
|
gameState.ctx = gameState.canvas.getContext('2d');
|
|
|
};
|
|
|
|
|
@@ -633,8 +732,6 @@ const draw = () => {
|
|
|
}
|
|
|
ctx.textAlign = 'center';
|
|
|
drawText(ctx, '请做投篮动作开始游戏', 640 / 2, 520, 40);
|
|
|
- drawText(ctx, '(?) Tap to throw ball. Try to make as many hoops before the time runs out!', 640 / 2, 940, 20);
|
|
|
- ctx.textAlign = 'left';
|
|
|
}
|
|
|
|
|
|
// 绘制游戏状态
|
|
@@ -713,6 +810,10 @@ onMounted(() => {
|
|
|
});
|
|
|
|
|
|
onBeforeMount(() => {
|
|
|
+ clientObj.value = {
|
|
|
+ width: document.documentElement.clientWidth,
|
|
|
+ height: document.documentElement.clientHeight,
|
|
|
+ }
|
|
|
getInit();
|
|
|
});
|
|
|
|