Web前端项目(一)- 迷宫游戏

简介: 【8月更文挑战第13天】本项目采用HTML页面,结合了JS和CSS创建一个简单的迷宫游戏,游戏特色包括自动寻路功能和可进行迷宫路线的更新。页面整体采用“毒药水式”的色彩搭配,给人一种迷幻,科技之感。并且为了大家能够二次创作,我在代码中标明了详细的注释

页面效果:本项目采用HTML页面,结合了JavaScriptCSS来创建一个简单的迷宫探索游戏,游戏特色包括自动寻路功能和可进行迷宫路线的更新。页面整体采用“毒药水式”的色彩搭配,给人一种迷幻,科技之感
migong.gif

代码展示:代码中标明了详细的注释

<html>
    <head>
        <meta charset="UTF-8">
        <style>
            /* 设置背景色为渐变色 */
            #stage {
    
                background: linear-gradient(to right, #11e015 0, #d9c123 20%, rgba(155, 21, 227, 0.54) 90%);
            }
            /* 设置重新开始按钮的样式 */
            .rebuild {
    
                width:170px;
                height:30px;
                line-height: 30px;
                text-align: center;
                background-image: linear-gradient(to right, rgba(154, 89, 168, 0.67), rgba(30, 145, 199, 0.67), rgba(106, 255, 0, 0.67));
                color: rgb(243, 18, 237);
                font-size: 18px;
                margin-bottom: 20px;
                cursor: pointer;
            }
            /* 设置html和canvas的宽度,并使其居中 */
            html,
            canvas{
    
                width: 530px;
                margin: auto;
            }
        </style>
    </head>
    <body>
        <table><tr>
            <!-- 游戏界面的canvas元素 -->
            <td><canvas id="stage"></canvas></td>
            <td valign="top" style="padding:10px">
                <!-- 重新开始按钮 -->
                <div class="rebuild">重新开始</div>
                <!-- 显示当前步数和位置 -->
                当前步数: <span id="goNum"></span>
                <br><br>当前位置 <span id="XY">(x: 1,y: 1)</span>
                <br><br>
                <!-- 自动寻路按钮 -->
                <input id="runBtn" type="button" value="自动寻路">
                <br><br>
                <!-- 显示自动寻路结果的区域 -->
                <div id="autoOut"></div>
            </td>
        </tr></table>
    </body>
    <script>
        var si = 8; // 设置每个格子的大小
        var mapSize = 40; // 设置地图的大小
        window.onload = function () {
    
            var stage = document.querySelector('#stage'),
                ctx = stage.getContext('2d');
            stage.width = si * (mapSize * 2 - 1); // 设置canvas的宽度
            stage.height = si * (mapSize * 2 - 1); // 设置canvas的高度

            // 取区域随机数x>=min && x<max
            function randInt(min, max) {
    
                max = max || 0;
                min = min || 0;
                var step = Math.abs(max - min);
                var st = (arguments.length < 2) ? 0 : min;
                var result;
                result = st + (Math.ceil(Math.random() * step)) - 1;
                return result;
            }

            // 使用普里姆算法生成连通图的二维数组
            function primMaze(r, c) {
    
                // 初始化数组
                function init(r, c) {
    
                    var a = new Array(2 * r + 1);
                    for (let i = 0, len = a.length; i < len; i++) {
    
                        var cols = 2 * c + 1;
                        a[i] = new Array(cols);
                        for (let j = 0, len1 = a[i].length; j < len1; j++) {
    
                            a[i][j] = 1;
                        }
                    }
                    a[a.length - 1][a[0].length - 2] = 0;
                    for (let i = 0; i < r; i++)
                        for (let j = 0; j < c; j++) {
    
                            a[2 * i + 1][2 * j + 1] = 0;
                        }
                    return a;
                }

                // 处理数组,产生最终的数组
                function process(arr) {
    
                    var acc = [],
                        noacc = [];
                    var r = arr.length >> 1,
                        c = arr[0].length >> 1;
                    var count = r * c;
                    for (var i = 0; i < count; i++) {
    
                        noacc[i] = 0;
                    }
                    var offs = [-c, c, -1, 1],
                        offR = [-1, 1, 0, 0],
                        offC = [0, 0, -1, 1];
                    var pos = randInt(count);
                    noacc[pos] = 1;
                    acc.push(pos);
                    while (acc.length < count) {
    
                        var ls = -1,
                            offPos = -1;
                        var pr = pos / c | 0,
                            pc = pos % c,
                            co = 0,
                            o = 0;
                        while (++co < 5) {
    
                            o = randInt(0, 5);
                            ls = offs[o] + pos;
                            var tpr = pr + offR[o];
                            var tpc = pc + offC[o];
                            if (tpr >= 0 && tpc >= 0 && tpr <= r - 1 && tpc <= c - 1 && noacc[ls] == 0) {
    
                                offPos = o;
                                break;
                            }
                        }
                        if (offPos < 0) {
    
                            pos = acc[randInt(acc.length)];
                        } else {
    
                            pr = 2 * pr + 1;
                            pc = 2 * pc + 1;
                            arr[pr + offR[offPos]][pc + offC[offPos]] = 0;
                            pos = ls;
                            noacc[pos] = 1;
                            acc.push(pos);
                        }
                    }
                }

                var a = init(r, c);
                process(a);
                return a;
            }

            // 栅格线条
            function drawGrid(context, color, stepx, stepy) {
    
                context.strokeStyle = color;
                context.lineWidth = 0.5;
                for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
    
                    context.beginPath();
                    context.moveTo(i, 0);
                    context.lineTo(i, context.canvas.height);
                    context.stroke();
                }
                for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
    
                    context.beginPath();
                    context.moveTo(0, i);
                    context.lineTo(context.canvas.width, i);
                    context.stroke();
                }
            }

            function createRect(x, y, r, c) {
    
                ctx.beginPath();
                ctx.fillStyle = c;
                ctx.rect(x, y, r, r);
                ctx.fill();
            }

            var region = 0; // 可探索区域
            function update() {
    
                ctx.clearRect(0, 0, mapSize * si * 2, mapSize * si * 2);
                drawGrid(ctx, 'lightgray', si, si);
                var mapArr = primMaze(mapSize - 1, mapSize - 1);
                region = 0;
                for (var i = 0, len = mapArr.length; i < len; i++) {
    
                    for (var j = 0, len1 = mapArr[i].length; j < len1; j++) {
    
                        if (mapArr[i][j]) {
    
                            createRect(i * si, j * si, si, "#999999");
                        } else {
    
                            region++;
                        }
                    }
                }
                return mapArr;
            }

            // 定义一个函数,用于在界面上绘制矩形,参数x, y表示矩形左上角的坐标,si是矩形的大小,c是矩形的颜色
            function draw(x, y, c) {
    
                createRect(x * si + 2, y * si + 2, si - 4, c);
            }

// 定义一个函数,用于在界面上绘制移动对象的当前位置,颜色为"#66aaff"
            function drawMove(x, y) {
    
                createRect(x * si + 1, y * si + 1, si - 2, "#66aaff");
                md[x][y] = 2;
            }

// 定义一个函数,用于在界面上绘制移动对象经过的路径,颜色为"#3333ff"
            function drawMove1(x, y) {
    
                createRect(x * si + 2, y * si + 2, si - 4, "#3333ff");
            }

// 定义一个函数,用于清除界面上指定位置的矩形
            function clearMove(x, y) {
    
                ctx.clearRect(x * si, y * si, si, si);
            }

// 初始化变量,包括地图md,当前位置currentXY,上一次位置onceXY,步数goNums等
            var md;
            var currentXY;
            var onceXY;
            var goNums = 0;
            var timeout = 10;
            var walk = 0;
            var Stop = true;

// 初始化函数,用于重置游戏状态
            function init() {
    
                document.getElementById("autoOut").innerHTML = "";
                md = update(); // 更新地图数据
                currentXY = [1, 1];
                document.getElementById("XY").innerHTML = "x: " + currentXY[0] + "&emsp;y: " + currentXY[1];
                onceXY = [1, 1];
                goNums = 0;
                drawMove(currentXY[0], currentXY[1]);
                document.getElementById("goNum").innerHTML = goNums;
                control = true;
                walk = 0;
                pathList = [];
                Stop = true;
            }

            init();

// 定义moveIt函数,用于处理键盘事件,控制移动对象的移动
            function moveIt() {
    
                if (!control) return;
                var oxy = [currentXY[0], currentXY[1]]; // 记录原来位置
                var moveIs = true;
                // 根据键盘按键改变移动对象的位置
                switch (event.keyCode) {
    
                    case 37:
                        currentXY[0]--;
                        break;  // left
                    case 38:
                        currentXY[1]--;
                        break;  // up
                    case 39:
                        currentXY[0]++;
                        break;  // right
                    case 40:
                        currentXY[1]++;
                        break;  // down
                }
                // 如果移动到地图外,则重新开始
                if (currentXY[0] >= md[0].length || currentXY[1] >= md.length) {
    
                    clearMove(currentXY[0], currentXY[1]);
                    init();
                    return;
                }
                // 如果新位置是墙,则不能移动
                if (md[currentXY[0]][currentXY[1]] == 1) {
    
                    moveIs = false;
                }
                // 如果可以移动,则更新画面和状态
                if (moveIs) {
    
                    clearMove(oxy[0], oxy[1]);
                    drawMove1(oxy[0], oxy[1]);
                    if (md[currentXY[0]][currentXY[1]] == 2) {
    
                        clearMove(onceXY[0], onceXY[1]);
                        md[onceXY[0]][onceXY[1]] = 0;
                    }
                    drawMove(currentXY[0], currentXY[1]);
                    goNums++;
                } else {
    
                    currentXY = [oxy[0], oxy[1]]; // 回原位
                }

                onceXY = [currentXY[0], currentXY[1]];

                document.getElementById("goNum").innerHTML = goNums;
                document.getElementById("XY").innerHTML = "x: " + currentXY[0] + "&emsp;y: " + currentXY[1];
            }

            var control = true;
            document.onkeydown = moveIt;
            document.querySelector('.rebuild').addEventListener('click', init);

// 定义一些用于自动寻路的变量
            var pathList;        // 当前路径
            var pathOkList;      // 成功的路径
            var pathNode;        // 分叉路口数
            var LukouList;

// 定义endAuto函数,用于寻路成功后的回调
            function endAuto() {
    
                // 找到最短的路径并画出
                var pathCount = pathOkList.length;
                var pNum = region;
                var zIndex = 0;

                for (var i = 0; i < pathOkList.length; i++) {
    
                    if (pathOkList[i].length < pNum) {
    
                        pNum = pathOkList[i].length;
                        zIndex = i;
                    }
                }

                var zdPath = pathOkList[zIndex];

                for (var j = 0; j < zdPath.length; j++) {
    
                    drawMove1(zdPath[j][0], zdPath[j][1]);
                }
                control = true;
            }

// 定义distance函数,用于计算两点之间的距离
            function distance(p1, p2) {
    
                return Math.sqrt(Math.pow(p1[0] - p2[0], 2) + Math.pow(p1[1] - p2[1], 2));
            };
            var clearPath = true;

// 定义autoFx函数,用于自动寻路
            function autoFx() {
    
                if (Stop) return;
                var x = currentXY[0];
                var y = currentXY[1];
                var oxy = [currentXY[0], currentXY[1]];
                var wayout = 0;
                // 检查四个方向是否可以走
                if (md[x + 1][y] == 0) {
    
                    wayout++;
                    pathNode.push([x + 1, y]);
                }
                if (md[x - 1][y] == 0) {
    
                    wayout++;
                    pathNode.push([x - 1, y]);
                }
                if (md[x][y + 1] == 0) {
    
                    wayout++;
                    pathNode.push([x, y + 1]);
                }
                if (md[x][y - 1] == 0) {
    
                    wayout++;
                    pathNode.push([x, y - 1]);
                }

                var htmlStr = "已探索区域:" + Math.round(walk / region * 100) + "%";
                htmlStr += "<br><br> 有效步数: " + pathList.length;
                document.getElementById("autoOut").innerHTML = htmlStr;
                document.getElementById("XY").innerHTML = "x: " + currentXY[0] + "&emsp;y: " + currentXY[1];
                document.getElementById("goNum").innerHTML = goNums;

                var ts = function () {
    
                    // 如果没有分叉路口,则结束寻路
                    if (pathNode.length == 0) {
    
                        endAuto();
                        return;
                    }
                    var lastGo = pathNode.pop();
                    // 清除无效路径
                    while (distance(lastGo, pathList[pathList.length - 1]) > 1) {
    
                        var p = pathList.pop();
                        setTimeout(clearMove(p[0], p[1]), timeout);
                        draw(p[0], p[1], "#aaaaaa");
                    }
                    currentXY[0] = lastGo[0];
                    currentXY[1] = lastGo[1];
                    walk++;

                    setTimeout(function () {
    
                        autoFx()
                    }, timeout);
                };

                // 如果到达地图右下角,则认为寻路成功
                if (x >= md[0].length - 2 && y >= md.length - 2) {
    
                    var ppi = pathOkList.length;
                    pathOkList[ppi] = [];
                    for (var i = 0; i < pathList.length; i++) {
    
                        pathOkList[ppi][i] = [];
                        pathOkList[ppi][i][0] = pathList[i][0];
                        pathOkList[ppi][i][1] = pathList[i][1];
                    }
                    endAuto();
                }

                // 如果有路走,则继续探索
                if (wayout > 0) {
    
                    goNums++;
                    pathList.push([x, y]);
                    drawMove1(x, y);
                    var newPath = pathNode.pop();
                    currentXY[0] = newPath[0];
                    currentXY[1] = newPath[1];
                    md[oxy[0]][oxy[1]] = 2;
                    walk++;
                    setTimeout(function () {
    
                        autoFx()
                    }, timeout);
                } else {
     // 死路,尝试其他路径
                    if (pathNode.length > 0) {
    
                        ts();
                    } else {
    
                        endAuto();
                    }
                }
            }

// 绑定点击事件,开始自动寻路
// 获取页面中ID为"runBtn"的元素,并将其onclick事件处理函数设置为以下函数
            document.getElementById("runBtn").onclick = function () {
    
                // 检查变量control的值,如果为false,则不执行以下代码,直接返回
                if (!control) return;
                // 将ID为"autoOut"的元素的innerHTML设置为空字符串,即清空其内容
                document.getElementById("autoOut").innerHTML = "";
                // 将变量currentXY的值设置为[1,1]
                currentXY = [1, 1];
                // 将变量pathList的值设置为空数组,用于存储路径信息
                pathList = [];
                // 将变量pathOkList的值设置为空数组,用于存储经过验证的路径信息
                pathOkList = [];
                // 将变量pathNode的值设置为空数组,用于存储路径节点信息
                pathNode = [];
                // 将变量walk的值设置为0
                walk = 0;
                // 将变量control的值设置为false
                control = false;
                // 将变量Stop的值设置为false
                Stop = false;
                autoFx();
            };
        }
    </script>
</html>
目录
相关文章
|
14天前
|
编解码 前端开发 JavaScript
.NET_web前端框架_layui_栅格布局
【8月更文挑战第27天】
33 4
|
12天前
|
JavaScript 前端开发 开发者
哇塞!Vue.js 与 Web Components 携手,掀起前端组件复用风暴,震撼你的开发世界!
【8月更文挑战第30天】这段内容介绍了Vue.js和Web Components在前端开发中的优势及二者结合的可能性。Vue.js提供高效简洁的组件化开发,单个组件包含模板、脚本和样式,方便构建复杂用户界面。Web Components作为新兴技术标准,利用自定义元素、Shadow DOM等技术创建封装性强的自定义HTML元素,实现跨框架复用。结合二者,不仅增强了Web Components的逻辑和交互功能,还实现了Vue.js组件在不同框架中的复用,提高了开发效率和可维护性。未来前端开发中,这种结合将大有可为。
50 0
|
2天前
|
前端开发 程序员
【前端web入门第二天】01 html语法实现列表与表格_合并单元格
本文介绍了HTML中的列表与表格的使用方法。列表包括无序列表(`&lt;ul&gt;`嵌套`&lt;li&gt;`)、有序列表(`&lt;ol&gt;`嵌套`&lt;li&gt;`)和定义列表(`&lt;dl&gt;`嵌套`&lt;dt&gt;`和`&lt;dd&gt;`)。
32 19
|
2天前
|
前端开发 Windows
【前端web入门第一天】02 HTML图片标签 超链接标签 音频标签 视频标签
本文档详细介绍了HTML中的图片、超链接、音频和视频标签的使用方法。首先讲解了`&lt;img&gt;`标签的基本用法及其属性,包括如何使用相对路径和绝对路径。接着介绍了`&lt;a&gt;`标签,用于创建超链接,并展示了如何设置目标页面打开方式。最后,文档还涵盖了如何在网页中嵌入音频和视频文件,包括简化写法及常用属性。
24 13
|
2天前
|
JavaScript 前端开发
【前端web入门第一天】03 综合案例 个人简介与vue简介
该网页采用“从上到下,先整体再局部”的制作思路,逐步分析并编写代码实现个人简介页面。内容涵盖尤雨溪的背景、学习经历及主要成就,同时介绍其开发的Vue.js框架特点。代码结构清晰,注重细节处理,如使用快捷键提高效率,预留超链接位置等,确保最终效果符合预期。
|
2天前
|
前端开发 程序员 C++
【前端web入门第一天】01 开发环境、HTML基本语法文本标签
本文档详细介绍了HTML文本标签的基础知识。首先指导如何准备开发环境,包括安装VSCode及常用插件;接着全面解析HTML的基本结构与标签语法,涵盖从基本骨架搭建到注释的使用,以及标题、段落、换行和平行线、文本格式化等标签的具体应用,适合初学者循序渐进地掌握HTML。
|
4天前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
35 3
|
11天前
|
开发者 图形学 开发工具
Unity编辑器神级扩展攻略:从批量操作到定制Inspector界面,手把手教你编写高效开发工具,解锁编辑器隐藏潜能
【8月更文挑战第31天】Unity是一款强大的游戏开发引擎,支持多平台发布与高度可定制的编辑器环境。通过自定义编辑器工具,开发者能显著提升工作效率。本文介绍如何使用C#脚本扩展Unity编辑器功能,包括批量调整游戏对象位置、创建自定义Inspector界面及项目统计窗口等实用工具,并提供具体示例代码。理解并应用这些技巧,可大幅优化开发流程,提高生产力。
38 1
|
11天前
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
22 1
|
6天前
|
前端开发 JavaScript 开发者
现代前端框架激烈交锋,高效响应式 Web 界面的归属扑朔迷离!
【9月更文挑战第6天】本文通过实际案例,比较了主流前端框架 Vue.js、React 和 Angular 的特点与优势。Vue.js 以简洁的语法和灵活的组件化架构著称,适合小型到中型项目;React 强调性能和可扩展性,适用于大型应用;Angular 凭借全面的功能和严格架构,适合企业级开发。开发者应根据项目需求和技术栈选择合适的框架。
24 0