当前位置: 首页 > news >正文

关键词优化的策略有哪些关键词优化收费标准

关键词优化的策略有哪些,关键词优化收费标准,wordpress自适应主体,哪个网站用帝国cms做的笔记内容转载自 AcWing 的 SpringBoot 框架课讲义,课程链接:AcWing SpringBoot 框架课。 CONTENTS 1. 地图优化改进2. 绘制玩家的起始位置3. 实现玩家移动4. 优化蛇的身体效果5. 碰撞检测实现 本节实现两名玩家即两条蛇的绘制与人工操作移动功能。 1. 地…

笔记内容转载自 AcWing 的 SpringBoot 框架课讲义,课程链接:AcWing SpringBoot 框架课。

CONTENTS

  • 1. 地图优化改进
  • 2. 绘制玩家的起始位置
  • 3. 实现玩家移动
  • 4. 优化蛇的身体效果
  • 5. 碰撞检测实现

本节实现两名玩家即两条蛇的绘制与人工操作移动功能。

1. 地图优化改进

之前我们设计的地图尺寸是13×13,两名玩家的起点横纵坐标之和均为偶数,因此可能在同一时刻走到同一个格子上,为了避免这种情况,可以将地图改为13×14的大小(即将 GameMap.js 中的 this.cols 改为14),这样两名玩家就不会在同一时刻走到同一个格子上。这样修改完之后就不能用轴对称了,需要改为中心对称:

// 添加地图内部的随机障碍物,需要有对称性因此枚举一半即可,另一半对称生成
for (let i = 0; i < this.inner_walls_count / 2; i++) {for (let j = 0; j < 10000; j++) {let r = parseInt(Math.random() * this.rows);let c = parseInt(Math.random() * this.cols);if (g[r][c] || g[this.rows - 1 - r][this.cols - 1 - c]) continue;if (r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2) continue;  // 判断是否覆盖到出生地g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = true;break;}
}

2. 绘制玩家的起始位置

刚开始玩家占一个格子,我们可以规定一下前十步的每一步将蛇的长度加一,之后改为每三步长度加一。每条蛇其实就是一堆格子的序列,我们可以将一个格子定义成一个 Cell 对象,在 scripts 目录下创建 Cell.js 记录格子的坐标。

我们在每个格子中绘制的是一个圆,若格子的左上角坐标为 (x, y) 则圆的圆心坐标应该是 (x + 0.5, y + 0.5)Cell.js 如下:

export class Cell {constructor(r, c) {this.r = r;this.c = c;this.x = c + 0.5;this.y = r + 0.5;}
}

此外每条蛇也可以定义成一个对象 Snake.js

import { AcGameObject } from "./AcGameObject";
import { Cell } from "./Cell";export class Snake extends AcGameObject {constructor(info, gamemap) {super();this.id = info.id; // 每条蛇有唯一的id进行区分this.color = info.color; // 颜色this.gamemap = gamemap;this.cells = [new Cell(info.r, info.c)];  // 初始化时只有一个点即cells[0]表示蛇头}start() {}update() {this.render();}render() {const L = this.gamemap.L;const ctx = this.gamemap.ctx;ctx.fillStyle = this.color;for (const cell of this.cells) {ctx.beginPath();ctx.arc(cell.x * L, cell.y * L, L / 2, 0, Math.PI * 2);  // 半径为半个单元格ctx.fill();}}
}

然后我们在 GameMap.js 中创建两条蛇:

import { AcGameObject } from "./AcGameObject";
import { Wall } from "./Wall";
import { Snake } from "./Snake";export class GameMap extends AcGameObject {constructor(ctx, parent) {  // ctx表示画布,parent表示画布的父元素super();this.ctx = ctx;this.parent = parent;this.L = 0;  // 一个单位的绝对长度this.rows = 13;  // 地图的行数this.cols = 14;  // 地图的列数this.inner_walls_count = 20;  // 地图内部的随机障碍物数量,需要是偶数this.walls = [];  // 所有的障碍物this.snakes = [new Snake({ id: 0, color: "#4876EC", r: this.rows - 2, c: 1 }, this),new Snake({ id: 1, color: "#F94848", r: 1, c: this.cols - 2 }, this),];}check_connectivity(g, sx, sy, tx, ty) {  // 用flood fill算法判断两名玩家是否连通...}create_walls() {...}start() {...}update_size() {  // 每一帧更新地图大小...}update() {...}render() {...}
}

3. 实现玩家移动

为了实现蛇移动的连续性,我们不对每个格子进行更新,只更新头部和尾部,头部创建一个新的点往前动,尾部直接往前动。首先在 Snake 对象中设置一些移动的属性:

import { AcGameObject } from "./AcGameObject";
import { Cell } from "./Cell";export class Snake extends AcGameObject {constructor(info, gamemap) {super();this.id = info.id; // 每条蛇有唯一的id进行区分this.color = info.color; // 颜色this.gamemap = gamemap;this.cells = [new Cell(info.r, info.c)];  // 初始化时只有一个点即cells[0]表示蛇头this.speed = 2;  // 蛇每秒走2个格子this.direction = -1;  // 下一步移动的指令,-1表示没有指令,0、1、2、3分别表示上、右、下、左this.status = "idle";  // 蛇的状态,idle表示静止,move表示正在移动,die表示死亡this.next_cell = null;  // 下一步的目标位置this.step = 0;  // 回合数this.dr = [-1, 0, 1, 0];this.dc = [0, 1, 0, -1];}start() {}next_step() {  // 将蛇的状态变为走下一步const d = this.direction;this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);this.direction = -1;  // 复原this.status = "move";this.step++;}set_direction(d) {  // 由于未来不一定只会从键盘获取输入,因此实现一个接口修改directionthis.direction = d;}update_move() {}update() {if (this.status === "move") {this.update_move();}this.render();}render() {...}
}

由于游戏是回合制的,因此移动的判定条件应该是获取到了两名玩家的指示后才能移动一次,且该指令既可以由键盘输入也可以由 AI 代码输入,判定两条蛇是否准备好执行下一步不能各自判断,需要由上层也就是 GameMap 判定,判定条件是两条蛇都处于静止状态且都已经获取到了下一步指令:

import { AcGameObject } from "./AcGameObject";
import { Wall } from "./Wall";
import { Snake } from "./Snake";export class GameMap extends AcGameObject {constructor(ctx, parent) {  // ctx表示画布,parent表示画布的父元素...}check_connectivity(g, sx, sy, tx, ty) {  // 用flood fill算法判断两名玩家是否连通...}create_walls() {...}start() {...}update_size() {  // 每一帧更新地图大小...}check_ready() {  // 判断两条蛇是否都准备好下一回合了for (const snake of this.snakes) {if (snake.status !== 'idle' || snake.direction === -1) return false;}return true;}next_step() {  // 让两条蛇进入下一回合for (const snake of this.snakes) {snake.next_step();}}update() {this.update_size();if (this.check_ready()) {this.next_step();}this.render();}render() {...}
}

现在我们只能从前端获得用户的操作,即获取用户的键盘输入。为了能够让 Canvas 获取键盘输入,需要添加一个 tabindex 属性,在 GameMap.vue 中进行修改:

<template><div ref="parent" class="gamemap"><canvas ref="canvas" tabindex="0"></canvas></div>
</template><script>
...
</script><style scoped>
...
</style>

这样我们就能够在 GameMap.js 中绑定键盘的监听事件:

import { AcGameObject } from "./AcGameObject";
import { Wall } from "./Wall";
import { Snake } from "./Snake";export class GameMap extends AcGameObject {...add_listening_events() {this.ctx.canvas.focus();  // 使Canvas聚焦const [snake0, snake1] = this.snakes;this.ctx.canvas.addEventListener("keydown", e => {if (e.key === "w") snake0.set_direction(0);else if (e.key === "d") snake0.set_direction(1);else if (e.key === "s") snake0.set_direction(2);else if (e.key === "a") snake0.set_direction(3);else if (e.key === "ArrowUp") snake1.set_direction(0);else if (e.key === "ArrowRight") snake1.set_direction(1);else if (e.key === "ArrowDown") snake1.set_direction(2);else if (e.key === "ArrowLeft") snake1.set_direction(3);});}start() {for (let i = 0; i < 10000; i++) {  // 暴力枚举直至生成合法的地图if (this.create_walls())break;}this.add_listening_events();}...
}

现在我们即可在 Snake.js 中实现蛇的移动:

import { AcGameObject } from "./AcGameObject";
import { Cell } from "./Cell";export class Snake extends AcGameObject {constructor(info, gamemap) {super();this.id = info.id; // 每条蛇有唯一的id进行区分this.color = info.color; // 颜色this.gamemap = gamemap;this.cells = [new Cell(info.r, info.c)];  // 初始化时只有一个点即cells[0]表示蛇头this.speed = 2;  // 蛇每秒走2个格子this.direction = -1;  // 下一步移动的指令,-1表示没有指令,0、1、2、3分别表示上、右、下、左this.status = "idle";  // 蛇的状态,idle表示静止,move表示正在移动,die表示死亡this.next_cell = null;  // 下一步的目标位置this.step = 0;  // 回合数this.dr = [-1, 0, 1, 0];this.dc = [0, 1, 0, -1];this.eps = 0.01;  // 误差}start() {}next_step() {  // 将蛇的状态变为走下一步const d = this.direction;this.next_cell = new Cell(this.cells[0].r + this.dr[d], this.cells[0].c + this.dc[d]);this.direction = -1;  // 复原this.status = "move";this.step++;const k = this.cells.length;for (let i = k; i > 0; i--) {  // 将所有节点向后移动一位,因为要在头节点前面插入新的头节点this.cells[i] = JSON.parse(JSON.stringify(this.cells[i - 1]));  // 注意要深层复制一份,还有一个细节是JS的数组越界不会出错}}set_direction(d) {  // 由于未来不一定只会从键盘获取输入,因此实现一个接口修改directionthis.direction = d;}update_move() {  // 将头节点cells[0]向目标节点next_cell移动const dx = this.next_cell.x - this.cells[0].x;  // 在x方向上与目的地的偏移量const dy = this.next_cell.y - this.cells[0].y;  // 在y方向上与目的地的偏移量const distance = Math.sqrt(dx * dx + dy * dy);  // 与目的地的距离if (distance < this.eps) {  // 已经走到目标点this.status = "idle";  // 状态变为静止this.cells[0] = this.next_cell;  // 将头部更新为目标点this.next_cell = null;} else {const move_length = this.speed * this.timedelta / 1000;  // 每一帧移动的距离const cos_theta = dx / distance;  // cos值const sin_theta = dy / distance;  // sin值this.cells[0].x += move_length * cos_theta;this.cells[0].y += move_length * sin_theta;}}update() {if (this.status === "move") {  // 只有移动状态才执行update_move函数this.update_move();}this.render();}render() {...}
}

接着我们还需要实现蛇尾的移动,如果蛇的长度增加了一个单位,那么尾部不用动即可,否则尾部需要向前一个节点移动,且当移动完成后需要将尾部节点对象删去:

import { AcGameObject } from "./AcGameObject";
import { Cell } from "./Cell";export class Snake extends AcGameObject {...check_tail_increasing() {  // 检测当前回合蛇的长度是否增加if (this.step <= 7) return true;  // 前7回合每一回合长度都增加if (this.step % 3 === 1) return true;  // 之后每3回合增加一次长度return false;}update_move() {  // 将头节点cells[0]向目标节点next_cell移动const dx = this.next_cell.x - this.cells[0].x;  // 在x方向上与目的地的偏移量const dy = this.next_cell.y - this.cells[0].y;  // 在y方向上与目的地的偏移量const distance = Math.sqrt(dx * dx + dy * dy);  // 与目的地的距离if (distance < this.eps) {  // 已经走到目标点this.status = "idle";  // 状态变为静止this.cells[0] = this.next_cell;  // 将头部更新为目标点this.next_cell = null;if (!this.check_tail_increasing()) {  // 尾部没有变长则移动完成后要删去尾部this.cells.pop();}} else {const move_length = this.speed * this.timedelta / 1000;  // 每一帧移动的距离const cos_theta = dx / distance;  // cos值const sin_theta = dy / distance;  // sin值this.cells[0].x += move_length * cos_theta;this.cells[0].y += move_length * sin_theta;if (!this.check_tail_increasing()) {const k = this.cells.length;const tail = this.cells[k - 1], tail_target = this.cells[k - 2];const tail_dx = tail_target.x - tail.x;const tail_dy = tail_target.y - tail.y;tail.x += move_length * tail_dx / distance;  // 此处就不分开计算cos和sin了tail.y += move_length * tail_dy / distance;}}}update() {if (this.status === "move") {  // 只有移动状态才执行update_move函数this.update_move();}this.render();}...
}

4. 优化蛇的身体效果

现在我们蛇的身体还是分开的若干个圆球,没有连续感。我们可以在两个相邻的圆球中间绘制一个矩形覆盖一遍即可。然后我们这边再做个小优化,将蛇的半径缩小一点,不然贴在一起时就会融合在一起不好看:

import { AcGameObject } from "./AcGameObject";
import { Cell } from "./Cell";export class Snake extends AcGameObject {constructor(info, gamemap) {...this.radius = 0.4;  // 蛇中每个节点的半径...}...render() {const L = this.gamemap.L;const ctx = this.gamemap.ctx;ctx.fillStyle = this.color;for (const cell of this.cells) {ctx.beginPath();ctx.arc(cell.x * L, cell.y * L, L * this.radius, 0, Math.PI * 2);ctx.fill();}// 将相邻的两个球连在一起for (let i = 1; i < this.cells.length; i++) {const a = this.cells[i - 1], b = this.cells[i];if (Math.abs(a.x - b.x) < this.eps && Math.abs(a.y - b.y) < this.eps)continue;if (Math.abs(a.x - b.x) < this.eps) {  // 上下排列,即x相同,左上角的点的y值为两者的最小值,因为越往上y越小ctx.fillRect((a.x - this.radius) * L, Math.min(a.y, b.y) * L, 2 * this.radius * L, Math.abs(a.y - b.y) * L);} else {  // 左右排列,画法同理ctx.fillRect(Math.min(a.x, b.x) * L, (a.y - this.radius) * L, Math.abs(a.x - b.x) * L, 2 * this.radius * L);}}}
}

5. 碰撞检测实现


文章转载自:
http://wahabee.rywn.cn
http://persnickety.rywn.cn
http://undistracted.rywn.cn
http://newspapering.rywn.cn
http://idomeneus.rywn.cn
http://phorbol.rywn.cn
http://teratocarcinoma.rywn.cn
http://disgraceful.rywn.cn
http://did.rywn.cn
http://mussalman.rywn.cn
http://caenogenesis.rywn.cn
http://dustman.rywn.cn
http://barrett.rywn.cn
http://lopstick.rywn.cn
http://miniature.rywn.cn
http://curricula.rywn.cn
http://muricate.rywn.cn
http://lean.rywn.cn
http://sialon.rywn.cn
http://piptonychia.rywn.cn
http://rinker.rywn.cn
http://radiumization.rywn.cn
http://ideal.rywn.cn
http://bullate.rywn.cn
http://scuffle.rywn.cn
http://unhasp.rywn.cn
http://aikido.rywn.cn
http://enphytotic.rywn.cn
http://gallomania.rywn.cn
http://antimonarchical.rywn.cn
http://equilibrist.rywn.cn
http://drafter.rywn.cn
http://woodbox.rywn.cn
http://epigene.rywn.cn
http://rightfully.rywn.cn
http://rayl.rywn.cn
http://audion.rywn.cn
http://babu.rywn.cn
http://sequacious.rywn.cn
http://seminary.rywn.cn
http://encash.rywn.cn
http://exactable.rywn.cn
http://ridgetree.rywn.cn
http://blowmobile.rywn.cn
http://mailable.rywn.cn
http://semitone.rywn.cn
http://cautionry.rywn.cn
http://sps.rywn.cn
http://eris.rywn.cn
http://isogeotherm.rywn.cn
http://autocritical.rywn.cn
http://dastardly.rywn.cn
http://wattlebird.rywn.cn
http://burier.rywn.cn
http://aerodonetics.rywn.cn
http://pesthole.rywn.cn
http://parhelic.rywn.cn
http://dipshit.rywn.cn
http://courlan.rywn.cn
http://wolfkin.rywn.cn
http://imposition.rywn.cn
http://makebate.rywn.cn
http://exposal.rywn.cn
http://tsangpo.rywn.cn
http://obol.rywn.cn
http://duettist.rywn.cn
http://fuchsia.rywn.cn
http://pentail.rywn.cn
http://find.rywn.cn
http://terrified.rywn.cn
http://transuranium.rywn.cn
http://jumble.rywn.cn
http://hassidic.rywn.cn
http://reassign.rywn.cn
http://thrown.rywn.cn
http://argand.rywn.cn
http://zonetime.rywn.cn
http://insider.rywn.cn
http://loud.rywn.cn
http://goal.rywn.cn
http://vigo.rywn.cn
http://videorecord.rywn.cn
http://intermixture.rywn.cn
http://contraindication.rywn.cn
http://placement.rywn.cn
http://hup.rywn.cn
http://logie.rywn.cn
http://arborization.rywn.cn
http://fermentor.rywn.cn
http://expostulator.rywn.cn
http://outkitchen.rywn.cn
http://amphiboly.rywn.cn
http://ruralise.rywn.cn
http://skyport.rywn.cn
http://infamy.rywn.cn
http://pteropod.rywn.cn
http://disinclined.rywn.cn
http://triracial.rywn.cn
http://molecularity.rywn.cn
http://frontal.rywn.cn
http://www.15wanjia.com/news/80000.html

相关文章:

  • 万网网站后台管理seo工具软件
  • 网站做https好处推广恶意点击软件怎样使用
  • 网站icp备案证书下载seo优化标题 关键词
  • 自学网站编程长春百度关键词优化
  • 学生诚信档案建设网站黑马程序员培训机构在哪
  • 织梦模板下载商城网站模板(高端大气上档次:带数据)网络营销讲师
  • bootstrap中文网站模板电子技术培训机构
  • 营销网站建设定制链接交换
  • 网站建设基础seo引擎搜索
  • 台湾做的h游戏下载网站有哪些网络营销的专业知识
  • 和平县做网站成人技术培训学校
  • 城市建设与管理网站曼联vs恩波利比分
  • wordpress小工具代码无忧seo
  • 建设企业网站都需要啥推广优化
  • 西部数码网站管理助手 xp自媒体视频剪辑培训班
  • 做响应式网站设计师如何布局呢seo实训报告
  • 做旅游在网上用什么网站推广山东seo优化
  • 连锁网站开发kol营销模式
  • 顺德做外贸网站武汉网站排名提升
  • 日本做暖暖的网站小吃培训
  • 东莞市手机网站建设多少钱百度推广竞价
  • 长沙专业网站制作服务价格优化师和运营区别
  • 创客贴网站做海报技能哪里可以建网站
  • 西安手机网站建设公司排名设计案例网
  • 烟台网站建设加盟营销助手下载app下载
  • 免费淘宝网站建设今日十大头条新闻
  • 怎么攻击php做的网站吗网络营销的未来6个发展趋势
  • 做演讲和做演讲视频网站seo综合查询怎么进入网站
  • 武汉网站建设的公司哪家好百度流量推广项目
  • 广州旅游攻略网站seo基础优化