vue3,贪吃蛇

<template>
  <div flex items-center justify-center h-100vh w-full relative>
    <div class="">
      <div class="header">
        <div>得分: {{ score }}</div>
        <div class="flex">
          <div w-100>速度: {{ speed }}</div>
          <el-slider
            v-model="speed"
            :min="2"
            :max="9"
            :step="0.1"
            @change="changeSpeed"
          />
        </div>
      </div>
      <div class="flex" v-for="(i, idx) in array" :key="idx">
        <div
          class="block flex items-center justify-center"
          v-for="(j, idx2) in i"
          :key="idx"
        >
          <div
            v-if="j"
            class="w-13 h-13"
            :class="{ food: j == 2, snake: j == 1 }"
            :style="{
              backgroundColor:
                j == 2
                  ? foodColor
                  : idx === head[0] && idx2 === head[1]
                  ? snakeHeadColor
                  : snakeColor,
              border: j == 2 ? '' : '1px solid #bdbdbd',
              borderRadius:
                j == 2
                  ? '12px'
                  : idx === head[0] && idx2 === head[1]
                  ? headRadius()
                  : '4px',
            }"
          ></div>
        </div>
      </div>
    </div>
    <div
      class="absolute left-0 bottom-0 right-0 top-0 flex items-center justify-center pause-bg"
      v-if="isPause"
    >
      <div text-center>
        <div text-36 mb-20 class="text-#fff">游戏暂停</div>
        <div text-14 class="text-#eee">按空格键开始/暂停</div>
      </div>
    </div>
    <div
      class="absolute left-0 bottom-0 right-0 top-0 flex items-center justify-center"
      v-if="isOver"
    >
      <el-button type="success" size="large" @click="refresh">重新开始</el-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import {
  ref,
  reactive,
  onMounted,
  toRefs,
  watch,
  computed,
  onUnmounted,
} from "vue";

/**
 * @param { value } 0-空格子 1-蛇蛇 2-食物
 * @param { ROW } 行数
 * @param { COL } 列数
 * @param { direction } 蛇蛇移动方向 0-上 1-右 2-下 3-左
 */
let ROW = 22;
let COL = 18;
let direction = 1;
let directionTemp = direction;
let food = ref([0, 0]);
let isEat = false;
let timer: any = null;
const isOver = ref(false);
const isPause = ref(false);
let score = 0;
const speed = ref(3.5);
const array = ref<any>([]);
const snake = ref<any>([]);

// #67C23A #409EFF #E6A23C #F56C6C #909399 #ffa4e4
const snakeColor = ref("#67C23A");
const snakeHeadColor = ref("#3C9212");
const foodColor = ref("#F56C6C");
console.log("array", array);

watch(
  () => snake.value,
  (v) => {
    // console.log("v", v);

    v.forEach((item) => {
      array.value[item[0]][item[1]] = 1;
    });
  },
  { deep: true }
);
watch(
  () => food.value,
  (v) => {
    // console.log('v', v);

    array.value[v[0]][v[1]] = 2;
  }
);

const init = () => {
  timer = setInterval(() => {
    if (isOver.value) {
      clearInterval(timer);
      return;
    }
    if (isPause.value) {
      return;
    }
    let next = [head.value[0], head.value[1]];
    direction = directionTemp;
    switch (direction) {
      case 0:
        next[0] -= 1;
        break;
      case 1:
        next[1] += 1;
        break;
      case 2:
        next[0] += 1;
        break;
      case 3:
        next[1] -= 1;
        break;
    }
    if (next[0] < 0 || next[0] >= ROW || next[1] < 0 || next[1] >= COL) {
      // 撞墙
      isOver.value = true;
      alert("game over");
      return;
    }
    if (array.value[next[0]][next[1]] == 1) {
      // 撞身体
      isOver.value = true;
      alert("game over");
      return;
    }
    if (array.value[next[0]][next[1]] == 2) {
      isEat = true;
      score++;
      if (score == 5) {
        speed.value += 0.4;
      } else if (score == 10) {
        speed.value += 0.4;
      } else if (score == 15) {
        speed.value += 0.4;
      } else if (score == 20) {
        speed.value += 0.5;
      } else if (score == 25) {
        speed.value += 0.5;
      } else if (score == 30) {
        speed.value += 0.5;
      } else if (score == 35) {
        speed.value += 0.6;
      } else if (score == 40) {
        speed.value += 0.6;
      }
      // 最多保留一位小数
      speed.value = Math.round(speed.value * 10) / 10;
    }
    /**
     * 移动 && 检测是否进食
     */
    snake.value.unshift(next);
    if (!isEat) {
      array.value[snake.value[snake.value.length - 1][0]][
        snake.value[snake.value.length - 1][1]
      ] = 0;
      snake.value.pop();
    } else {
      isEat = false;
      newFoodPosition();
    }
    array.value[next[0]][next[1]] = 1;
  }, 1000 / speed.value);
};
const newFoodPosition = () => {
  // 生成新的食物点,并且不能在蛇的身体
  let x = Math.floor(Math.random() * ROW);
  let y = Math.floor(Math.random() * COL);
  while (snake.value.some((item) => item[0] == x && item[1] == y)) {
    x = Math.floor(Math.random() * ROW);
    y = Math.floor(Math.random() * COL);
  }
  food.value = [x, y];
};
const head = computed(() => {
  return snake.value[0];
});

const keydown = (e: { keyCode: number; preventDefault: any }) => {
  e.preventDefault();

  if (e.keyCode == 37) {
    //左
    if (direction == 1) return;
    directionTemp = 3;
  } else if (e.keyCode == 38) {
    //上
    if (direction == 2) return;
    directionTemp = 0;
  } else if (e.keyCode == 39) {
    //右
    if (direction == 3) return;
    directionTemp = 1;
  } else if (e.keyCode == 40) {
    //下
    if (direction == 0) return;
    directionTemp = 2;
  } else if (e.keyCode == 32) {
    // 空格键暂停
    isPause.value = !isPause.value;
  }
};

const changeSpeed = (v) => {};
const headRadius = () => {
  switch (direction) {
    case 0:
      return "12px 12px 0px 0px";
    case 1:
      return "0px 12px 12px 0px";
    case 2:
      return "0px 0px 12px 12px";
    case 3:
      return "12px 0px 0px 12px";
  }
};
watch(
  () => speed.value,
  (v) => {
    clearInterval(timer);
    init();
  }
);
const start = () => {
  clearInterval(timer);
  array.value = Array.from({ length: ROW }, () =>
    Array.from({ length: COL }, () => 0)
  );
  snake.value = [
    [2, 7],
    [2, 6],
    [2, 5],
    [2, 4],
    [2, 3],
  ];
  newFoodPosition();
  init();
};
const refresh = () =>{
  window.location.reload();
}
onMounted(() => {
  // console.log("game start");
  start();
  document.addEventListener("keydown", keydown);
});
onUnmounted(() => {
  clearInterval(timer);
  document.removeEventListener("keydown", keydown);
});
</script>

<style lang="scss" scoped>
$width: 14px;
.block {
  width: $width;
  height: $width;
  background-color: #e4e4e4;
  margin: 1px;
}
.food {
  clip-path: polygon(10% 10%, 90% 10%, 90% 90%, 10% 90%);
  animation: food alternate 1.5s ease-in-out infinite;
}
@keyframes food {
  0% {
    transform: scale(1);
  }
  25% {
    transform: scale(0.92);
    filter: brightness(0.9);
  }
  50% {
    transform: scale(1.1);
  }
  75% {
    transform: scale(1.24);
    filter: brightness(1.15);
  }
  100% {
    transform: scale(1.16);
  }
}
.snake {
  background-image: linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.16) 25%,
    transparent 25%,
    transparent 50%,
    rgba(255, 255, 255, 0.16) 50%,
    rgba(255, 255, 255, 0.16) 75%,
    transparent 75%,
    transparent
  );
}

.game {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  background-color: #f0f0f0;
  .score {
    font-size: 30px;
    margin-bottom: 20px;
  }
  .speed {
    font-size: 30px;
    margin-bottom: 20px;
  }
}
.pause-bg {
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 2;
}
</style>

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值