VR场景切换-微信小程序

VR场景切换-微信小程序

  • 业务需求
  • 开发软件
  • 技术方案
  • 技术文档
  • 微信小程序项目关键代码
    • app.json
    • app.js
    • index.json
    • index.js
    • index.wxml
    • index.wxss
    • Connector.js
    • Connector.wxml
    • Connector.wxss
  • 总结

业务需求

  1. 使用UnrealEngine4开发一套基于VR一体机(安卓系统)的应用程序
  2. 内容为720VR场景浏览,通过控制器在菜单中切换图标来控制切换相应场景,效果如下图
    VR场景切换-微信小程序_第1张图片
  3. 因为VR一体机浏览只有客户能看到场景内容,需开发一个微信小程序帮助操控切换场景引导客户浏览,微信小程序效果如下
    VR场景切换-微信小程序_第2张图片

开发软件

  1. UnrealEngine4
  2. 微信小程序

技术方案

方案1:通过WebSocket通信(因为需要后端,开发流程较长,所以不采用)
方案2:通过TCP通信(因为微信小程序不支持,所以不采用)
方案3:通过UDP通信(因为需求很简单又在局域网范围,所以采用)

  1. 开启VR一体机中的VR应用程序,初始化创建UdpSocket固定绑定端口号(如:10000),获取当前设备的IP,并显示IP(如:192.168.19.106)到界面上以便后续微信小程序发送消息所用
  2. 开启微信小程序,初始创建UdpSocket绑定随机端口号,在IP输入栏中输入VR一体机界面中显示的IP号,并发送消息到 192.168.19.106:10000,若成功收到返回数据则表示通信正常
  3. 自定义通信数据,区分消息指令,实现各类功能
  4. 微信小程序项目中准备好相应素材(场景缩略图,数据文件等)
    VR场景切换-微信小程序_第3张图片

技术文档

  1. 微信小程序项目所需的data.js
var projects = [
	{
		project_id:0,
		project_name:"BingFengMobile",
		scenes:[
			{
				scene_id:0,
				title:"前台",
				image:"BingFengMobile/images/reception.png",
			},
			{
				scene_id: 1,
				title: "展厅",
				image: "BingFengMobile/images/showroom.png",
			},
			{
				scene_id: 2,
				title: "入口",
				image: "BingFengMobile/images/enter.png",
			},
			{
				scene_id: 3,
				title: "休息区",
				image: "BingFengMobile/images/restroom.png",
			},
			{
				scene_id: 4,
				title: "工作区1",
				image: "BingFengMobile/images/work1.png",
			},
			{
				scene_id: 5,
				title: "工作区2",
				image: "BingFengMobile/images/work2.png",
			},
			{
				scene_id: 6,
				title: "工作区3",
				image: "BingFengMobile/images/work3.png",
			},
			{
				scene_id: 7,
				title: "会议室",
				image: "BingFengMobile/images/meetingroom.png",
			},
			{
				scene_id: 8,
				title: "走廊",
				image: "BingFengMobile/images/corridor.png",
			},
			{
				scene_id: 9,
				title: "电梯口",
				image: "BingFengMobile/images/elevator.png",
			}
		]
	},
	{
		project_id:1,
		project_name:"PaiMaiHang",
		scenes:[
			{
				scene_id:0,
				title:"默认",
				image:"",
			}
		]
	}
]

module.exports={
	projects:projects
}
  1. 自定义通信数据协议
/* 
* UDP发送字符串
* 字符串格式:"xxx:xxx"
*/
// 小程序发送通信连接消息内容:
"connect:request"
//VR程序回复通信连接消息内容:
“connect:ok”

// 小程序发送场景切换消息内容:
“scene:3” //注意:此处的3为场景的对应id
//若需要VR切换场景做回应则同上即可

微信小程序项目关键代码

app.json

//app.json
{
  "pages": [
    "pages/index/index",
    "pages/mine/mine"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle": "black"
  },
  "tabBar": {
    "color": "#999999",
    "selectedColor": "#006AB4",
    "backgroundColor": "#EEEEEE",
    "borderStyle":"white",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "./icons/home.png",
        "selectedIconPath": "./icons/home_s.png"
      },
      {
        "pagePath": "pages/mine/mine",
        "text": "我的",
        "iconPath": "./icons/mine.png",
        "selectedIconPath": "./icons/mine_s.png"
      }
    ]
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

app.js

// app.js
App({
  globalData:{
    udpSocket: null,
    locationPort: -1
  }
})

index.json

// index.json
{
  "navigationBarTitleText":"冰峰科技VR中控",
  "usingComponents": {
    "Connector":"../../components/Connector/Connector"
  }
}

index.js

// index.js
var projectData = require("../../data/data.js")
let util = require('../../utils/util.js')
var app = getApp()

Page({
  /**
   * 页面的初始数据
   */
  data: {
    // UDP
    ip: "192.168.19.100",
    port: 10000,
    connectState: false,

    // 项目数据
    projects: null,
    project: {
      project_id: 0,
      project_name: "BingFengMobile",
      scenes: null
    },

    // 被点击的场景索引
    curSceneIndex: 0
  },

  initUDP() {
    // 检测是否已经初始化udpSocket
    if (app.globalData.udpSocket != null) {
      this.setData({
        'locationUrl.port': app.globalData.locationPort
      })
      return
    }

    app.globalData.udpSocket = wx.createUDPSocket();
    if (app.globalData.udpSocket === null) {
      console.log('udpSocket创建失败')
      return
    }

    app.globalData.locationPort = app.globalData.udpSocket.bind()
    this.setData({
      'locationUrl.port': app.globalData.locationPort
    })

    app.globalData.udpSocket.onListening(res => {

    })

    app.globalData.udpSocket.onMessage(res => {
      console.log(res.message)
      let encodedString = util.newAb2Str(res.message)
      console.log(encodedString)
      let keyValue = encodedString.split(":")
      if (keyValue[0] === "connect") {
        if (keyValue[1] == "ok") {
          setTimeout(() => {
            this.setData({
              connectState: true
            })
          }, 500);
        }
      } else if (keyValue[0] === "scene") {

      }
    })
  },

  // 发送UDP消息 
  sendMessage(message) {
    let ip = this.data.ip
    let port = this.data.port

    app.globalData.udpSocket.send({
      address: ip,
      port: port,
      message: message
    })
  },

  // ip输入改变
  bindIpChanged(e) {
    this.setData({
      ip: e.detail.ip
    })
    console.log(this.data.ip)
  },

  // 点击连接设备
  connectDevice(e) {
    // console.log("connectDevice")
    this.setData({
      connectState: false
    })
    this.sendMessage("connect:request")
  },

  // 点击首页场景
  activeScene(e) {
    this.setData({
      curSceneIndex: e.currentTarget.dataset.index
    })
    let message = "scene:" + this.data.curSceneIndex;
    this.sendMessage(message)
  },

  getProjectInfo(id) {
    for (let index in this.data.projects) {
      if (this.data.projects[index].project_id == id) {
        return this.data.projects[index]
      }
    }
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {
    this.initUDP()

    this.setData({
      projects: projectData.projects
    })

    if (this.data.projects != null) {
      //获取项目id为0的项目信息,并赋值给 project
      this.setData({
        project: this.getProjectInfo(0)
      })
    }
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function() {},

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {},

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function() {},

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function() {},

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function() {},

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function() {},

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function() {}
})

index.wxml



  
  

  
    
      
      
        
        
        
          {
    {item.title}}
        
      
    
  

index.wxss

/*index.wxss*/
.scene_wrap{
  display:flex;
  flex-wrap: wrap;
  padding:5rpx;
  margin: 20rpx;
  justify-content: space-between;
}

.scene_item{
  width:32%;
  margin-bottom: 10rpx;
  border:5rpx solid rgba(0, 0, 0, 0);
}

.scene_item.active{
  border:4rpx solid #EF8536;
}

.scene_img{
  position: relative;
}
.scene_img image{
  width:100%;
}

.scene_title_wrap{
  position: absolute;
  bottom:0;
  left:0;
  width:100%;
  background: rgba(7, 0, 2, 0.5);
  display: flex;
  justify-content:center;
}

.scene_title{
  color:white;
  font-size: 28rpx;
  padding: 5rpx;
}

Connector.js

// components/Connector.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    ip: String,
    connectState:Boolean
  },

  /**
   * 组件的初始数据
   */
  data: {},

  /**
   * 组件的方法列表
   */
  methods: {
    bindIpInput(e) {
      let val = e.detail.value
      this.triggerEvent('ipEvent', {ip: val})
    },
    onConnect(e){
      this.triggerEvent('connectEvent')
    }
  }
})

Connector.wxml



  
    
      
      
      
      
        
      
    
  

  
    
    连接状态:
    {
    {connectState? '已连接' : '未连接'}}
  

Connector.wxss

/*components/Connector.wxss*/
.content {
  flex-basis: fit-content;
  display: flex;
  flex-direction: row;
  padding: 10rpx 30rpx;
  background: #fff;
}

.content .input-btn-group {
  flex: 1;
  display: flex;
  flex-direction: row;
}

.input-btn-group .text-input {
  flex: 1;
  font-size: 16px;
  height: 30px;
  border: 1px solid transparent;
  border-radius: 5px;
  padding: 3px 6px;
  margin: 0 10px 0 5px;
  background: #eee;
}

.input-btn-group .btn-connect {
  width: 80rpx;
  height: 80rpx;
  align-self: center;
}

.content_state {
  display: flex;
  flex-direction: row;
}

.scale_img {
  transform: scale(0.9, 0.9);
}

.img-state {
  width: 50rpx;
  height: 50rpx;
  color:red;
}

总结

  1. 微信小程序的UDP接收消息onMessage(res=>{})里,PC调试时需要用res.message.data获取数据,而真机调试或真机中需要用res.message才能获取数据。
  2. data.js中的缩略图名为中文时,PC调试可以正常显示,真机中图片却丢失了,于是保证图片路径中为英文,不过也有可能是data.js文件编码问题,不过没有测试转码后的结果。
  3. 测试时几次忘记手机需要与VR设备连接同一wifi保证在同一局域网,导致UDP消息接收不到,因为微信小程序UDP只支持局域网。

你可能感兴趣的:(微信小程序,UE4,Android,微信小程序,UE4,UDP)