OpenHarmony游戏应用程序-实现的一个手柄游戏

news2024/11/18 9:22:36

介绍

本篇Codelab是基于TS扩展的声明式开发范式编程语言,以及OpenHarmony的分布式能力实现的一个手柄游戏。

说明: 本示例涉及使用系统接口,需要手动替换Full SDK才能编译通过。

完成本篇Codelab需要两台开发板,一台开发板作为游戏端,一台开发板作为手柄端,实现如下功能:

  • 游戏端呈现飞机移动、发射子弹等效果。
  • 游戏端分布式拉起手柄端FA。
  • 手柄端与游戏端建立连接,发送指令给游戏端,比如移动飞机,发射子弹和释放技能等。

最终效果图如下:

搭建OpenHarmony环境

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。

      以3.1版本为例:

2.搭建烧录环境。

  • 完成DevEco Device Tool的安装
  • 完成RK3568开发板的烧录

3.搭建开发环境。

  • 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  • 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。
  • 工程创建完成后,选择使用真机进行调测。

分布式组网

本章节以系统自带的音乐播放器为例(具体以实际的应用为准),介绍如何完成两台设备的分布式组网。

  1. 硬件准备:准备两台烧录相同的版本系统的RK3568开发板A、B。
  2. 开发板A、B连接同一个WiFi网络。

打开设置-->WLAN-->点击右侧WiFi开关-->点击目标WiFi并输入密码。

3.将设备A,B设置为互相信任的设备。

  • 找到系统应用“音乐”。

  • 设备A打开音乐,点击左下角流转按钮,弹出列表框,在列表中会展示远端设备的id。

  • 选择远端设备B的id,另一台开发板(设备B)会弹出验证的选项框。

  • 设备B点击允许,设备B将会弹出随机PIN码,将设备B的PIN码输入到设备A的PIN码填入框中。

  1. 配网完毕。

代码结构解读

  • HandleEtsOpenHarmony
  • GameEtsOpenHarmony

本篇Codelab只对核心代码进行讲解,首先介绍一下整个工程的代码结构:

└── HandleGameApplication
	│── GameEtsOpenHarmony
	│  
	└── HandleEtsOpenHarmony

其中HandleEtsOpenHarmony为手柄端工程代码,GameEtsOpenHarmony为游戏端工程代码。

HandleEtsOpenHarmony

  • MainAbility:存放应用主页面。
    • pages/index.ets:应用主页面。
    • common/images:存放图片资源的目录。
  • ServiceAbility:存放ServiceAbility相关文件。
    • service.ts:service服务,用于跨设备连接后通讯。

GameEtsOpenHarmony

  • MainAbility:存放应用主页面。
    • pages/index.ets:应用主页面。
    • common/images:存放图片资源。
  • model:存放获取组网内的设备列表相关文件。
    • RemoteDeviceModel.ets:获取组网内的设备列表。
    • GameElement.ets:游戏端界面元素的实体类,用于封装子弹、飞机等元素的属性。
  • ServiceAbility:存放ServiceAbility相关文件。
    • service.ts:service服务,用于跨设备连接后通讯。

实现手柄端功能

  1. 实现布局和样式。

手柄端有两个功能:向游戏端发送指令和实时获取游戏端得分数据。界面上有三个功能组件:蓝色图形组件用于控制游戏端飞机移动方向,黄色图形组件用于发射子弹,绿色图形组件用于释放技能,效果图如下:

主要代码如下:

@Entry
@Component
struct Index {
...
  build() {
    Stack() {
	...
		Text('score:' + this.score)
		...
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start, justifyContent: FlexAlign.SpaceBetween }) {
        Stack() {
          Image('/common/images/bigcircle.png')
            .width(300)
            .height(300)
          Image('/common/images/smallcircle.png')
            .width(140)
            .height(140)
            .position({ x: this.smallPosX, y: this.smallPosY }) // 30+75-35
        }
       ...
        Row() {
          Image('/common/images/a.png')
            .width(160)
            .height(160)
            .margin({ right: 20, bottom: 80 })
          Image('/common/images/b.png')
            .width(200)
            .height(200)
        }.alignItems(VerticalAlign.Bottom)
		...
    }
  }
}

2.实现摇杆功能。

给摇杆(蓝色小圆图形)添加TouchEvent,动态改变摇杆position属性使摇杆跟随手指移动,主要代码如下:

onTouchEvent(event: TouchEvent) {
  switch (event.type) {
    case TouchType.Down:
      this.startX = event.touches[0].screenX;
      this.startY = event.touches[0].screenY;
      break;
    case TouchType.Move:
      this.curX = event.touches[0].screenX;
      this.curY = event.touches[0].screenY;
      this.getSmallCurrentPos(this.curX - this.smallR - 60, this.curY - this.smallR - 60)
      angle = Math.round(this.calculateAngle());
      break;
    default:
      break;
  }
}

3.计算摇杆偏移角度。

主要代码如下:

calculateAngle() {
  var angle = 0
  var degree = Math.atan(this.getDisAbsY() / this.getDisAbsX()) * 180 / Math.PI
  var quadrant = this.quadrant();
  switch (quadrant) {
    case this.QUADRANT_1:
    // 向右上移动
      angle = degree;
      break;
    case this.QUADRANT_2:
    // 向左上移动
      angle = 180 - degree;
      break;
    case this.QUADRANT_3:
    // 向左下移动
      angle = -180 + degree;
      break;
    case this.QUADRANT_4:
    // 向右下移动
      angle = -degree;
      break;
    default:
      angle = 0;
      break;
  }
  return angle;
}

4.连接游戏端Service。

当手柄端被游戏端拉起时,获取游戏端传递的数据:游戏端deviceId和分数score。然后通过deviceId连接游戏端Service,主要代码如下:

aboutToAppear() {
  // 当被拉起时,通过want传递的参数同步对端界面UI
  await featureAbility.getWant((error, want) => {
    // 远端被拉起后,连接游戏端的service
    if (want.parameters.deviceId) {
      let remoteDeviceId = want.parameters.deviceId
      connectRemoteService(remoteDeviceId)
    }
  });
}

async function connectRemoteService(deviceId) {
...
  await featureAbility.connectAbility(
    {
      'deviceId': deviceId,
      'bundleName': "com.huawei.cookbook",
      'abilityName': "com.huawei.cookbook.ServiceAbility",
    },
    {
      onConnect: onConnectCallback,
      onDisconnect: onDisconnectCallback,
      onFailed: onFailedCallback,
    },
  );
}

5.通过RPC发送数据到游戏端。

连接游戏端Service之后,摇杆角度angle和操作类型actionType(1为发射子弹,2为释放技能)发送给游戏端,主要代码如下:

async function sendMessageToRemoteService() {
...
  let option = new rpc.MessageOption();
  let data = new rpc.MessageParcel();
  let reply = new rpc.MessageParcel();
  data.writeInt(actionType);
  data.writeInt(angle);
  await mRemote.sendRequest(1, data, reply, option);
}

实现游戏端功能

  1. 实现布局和样式。

游戏界面主要由玩家飞机、敌机、子弹和道具(降落伞)等组成,由于敌机和子弹都是多个的,所以使用ForEach来实现,主要代码如下:

@Entry
@Component
struct Index {

  build() {
    Stack() {
    ... 

      ForEach(this.bullets, item => {
        Image(item.imgSrc)
          .width(item.imgWidth)
          .height(item.imgHeight)
          .position({ x: item.positionX, y: item.positionY })
      }, item => item.timestamp.toString())

      ForEach(this.enemyPlanes, item => {
        Image(item.imgSrc)
          .width(item.imgWidth)
          .height(item.imgHeight)
          .position({ x: item.positionX, y: item.positionY })
      }, item => item.timestamp.toString())

      Image('/common/images/planeOne.png')
        .width(this.planeSize)
        .height(this.planeSize)
        .position({ x: this.planePosX, y: this.planePosY })
        .onTouch((event: TouchEvent) => {
          this.onTouchEvent(event)
        })

      Image('/common/images/props.png')
        .width(this.propsSize)
        .height(this.propsSize)
        .position({ x: this.propsPosX, y: this.propsPosY })
    ...
    }
    .height('100%')
    .width('100%')
  }
}

2.实现游戏端元素动画效果。

飞机、子弹和道具等元素的移动是通过动态改变Image的position属性来实现的。使用定时器setInterval每隔16ms重新设置界面元素position属性的值,主要实现代码如下:

 startGame() {
    var that = this
    setInterval(function () {   
      // 每60*16ms创建一个敌机
      if (that.num % 60 == 0) {
        that.createEnemyPlane()
      }
      // 移动子弹
      var bulletsTemp: GameElement[] = []
      for (var i = 0; i < that.bullets.length; i++) {
        var bullet = that.bullets[i]
        bullet.positionY -= 8
        // 当子弹移除屏幕外的时候,释放掉
        if (bullet.positionY > 0) {
          bulletsTemp.push(bullet)
        }
      }
      that.bullets = bulletsTemp
      // 移动飞机
      var enemyPlanesTemp: GameElement[] = []
      for (var j = 0; j < that.enemyPlanes.length; j++) {
        var enemyPlane = that.enemyPlanes[j]
        enemyPlane.positionY += 6

        // 当飞机移除屏幕外的时候,释放掉
        if (enemyPlane.positionY < that.screenHeight) {
          enemyPlanesTemp.push(enemyPlane)
        }
      }
      that.enemyPlanes = enemyPlanesTemp
      // 每隔 500*16ms显示降落伞
      if (that.num % 500 == 0) {
        that.getPropsFlag = true
        that.propsPosY = -that.propsSize
        that.propsPosX = Math.round((Math.random() * (that.screenWidth - that.propsSize)))
      }
      // 刷新道具位置
      if (that.propsPosY < that.screenHeight) {
        that.propsPosY += 6
      }
      that.checkCollision()
    }, 16);
  }

3.判断元素是否发生碰撞。

在setInterval中改变元素位置的时候同时检测元素之间是否发生碰撞,子弹和敌机发生碰撞则分数值改变(摧毁小飞机加50分,摧毁大飞机加100分),玩家飞机和道具发生碰撞则道具加1,主要实现代码如下:

 checkCollision() {
 ...
    for (var i = 0; i < this.enemyPlanes.length; i++) {
      var enemy = this.enemyPlanes[i];
      for (var j = 0; j < this.bullets.length; j++) {
        var bullet = this.bullets[j];
        var inside = this.isInside(bullet, enemy);
        // 发生碰撞
        if (inside) {
          enemy.imgSrc = '/common/images/boom.png'
          if (enemy.flag == 1) {
            this.score += 50
            sendMessageToRemoteService(that.score)
          } else if (enemy.flag == 2) {
            this.score += 100
            sendMessageToRemoteService(that.score)
          }
          // 清除子弹
          this.enemyPlanes.splice(i, 1);
          i--;
          enemy.flag = 3
          // 清除被子弹打中敌机
          that.bullets.splice(j, 1);
          j--;
        }
      }
    }
    // 飞机和降落伞是否发生碰撞
    var isGetProps = this.isInside(myPlane, props);
    if (isGetProps && this.getPropsFlag) {
      this.getPropsFlag = false
      this.bombNum++
      this.propsPosY = 2000
    }
  }

4.获取设备列表。

点击界面右上角的“电脑”图标,调用registerDeviceListCallback()发现设备列表,并弹出设备列表选择框DeviceListDialog ,选择设备后拉起远端FA。DeviceListDialog 主要代码如下:

@CustomDialog
export struct DeviceListDialog {
  controller: CustomDialogController

  build() {
    Column() {
      Text("选择设备")
        .fontWeight(FontWeight.Bold)
        .fontSize(20)
        .margin({ top: 20, bottom: 10 })

      List() {
        ForEach(deviceList, item => {
          ListItem() {
            Stack() {
              Text(item)
                .fontSize(12)
                .margin({ top: 10 })
            }
            .onClick(() => {
              startRemoteAbility(item)
              this.controller.close();
            })
            .padding({ left: 30, right: 30 })
          }
        }, item => item.toString())
      }
      .height("30%")
      .align(Alignment.TopStart)
...
    }
  }
}

5.拉起手柄端FA。

点击设备列表获取远程设备id后,拉起手柄端FA,代码如下:

function startRemoteAbility(deviceId) {
  var params = {
    deviceId: localDeviceId
  }
  var wantValue = {
    bundleName: 'com.huawei.cookbook',
    abilityName: 'com.huawei.cookbook.MainAbility',
    deviceId: deviceId,
    parameters: params
  };
  featureAbility.startAbility({
    want: wantValue
  }).then((data) => {
    console.info('[game] featureAbility.startAbility finished, localDeviceId=' + localDeviceId + '----deviceId:' + deviceId);
    // 拉起远端后,连接远端service
    connectRemoteService(deviceId)
  });
}

6.连接手柄端Service。

拉起手柄端FA后,连接手柄端Service,代码如下:

async function connectRemoteService(deviceId) {
  // 连接成功的回调
  async function onConnectCallback(element, remote) {
    mRemote = remote;
  }
...
  if (remoteDeviceModel.deviceList.length === 0) {
    return;
  }
  await featureAbility.connectAbility(
    {
      'deviceId': deviceId,
      'bundleName': "com.huawei.cookbook",
      'abilityName': "com.huawei.cookbook.ServiceAbility",
    },
    {
      onConnect: onConnectCallback,
      onDisconnect: onDisconnectCallback,
      onFailed: onFailedCallback,
    },
  );
}

7.通过RPC发送数据到手柄端。

通过RPC将游戏分数发送给手柄端,主要代码如下:

async function sendMessageToRemoteService(score) {
  console.log('[game]connectRemoteService sendMessageToRemoteService:')
  if (mRemote == null) {
    return;
  }
  let option = new rpc.MessageOption();
  let data = new rpc.MessageParcel();
  let reply = new rpc.MessageParcel();
  data.writeInt(score);
  await mRemote.sendRequest(1, data, reply, option);
}

8.Service发布公共事件。

通过Service接收手柄端数据,然后使用CommonEvent模块将数据发送给FA,主要代码如下:

class GameServiceAbilityStub extends rpc.RemoteObject {
...
    onRemoteRequest(code, data, reply, option) {
        console.log('[game]Service onRemoteRequest');
        var publishCallBack;
        if (code === 1) {
            // 读取手柄端发送的数据
            let actionType = data.readInt();
            let angle = data.readInt();
            reply.writeInt(100);
            var params = {
                actionType: actionType,
                angle: angle,
            }
            var options = {
                code: 1,
                data: 'init data',
                isOrdered: true,
                bundleName: 'com.huawei.cookbook',
                parameters: params
            }
            publishCallBack = function () {}
            // 发布公共事件
            commonEvent.publish("publish_action", options, publishCallBack);
        } 
        return true;
    }
}

9.FA订阅公共事件。

订阅公共事件,接收从Service发送的公共事件数据,actionType 为操作类型(1表示发送子弹指令,2表示释放技能指令),angle 为飞机移动的角度。接收到数据后执行手柄端发送的指令:移动玩家飞机、发射子弹和释放技能摧毁所有敌机,主要代码如下:

subscribeEvent() {
...
  // 订阅公共事件回调
  function SubscribeCallBack(err, data) {
    let msgData = data.data;
    let code = data.code;
...
    // 处理接收到的数据data
    that.actionType = data.parameters.actionType;
    that.angle = data.parameters.angle;

    if (that.actionType == 1) {
      that.createBullet()
    }
    if (that.actionType == 2) {
      if (that.bombNum > 0) {
        that.bombNum--
        that.destroyAllEnemy()
      }
    }
    if (that.angle != 0) {
      that.movePlaneByHandle()
    }
  }
  //创建订阅者回调
  function CreateSubscriberCallBack(err, data) {
    subscriber = data;
    //订阅公共事件
    commonEvent.subscribe(subscriber, SubscribeCallBack);
  }
  //创建订阅者
  commonEvent.createSubscriber(subscribeInfo, CreateSubscriberCallBack);
}

恭喜您

通过本篇Codelab,您可以学到:

如何跨设备拉起远程FA。

如何连接远程Service。

使用RPC实现本地FA和远程Servcice通信。

通过CommonEvent发布与订阅实现Service和FA之间通信。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频

鸿蒙语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取白皮书完整版方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

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

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

相关文章

python 函数(解包**、互相调用、作用域、函数的封装、内置函数:eval()、zip()、文件处理open())

函数解包 """ 1、函数的注释&#xff1a;参数和返回值 在注释里可以自动添加显示&#xff0c;只需手动加说明。2、函数的解包【拆包】&#xff1a;函数的参数要传递数据有多个值的时候&#xff0c;中间步骤拿到数据 保存在元组或者列表 或者字典里。 - 传递参数…

【超全详解】Maven工程配置与常见问题解决指南

Maven工程 目录 Maven工程一、如何检查Maven工程是否配置正确&#xff1f;1、检查路径2、检查基本配置3、其他配置 二、Maven的基本操作基本操作install和package的区别 三、获取别人的Maven工程之后如何修改&#xff1f;四、如何正确写好配置文件&#xff1f;1.寻找配置资源2.…

css的active事件在手机端不生效的解决方法

需求&#xff1a;需求就是实现点击图中的 “抽奖” 按钮&#xff0c;实现一个按钮Q弹的放大缩小动画 上面是实现的效果&#xff0c;pc端&#xff0c;点击触发 :active 问题&#xff1a;但是这种方式在模拟器上可以&#xff0c;真机H5一调试就没生效了&#xff0c;下面是简单…

图像几何变换(仿射变换和透视变换...)及python-opencv实现

文章目录 图像变换类型仿射变换透视变换python-opencv实现参考文献 图像变换类型 图像几何变换主要包括以下几种类型&#xff1a; 平移&#xff08;Translation&#xff09;&#xff1a;将图像在水平或垂直方向上移动&#xff0c;不改变图像的尺寸和形状。缩放&#xff08;Sca…

【深度学习】基于机器学习的无机钙钛矿材料形成能预测,预测形成能,神经网络,回归问题

文章目录 任务分析数据处理处理离散数值处理缺失值处理不同范围的数据其他注意事项 我们的数据处理模型训练网页web代码、指导 任务分析 简单来说&#xff0c;就是一行就是一个样本&#xff0c;要用绿色的9个数值&#xff0c;预测出红色的那1个数值。 数据处理 在进行深度数…

linux命令详解——uniq,wc,tr

uniq uniq可以对查看内容去重 但在我们使用时会发现&#xff0c;uniq的去重逻辑是&#xff0c;当遇到连续多行内容相同时&#xff0c;去除重复行&#xff0c;而对间隔重复内容&#xff0c;无法实现去重功能 这里想到可以将sort与uniq结合使用&#xff0c;先对文件内容进行排序…

Visual Studio 插件 AnAPI++ for VS 2022

Anmial API abbreviation AnAPIis an automatically generated WebAPI project that has encapsulated Jwt Oauth2 token authentication, SqlSugar, Swagger, Nlog, Cross domain technologies, and supports Net6 and above versions Anmial API缩写AnAPI是一个自动生成的Web…

嵌入式开发--STM32G431RBTx-定时器中断流水灯

嵌入式开发–STM32G431RBTx-定时器中断流水灯 定时器工作原理 如图有反映stm32g431的定时器资源。 共10个定时器 定时器定时器类型个数TIM6&#xff0c;7基本定时器2TIM2&#xff0c;3&#xff0c;4全功能通用定时器3TIM15&#xff0c;16&#xff0c;17通用定时器(只有1或2个…

uniapp安装axios

先npm安装 npm i axios然后在项目里面建一个utils文件&#xff0c;再建一个index.js 以下是index.js代码&#xff1a; import axios from axios; const service axios.create({baseURL: //xxxx.xxxxx.com///你的请求接口域名, timeout: 6000, // request timeoutcrossDomai…

2024年通信工程专业-毕业论文

2024年毕业设计-通信专业VoLTE掉话分析资源-CSDN文库 毕业设计 ----移动通信中VoLTE信令流程分析 学生姓名 专业班级 学 号 指导教师 完成时间 …

比一比gitee、gitlab、github

gitee、gitlab、github&#xff0c;哪个是目前国内大型公司使用最多的呢&#xff1f;共同点&#xff1a;三者都是基于git的代码托管工具&#xff0c;都支持版本管理。 gitee&#xff1a;适合国内开发者&#xff0c;更友好的本地化服务&#xff0c;形成了一个适合中国宝宝学习的…

SQLiteC/C++接口详细介绍sqlite3_stmt类(十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十二&#xff09; 43、sqlite3_reset sqlite3_reset 函数用于重置已经编…

【云呐】事业单位资产盘点报告总结怎么写

事业单位固定资产盘点报告总结主要内容包括:  一、概述  说明本次盘点的目的和任务  明确盘点范围(如某处所有固定资产)  说明盘点时间(起止日期)  二、准备工作  组建盘点工作组  制定盘点计划和工作流程  录入联网资产管理系统或编制盘点清单  三、盘点实施…

一站式App流量统计,Xinstall助您洞悉用户行为

在如今的移动互联网时代&#xff0c;App的推广和运营对于开发者来说至关重要。然而&#xff0c;想要精准掌握App的流量情况&#xff0c;却并不是一件容易的事情。这时&#xff0c;一款强大的App流量统计工具就显得尤为重要。而Xinstall&#xff0c;正是这样一款能够帮助开发者轻…

后端项目中构建前端模块问题记录

后端项目中在登陆页面使用jsp&#xff0c;后端项目会通过接口返回给前端几个js的路径&#xff0c;这几个js呢&#xff0c;是由后端先构建好&#xff0c;然后返回给前端路径的&#xff0c;前端通过这个路径访问js执行。。。 总之&#xff0c;很奇怪的项目。。 1、首先要安装no…

selenium自动化登录模块HTMLTestRunner测试报告

1.下载HTMLTestRunner.py放到python的Lib目录下&#xff0c;python3之后的&#xff0c;文件要修改以下内容&#xff1a; 第94行&#xff0c;将import StringIO修改成import io 第539行&#xff0c;将self.outputBuffer StringIO.StringIO()修改成self.outputBuffer io.Strin…

stable diffusion 提示词进阶语法-年龄身材肤色-学习小结

stable diffusion 提示词进阶语法-年龄&身材&肤色 前言年龄提示词青年&#xff08;18-25岁&#xff09;幼年、少年&#xff08;1-18&#xff09;中年&#xff08;35-60岁&#xff09;老年&#xff08;65-80岁 老爷爷 老奶奶&#xff09; 身材提示词肤色关键词(人物基础…

linux网络服务学习(3):tftp与sftp

1.TFTP TFTP是基于UDP协议实现的一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务&#xff0c;不具备通常的FTP的许多功能。端口号为69。 1.1 centos7安装TFTP yum -y install tftp tftp-server #安装tftp客户端与服务器…

XSKY 智能存储,助力“数据要素 X”先进制造

3 月 21-22 日&#xff0c;主题为“突破 智行”的 IMC2024 第七届中国智造数字科技峰会在重庆召开。作为在先进制造领域拥有领先存储解决方案以及众多应用实践的企业&#xff0c;星辰天合受邀参加了此次峰会并荣获大会颁发的“最佳存储解决方案奖”。同时&#xff0c;星辰天合先…

QT gridlayout 循环设置组件,表格也通用 已解决

在需求中。经常遇到&#xff0c;表格 展示需求。 几乎都是json格式的。 // 列表配置文件QJsonArray listJsonArray getCfgJsonData("details_tab_table_config.json");if (listJsonArray.isEmpty()){return;}ui->gridWidget->setMaximumSize(QSize(310, 180)…