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

【OpenGL】OpenGL游戏案例(二)

文章目录

    • 特殊效果
      • 数据结构
      • 生成逻辑
      • 更新逻辑
    • 文本渲染
      • 类结构
      • 构造函数
      • 加载函数
      • 渲染函数

特殊效果

为提高游戏的趣味性,在游戏中提供了六种特殊效果。

数据结构

PowerUp

类只存储存活数据,实际逻辑在游戏代码中通过Type字段来区分执行

class PowerUp : public GameObject
{
public:// powerup statestd::string Type;float       Duration;bool        Activated;// constructorPowerUp(std::string type, glm::vec3 color, float duration, glm::vec2 position, Texture2D texture): GameObject(position, POWERUP_SIZE, texture, color, VELOCITY), Type(type), Duration(duration), Activated() { }
};

Game中存在容器存储目前存活的PowerUp对象

std::vector<PowerUp>  PowerUps;

生成逻辑

当玩家消灭一个方块后,按固定概率随机生成或不生成一个随机能力的砖块。

//Game::DoCollision()// destroy block if not solid
if (!box.IsSolid)
{box.Destroyed = true;this->SpawnPowerUps(box);
}

生成策略

bool ShouldSpawn(unsigned int chance)
{unsigned int random = rand() % chance;return random == 0;
}
void Game::SpawnPowerUps(GameObject& block)
{if (ShouldSpawn(75)) // 1 in 75 chancethis->PowerUps.push_back(PowerUp("speed", glm::vec3(0.5f, 0.5f, 1.0f), 0.0f, block.Position, ResourceManager::GetTexture("powerup_speed")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("sticky", glm::vec3(1.0f, 0.5f, 1.0f), 20.0f, block.Position, ResourceManager::GetTexture("powerup_sticky")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("pass-through", glm::vec3(0.5f, 1.0f, 0.5f), 10.0f, block.Position, ResourceManager::GetTexture("powerup_passthrough")));if (ShouldSpawn(75))this->PowerUps.push_back(PowerUp("pad-size-increase", glm::vec3(1.0f, 0.6f, 0.4), 0.0f, block.Position, ResourceManager::GetTexture("powerup_increase")));if (ShouldSpawn(15)) // Negative powerups should spawn more oftenthis->PowerUps.push_back(PowerUp("confuse", glm::vec3(1.0f, 0.3f, 0.3f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_confuse")));if (ShouldSpawn(15))this->PowerUps.push_back(PowerUp("chaos", glm::vec3(0.9f, 0.25f, 0.25f), 15.0f, block.Position, ResourceManager::GetTexture("powerup_chaos")));
}

更新逻辑

生成完后会在Update中调用以下函数下落更新,并每帧检查是否与玩家发生碰撞

不同状态下能力内部标志位变化:

DestoryedActivated
下落状态falsefalse
与玩家碰撞后truetrue
倒计时结束后truefalse

Destroyed决定是否要渲染其图像,Activated决定是否要启用其计时器,最后的状态则决定了是否要将其移除出列表

刚碰撞时根据Type字段执行附加能力的逻辑,倒计时结束后执行取消附加能力的逻辑。流程完毕后移除出数组

//绘制
for (PowerUp& powerUp : this->PowerUps)if (!powerUp.Destroyed)powerUp.Draw(*Renderer);//检查碰撞
for (PowerUp& powerUp : this->PowerUps)
{if (!powerUp.Destroyed){if (powerUp.Position.y >= this->Height)powerUp.Destroyed = true;if (CheckCollision(*Player, powerUp)){	// collided with player, now activate powerupActivatePowerUp(powerUp);powerUp.Destroyed = true;powerUp.Activated = true;}}
}//启用能力
void ActivatePowerUp(PowerUp& powerUp)
{if (powerUp.Type == "speed"){Ball->Velocity *= 1.2;}else if (powerUp.Type == "sticky"){Ball->Sticky = true;Player->Color = glm::vec3(1.0f, 0.5f, 1.0f);}else if (powerUp.Type == "pass-through"){Ball->PassThrough = true;Ball->Color = glm::vec3(1.0f, 0.5f, 0.5f);}else if (powerUp.Type == "pad-size-increase"){Player->Size.x += 50;}else if (powerUp.Type == "confuse"){if (!Effects->Chaos)Effects->Confuse = true; // only activate if chaos wasn't already active}else if (powerUp.Type == "chaos"){if (!Effects->Confuse)Effects->Chaos = true;}
}//每帧更新,轮询何时撤销能力
void Game::UpdatePowerUps(float dt)
{for (PowerUp& powerUp : this->PowerUps){powerUp.Position += powerUp.Velocity * dt;if (powerUp.Activated){powerUp.Duration -= dt;if (powerUp.Duration <= 0.0f){// remove powerup from list (will later be removed)powerUp.Activated = false;// deactivate effectsif (powerUp.Type == "sticky"){if (!IsOtherPowerUpActive(this->PowerUps, "sticky")){	// only reset if no other PowerUp of type sticky is activeBall->Sticky = false;Player->Color = glm::vec3(1.0f);}}else if (powerUp.Type == "pass-through"){if (!IsOtherPowerUpActive(this->PowerUps, "pass-through")){	// only reset if no other PowerUp of type pass-through is activeBall->PassThrough = false;Ball->Color = glm::vec3(1.0f);}}else if (powerUp.Type == "confuse"){if (!IsOtherPowerUpActive(this->PowerUps, "confuse")){	// only reset if no other PowerUp of type confuse is activeEffects->Confuse = false;}}else if (powerUp.Type == "chaos"){if (!IsOtherPowerUpActive(this->PowerUps, "chaos")){	// only reset if no other PowerUp of type chaos is activeEffects->Chaos = false;}}}}}// Remove all PowerUps from vector that are destroyed AND !activated (thus either off the map or finished)// Note we use a lambda expression to remove each PowerUp which is destroyed and not activatedthis->PowerUps.erase(std::remove_if(this->PowerUps.begin(), this->PowerUps.end(),[](const PowerUp& powerUp) { return powerUp.Destroyed && !powerUp.Activated; }), this->PowerUps.end());
}

文本渲染

文本渲染部分依赖于一个库FreeType,在项目中只拿来读取ttf生成像素数据,然后记录在自定义的Character结构中。

// 宽度、高度、左上角偏移、水平距离
struct Character
{unsigned int TextureID;glm::ivec2    Size;glm::ivec2    Bearing;unsigned int  Advance;
};

创建文字渲染器类TextRenderer来负责文字的渲染

类结构

class TextRenderer
{
public:// 预处理记录的需要的字符内容(借助FreeType)std::map<char, Character> Characters;// 文字渲染Shader,就是渲染一个四边形,然后采样纹理并渲染上去Shader TextShader;TextRenderer(unsigned int width, unsigned int height);void Load(std::string font, unsigned int fontSize);void RenderText(std::string text, float x, float y, float scale, glm::vec3 color = glm::vec3(1.0f));private:unsigned int VAO, VBO;
};

构造函数

构造函数中负责初始化shader参数,初始化VAO和VBO,但VBO的具体顶点数据会在绘制时动态计算出来

TextRenderer::TextRenderer(unsigned int width, unsigned int height)
{// load and configure shaderthis->TextShader = ResourceManager::LoadShader("res/shaders/text.vs", "res/shaders/text.frag", nullptr, "text");this->TextShader.SetMatrix4("projection", glm::ortho(0.0f, static_cast<float>(width), static_cast<float>(height), 0.0f), true);this->TextShader.SetInteger("text", 0);// configure VAO/VBO for texture quadsglGenVertexArrays(1, &this->VAO);glGenBuffers(1, &this->VBO);//绑定VAO,VBOglBindVertexArray(this->VAO);glBindBuffer(GL_ARRAY_BUFFER, this->VBO);//填充VBO数据glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);//规划VBO数据布局glEnableVertexAttribArray(0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);//解绑glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);
}

加载函数

外部提供字体文件(.ttf)路径和字体大小,内部利用FreeType将前128字符转换为使用Characters存储

void TextRenderer::Load(std::string font, unsigned int fontSize)
{// 如果之前已经Load过,清除this->Characters.clear();// 初始化并加载 FreeType libraryFT_Library ft;if (FT_Init_FreeType(&ft))std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;// 将字符加载为faceFT_Face face;if (FT_New_Face(ft, font.c_str(), 0, &face))std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl;// 设置加载字体的大小,这里是设置为 fontSize 参数指定的大小。第一个参数为 0,表示不限制宽度,第二个参数是目标字体大小FT_Set_Pixel_Sizes(face, 0, fontSize);// 关闭了 OpenGL 中的字节对齐限制,使得字节数据可以按 1 字节对齐,这样可以避免在纹理生成时出现内存填充问题glPixelStorei(GL_UNPACK_ALIGNMENT, 1);// 通过循环加载前 128 个 ASCII 字符。for (GLubyte c = 0; c < 128; c++){// FT_Load_Char 用来加载字符的字形信息,如果加载失败,则输出错误信息并跳过该字符if (FT_Load_Char(face, c, FT_LOAD_RENDER)){std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;continue;}// 为每个字符生成一个纹理unsigned int texture;glGenTextures(1, &texture);glBindTexture(GL_TEXTURE_2D, texture);//使用 GL_RED 格式来存储单通道的灰度数据glTexImage2D(GL_TEXTURE_2D,0,GL_RED,face->glyph->bitmap.width,face->glyph->bitmap.rows,0,GL_RED,GL_UNSIGNED_BYTE,face->glyph->bitmap.buffer);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//将原始数据转换为Character数组Character character = {texture,glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),face->glyph->advance.x};Characters.insert(std::pair<char, Character>(c, character));}// 清理资源glBindTexture(GL_TEXTURE_2D, 0);FT_Done_Face(face);FT_Done_FreeType(ft);
}

渲染函数

  • 使用并设置文字shader,使用当前的VAO
  • 遍历所有提供的字符,在Characters中取出对应的数据
  • 计算位置长宽,更新顶点缓存,绑定记录的纹理
  • 绘制完后偏移光标位置
void TextRenderer::RenderText(std::string text, float x, float y, float scale, glm::vec3 color)
{// activate corresponding render state	this->TextShader.Use();this->TextShader.SetVector3f("textColor", color);glActiveTexture(GL_TEXTURE0);glBindVertexArray(this->VAO);// iterate through all charactersstd::string::const_iterator c;for (c = text.begin(); c != text.end(); c++){Character ch = Characters[*c];// 计算位置和长宽float xpos = x + ch.Bearing.x * scale;float ypos = y + (this->Characters['H'].Bearing.y - ch.Bearing.y) * scale;float w = ch.Size.x * scale;float h = ch.Size.y * scale;// 为每个字符更新对应的VBO缓存float vertices[6][4] = {{ xpos,     ypos + h,   0.0f, 1.0f },{ xpos + w, ypos,       1.0f, 0.0f },{ xpos,     ypos,       0.0f, 0.0f },{ xpos,     ypos + h,   0.0f, 1.0f },{ xpos + w, ypos + h,   1.0f, 1.0f },{ xpos + w, ypos,       1.0f, 0.0f }};// 绑定指定的纹理glBindTexture(GL_TEXTURE_2D, ch.TextureID);// update content of VBO memoryglBindBuffer(GL_ARRAY_BUFFER, this->VBO);glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);glBindBuffer(GL_ARRAY_BUFFER, 0);// render quadglDrawArrays(GL_TRIANGLES, 0, 6);// 更新光标位置x += (ch.Advance >> 6) * scale; // bitshift by 6 to get value in pixels (1/64th times 2^6 = 64)}glBindVertexArray(0);glBindTexture(GL_TEXTURE_2D, 0);
}

相关文章:

【OpenGL】OpenGL游戏案例(二)

文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性&#xff0c;在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据&#xff0c;实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...

28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正

这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能&#xff1a;报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。 一、需求 数据统计方面&…...

Java 泛型<? extends Object>

在 Java 泛型中&#xff0c;<? extends Object> 和 <?> 都表示未知类型&#xff0c;但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性&#xff0c;使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型&#xff0c;以消除编译时…...

FPGA|使用quartus II通过AS下载POF固件

1、将开发板设置到AS下载挡位&#xff0c;或者把下载线插入到AS端口 2、打开quartus II&#xff0c;选择Tools→Programmer→ Mode选择Active Serial Programming 3、点击左侧Add file…&#xff0c;选择 .pof 文件 →start 4、勾选program和verify&#xff08;可选&#xff0…...

“新月之智”智能战术头盔系统(CITHS)

新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 相关文章链接&#xff08;更新&#xff09;&#xff1a; 星际战争模拟系统&#xff1a;新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 目录 一、引言 二、智能头盔控制系统概述 三、系统架…...

php:代码中怎么搭建一个类似linux系统的crontab服务

一、前言 最近使用自己搭建的php框架写一些东西&#xff0c;需要用到异步脚本任务的执行&#xff0c;但是是因为自己搭建的框架没有现成的机制&#xff0c;所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦&#xff0…...

【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

MinDoc 安装与部署

下载可执行文件 mindoc mindoc_linux_amd64.zip 上传并解压压缩包 cd /opt mkdir mindoc cd mindocunzip mindoc_linux_amd64.zip 创建数据库 CREATE DATABASE mindoc_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 配置数据库 将解压目录下 conf/app.conf.exam…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体&#xff1f; 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…...

windows系统如何检查是否开启了mongodb服务

windows系统如何检查是否开启了mongodb服务&#xff01;我们有很多软件开发&#xff0c;网站开发时候需要使用到这个mongodb数据库&#xff0c;下面我们看看&#xff0c;如何在windows系统内排查&#xff0c;是否已经启动了本地服务。 在 Windows 系统上&#xff0c;您可以通过…...

VS安卓仿真器下载失败怎么办?

如果网络不稳定&#xff0c;则VS的安卓仿真器很容易下载失败&#xff0c;如下 Downloaded file <USER_HOME>\AppData\Local\Temp\xamarin-android-sdk\x86_64-35_r08.zip not found for Android SDK archive https://dl.google.com/android/repository/sys-img/google_a…...

计算机网络一点事(24)

TCP可靠传输&#xff0c;流量控制 可靠传输&#xff1a;每字节对应一个序号 累计确认&#xff1a;收到ack则正确接收 返回ack推迟确认&#xff08;不超过0.5s&#xff09; 两种ack&#xff1a;专门确认&#xff08;只有首部无数据&#xff09; 捎带确认&#xff08;带数据…...

视频拼接,拼接时长版本

目录 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg imageio&#xff0c;适合视频较短 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg import subprocess import glob import os from nats…...

制造企业的成本核算

一、生产成本与制造费用的区别 (1)生产成本,是直接用于产品生产,构成产品实体的材料成本。 包括企业在生产经营过程中实际消耗的原材料、辅助材料、备品备件、外购半成品、燃料、动力包装物以及其它直接材料,和直接参加产品生产的工人工资,以及按生产工人的工资总额和规…...

doris:高并发导入优化(Group Commit)

在高频小批量写入场景下&#xff0c;传统的导入方式存在以下问题&#xff1a; 每个导入都会创建一个独立的事务&#xff0c;都需要经过 FE 解析 SQL 和生成执行计划&#xff0c;影响整体性能每个导入都会生成一个新的版本&#xff0c;导致版本数快速增长&#xff0c;增加了后台…...

LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略

LLMs之WebRAG&#xff1a;STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略 目录 STORM系统简介 1、Co-STORM 2、更新新闻 STORM系统安装和使用方法 1、安装 pip安装 直接克隆GitHub仓库 2、模型和数据集 两个数据集 FreshWiki数据集 WildSeek数据集 支持…...

鸿蒙HarmonyOS实战-ArkUI动画(页面转场动画)_鸿蒙arkui tab 切换动画

PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number}) 在HarmonyOS中&#xff0c;PageTransitionEnter和PageTransitionExit是用于控制页面切换动画的参数。它们分别表示页面进入和退出时的动画。1. type&#xff08;动画类型…...

图漾相机-ROS2-SDK-Ubuntu版本编译(新版本)

文章目录 前言1.Camport ROS2 SDK 介绍1.1 Camport ROS2 SDK源文件介绍1.2 Camport ROS2 SDK工作流程1.2.1 包含头文件1.2.2 2 初始化 ROS 2 节点1.2.3 创建节点对象1.2.4 创建发布者对象并实现发布逻辑1.2.5 启动 ROS 2 1.3 ROS2 SDK环境配置与编译1.3.1 Ubuntu 20.04 下ROS2 …...

小程序的协同工作与发布

1.小程序API的三大分类 2.小程序管理的概念&#xff0c;以及成员管理两个方面 3.开发者权限说明以及如何维护项目成员 4.小程序版本...

解锁维特比算法:探寻复杂系统的最优解密码

引言 在复杂的技术世界中&#xff0c;维特比算法以其独特的魅力和广泛的应用&#xff0c;成为通信、自然语言处理、生物信息学等领域的关键技术。今天&#xff0c;让我们一同深入探索维特比算法的奥秘。 一、维特比算法的诞生背景 维特比算法由安德鲁・维特比在 1967 年提出…...

计算机网络一点事(20)

IEEE802.11 无线局域网 分类有无基础设施 星型拓扑&#xff0c;基本服务集BSS一基站多移动站&#xff0c;服务集标识符SSID不超过32b&#xff0c;可接入802.3 漫游&#xff1a;移动站从一个基本服务集切换到另一个&#xff08;类似换联WiFi&#xff09; 802.11帧&#xff1…...

java求职学习day23

MySQL 单表 & 约束 & 事务 1. DQL操作单表 1.1 创建数据库,复制表 1) 创建一个新的数据库 db2 CREATE DATABASE db2 CHARACTER SET utf8; 2) 将 db1 数据库中的 emp 表 复制到当前 db2 数据库 1.2 排序 通过 ORDER BY 子句 , 可以将查询出的结果进行排序 ( 排序只…...

Vue-cli 脚手架搭建

安装node.js 官网下载node.js安装包&#xff0c;地址&#xff1a;Node.js — Download Node.js 先在node.js即将要安装的路径下创建两个文件夹&#xff1a;node_cache&#xff08;缓存&#xff09;、node_global&#xff08;全局&#xff09; 点击安装包&#xf…...

认识小程序的基本组成结构

1.基本组成结构 2.页面的组成部分 3.json配置文件 4.app.json文件(全局配置文件&#xff09; 5.project.config.json文件 6.sitemap.json文件 7.页面的.json配置文件 通过window节点可以控制小程序的外观...

Spring Boot 热部署实现指南

在开发 Spring Bot 项目时&#xff0c;热部署功能能够显著提升开发效率&#xff0c;让开发者无需频繁重启服务器就能看到代码修改后的效果。下面为大家详细介绍一种实现 Spring Boot 热部署的方法&#xff0c;同时也欢迎大家补充其他实现形式。 步骤一、开启 IDEA 自动编译功能…...

深度学习编译器的演进:从计算图到跨硬件部署的自动化之路

第一章 问题的诞生——深度学习部署的硬件困境 1.1 计算图的理想化抽象 什么是计算图&#xff1f; 想象你正在组装乐高积木。每个积木块代表一个数学运算&#xff08;如加法、乘法&#xff09;&#xff0c;积木之间的连接代表数据流动。深度学习框架正是用这种"积木拼接…...

【数据结构】_顺序表经典算法OJ(力扣版)

目录 1. 移除元素 1.1 题目描述及链接 1.2 解题思路 1.3 程序 2. 合并两个有序数组 1.1 原题链接及题目描述 1.2 解题思路 1.3 程序 1. 移除元素 1.1 题目描述及链接 原题链接&#xff1a;27. 移除元素 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a…...

数据结构:队列篇

图均为手绘,代码基于vs2022实现 系列文章目录 数据结构初探: 顺序表 数据结构初探:链表之单链表篇 数据结构初探:链表之双向链表篇 链表特别篇:链表经典算法问题 数据结构:栈篇 文章目录 系列文章目录前言一.队列的概念和结构1.1概念一、动态内存管理优势二、操作效率与安全性…...

第05章 17 Contour 过滤器介绍与例子

vtkContourFilter 是 VTK&#xff08;Visualization Toolkit&#xff09;中的一个关键类&#xff0c;用于从输入数据生成等值线或等值面。它是基于阈值的过滤器&#xff0c;可以从标量字段中提取等值线或等值面。vtkContourFilter 的核心功能是根据用户指定的值生成等值线或等值…...

【落羽的落羽 数据结构篇】顺序表

文章目录 一、线性表二、顺序表1. 概念与分类2. 准备工作3. 静态顺序表4. 动态顺序表4.1 定义顺序表结构4.2 顺序表的初始化4.3 检查空间是否足够4.3 尾部插入数据4.4 头部插入数据4.5 尾部删除数据4.6 头部删除数据4.7 在指定位置插入数据4.8 在指定位置删除数据4.9 顺序表的销…...