首页 > 编程语言 >js 游戏编程:(平滑跟随算法+碰撞检测算法) 贪吃蛇

js 游戏编程:(平滑跟随算法+碰撞检测算法) 贪吃蛇

时间:2023-09-28 22:11:22浏览次数:38  
标签:body style js 碰撞检测 算法 let var document left

相信大家都用 c 语言写过贪吃蛇吧!今天让我们来试试 js 写的贪吃蛇!

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
<style>
@keyframes rot {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
span {
    position: absolute;
    border: 1px solid;
    color: white;
    font-size: larger;
    background-color: rgb(3, 108, 228);
    animation: rot 2s linear infinite;
    text-shadow: 1px 1px 2px red, 0 0 1em blue, 0 0 0.2em blue;
}
.food {
    position: absolute;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: 1px solid;
    color: white;
    background-color: rgb(5, 230, 54);
}
body {
    background-image: url(https://c-ssl.dtstatic.com/uploads/blog/202204/03/20220403215603_6fb22.thumb.1000_0.png);
    background-size: contain;
}
button {
    position: absolute;
    width: 50px;
    height: 50px;
    background: #333;
    color: #fff;
}
#left {
  left: 50px;
  bottom: 100px;
  border-radius: 50%;
}

#right {
  left: 150px; 
  bottom: 100px;
  border-radius: 50%;
}

#up {
  left: 100px;
  bottom: 150px;
  border-radius: 50%;
}

#down {
  left: 100px;
  bottom: 50px;
  border-radius: 50%; 
}
</style>
<p style="text-align: center;font-size: larger;background-color: rgb(248, 166, 166);">tip: 点击开始按钮之后,按一个方向键,蛇蛇才能移动 *^_^*</p>
<div id="food"></div>
<div id="body">
<span id="head" style="left:10px;top:10px;">原</span>
</div>
<button id="game" onclick="game()">开始游戏</button>
<button id="left" onclick="(() => flg = 0)()">左</button>
<button id="right" onclick="(() => flg = 2)()">右</button>
<button id="up" onclick="(() => flg = 1)()">上</button>
<button id="down" onclick="(() => flg = 3)()">下</button>
<script>
const names = ['原','神','启','动']
let startGame = document.getElementById("game");
let game = () => {
    startGame.style.display = 'none';
    ctlMove(head);
    // 创造墙
    // addWall();
    //ctlAngMove(head);
}
let initGame = () => {
    startGame.style.display = 'block';
}
// 游戏结束
// testBigger();
let endGame = ()=>{
    alert("你已撞墙,游戏结束!\n    得分:" + (size*100) + "分");
    flg = 4; // 重置方向
    clearBody();
    clearFoods();
    initGame();
}

// 获取 当前屏幕宽高
var width = document.body.clientWidth || window.innerWidth || document.documentElement.clientWidth || screen.width || document.body.scrollWidth || 0;  
var height = document.body.clientHeight || window.innerHeight || document.documentElement.clientHeight || screen.height || document.body.scrollHeight || 0;
// 在屏幕随机位置生成物品
let rdmLeft = () => 15 + Math.random() * (width - 30);
let rdmTop = () => 15 + Math.random() * (height - 30);
// 食物
let foodlist = []
let foods = document.getElementById("food");

// 随机生成食物
let gfCdtime = false; // 是否冷却时间
let geneFood = () => {
    if(gfCdtime) return;
    // 配置食物的属性
    let food = document.createElement('div');
    food.className = 'food';
    food.style.left = rdmLeft();
    food.style.top = rdmTop();
    // 存储和页面加载食物
    foodlist.push(food);
    foods.append(food);
    // 进入冷却时间(加锁)
    gfCdtime = true;
    setTimeout(()=>{
        gfCdtime = false;
    }, 5000);
}

// 清空食物列表
let clearFoods = ()=>{
    for(var i = 0; i < foodlist.length; i++){
        foods.remove(foodlist[i][0]);
    } foodlist = [];
    foods = document.createElement('div');
    foods.id = 'food';
    document.body.appendChild(foods);
}

// 头和身体
let head = document.getElementById("head");
let body = document.getElementById("body");
// 身体列表
let bodylist = [head];
let size = 0;
// 生成随机单色
let rdmSingClr = () => parseInt(Math.random() * 254);
// 生成随机颜色
let rdmClr = () => 
  `rgb(${rdmSingClr()}, ${rdmSingClr()}, ${rdmSingClr()})`;
// 扩大身体算法
let bigger = () => {
    let e = document.createElement('span');
    e.innerText = names[(size + 1) % 4];
    e.style.background = rdmClr(); // 生成随机身体颜色
    e.style.left = bodylist[size].style.left;
    e.style.top = bodylist[size ++].style.top;
    bodylist.push(e);
    body.append(e);
}
// 身体扩大测试
let testBigger = () => {
    setInterval(()=>{
        bigger();
        // console.log(bodylist)
    }, 1000);
}
// 吃食物身体扩大
let eat = (e) => {
    // 检测是否吃到
    let eatnum = collisionEat(e);
    // 扩大身体
    for(var i = 0; i < eatnum; i++) bigger();
}

// 清空身体
let clearBody = ()=>{
    // 清空头节点的位置
    head.style = '';
    // 在页面上移除身体
    for(var i = 0; i < bodylist.length; i++){
        // console.log(bodylist[i][0]);
        body.remove(bodylist[i]);
    } // 清空身体列表
    head = document.createElement('span');
    head.id = 'head';
    head.innerHTML = names[0];
    head.style = 'left:10px;top:10px;';
    body = document.createElement('div');
    body.id = 'body';
    document.body.appendChild(body);
    body.appendChild(head);
    bodylist = [head];
    size = 0;
}

/*** 
 * todo 1. 4 方向移动算法
 */
// 方向标记位
let flg = 4;
// 事件 ---- 读取方向按键
//// 
document.onkeydown = 
function ev1(evt){
    var e = evt || event;
    var keyCode = e.keyCode || e.which;
    switch(keyCode){
        case 37 : flg = 0; break;
        case 38 : flg = 1; break;
        case 39 : flg = 2; break;
        case 40 : flg = 3; break;
    }
}
// 可控制方向的移动算法
let ctlMove = (e)=>{
    let posx = 1;
    let posy = 1;
    let stopMove = setInterval(() => {
        // 随机生成食物
        geneFood();
        // 检测当前吃到多少食物
        eat(head);
        // 检测当前是否撞墙:
        if(collisionDeath(head)) {
            clearInterval(stopMove);
            endGame();
            return;
        }
        // 移动 头部
        if(flg == 0){
            e.style.left = posx -= 4 + (size >> 1);
            //console.log(flg,posx)
        } else if(flg == 1){
            e.style.top = posy -= 4 + (size >> 1);
            //console.log(flg)
        } else if(flg == 2){
            e.style.left = posx += 4 + (size >> 1);
            //console.log(flg,posx)
        } else if(flg == 3){
            // debugger
            e.style.top = posy += 4 + (size >> 1);
        }
        // 平滑移动 身体
        smoMoveBody();
    }, 25);
}

/*** 
 * todo 2. 可控制角度的移动算法
 */
// 求 当前角度 对应的 x / y 方向上的偏移比例
let angle = 0;
let sin = 0;
let cos = 0;
let updateAngle = () => {
    sin = Math.sin(angle);
    cos = Math.cos(angle);
}
updateAngle();
// 可控制角度的移动算法
let ctlAngMove = (e) => {
    let x = 1;
    let y = 1;
    setInterval(() => {
        e.style.left = x += cos * 4;
        e.style.top = y -= sin * 4;
        smoMoveBody();
    }, 25);
}
// 读取方向按键 改变角度
//// document.onkeydown = 
function ev2(evt){
    var e = evt || event;
    var keyCode = e.keyCode || e.which;
    switch(keyCode){
        case 37 : angle += Math.PI / 12; break;
        case 39 : angle -= Math.PI / 12;  break;
    } updateAngle();
}

// 平滑移动算法
let smoMoveBody = () => {
    var prex = parseFloat(bodylist[0].style.left);
    var prey = parseFloat(bodylist[0].style.top);
    for(var i = 1; i <= size; i++){
        var l = bodylist[i].style.left;
        var t = bodylist[i].style.top;
        var curx = parseFloat(l === "" ? 0 : l);
        var cury = parseFloat(t === "" ? 0 : t);
        bodylist[i].style.left = prex = (prex - curx) / 6 + curx;
        bodylist[i].style.top = prey = (prey - cury) / 6 + cury;
        //var len = Math.sqrt(x * x + y * y);
        //bodylist[i].style.left = x / len * 5 + curx;
        //bodylist[i].style.top = y / len * 5 + cury;
    }
}

// 检测是否碰到所有食物
let collisionEat = (e) => {
    var arr = []
    for(var i = 0; i < foodlist.length; i++){
        if(collisionFood(foodlist[i], e, 2000)) arr.push(i);
    }
    for(var i = 0; i < arr.length; i++){
        console.log(arr[i]);
        if(arr[i] === undefined || null || NaN) continue;
        var remvdFood = foodlist.splice(arr[i], 1);
        console.log(remvdFood[0]);
        foods.removeChild(remvdFood[0]);
    }
    return arr.length;
}
// 检测是否碰到一个食物
let collisionFood = (src, tar, dist/* 两点间距的平方 */) => {
    var dx = parseFloat(src.style.left) - parseFloat(tar.style.left);
    var dy = parseFloat(src.style.top) - parseFloat(tar.style.top);
    if(dx * dx + dy * dy < dist) return true;
    return false;
}
// 检测是否撞到边界,死亡
let collisionDeath = (src) => {
    if(src.style.left === "" || src.style.top === "") return;
    var dx = parseFloat(src.style.left);
    var dy = parseFloat(src.style.top);
    if(dx < 0 || dx > width || dy < 0 || dy > height) {
        console.log(dx + " " + dy);
        return true;
    }
    return false;;
}
// 增加边界墙
// tip: 目前这个方法不行
let addWall = () => {
    let wall = document.createElement('div');
    wall.style = `color: black; width: 3rem; height: 95%; position: abstract;left: ${width - 15}`;
    document.body.appendChild(wall);
    console.log(wall)
}
</script>

标签:body,style,js,碰撞检测,算法,let,var,document,left
From: https://www.cnblogs.com/luoyicode/p/17736564.html

相关文章

  • 基础算法:离散化实现
    1、离散化值域大而数值稀疏的题目,通常先将需要操作的数映射到一个数组中,再做后续操作,可以大大减少时间复杂度。以AcWing.802为例,是一个典型的前缀和问题,但问题在于,若仅仅使用前缀和算法,时间复杂度会很高,因此需要先做离散化映射。题目要求如下:假定有一个无限长的数轴,数轴上每个......
  • [算法分析与设计] 1. 全源最短路近似
    全源最短路(APSP)近似。有两种近似stretch\(k\).\(\delta(u,v)\leqd(u,v)\leqk\cdot\delta(u,v)\).surplus\(t\).\(\delta(u,v)\leqd(u,v)\leq\delta(u,v)+t\).其中,\(\delta(u,v)\)表示\(u,v\)间真实的最短路长度。先来考虑无权图上的surplus......
  • 机床控制更换刀具小算法
    很简单的一个小算法,大家看图分析分析下就懂了,感觉已经写的很清楚了,就不多说了。 主要的是上面的顺逆换刀判断,下面是我写的应用程序,可以参考下(可能描述的不太清楚,勉强看看吧,哈哈!!也是很简单的) 三菱SFC逆时针换刀程序:三菱SFC顺时针换刀程序:  本文章为原创作品,各路大佬......
  • 文心一言 VS 讯飞星火 VS chatgpt (93)-- 算法导论9.2 1题
    一、用go语言,证明:在RANDOMIZED-SELECT中,对长度为0的数组,不会进行递归调用。文心一言:在Go语言中,我们可以通过实现一个函数来检查数组的长度,如果长度为0,则不进行递归调用。这里是一个简单的示例:packagemainimport("fmt")funcrandomizedSelect(arr[]int,k......
  • 文心一言 VS 讯飞星火 VS chatgpt (92)-- 算法导论8.3 5题
    五、用go语言,在本节给出的第一个卡片排序算法中,为排序d位十进制数,在最坏情况下需要多少轮排序?在最坏情况下,操作员需要记录多少堆卡片?文心一言:你提到的第一个卡片排序算法可能是指的基数排序(RadixSort)。基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数......
  • 文心一言 VS 讯飞星火 VS chatgpt (89)-- 算法导论8.3 2题
    二、用go语言,下面的排序算法中哪些是稳定的:插入排序、归并排序、堆排序和快速排序?给出一个能使任何排序算法都稳定的方法。你所给出的方法带来的额外时间和空间开销是多少?文心一言:在Go语言中,以下排序算法是稳定的:插入排序归并排序而以下的排序算法是不稳定的:堆排序快速排序要使任......
  • 什么是AI客流量算法?如何应用在实际场景中?
    客流量分析算法简而言之就是一种利用数据分析和机器学习技术进行人流量统计、预测和分析的算法。它能够根据不同的数据来源,如摄像头、传感器等,对特定区域内的客流量进行实时监测和分析,并通过对历史数据的综合分析,提供客流趋势预测和优化策略。TSINGSEE青犀视频智能分析网关的客流量......
  • 基于视频技术与AI检测算法的体育场馆远程视频智能化监控方案
    一、方案背景近年来,随着居民体育运动意识的增强,体育场馆成为居民体育锻炼的重要场所。但使用场馆内的器材时,可能发生受伤意外,甚至牵扯责任赔偿纠纷问题。同时,物品丢失、人力巡逻成本问题突出,体育场馆在给居民提供运动场地的同时,还需特别关注场馆内人员的人身和财产安全以及运动器械......
  • 必须收藏!双目立体匹配算法:Patch Match Stereo实用详解教程
    计算机视觉life聚焦计算机视觉、机器人SLAM、自动驾驶、AR领域核心技术。公众号本文对立体匹配算法:PatchMatchStereo实用进行了教程详解。作者丨3D视觉开发者社区01简介我们知道,现有立体匹配算法一般被分类为局部算法、全局算法和半全局算法,其中局部算法和半全局算法是应用最为广......
  • 隐马尔可夫模型之Baum-Welch算法详解
    隐马尔可夫模型之Baum-Welch算法详解前言在上篇博文中,我们学习了隐马尔可夫模型的概率计算问题和预测问题,但正当要准备理解学习问题时,发现学习问题中需要EM算法的相关知识,因此,上一周转而学习了EM算法和极大似然估计,对隐藏变量的求解有了一些自己的理解,现在我们继续回过头来学习隐马......