How to play: The player uses the direction keys to control a long snake to continuously swallow beans. The body continues to grow as the beans are swallowed, and the game ends when the snake's head hits the snake's body or a barrier.
Elements: boundary, snake head, snake body, food
Boundary: Enter the number of rows x, the number of columns y to generate a boundary map, and use two-dimensional coordinates to identify each Point position;
Snake head, snake body: The snake head and snake body are separated. When food is eaten, one is added to the tail of the snake body.
Food: The position is randomly generated;
Selectvue3, vite
Infrastructure; View selectioncanvas
technology to achieve better performance than dom;
<script setup lang="ts"> import { ref, onMounted } from 'vue' let width = ref(600) // 地图默认宽度 let height = ref(400) // 地图默认高度 let canvas: any = null // canvas 对象 let ctx: any = null // canvas 渲染上下文对象 let snakeList = [[0, 100], [10, 100],] // 蛇的点位坐标 let direction = 'right' // top | down | left | right // 当前方向 let elementWidth = 10 // 元素尺寸 let step = 10 // 速度 let store = ref(0) // 分数 let status = ref('start') // unStart | start | pause | over | success(通关) // 状态 let foodCoordinate: any = [ ((Math.random() * width.value) / 10) | 0, ((Math.random() * height.value) / 10) | 0, ] // 食物坐标 let process: any = null // 定时器 Id </script>
Executed in onMounted, mainly for map drawing , mouse coordinate detection, direction monitoring, food drawing, timer activation and other operations.
function handleInit() { canvas = document.getElementById('canvas') if (canvas?.getContext) { ctx = canvas?.getContext('2d') canvas.addEventListener('mousemove', e => { ctx.clearRect(10, height.value - 20, 120, 40) ctx.fillText(`当前鼠标位置:${e.offsetX}, ${e.offsetY}`, 10, height.value - 10) }) document.addEventListener('keydown', e => { e.preventDefault() if (Direction[e.keyCode]) { direction = Direction[e.keyCode] } }) process = setInterval(handleRenderSnake, 150) handleRenderFood() // window.requestAnimationFrame(handleRenderSnake) } else { alert('您的浏览器不支持 canvas') } }
When food is eaten, it needs to be destroyed and regenerated
// 绘制食物 function handleRenderFood() { ctx.clearRect(foodCoordinate[0], foodCoordinate[1], 10, 10) foodCoordinate = [(Math.random() * width.value) | 0, (Math.random() * height.value) | 0] ctx.fillStyle = '#eb2f96' ctx.fillRect(foodCoordinate[0], foodCoordinate[1], 10, 10) }
Snake is two-dimensional Represented in an array, each node represents a part of the body, and the first node represents the snake's head. The movement of the snake is achieved by deleting the tail node and adding the head node. The middle node does not need to move, and the processing in the four directions is slightly different. Note that when food is eaten, the tail node of the current frame is no longer deleted, and the length of the snake body is increased by 1.
function handleRenderSnake() { switch (direction) { case 'top': if (snakeList.slice(-1)[0][1] <= 0) { status.value = 'over' return } snakeList.push([ snakeList[snakeList.length - 1][0], snakeList[snakeList.length - 1][1] - step, ]) handleUpdateVerify() break case 'down': if (snakeList.slice(-1)[0][1] >= height.value - 1) { status.value = 'over' return } snakeList.push([ snakeList[snakeList.length - 1][0], snakeList[snakeList.length - 1][1] + step, ]) handleUpdateVerify() break ...
When the snake head touches the edge of the map, the game will be over. You only need to calculate whether the next coordinates will exceed the map size based on the current coordinates and current direction of the snake head. That’s it.
Calculation method of eating food: Calculate the absolute values of the x and y axes of the snake head coordinates and food coordinates respectively. If it is smaller than the element size, it is considered to have been in contact.
// 更新校验 function handleUpdateVerify() { if (status.value === 'pause') { clearInterval(process) } if (store.value >= 100) { status.value = 'success' return } for (let i of snakeList) { ctx.clearRect(i[0], i[1], elementWidth, elementWidth) } let currentSnake = snakeList.slice(-1)[0] if ( Math.abs(currentSnake[0] - foodCoordinate[0]) < 10 && Math.abs(currentSnake[1] - foodCoordinate[1]) < 10 ) { store.value++ handleRenderFood() } else { snakeList.shift() } }
The global variable status represents the status of the current situation. When status === 'pause', the pause operation is triggered, the timer variable is deleted, click Restart button to generate a new timer.
When food is eaten, the global variable store is bidirectionally bound to the page for display. Temporarily set the score to exceed 100 to pass the level.
The above is the detailed content of How to use Vue3+Canvas to implement a simple snake game. For more information, please follow other related articles on the PHP Chinese website!