游戏理解入门:Rust+Bracket开发一个小游戏

1. Game loop

使用game loop可以使得游戏运行更加流畅和顺滑,它可以:

  • 初始化窗口、图形和其他资源;
  • 每当屏幕刷新他都会运行(通常是每秒30,60 );
  • 每次通过循环,他都会调用游戏的tick()函数。

大致的原理流程如下:

image-20240428105116174


2. 游戏引擎/库

这里选择使用一款名为bracket-Lib的游戏编程库,这是基于rust

  • 抽象了游戏开发中很多复杂的东西,但是保留了相关的概念,可以作为简单的教学工具。
  • 包括了随机数生成、几何、寻路、颜色处理、常用算法等。

2.1 Bracket-terminal

这个终端主要负责Bracket-Lib中的显示部分。

  • 提供了模拟控制台;
  • 可以与多种渲染平台配合
    • 从文本控制台到Web Assembly
    • 例如:OpenGL,Vulkan,Metal;
  • 支持sprites和原生的OpenGL开发。

2.2 Codepage437

  • 这是IBM扩展的ACSLL字符集。来自Dos PC上得到字符,用于终端输出,除了字母和数字,还提供一些符号。
  • Bracket-lib会把字符翻译为图形sprites并提供一个有限的字符集,字符所展示的是相应的图片;

3. 开始编码

3.1 游戏窗口初始化

使用cargo new创建游戏项目并导入Gracket-lib依赖。下面是第一部分代码实现,创建了游戏终窗口并打印一条简单的输出:

use bracket_lib::prelude::*;// 保留帧状态struct State {}// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {// 实现tick函数fn tick(&mut self, ctx: &mut BTerm) {// 清屏ctx.cls();// 在屏幕上打印输出,坐标系x,y从屏幕左上角开始计算(0,0)ctx.print(1, 1, "Hello,Bracket Terminall!");}
}
fn main() -> BError {// 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功let context = BTermBuilder::simple80x50().with_title("Flappy Dragon").build()?;main_loop (context,State{})
}

运行结果:

image-20240428112645545


3.2 游戏模式

一般情况下,游戏都是有一些明确的游戏模式,每种模式会明确游戏在当前的tick()中应该作的任务。

这个游戏也不例外,主要涉及三种模式:

  • 菜单
  • 游戏中
  • 结束

下面先将大致的框架构建好。

use bracket_lib::prelude::*;// 保留帧状态
struct State {mode:GameMode,}
// 为游戏状态实现一个叫new的关联函数
impl State {fn new() ->Self {State {mode:GameMode::Menu, // 设置游戏初始状态为菜单模式}}// 实现play方法fn play(&mut self,ctx:&mut BTerm) {//TODOself.mode = GameMode::End;}// restartfn resatrt(&mut self) {self.mode = GameMode::Playing;}fn main_menu(&mut self, ctx: &mut BTerm) {// TODO}// 实现end方法fn dead(&mut self, ctx: &mut BTerm) {}// 实现menu方法}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{Menu,Playing,End,
}// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {// 实现tick函数fn tick(&mut self, ctx: &mut BTerm) {// 根据游戏状态选择方向match self.mode {GameMode::Menu =>self.main_menu(ctx),GameMode::Playing => self.dead(ctx),GameMode::End => self.play(ctx),}}
}
fn main() -> BError {// 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功let context = BTermBuilder::simple80x50().with_title("Flappy Dragon").build()?;main_loop (context,State::new())
}

3.2.1 游戏菜单实现

游戏菜单的实现逻辑比较简单,主要是提供一个游戏操作的入口以供玩家进行选择操作:

  • 清理屏幕
  • 打印欢迎语
  • 开始游戏§
  • 离开游戏(Q)
fn main_menu(&self, ctx: &mut BTerm) {// TODOctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"欢迎来到Flappy Dragon!");ctx.print_centered( 8, "(P) 开始游戏");ctx.print_centered(9, " (Q) 离开游戏");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}
}

3.2.2 游戏结束的实现

这块代码和游戏菜单差不多,把提示词换一下

// 实现end方法
fn dead(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"小菜鸡,你已经嘎了!");ctx.print_centered( 8, "(P) 不服,再战");ctx.print_centered(9, " (Q) 离开游戏" );if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}
}

3.3.3 第一阶段效果

下面是该阶段全部代码,实现了游戏基本窗口以及三个基本模式的逻辑。

use bracket_lib::prelude::*;// 保留帧状态
struct State {mode:GameMode,}
// 为游戏状态实现一个叫new的关联函数
impl State {fn new() ->Self {State {mode:GameMode::Menu, // 设置游戏初始状态为菜单模式}}// 实现play方法fn play(&mut self,ctx:&mut BTerm) {//TODOself.mode = GameMode::End;}// menu方法fn main_menu(&mut self, ctx: &mut BTerm) {// TODOctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"Welcome to Flappy Dragon!");ctx.print_centered( 8, "(P) Start play");ctx.print_centered(9, " (Q) Quit game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}// restartfn resatrt(&mut self) {self.mode = GameMode::Playing;}// 实现end方法fn dead(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"You are dead!");ctx.print_centered( 8, "(P) replay");ctx.print_centered(9,  "(Q) quit game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{Menu,Playing,End,
}// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {// 实现tick函数fn tick(&mut self, ctx: &mut BTerm) {// 根据游戏状态选择方向match self.mode {GameMode::Menu =>self.main_menu(ctx),GameMode::Playing => self.dead(ctx),GameMode::End => self.play(ctx),}}
}
fn main() -> BError {// 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功let context = BTermBuilder::simple80x50().with_title("Flappy Dragon").build()?;main_loop (context,State::new())
}
  • 运行效果:

image-20240428121025278


3.3 添加play

这部分主要在游戏窗口添加一个玩家角色,这里以字符@作为龙,实现玩家通过空格键控制该角色的上下移动:

  • 一定时间不按空格,角色会下落,当下落碰到屏幕时游戏失败并结束游戏;
  • 按下空格时,龙会网上移动。
use bracket_lib::prelude::*;// 保留帧状态
struct State {player:Player,frame_time:f32,// 结果多少帧后累计的时间mode:GameMode,}const SCREEN_WIDTH:i32 = 80; // 屏幕宽度
const SCREEN_HEIGHT:i32 = 50; // 屏幕高度
const FRAME_DURATION:f32 = 75.0; //struct Player {x:i32,y:i32,velocity:f32,// 纵向速度 > 0 玩家就会往下掉
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{Menu,Playing,End,
}
impl Player {fn new(x:i32,y:i32)-> Self {Player {x:0,y:0,velocity:0.0, // 下落更加丝滑}}// 使用'@’在屏幕上表示玩家fn render (&mut self,ctx:&mut BTerm) {ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))}fn gravity_and_move (&mut self) {// 当下降速度小于2.0时让它的重力加速度每次增加0.2if self.velocity < 2.0 {self.velocity += 0.2;}self.y += self.velocity as i32;self.x += 1;if self.y < 0 {self.y = 0;}}// 按下空格实现玩家角色的向上移动fn flap (&mut self) {self.velocity = -2.0;}}// 为游戏状态实现一个叫new的关联函数
impl State {fn new() ->Self {State {player:Player::new(5,25),frame_time:0.0,mode:GameMode::Menu, // 设置游戏初始状态为菜单模式}}// 实现play方法fn play(&mut self,ctx:&mut BTerm) {ctx.cls_bg(NAVY);self.frame_time += ctx.frame_time_ms;if self.frame_time >FRAME_DURATION {self.frame_time = 0.0;self.player.gravity_and_move();}if let Some(VirtualKeyCode::Space) = ctx.key {self.player.flap();}self.player.render(ctx);ctx.print(0,0,  "Press Space to Flap");if self.player.y > SCREEN_HEIGHT {self.mode = GameMode::End;}}// menu方法fn main_menu(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"Welcome to Flappy Dragon!");ctx.print_centered( 8, "(P) Play Game");ctx.print_centered(9,  "(Q) Quit Game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.mode = GameMode::Playing,VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}// restartfn resatrt(&mut self) {self.player = Player::new(5,25);self.frame_time = 0.0;self.mode = GameMode::Menu;}// 实现end方法fn dead(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"You are dead!");ctx.print_centered( 8, "(P) replay");ctx.print_centered(9,  "(Q) quit game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}}// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {// 实现tick函数fn tick(&mut self, ctx: &mut BTerm) {// 根据游戏状态选择方向match self.mode {GameMode::Menu =>self.main_menu(ctx),GameMode::Playing => self.play(ctx),GameMode::End => self.dead(ctx),}}
}
fn main() -> BError {// 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功let context = BTermBuilder::simple80x50().with_title("Flappy Dragon").build()?;main_loop (context,State::new())
}

3.4 添加障碍物

use std::fmt::format;use bracket_lib::{prelude::*, random};// 保留帧状态
struct State {player:Player,frame_time:f32,// 结果多少帧后累计的时间mode:GameMode,obstacle:Obstacle,score:i32,}const SCREEN_WIDTH:i32 = 80; // 屏幕宽度
const SCREEN_HEIGHT:i32 = 50; // 屏幕高度
const FRAME_DURATION:f32 = 75.0; //struct Player {x:i32,y:i32,velocity:f32,// 纵向速度 > 0 玩家就会往下掉
}
// 游戏模式枚举并存储到游戏状态中
enum GameMode{Menu,Playing,End,
}
impl Player {fn new(x:i32,y:i32)-> Self {Player {x:0,y:0,velocity:0.0, // 下落更加丝滑}}// 使用'@’在屏幕上表示玩家fn render (&mut self,ctx:&mut BTerm) {ctx.set(0, self.y, YELLOW, BLACK, to_cp437('@'))}fn gravity_and_move (&mut self) {// 当下降速度小于2.0时让它的重力加速度每次增加0.2if self.velocity < 2.0 {self.velocity += 0.2;}self.y += self.velocity as i32;self.x += 1;if self.y < 0 {self.y = 0;}}// 按下空格实现玩家角色的向上移动fn flap (&mut self) {self.velocity = -2.0;}}// 为游戏状态实现一个叫new的关联函数
impl State {fn new() ->Self {State {player:Player::new(5,25),frame_time:0.0,mode:GameMode::Menu, // 设置游戏初始状态为菜单模式obstacle:Obstacle::new(SCREEN_WIDTH,0),score:0,}}// 实现play方法fn play(&mut self,ctx:&mut BTerm) {ctx.cls_bg(NAVY);self.frame_time += ctx.frame_time_ms;if self.frame_time >FRAME_DURATION {self.frame_time = 0.0;self.player.gravity_and_move();}if let Some(VirtualKeyCode::Space) = ctx.key {self.player.flap();}self.player.render(ctx);ctx.print(0,0,  "Press Space to Flap");ctx.print(0, 1, &format!("Score:{}",self.score));self.obstacle.render(ctx, self.player.x);if self.player.x > self.obstacle.x {self.score += 1;self.obstacle = Obstacle::new(self.player.x + SCREEN_WIDTH,self.score);}if self.player.y > SCREEN_HEIGHT || self.obstacle.hit_obstacle(&self.player) {self.mode = GameMode::End;}if self.player.y > SCREEN_HEIGHT {self.mode = GameMode::End;}}// menu方法fn main_menu(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"Welcome to Flappy Dragon!");ctx.print_color_right(60, 7, WEB_GREEN, BLACK,"by:Gemini48");ctx.print_centered( 8, "(P) Play Game");ctx.print_centered(9,  "(Q) Quit Game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.mode = GameMode::Playing,VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}// restartfn resatrt(&mut self) {self.player = Player::new(5,25);self.frame_time = 0.0;//self.mode = GameMode::Menu;self.mode = GameMode::Playing;self.obstacle = Obstacle::new(SCREEN_WIDTH,0);self.score = 0;}// 实现end方法fn dead(&mut self, ctx: &mut BTerm) {ctx.cls();// print_centered会在屏幕水平中间位置进行打印ctx.print_centered( 5,"You are dead!");ctx.print_centered(6,&format!("You earned {} points",self.score));ctx.print_centered( 8, "(P) Play Again");ctx.print_centered(9,  "(Q) Quit Game");if let Some(key) =ctx.key {match key {VirtualKeyCode::P => self.resatrt(),VirtualKeyCode::Q => ctx.quitting = true,_ => {}}}}}// 状态怎么和哟游戏帧关联上呢?,这就用到了一个名为GaemState的trait
impl GameState for State {// 实现tick函数fn tick(&mut self, ctx: &mut BTerm) {// 根据游戏状态选择方向match self.mode {GameMode::Menu =>self.main_menu(ctx),GameMode::Playing => self.play(ctx),GameMode::End => self.dead(ctx),}}
}struct Obstacle {x:i32,gap_y:i32, // 表示上下两个障碍物之间的空隙size:i32,
}impl Obstacle {fn new(x:i32,score:i32) -> Self {let mut random = RandomNumberGenerator::new();Obstacle {x,gap_y:random.range(10, 40), // 障碍纵向高度缝隙随机size:i32::max(2,20-score),}}fn render(&mut self,ctx:&mut BTerm,player_x:i32) {let screen_x = self.x -  player_x; // 屏幕空间let half_size:i32  = self.size / 2;for y in 0..self.gap_y - half_size {ctx.set(screen_x,y, RED,BLACK, to_cp437('|'));}for y in self.gap_y + half_size..SCREEN_HEIGHT {ctx.set(screen_x,y,RED,BLACK,to_cp437('|'));}}// 玩家碰撞到障碍物的处理fn hit_obstacle(&self,player:&Player) -> bool {let half_size = self.size / 2;let does_x_match = player.x == self.x; // 玩家x和障碍物x坐标let player_above_gap  =player.y < self.gap_y - half_size;let player_below_gap = player.y > self.gap_y + half_size;does_x_match && (player_above_gap || player_below_gap)}
}fn main() -> BError {// 创建一个80x50的简单窗口,标题为游戏名称,?表示这个build可能会出错,出错就捕获返回,否则成功let context = BTermBuilder::simple80x50().with_title("Flappy Dragon").build()?;main_loop (context,State::new())
}

4. 效果截图

image-20240508210136226

image-20240508210155766

源码地址

参考&引用

  • Rust依赖库:crates.io
  • bracket-lib

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/834058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何查看公网IP开放端口?

在计算机网络中&#xff0c;公网IP是指能够直接访问互联网的IP地址&#xff0c;而开放端口则是指外部网络可以访问的服务端口。查看公网IP开放端口可以帮助我们了解当前网络环境中哪些服务可以被外部网络访问&#xff0c;对于网络安全和远程连接非常重要。 天联组网 天联组网是…

2024蓝桥杯网络安全部分赛题wp

爬虫协议 题目给了提示访问robots.txt 会出三个目录 访问最后一个 点进去就flag{22560c15-577c-4c8b-9944-815473758bad} packet 下载附件&#xff0c;这个是流量包 放wireshark流量分析 搜http协议 发现有cat flag命令&#xff0c;直接看他返回的流量 最后base64解码即可…

Linux-笔记 i2c-tools

1、i2c-tools介绍 1、在日常linux开发中&#xff0c;有时候需要确认i2c硬件是否正常连接&#xff0c;设备是否正常工作&#xff0c;设备的地址是多少等等&#xff0c;这里我们就需要使用一个用于测试I2C总线的工具——i2c-tools&#xff0c;i2c-tools原理是通过操作/dev 路径 …

存储大作战:探索Local Storage与Session Storage的奥秘

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 存储大作战&#xff1a;探索Local Storage与Session Storage的奥秘 前言Local Storage与Session Storage简介数据存储生命周期容量限制安全性 前言 在Web的世界里&#xff0c;数据就像是一群流浪者&a…

TypeScript学习日志-第二十三天(装饰器Decorator)

装饰器Decorator 一、类装饰器 ClassDecorator 其中返回的 target 是 Http 的构造函数&#xff0c;有了构造函数就不会去破坏其自身原有的结构&#xff0c;当我们 Http 里面有多个属性或者方法的&#xff0c;当是我们不想看或者改变它&#xff0c;这时候可以在构造函数中增加即…

Ubuntu18.04 安装 anconda

anaconda官网 bash Anaconda3-2021.11-Linux-x86_64.sh一直回车&#xff0c;输入yes 选择安装目录 是否希望更新shell配置文件以自动初始化conda

组合数学汇总

阶乘、排列、组合 阶乘 x ! Π i : 1 x i x! \Pi_{i:1}^xi x!Πi:1x​i 。特殊情况0的阶乘是1。 排列 排列 P n m P_n^m Pnm​&#xff0c;从n个不同元素中取出m&#xff08;m≤n&#xff09;个元素&#xff0c;按照一定的顺序排成一列。第一个元素有n种选择&#xff0c;第…

AR人脸美妆SDK解决方案,让妆容更加贴合个人风格

美妆行业正迎来前所未有的变革&#xff0c;为满足企业对高效、精准、创新的美妆技术需求&#xff0c;美摄科技倾力打造了一款企业级AR人脸美妆SDK解决方案&#xff0c;为企业打开美妆领域的新世界大门。 革命性的人脸美妆技术 美摄科技的AR人脸美妆SDK解决方案&#xff0c;不…

IDEA设置 | 个性化设置

文章目录 IDEA设置总结IDEA自动生成序列化ID IDEA设置总结 本篇博客将专注于整理IDEA新UI界面的相关设置 IDEA自动生成序列化ID CtrlAltS快捷键打开设置界面 选择Editor→Inspections→JVM languages→Test frameworks&#xff0c;勾选上Serializable class without serialVe…

SpringCloud微服务之Eureka、Ribbon、Nacos详解

SpringCloud微服务之Eureka、Ribbon、Nacos详解 1、认识微服务1.1、单体架构1.2、分布式架构1.3、微服务1.4、SpringCloud 2、服务拆分与远程调用2.1、服务拆分的原则2.2、服务拆分示例2.2、提供者与消费者 3、Eureka注册中心3.1、Eureka的结构和作用3.2、搭建eureka-server3.2…

《构建高效审批系统:架构设计与实践》

在现代企业管理中&#xff0c;审批系统扮演着至关重要的角色&#xff0c;它不仅能够规范业务流程&#xff0c;提高工作效率&#xff0c;还能够增强企业的管理控制力和信息化水平。本文将探讨如何设计和构建一套高效的审批系统架构&#xff0c;以满足企业日常审批需求&#xff0…

docker-compose部署gitlab

需要提前安装docker和docker-compose环境 参考&#xff1a;部署docker-ce_安装部署docker-ce-CSDN博客 参考&#xff1a;docker-compose部署_docker compose部署本地tar-CSDN博客 创建gitlab的数据存放目录 mkdir /opt/gitlab && cd mkdir /opt/gitlab mkdir {conf…

纹理映射技术在AI去衣应用中的关键作用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;其在图像处理领域中的应用也日益广泛。AI去衣&#xff0c;作为一种颇具争议的技术应用&#xff0c;指的是利用深度学习算法自动移除或替换图片中的衣物。在这一过程中&#xff0c;纹理映射技术扮演了不可或缺的角色。本…

初识指针(3)<C语言>

前言 前面两篇文章已经介绍了一些关于指针的基础知识&#xff0c;下面我们可以涉及一些指针较容易混淆的概念&#xff0c;本篇文章将介绍数组名的理解、指针输入打印数组的不同格式、一维数组传参的本质&#xff0c;冒泡排序&#xff0c;二级指针&#xff0c;指针数组等。 数组…

学SQL啦

3 SQL 3.1 SQL查询语言 新手学习网址&#xff1a;https://sqlzoo.net/wiki/SQL_Tutorial SQL查询语句语法结构和运行顺序 语法结构&#xff1a;select--from--where--group by--having--order by--limit运行顺序&#xff1a;from--where--group by--having--order by--limit-…

十二届蓝桥杯Python组1月中/高级试题 第一题

** 十二届蓝桥杯Python组1月中/高级试题 第一题 第一题&#xff08;难度系数2&#xff0c;18 个计分点&#xff09; 编程实现&#xff1a; 输入一个字符串&#xff0c;输出这个字符串的最后一个字符。 输入描述&#xff1a;输入一个字符串 输出描述&#xff1a;输出这个字符串…

短视频批量下载解决方案分享

对于作短视频运营的行业人员&#xff0c;获取对应的视频资源和素材是必不可少的。 所以需要一个批量搜索视频并且下载的工具非常重要 一&#xff1a;行业痛点&#xff1a; 1&#xff1a;只能通过单链接进行下载 2&#xff1a;不能通过关键词批量下载 3&#xff1a;无法获取…

【Ubuntu20.04安装java-8-openjdk】

1 下载 官网下载链接&#xff1a; https://www.oracle.com/java/technologies/downloads/#java8 下载 最后一行 jdk-8u411-linux-x64.tar.gz&#xff0c;并解压&#xff1a; tar -zxvf jdk-8u411-linux-x64.tar.gz2 环境配置 1、打开~/.bashrc文件 sudo gedit ~/.bashrc2、…

Day2 | Java基础 | 2 数据类型

Day1 | Java基础 | 2 数据类型 基础版staticstatic的用法static修饰内部类static修饰方法static修饰变量static修饰代码块 深入分析static小结 问题回答版参数传递形参和实参的区别是什么&#xff1f;Java是值传递还是引用传递&#xff1f;值传递和引用传递的区别是什么&#x…

架空光缆用什么型号

架空光缆是什么意思 , 架空光缆用什么型号的 GYTC8A , 架空光缆型号是啥 8字形光缆 产品描述 Description GYTC8A光缆的结构是将250m光纤套入高模量材料制成的松套管中&#xff0c;松套管内填充防水化合物。缆芯的中心是一根金属加强芯&#xff0c;松套管(和填充绳 )围绕中心…