返回顶部

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

相信大家都用 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>
posted @ 2023-09-28 22:02  你好,一多  阅读(32)  评论(0编辑  收藏  举报