创建菜单与游戏页面

news/2024/7/9 23:48:04 标签: springboot, vue, vue.js

bootstrap地址

Bootstrap v5 中文文档 · Bootstrap 是全球最受欢迎的 HTML、CSS 和 JS 前端工具库。 | Bootstrap 中文网 (bootcss.com)

 

创建导航栏组件

web--src--components--NavBar.vue

<!-- html -->
<template>
    <nav class="navbar navbar-expand-lg  navbar-dark bg-dark">
        <div class="container">
            <!-- 刷新 -->
            <!-- <a class="navbar-brand" href="/">King Of Bots</a> -->
            <!-- 点击页面不刷新用router-link -->
            <router-link class="navbar-brand" :to="{ name: 'pk_index' }">King Of Bots</router-link>
            <div class="collapse navbar-collapse" id="navbarText">
                <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <!-- active高亮 -->
                        <!-- <router-link class="nav-link active " :to="{ name: 'pk_index' }">对战</router-link> -->
                        <!-- 选中的高亮 -->
                        <router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'"
                            :to="{ name: 'pk_index' }">对战</router-link>
                    </li>
                    <li class="nav-item">
                        <router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link'"
                            :to="{ name: 'record_index' }">对局列表</router-link>
                    </li>
                    <li class="nav-item">
                        <router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link'"
                            :to="{ name: 'ranklist_index' }">排行榜</router-link>
                    </li>
                </ul>
                <ul class="navbar-nav ">
                    <li class="nav-item dropdown">
                        <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
                            aria-expanded="false">
                            USERNAME
                        </a>
                        <ul class="dropdown-menu">
                            <li>
                                <router-link class="dropdown-item" :to="{ name: 'user_bot_index' }">my bot</router-link>
                            </li>
                            <li><a class="dropdown-item" href="#">exit</a></li>
                        </ul>
                    </li>
                </ul>
            </div>
        </div>
    </nav>
</template>

<!-- js -->
<script >
// 实现选中的页面高亮
import { useRoute } from 'vue-router';
import { computed } from 'vue';
export default {
    setup() {
        const route = useRoute();
        let route_name = computed(() => route.name)
        return {
            route_name
        }
    }
}
</script>

<!-- css -->
<!-- scoped 作用:写的css会加上一个随机字符串,使得样式不会影响组件以外的部分 -->
<style scoped></style>

App.vue

<template>
  <NavBar />
  <router-view></router-view>
</template>

<script>
import NavBar from './components/NavBar.vue'
import "bootstrap/dist/css/bootstrap.min.css"
import "bootstrap/dist/js/bootstrap"
export default {
  components: {
    NavBar
  }
}
</script>

<style>
body {
  background-image: url("@/assets/background.png");
  background-size: 100% 900%;
}
</style>

创建各页面组件

<template>
    <div>404</div>
</template>

<script>
</script>

<style scoped></style>
<template>
    <ContentField>
        对战
    </ContentField>
</template>

<script>
import ContentField from "../../components/ContentField.vue"
export default {
    components: {
        ContentField
    }
}
</script>

<style scoped></style>
<template>
    <ContentField>
        RankList排行榜
    </ContentField>
</template>

<script>
import ContentField from "../../components/ContentField.vue"
export default {
    components: {
        ContentField
    }
}
</script>

<style scoped></style>
<template>
    <ContentField>
        对局列表
    </ContentField>
</template>

<script>
import ContentField from "../../components/ContentField.vue"
export default {
    components: {
        ContentField
    }
}
</script>

<style scoped></style>
<template>
    <ContentField>
        我的Bot
    </ContentField>
</template>

<script>
import ContentField from "../../../components/ContentField.vue"
export default {
    components: {
        ContentField
    }
}
</script>

<style scoped></style>

 router---组件

import { createRouter, createWebHistory } from 'vue-router'
import NotFound from "../views/error/NotFound"
import PkIndexView from "../views/pk/PkIndexView"
import RanklistIndexView from "../views/ranklist/RanklistIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import UserBotIndexView from "../views/user/bot/UserBotIndexView"
const routes = [
  {
    path: "/pk/",
    name: "pk_index",
    component: PkIndexView
  },
  {
    path: "/error/",
    name: "404",
    component: NotFound
  },
  {
    path: "/record/",
    name: "record_index",
    component: RecordIndexView
  },
  {
    path: "/ranklist/",
    name: "ranklist_index",
    component: RanklistIndexView
  },
  {
    path: "/user/bot/",
    name: "user_bot_index",
    component: UserBotIndexView
  },
  // 重定向到404
  {
    path: "/:catchAll(.*)",
    redirect: "/error/"
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

 白板--组件

各页面白板构成一个组件components->ContentField.vue

<template>
    <div class="container content-field">
        <div class="card">
            <div class="card-body">
                <!-- 渲染的东西放到  slot 中 -->
                <slot></slot>
            </div>
        </div>
    </div>
</template>

<script>
</script>

<style scoped>
/* 20px的上边距 */
div.content-field {
    margin-top: 20px;
}
</style>

 游戏对象类

scrips---->ACGameObject.js

const AC_GAME_OBJECTS = []; //存储所有运动对象
export class AcGameObject {
    constructor() {
        AC_GAME_OBJECTS.push(this);
        // 走的距离=速度*时间间隔
        //当前帧执行的时刻距离上一帧执行时刻的间隔,浏览器每一帧时间间隔可能有误差不一样因此需要记录
        this.timedelta = 0;
        // 是否执行过start函数
        this.has_called_start = false;
    }
    start() {    //创建时,只执行一次

    }
    update() {  // 除了第一帧外每一帧执行一次

    }
    // 删除之前用到的回调函数
    on_destroyed() {

    }
    destroyed() {
        this.on_destroyed();
        for (let i in AC_GAME_OBJECTS) {
            const obj = AC_GAME_OBJECTS[i];
            if (obj == this) {
                AC_GAME_OBJECTS.splice(i);      //从数组删除
                break;
            }
        }
    }
}
let last_timestamp; //上一次执行的时刻
// requestAnimationFrame(函数) 函数在浏览器下一次渲染之前执行一遍
// 迭代执行step
// in是下标  of是值
const step = timestamp => {             // 每次调用会传入当前时刻
    for (let obj of AC_GAME_OBJECTS) {
        // 如果没执行start()
        if (!obj.has_called_start) {
            obj.has_called_start = true;
            obj.start();
        } else {
            obj.timedelta = obj.timestamp - last_timestamp;
            obj.update();
        }
    }
    last_timestamp = timestamp;
    // 下一帧执行step
    requestAnimationFrame(step)
}
requestAnimationFrame(step)

 地图

动起来:60帧/s

地图对象类


// export  import {}     export default import 

// 游戏地图的对象
import { AcGameObject } from "./AcGameObject";
import { Wall } from "./Wall";
export class GameMap extends AcGameObject {
    // 构造函数的两个参数,  画布,画布的父元素用于动态修改画布的长宽
    constructor(ctx, parent) {  // ctx表示画布,parent表示画布的父元素
        // 先执行AcGameObject的构造函数
        super();
        this.ctx = ctx;
        this.parent = parent;
        this.L = 0;  // 一个单位的绝对长度
        this.rows = 13;  // 地图的行数
        this.cols = 13;  // 地图的列数

        this.inner_walls_count = 6;
        this.wall = [];
    }
    check_connectivity(sx, sy, tx, ty, g) {
        if (sx == tx && sy == ty) return true;
        g[sx][sy] = true;
        let dx = [-1, 0, 1, 0], dy = [0, 1, 0, -1];
        for (let i = 0; i < 4; i++) {
            let x = sx + dx[i], y = sy + dy[i];
            // 没有撞墙,可以走到终点
            if (!g[x][y] && this.check_connectivity(x, y, tx, ty, g)) {
                return true;
            }
        }
        return false;
    }
    create_walls() {
        //new Wall(0, 0, this);

        // 墙true,无墙false
        const g = [];
        for (let r = 0; r < this.cols; r++) {
            g[r] = [];
            for (let c = 0; c < this.cols; c++) {
                g[r][c] = false;
            }
        }
        // 四周加上墙
        for (let r = 0; r < this.rows; r++) {
            g[r][0] = g[r][this.cols - 1] = true;
        }
        for (let c = 0; c < this.cols; c++) {
            g[0][c] = g[this.rows - 1][c] = true;
        }
        // 创建随机障碍物,数量为inner_walls_count
        for (let i = 0; i < this.inner_walls_count; i++) {
            for (let j = 0; j < 1000; j++) {
                let r = parseInt(Math.random() * this.rows);
                let c = parseInt(Math.random() * this.cols);
                if (g[r][c] || g[c][r]) continue;
                if ((r == this.rows - 2 && c == 1) || (r == 1 && c == this.cols - 2)) continue;
                g[r][c] = g[c][r] = true;
                break;
            }
        }
        // 转换成JSON再转换回来
        const copy_g = JSON.parse(JSON.stringify(g));
        if (!this.check_connectivity(this.rows - 2, 1, 1, this.cols - 2, copy_g)) {
            return false;
        }
        for (let r = 0; r < this.rows; r++) {
            for (let c = 0; c < this.cols; c++) {
                if (g[r][c]) {
                    this.wall.push(new Wall(r, c, this));
                }
            }
        }
        return true;
    }
    start() {
        for (let i = 0; i < 1000; i++) {
            if (this.create_walls())
                break;
        }
    }
    update_size() {  //每一帧都更新边长
        // 动态求最小正方形边长
        // 取整数
        this.L = Math.min(this.parent.clientWidth / this.cols, this.parent.clientHeight / this.rows);
        this.ctx.canvas.width = this.L * this.cols;
        this.ctx.canvas.height = this.L * this.rows;
    }
    update() {     //每帧渲染一次
        this.update_size();
        this.render();
    }

    render() {      //渲染  
        // 画地图
        const color_even = "#AAD751", color_odd = "#A2D149";
        for (let r = 0; r < this.rows; r++) {
            for (let c = 0; c < this.cols; c++) {
                if ((r + c) % 2 == 0) {
                    this.ctx.fillStyle = color_even;
                } else {
                    this.ctx.fillStyle = color_odd;
                }
                // 左上角左边,明确canvas坐标系
                this.ctx.fillRect(c * this.L, r * this.L, this.L, this.L);
            }

        }
    }
}

游戏区域

components--->PlayGround.vue

<!-- 游戏区域 -->
<template>
    <div class="playground">
        <GameMap />
    </div>
</template>
 
<script>
import GameMap from "./GameMap.vue";

export default {
    components: {
        GameMap,
    }
}
</script>

<style scoped>
div.playground {
    width: 60vw;
    height: 70vh;
    margin: 40px auto;
    background-color: rgb(247, 248, 248);
}
</style>

地图组件 

GameMap.vue

<template>
    <!-- ref映射关联 -->
    <div ref="parent" class="gamemap">
        <!-- 画布 -->
        <canvas ref="canvas"></canvas>
    </div>
</template>
  
<script>
import { ref, onMounted } from "vue";
import { GameMap } from "@/assets/scripts/GameMap";

export default {
    setup() {
        let parent = ref(null);
        let canvas = ref(null);
        //组件挂载完之后,创建GameMap对象
        onMounted(() => {
            new GameMap(canvas.value.getContext("2d"), parent.value);
        });

        return {
            parent,
            canvas,
        };
    },
};
</script>
  
<style scoped>
div.gamemap {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}
</style>
  

 实体墙

scrips----->wall.js

// 实体墙
import { AcGameObject } from "./AcGameObject";
export class Wall extends AcGameObject {
    constructor(r, c, gamemap) {
        super();
        this.r = r;
        this.c = c;
        this.gamemap = gamemap;
        this.color = "#B37226";
    }
    update() {

        this.render();
    }
    render() {
        const L = this.gamemap.L;
        const ctx = this.gamemap.ctx;
        ctx.fillStyle = this.color;
        ctx.fillRect(this.c * L, this.r * L, L, L);
    }
}

 参考

项目实战——创建菜单与游戏页面(上)_游戏页面是如何实现的-CSDN博客​​​​​​​

 


http://www.niftyadmin.cn/n/5376266.html

相关文章

【Chrono Engine学习总结】4-vehicle-4.1-vehicle的基本概念

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 1、基本介绍 Vehicle Overview Vehicle Mannel Vehicle的官方demo 1.1 Vehicle的构型 一个车辆由许多子系统构成&#xff1a;悬挂、转向、轮子/履带、刹车/油门、动…

[ubuntu]split命令分割文件

split 命令 $ split --help Usage: split [OPTION]... [INPUT [PREFIX]] Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default size is 1000 lines, and default PREFIX is x. With no INPUT, or when INPUT is -, read standard input.Mandatory argume…

随机过程及应用学习笔记(二)随机过程的基本概念

随机过程论就是研究随时间变化的动态系统中随机现象的统计规律的一门数学学科。 目录 前言 一、随机过程的定义及分类 1、定义 2、分类 二、随机过程的分布及其数字特征 1、分布函数 2、数字特征 均值函数和方差函数 协方差函数和相关函数 3、互协方差函数与互相关函…

18 19 SPI接口的74HC595驱动数码管实验

1. 串行移位寄存器原理&#xff08;以四个移位寄存器为例&#xff09; 1. 通过移位寄存器实现串转并&#xff1a;一个数据输入端口可得到四位并行数据。 通过给data输送0101数据&#xff0c;那么在经过四个时钟周期后&#xff0c;与data相连的四个寄存器的输出端口得到了0101…

017_逆向工程搭建和使用

文章目录 启动代码生成器然后访问第一步处理:前端代码删除逆向生成的代码中有好多东西要引入创建gulimall-common插曲:修改模块名dao层entity层service层controllerRQuery文件当中的报错☆ 调整renren-generator的逆向工程逆向生成代码当中有什么总结

寒假作业:2024/2/13

https://kdocs.cn/l/cl2SWN71CuKK 21.C 22.D 23.B 5先出栈表示1&#xff0c;2&#xff0c;3&#xff0c;4已经入栈了&#xff0c;5出后4出&#xff0c;但之后想出1得先让3&#xff0c;2先后出栈&#xff0c;所以 B 不可能 24.10&#xff0c;12&#xff0c;120 25.2…

微服务—ES数据同步

目录 数据同步 问题分析 方案1. 同步调用 方案2. 异步通知 方案3. 监听binlog​编辑 各方案对比 案例——利用MQ实现数据同步 步骤1. 导入hotel-admin项目 步骤2. 声明交换机、队列 步骤3. 发送MQ消息 步骤4. 接收MQ消息 步骤5. 测试同步功能 数据同步 elasticsea…

01.数据结构篇-链表

1.找出两个链表的交点 160. Intersection of Two Linked Lists (Easy) Leetcode / 力扣 例如以下示例中 A 和 B 两个链表相交于 c1&#xff1a; A: a1 → a2↘c1 → c2 → c3↗ B: b1 → b2 → b3 但是不会出现以下相交的情况&#xff0c;因为每个节点只有一个…