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

介绍

本篇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.mfbz.cn/a/480035.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

罗德与施瓦茨CMA180电信无线电测试仪

181/2461/8938产品概述&#xff1a; R&S CMA180 是适用于在 100 kHz 至 3 GHz 范围内操作的无线电系统的无线电通信测试仪。其技术完全基于数字信号处理及先进计算。 简介&#xff1a;R&S CMA180 无线电通信测试仪 R&SCMA180 是适用于在 100 kHz 至 3 GHz 范围内…

电商系统秒杀二 秒杀场景下如何进行限流

本章学习内容 1、在秒杀页面&#xff0c;客户点击秒杀后&#xff0c;在前台弹出一个验证码&#xff0c;需要用户输入验证码才能往后端发送请求&#xff0c;这样能够错开秒杀下单的时间。 2、通过验证码&#xff0c;对后台下单请求进行保护&#xff0c;防止刷单&#xff0c;即防…

通过JWT完成token登录验证

前言 什么是JWT&#xff1f; 全称是JSON Web token&#xff0c;是用于对应用程序上的用户进行身份验证的标记&#xff0c;使用 JWTS 的应用程序不再需要保存有关其用户的 cookie 或其他session数据 使用JWT的优势 提高了程序的可伸缩性&#xff0c;也极大的提高了应用程序的安全…

2024蓝桥杯每日一题(单调队列)

备战2024年蓝桥杯 -- 每日一题 Python大学A组 试题一&#xff1a;单调栈 试题二&#xff1a;滑动窗口 试题三&#xff1a;子矩阵 试题四&#xff1a;最大子序和 试题一&#xff1a;单调栈 【题目描述】 给定一个长度为 N 的整数数列&#xff0c;输出每…

第十四届蓝桥杯JavaB组省赛真题 - 幸运数字

进制转换可以参考如下的十进制&#xff0c;基本一样的&#xff0c;只是把10变成了其他数字&#xff0c; sum就是各个数位之和 public static int myUtil(int n) {int sum 0;while(n > 0) {sum n % 10;n / 10;}return sum;} 注意&#xff1a; 如果写在同一个类里面&…

基于javaSpringboot+mybatis+layui的装修验收管理系统设计和实现

基于javaSpringbootmybatislayui的装修验收管理系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留…

Java安全 反序列化(5) CC6链原理分析

Java安全 反序列化(5) CC6链原理分析 CC6学习的目的在于其可以无视jdk版本&#xff0c;这条链子更像CC1-LazyMap和URLDNS链子的缝合版 文章目录 Java安全 反序列化(5) CC6链原理分析前言一.CC6的原理和实现以及易错点我们如何实现调用LazyMap.get()方法一个易错点 二.完整CC6P…

亚马逊服务器ssh以及scp

ssh awspass.pem为创建服务器时创建的密钥&#xff0c;ubuntu用户 ssh -i "awspass.pem" ubuntuipscp scp -i "awspass.pem" -r dist/* ubuntuip:/home/ubuntu/

macOS下Java应用的打包和安装程序制作

文章目录 macOS应用程序结构Java应用打包JavaAppLauncherjpackage其它相关JDK命令附录JavaAppLauncher源码链接macOS应用程序结构 macOS通常以dmg或pkg作为软件发行包,安装到/Applications下后,结构比较统一。 info.plist里的CFBundleExecutable字段可以指定入口,如果不指定…

使用uniapp 的 plus.sqlite 操作本地数据库报错:::table xxx has no column named xxxx

背景&#xff1a; 1、使用uniapp 的 plus.sqlite 进行APP本地数据库操作 2、SQLite 模块用于操作本地数据库文件&#xff0c;可实现数据库文件的创建&#xff0c;执行SQL语句等功能。 遇到&#xff1a;在之前创建的表上进行新增字段的操作时候&#xff0c;出现问题&#xff1a…

蓝桥杯 2022 省B 砍竹子

思路&#xff1a; 非常明显&#xff0c;这题是个贪心。因为这题是求最小操作次数&#xff0c;而且每次操作都会变小&#xff0c;所以肯定要优先操作大的元素&#xff0c;这样它变小之后才可能和其它元素一起操作以减少操作次数。 所以&#xff1a;建立两个数组&#xff0c;一…

Canine IP-10/CXCL 10 ELISA试剂盒上新

科研用Canine IP-10/CXCL 10 ELISA试剂盒重磅来袭&#xff0c;将在免疫学、癌症研究与神经科学等多个领域助力各位老师们的研究&#xff01; 图1&#xff1a;犬IP-10/CXCL10结构预测&#xff08;图片来源&#xff1a;UniProt&#xff09; C-X-C基序趋化因子(C-X-C motif chemok…

绿色节能|AIRIOT智慧建材能耗管理解决方案

建材供应是建筑业不可或缺的一个重要环节&#xff0c;在环保和企业可持续发展的双重需求下&#xff0c;建材生产商对建材生产过程中的能耗掌握和能耗管理尤其关注。但在实际生产和运营过程中&#xff0c;传统的建材能耗管理方式往往存在如下痛点&#xff1a; 用户管理权限不完善…

什么是单点登录?

单点登录&#xff08;Single Sign On&#xff0c;简称 SSO&#xff09;简单来说就是用户只需在一处登录&#xff0c;不用在其他多系统环境下重复登录。用户的一次登录就能得到其他所有系统的信任。 为什么需要单点登录 单点登录在大型网站应用频繁&#xff0c;比如阿里旗下有淘…

Unity发布webgl之后打开PDF文件,不使用js,不和浏览器交互

创建一个按钮&#xff0c;然后点击就会打开 在webgl下要使用这样的路径拼接&#xff0c;不然就会报错。 btnBook.onClick.AddListener(() >{var uri new System.Uri(Path.Combine(Application.streamingAssetsPath "/Books", "文档.pdf"));Debug.Log…

短视频矩阵系统---php7.40版本升级自研

短视频矩阵系统---php7.40版本升级自研 1.部署及搭建 相对于其他系统&#xff0c;该系统得开发及部署难度主要在各平台官方应用权限的申请上&#xff0c;据小编了解&#xff0c;目前抖音短视频平台部分权限内侧名额已满&#xff0c;巧妇难为无米之炊&#xff0c;在做相关程序…

Xilink 简单双口ram ip的读写仿真

简单双口RAM有两个端口Port A和port B,其中Port A用于写数据,Port B用于读数据,读写接口可以独立时钟工作。这一点和真双口RAM是有区别的,真双口RAM的A B两个Port都可以进行读写操作。 RAM是FPGA中重要的数据结构,可用于数据的缓存和跨时钟域信号处理。本文就xilin…

Java项目:71 ssm基于ssm+vue的外卖点餐系统+vue

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统功能 系统分为前台订餐和后台管理&#xff1a; 1.前台订餐 用户注册、用户登录、我的购物车、我的订单、商品列表 2.后台管理 商品管理&#xf…

基于OpenCV的图像处理案例之图像矫正(Python)

Index 目录索引 写在前面解决思路参考 写在前面 本文通过一个案例介绍如何使用OpenCV将倾斜的扫描文档图像进行水平矫正。 解决思路 因为扫描图像中的大部分文字倾斜后&#xff0c;同一行文字也在同一条直线&#xff0c;所以可以通过拟合直线来计算文本倾斜角度&#xff0c;…

《剑指 Offer》专项突破版 - 面试题 89 和 90 : 房屋偷盗和环形房屋偷盗(C++ 实现)

目录 面试题 89 : 房屋偷盗 面试题 90 : 环形房屋偷盗 面试题 89 : 房屋偷盗 题目&#xff1a; 输入一个数组表示某条街道上的一排房屋内财产的数目。如果这条街道上相邻的两幢房屋被盗就会自动触发报警系统。请计算小偷在这条街道上最多能偷取到多少财产。例如&#xff0c…