0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內(nèi)不再提示

OpenHarmony南向開發(fā)實例:【游戲手柄】

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-04-17 10:21 ? 次閱讀

介紹

基于TS擴展的聲明式開發(fā)范式編程語言,以及OpenHarmony的分布式能力實現(xiàn)的一個手柄游戲。

完成本篇Codelab需要兩臺開發(fā)板,一臺開發(fā)板作為游戲端,一臺開發(fā)板作為手柄端,實現(xiàn)如下功能:

  • 游戲端呈現(xiàn)飛機移動、發(fā)射子彈等效果。
  • 游戲端分布式拉起手柄端FA。
  • 手柄端與游戲端建立連接,發(fā)送指令給游戲端,比如移動飛機,發(fā)射子彈和釋放技能等。

最終效果圖如下:

GamePlane1.gif

搭建OpenHarmony環(huán)境

完成本篇Codelab我們首先要完成開發(fā)環(huán)境的搭建,本示例以RK3568開發(fā)板為例,參照以下步驟進行:

  1. [獲取OpenHarmony系統(tǒng)版本]:標準系統(tǒng)解決方案(二進制)。
    以3.1版本為例:
  2. 搭建燒錄環(huán)境。
    1. [完成DevEco Device Tool的安裝]
    2. [完成RK3568開發(fā)板的燒錄]
    3. 鴻蒙開發(fā)指導:[qr23.cn/AKFP8k]
  3. 搭建開發(fā)環(huán)境。
    1. 開始前請參考[工具準備],完成DevEco Studio的安裝和開發(fā)環(huán)境配置。
    2. 開發(fā)環(huán)境配置完成后,請參考[使用工程向?qū)創(chuàng)建工程(模板選擇“Empty Ability”),選擇JS或者eTS語言開發(fā)。
    3. 工程創(chuàng)建完成后,選擇使用[真機進行調(diào)測]。
      2.鴻蒙next文檔籽料+mau123789直接去v拿取

搜狗高速瀏覽器截圖20240326151547.png

分布式組網(wǎng)

本章節(jié)以系統(tǒng)自帶的音樂播放器為例(具體以實際的應用為準),介紹如何完成兩臺設備的分布式組網(wǎng)。

  1. 硬件準備:準備兩臺燒錄相同的版本系統(tǒng)的RK3568開發(fā)板A、B。

  2. 開發(fā)板A、B連接同一個WiFi網(wǎng)絡
    打開設置-->WLAN-->點擊右側(cè)WiFi開關(guān)-->點擊目標WiFi并輸入密碼。

  3. 將設備A,B設置為互相信任的設備。

    • 找到系統(tǒng)應用“音樂”。

    • 設備A打開音樂,點擊左下角流轉(zhuǎn)按鈕,彈出列表框,在列表中會展示遠端設備的id。
    • 選擇遠端設備B的id,另一臺開發(fā)板(設備B)會彈出驗證的選項框。
    • 設備B點擊允許,設備B將會彈出隨機PIN碼,將設備B的PIN碼輸入到設備A的PIN碼填入框中。


    配網(wǎng)完畢。

代碼結(jié)構(gòu)解讀

  • [HandleEtsOpenHarmony]
  • [GameEtsOpenHarmony]

本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在參考章節(jié)中提供下載方式,首先介紹一下整個工程的代碼結(jié)構(gòu):

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

其中HandleEtsOpenHarmony為手柄端工程代碼,GameEtsOpenHarmony為游戲端工程代碼。

HandleEtsOpenHarmony

  • MainAbility:存放應用主頁面。
    • pages/index.ets:應用主頁面。
    • common/images:存放圖片資源的目錄。
  • ServiceAbility:存放ServiceAbility相關(guān)文件。
    • service.ts:service服務,用于跨設備連接后通訊。

GameEtsOpenHarmony

  • MainAbility:存放應用主頁面。
    • pages/index.ets:應用主頁面。
    • common/images:存放圖片資源。
  • model:存放獲取組網(wǎng)內(nèi)的設備列表相關(guān)文件。
    • RemoteDeviceModel.ets:獲取組網(wǎng)內(nèi)的設備列表。
    • GameElement.ets:游戲端界面元素的實體類,用于封裝子彈、飛機等元素的屬性。
  • ServiceAbility:存放ServiceAbility相關(guān)文件。
    • service.ts:service服務,用于跨設備連接后通訊。

實現(xiàn)手柄端功能

  1. 實現(xiàn)布局和樣式。
    手柄端有兩個功能:向游戲端發(fā)送指令和實時獲取游戲端得分數(shù)據(jù)。界面上有三個功能組件:藍色圖形組件用于控制游戲端飛機移動方向,黃色圖形組件用于發(fā)射子彈,綠色圖形組件用于釋放技能,效果圖如下:

    主要代碼如下:
    @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. 實現(xiàn)搖桿功能。
    給搖桿(藍色小圓圖形)添加TouchEvent,動態(tài)改變搖桿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。
    當手柄端被游戲端拉起時,獲取游戲端傳遞的數(shù)據(jù):游戲端deviceId和分數(shù)score。然后通過deviceId連接游戲端Service,主要代碼如下:
    aboutToAppear() {
      // 當被拉起時,通過want傳遞的參數(shù)同步對端界面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發(fā)送數(shù)據(jù)到游戲端。
    連接游戲端Service之后,搖桿角度angle和操作類型actionType(1為發(fā)射子彈,2為釋放技能)發(fā)送給游戲端,主要代碼如下:
    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);
    }
    

實現(xiàn)游戲端功能

  1. 實現(xiàn)布局和樣式。
    游戲界面主要由玩家飛機、敵機、子彈和道具(降落傘)等組成,由于敵機和子彈都是多個的,所以使用ForEach來實現(xiàn),主要代碼如下:
    @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. 實現(xiàn)游戲端元素動畫效果。
    飛機、子彈和道具等元素的移動是通過動態(tài)改變Image的position屬性來實現(xiàn)的。使用定時器setInterval每隔16ms重新設置界面元素position屬性的值,主要實現(xiàn)代碼如下:
    startGame() {
        var that = this
        setInterval(function () {   
          // 每60*16ms創(chuàng)建一個敵機
          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. 判斷元素是否發(fā)生碰撞。
    在setInterval中改變元素位置的時候同時檢測元素之間是否發(fā)生碰撞,子彈和敵機發(fā)生碰撞則分數(shù)值改變(摧毀小飛機加50分,摧毀大飛機加100分),玩家飛機和道具發(fā)生碰撞則道具加1,主要實現(xiàn)代碼如下:
    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);
            // 發(fā)生碰撞
            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--;
            }
          }
        }
        // 飛機和降落傘是否發(fā)生碰撞
        var isGetProps = this.isInside(myPlane, props);
        if (isGetProps && this.getPropsFlag) {
          this.getPropsFlag = false
          this.bombNum++
          this.propsPosY = 2000
        }
      }
    
  4. 獲取設備列表。
    點擊界面右上角的“電腦”圖標,調(diào)用registerDeviceListCallback()發(fā)現(xiàn)設備列表,并彈出設備列表選擇框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) {
      // 連接成功的回調(diào)
      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發(fā)送數(shù)據(jù)到手柄端。
    通過RPC將游戲分數(shù)發(fā)送給手柄端,主要代碼如下:
    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發(fā)布公共事件。
    通過Service接收手柄端數(shù)據(jù),然后使用CommonEvent模塊將數(shù)據(jù)發(fā)送給FA,主要代碼如下:
    class GameServiceAbilityStub extends rpc.RemoteObject {
    ...
        onRemoteRequest(code, data, reply, option) {
            console.log('[game]Service onRemoteRequest');
            var publishCallBack;
            if (code === 1) {
                // 讀取手柄端發(fā)送的數(shù)據(jù)
                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 () {}
                // 發(fā)布公共事件
                commonEvent.publish("publish_action", options, publishCallBack);
            } 
            return true;
        }
    }
    
  9. FA訂閱公共事件。
    訂閱公共事件,接收從Service發(fā)送的公共事件數(shù)據(jù),actionType 為操作類型(1表示發(fā)送子彈指令,2表示釋放技能指令),angle 為飛機移動的角度。接收到數(shù)據(jù)后執(zhí)行手柄端發(fā)送的指令:移動玩家飛機、發(fā)射子彈和釋放技能摧毀所有敵機,主要代碼如下:
    subscribeEvent() {
    ...
      // 訂閱公共事件回調(diào)
      function SubscribeCallBack(err, data) {
        let msgData = data.data;
        let code = data.code;
    ...
        // 處理接收到的數(shù)據(jù)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()
        }
      }
      //創(chuàng)建訂閱者回調(diào)
      function CreateSubscriberCallBack(err, data) {
        subscriber = data;
        //訂閱公共事件
        commonEvent.subscribe(subscriber, SubscribeCallBack);
      }
      //創(chuàng)建訂閱者
      commonEvent.createSubscriber(subscribeInfo, CreateSubscriberCallBack);
    }
    

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2351

    瀏覽量

    42849
  • OpenHarmony
    +關(guān)注

    關(guān)注

    25

    文章

    3722

    瀏覽量

    16313
收藏 人收藏

    評論

    相關(guān)推薦

    OpenHarmony南向能力征集令

    1、適配過程中缺少哪些接口能力或者南向能力,需要OpenHarmony去補齊的?例如內(nèi)核、編譯、器件適配、單板適配等; 2、對標linux,需要OpenHarmony提供哪些能力?比如V4L2
    發(fā)表于 04-09 15:32

    鴻蒙OpenHarmony南向/北向快速開發(fā)教程-迅為RK3568開發(fā)

    P2_OpenHarmony功能框架 P3_OpenHarmony技術(shù)特性 P4_OpenHarmony支持設備類型 p5_南向開發(fā)和北向
    發(fā)表于 07-23 10:44

    求教 游戲手柄

    游戲手柄發(fā)出的信號是什么啊 ???不一樣的手柄一樣的不???
    發(fā)表于 12-01 18:37

    藍牙游戲手柄

    藍牙游戲手柄是怎樣實現(xiàn)控制智能手機的游戲的?請大神賜教!
    發(fā)表于 03-18 15:04

    vr藍牙手柄方案 藍牙游戲手柄方案

    。DayDream手柄,小米手柄方案開發(fā),九軸體感手柄,隨心所欲體驗各類游戲視頻,10毫秒超低延時,待機電流0.6微安。優(yōu)勢:VR
    發(fā)表于 04-23 16:47

    HarmonyOS教程—分布式游戲手柄

    大招的***)。c) 游戲結(jié)束,大屏端顯示所有玩家得分,手柄端顯示自己得分。 2. 搭建HarmonyOS環(huán)境我們首先需要完成HarmonyOS開發(fā)環(huán)境搭建,可參照如下步驟進行。安裝DevEco
    發(fā)表于 09-08 14:13

    游戲手柄按鍵的設計資料分享

    一、背景近期開發(fā)了一個空鼠遙控器的外設產(chǎn)品,採用Nordic51822 MCU芯片,基于BLE4.0標準,與OTT盒子連接,同一時候具有遙控器、空鼠、游戲手柄的功能。當中在按鍵的設計這塊我們走了一些
    發(fā)表于 11-11 09:12

    OpenHarmony開發(fā)板運行俄羅斯方塊游戲

    本案例展示在OpenHarmony開發(fā)板上運行俄羅斯方塊游戲, 通過12864液晶屏進行顯示. 項目底層通過OpenHarmony的HDF框架來驅(qū)動, 并基于linkboy圖形引擎編程
    發(fā)表于 12-03 17:27

    如何通過STM32來驅(qū)動FC游戲機手柄

    相信80后小時候都玩過FC游戲機(又稱:紅白機/小霸王游戲機),那是一代經(jīng)典,給童年帶來了無限樂趣。本章,介紹如何通過STM32來驅(qū)動FC游戲機手柄,將FC游戲機的
    發(fā)表于 01-05 07:57

    基于 OpenHarmony 拳擊健康游戲應用

    樣例簡介拳擊健康游戲應用是基于OpenHarmony 3.2 Beta標準系統(tǒng)上開發(fā)的eTS應用,本應用運行于RK3568,游戲開始會隨著音樂播放會拳擊方庫進行隨機速度下落,樣例利用N
    發(fā)表于 08-31 11:20

    藍牙吃雞游戲手柄方案分解資料

    藍牙吃雞游戲手柄方案 藍牙吃雞游戲手柄方案開發(fā)游戲手柄
    發(fā)表于 03-31 10:54 ?4253次閱讀

    OpenHarmony Dev-Board-SIG專場:南向共建案例分享

    OpenHarmony南向共建案例分享: 審核編輯:金巧
    的頭像 發(fā)表于 12-28 14:16 ?964次閱讀
    <b class='flag-5'>OpenHarmony</b> Dev-Board-SIG專場:<b class='flag-5'>南向</b>共建案例分享

    基于2.4G RF開發(fā)的無線游戲手柄解決方案

    平時喜歡玩游戲的朋友,肯定知道鍵鼠在某些類型的游戲適配和操作方面,不如手柄。作為一個游戲愛好者,還得配上一個游戲
    的頭像 發(fā)表于 08-08 18:02 ?1285次閱讀

    游戲手柄震動馬達的技術(shù)特點分析

    游戲手柄震動馬達在現(xiàn)代電子游戲中扮演著重要的角色,其技術(shù)特點直接影響到玩家的游戲體驗。游戲手柄
    的頭像 發(fā)表于 10-09 11:12 ?338次閱讀
    <b class='flag-5'>游戲</b><b class='flag-5'>手柄</b>震動馬達的技術(shù)特點分析

    OpenHarmony人才生態(tài)大會南向生態(tài)社區(qū)發(fā)展論壇在武漢圓滿舉辦

    11月27日,OpenHarmony人才生態(tài)大會2024在武漢隆重舉行。當日下午的 OpenHarmony南向生態(tài)社區(qū)發(fā)展論壇(以下簡稱“論壇”)上,眾多社區(qū)伙伴、企業(yè)代表、技術(shù)專家與優(yōu)秀開發(fā)
    的頭像 發(fā)表于 11-29 10:06 ?176次閱讀
    <b class='flag-5'>OpenHarmony</b>人才生態(tài)大會<b class='flag-5'>南向</b>生態(tài)社區(qū)發(fā)展論壇在武漢圓滿舉辦