UnityController.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <div class="main-container">
  3. <!-- Unity Canvas 容器 -->
  4. <div id="unity-container" class="unity-desktop">
  5. <canvas id="unity-canvas" ref="unityCanvas"></canvas>
  6. <div id="unity-loading-bar">
  7. <div id="unity-logo"></div>
  8. <div id="unity-progress-bar-empty">
  9. <div id="unity-progress-bar-full" :style="{ width: progress + '%' }"></div>
  10. </div>
  11. </div>
  12. <div id="unity-warning"></div>
  13. <div id="unity-footer">
  14. <div id="unity-webgl-logo"></div>
  15. <div id="unity-fullscreen-button" @click="setFullscreen">⛶</div>
  16. </div>
  17. </div>
  18. <!-- 控制面板 -->
  19. <div class="controls">
  20. <h2>模型控制器</h2>
  21. <!-- 创建 / 销毁模块 -->
  22. <div class="control-group">
  23. <h3>创建 / 销毁</h3>
  24. <label>模型ID:
  25. <input v-model="create.modelId" type="text" />
  26. </label>
  27. <label>模型类型:
  28. <select v-model="create.modelType">
  29. <option value="red">red</option>
  30. <option value="green">green</option>
  31. <option value="blue">blue</option>
  32. </select>
  33. </label>
  34. <label>位置 (x,y,z):
  35. <input v-model="create.position" type="text" />
  36. </label>
  37. <label>旋转 (x,y,z):
  38. <input v-model="create.rotation" type="text" />
  39. </label>
  40. <button @click="createModel">创建模型</button>
  41. <button @click="destroyModel">销毁模型</button>
  42. </div>
  43. <!-- 控制模块 -->
  44. <div class="control-group">
  45. <h3>控制指定ID的模型</h3>
  46. <label>目标ID:
  47. <input v-model="control.modelId" type="text" />
  48. </label>
  49. <hr />
  50. <label>新位置:
  51. <input v-model="control.position" type="text" />
  52. </label>
  53. <label>新旋转:
  54. <input v-model="control.rotation" type="text" />
  55. </label>
  56. <button @click="setModelTransform">设置变换</button>
  57. <hr />
  58. <label>颜色:
  59. <input v-model="control.color" type="color" />
  60. </label>
  61. <button @click="changeModelColor">改变颜色</button>
  62. </div>
  63. </div>
  64. </div>
  65. </template>
  66. <script>
  67. export default {
  68. name: 'UnityController',
  69. data() {
  70. return {
  71. unityInstance: null, // 存储 Unity 实例
  72. progress: 0, // 加载进度
  73. // 创建表单数据
  74. create: {
  75. modelId: 'my_web_cube_1',
  76. modelType: 'red',
  77. position: '0,0.5,0',
  78. rotation: '0,0,0'
  79. },
  80. // 控制表单数据
  81. control: {
  82. modelId: 'my_web_cube_1',
  83. position: '2,1,0',
  84. rotation: '0,90,0',
  85. color: '#ff0000'
  86. }
  87. }
  88. },
  89. mounted() {
  90. this.loadUnity()
  91. },
  92. methods: {
  93. loadUnity() {
  94. const container = document.getElementById('unity-container')
  95. const canvas = this.$refs.unityCanvas
  96. const loadingBar = document.getElementById('unity-loading-bar')
  97. const progressBarFull = document.getElementById('unity-progress-bar-full')
  98. const fullscreenButton = document.getElementById('unity-fullscreen-button')
  99. const warningBanner = document.getElementById('unity-warning')
  100. function unityShowBanner(msg, type) {
  101. const div = document.createElement('div')
  102. div.innerHTML = msg
  103. warningBanner.appendChild(div)
  104. if (type === 'error') {
  105. div.style = 'background: red; padding: 10px; color: white;'
  106. } else {
  107. if (type === 'warning') div.style = 'background: yellow; padding: 10px;'
  108. setTimeout(() => {
  109. warningBanner.removeChild(div)
  110. }, 5000)
  111. }
  112. }
  113. // 假设 Unity 文件放在 public/unity 下
  114. const buildUrl = '/3DModel/Build'
  115. const config = {
  116. dataUrl: `${buildUrl}/Build.data`,
  117. frameworkUrl: `${buildUrl}/Build.framework.js`,
  118. codeUrl: `${buildUrl}/Build.wasm`,
  119. streamingAssetsUrl: 'StreamingAssets',
  120. companyName: 'DefaultCompany',
  121. productName: 'My project',
  122. productVersion: '0.1',
  123. showBanner: unityShowBanner
  124. }
  125. // 移动端适配
  126. if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
  127. container.className = 'unity-mobile'
  128. canvas.className = 'unity-mobile'
  129. } else {
  130. canvas.style.width = '960px'
  131. canvas.style.height = '600px'
  132. }
  133. loadingBar.style.display = 'block'
  134. const script = document.createElement('script')
  135. script.src = '/3DModel/Build/Build.loader.js' // 路径需匹配
  136. script.onload = () => {
  137. createUnityInstance(canvas, config, (progress) => {
  138. this.progress = 100 * progress
  139. progressBarFull.style.width = this.progress + '%'
  140. })
  141. .then((instance) => {
  142. this.unityInstance = instance
  143. loadingBar.style.display = 'none'
  144. })
  145. .catch((message) => {
  146. alert(message)
  147. })
  148. }
  149. document.body.appendChild(script)
  150. },
  151. // 发送消息到 Unity 的通用方法
  152. sendMessage(methodName, data) {
  153. if (!this.unityInstance) {
  154. alert('Unity 实例尚未加载完成!')
  155. return
  156. }
  157. this.unityInstance.SendMessage('WebGLBridgeManager', methodName, JSON.stringify(data))
  158. },
  159. createModel() {
  160. this.sendMessage('CreateModel', {
  161. modelId: this.create.modelId,
  162. modelType: this.create.modelType,
  163. position: this.create.position,
  164. rotation: this.create.rotation
  165. })
  166. },
  167. destroyModel() {
  168. this.sendMessage('DestroyModel', {
  169. modelId: this.create.modelId
  170. })
  171. },
  172. setModelTransform() {
  173. this.sendMessage('SetModelTransform', {
  174. modelId: this.control.modelId,
  175. position: this.control.position,
  176. rotation: this.control.rotation
  177. })
  178. },
  179. changeModelColor() {
  180. const hex = this.control.color
  181. const r = parseInt(hex.slice(1, 3), 16) / 255
  182. const g = parseInt(hex.slice(3, 5), 16) / 255
  183. const b = parseInt(hex.slice(5, 7), 16) / 255
  184. this.sendMessage('ChangeModelColor', {
  185. modelId: this.control.modelId,
  186. r, g, b
  187. })
  188. },
  189. setFullscreen() {
  190. if (this.unityInstance) {
  191. this.unityInstance.SetFullscreen(1)
  192. }
  193. }
  194. }
  195. }
  196. </script>
  197. <style scoped>
  198. .main-container {
  199. display: flex;
  200. align-items: flex-start;
  201. gap: 20px;
  202. padding: 20px;
  203. font-family: Arial, sans-serif;
  204. }
  205. /* Unity 容器样式 */
  206. #unity-container {
  207. flex: 1;
  208. min-width: 900px;
  209. border: 1px solid #ccc;
  210. border-radius: 8px;
  211. background: #000;
  212. position: relative;
  213. }
  214. #unity-canvas {
  215. width: 100%;
  216. height: 700px;
  217. display: block;
  218. }
  219. #unity-loading-bar {
  220. display: none;
  221. position: absolute;
  222. top: 50%;
  223. left: 50%;
  224. transform: translate(-50%, -50%);
  225. width: 200px;
  226. height: 20px;
  227. background: rgba(0, 0, 0, 0.8);
  228. border-radius: 10px;
  229. padding: 4px;
  230. }
  231. #unity-progress-bar-empty {
  232. width: 100%;
  233. height: 100%;
  234. background: #444;
  235. border-radius: 8px;
  236. overflow: hidden;
  237. }
  238. #unity-progress-bar-full {
  239. height: 100%;
  240. background: #0078d4;
  241. width: 0;
  242. transition: width 0.2s;
  243. }
  244. /* 控制面板样式 */
  245. .controls {
  246. width: 300px;
  247. }
  248. .control-group {
  249. border: 1px solid #ccc;
  250. padding: 15px;
  251. border-radius: 8px;
  252. background-color: #f9f9f9;
  253. box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  254. }
  255. h3 {
  256. margin-top: 0;
  257. color: #333;
  258. }
  259. label {
  260. display: block;
  261. margin-bottom: 5px;
  262. font-size: 14px;
  263. color: #555;
  264. }
  265. input[type="text"], select, input[type="color"] {
  266. width: 100%;
  267. padding: 8px;
  268. margin-bottom: 10px;
  269. border: 1px solid #ccc;
  270. border-radius: 4px;
  271. box-sizing: border-box;
  272. }
  273. button {
  274. width: 100%;
  275. padding: 10px;
  276. background-color: #0078d4;
  277. color: white;
  278. border: none;
  279. border-radius: 4px;
  280. cursor: pointer;
  281. font-weight: bold;
  282. margin-bottom: 10px;
  283. }
  284. button:hover {
  285. background-color: #005a9e;
  286. }
  287. hr {
  288. border: 0;
  289. border-top: 1px solid #eee;
  290. margin: 15px 0;
  291. }
  292. </style>