【Unity3D日常开发】Unity3D中适用WEBGL打开Window文件对话框打开/上传文件

news2025/1/12 3:10:03

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • QQ群:398291828
  • 小红书
  • 小破站

大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。

一、前言

Unity3D发布的WEBGL程序是不支持直接的I/O操作,所以也就不能直接访问用户电脑的文件。

但是,也是有办法去访问电脑中的文件,打开文件的。

比如情况一:如果知道电脑文件在什么位置、什么名字。

可以参考这篇文章:
【Unity3D小技巧】Unity3D中打包WEBGL后读取本地文件数据

情况二:知道要上传什么类型的文件,但是需要弹出窗口来选择文件并打开。

这篇文章就来讨论一下WEBGL打开Window文件对话框并打开文件后上传文件,显示图片/文本。

二、实现过程

先来看一下效果:
在这里插入图片描述

2-1、实现分析

既然Unity3D没办法直接进行I/O操作,但是JavaScript可以进行I/O操作,那是不是就可以让Unity跟JS进行通信,来进行操作。

JavaScript打开文件的代码很简单:

<input type="file" /></input>

那么就可以用Unity的按钮去调用这个组件,从这个组件中获取到文件流。

OK,理论存在,实践开始。

2-2、Unity3D中做的准备

(1)在Unity的工程中新建Plugins文件夹:
在这里插入图片描述
(2)在里面新建WebGl.jslib文件,编辑代码,这个文件是JS与WEBGL交互用的。

详情参照Unity官网:https://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html

文件里面的内容如下:

mergeInto(LibraryManager.library, {
  clickSelectFileBtn:function () {
    console.log("Enter");
    clickSelectFileBtn();
  },
});

(3)搭建Unity程序:
在这里插入图片描述
一个Button用来响应Js函数,一个Image用来显示图片,Text用来显示文本内容。

(3)在Hierarchy视图新建一个Scripts对象(名字需要跟我一样,因为后面要跟js脚本),挂载脚本,脚本名随意:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

public class ReceiveDataHandle : MonoBehaviour
{
    public Button BtnLoad;
    public Image showImg;
    public Text showText;

    [DllImport("__Internal")]
    private static extern void clickSelectFileBtn();
    /// <summary>
    /// 点击Open按钮
    /// </summary>
    public static void BtnLoadEvent()
    {
        clickSelectFileBtn();
    }

    void Start()
    {
        BtnLoad.onClick.AddListener(BtnLoadEvent);
    }

    /// <summary>
    /// 接收base64字符串
    /// </summary>
    /// <param name="base64Str"></param>
    public void GetBase64(string base64Str)
    {
        Debug.Log(base64Str);

        string[] split = base64Str.Split('|');
        if (split[0].Contains("jpg"))
        {
            StartCoroutine(ReadTexture(split[1], LoadImg));
        }
        else if (split[0].Contains("txt"))
        {
            StartCoroutine(ReadData(split[1], LoadText));
        }
        else
        {
            Debug.Log("文件格式错误");
            return;
        }
        
    }

    /// <summary>
    /// 读取图片
    /// </summary>
    /// <param name="path"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    IEnumerator ReadTexture(string path,UnityAction<Texture> action)
    {
        Debug.Log(path);
        UnityWebRequest request = UnityWebRequestTexture.GetTexture(path);
        yield return request.SendWebRequest();
        if (request.isNetworkError || request.isHttpError)
        {
            Debug.Log(request.error);
        }
        else
        {
            byte[] imgdata = request.downloadHandler.data;

            action(DownloadHandlerTexture.GetContent(request));
        }
    }

    /// <summary>
    /// 加载图片
    /// </summary>
    /// <param name="texture"></param>
    void LoadImg(Texture texture)
    {
        Sprite ImgSprite = Sprite.Create((Texture2D)texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f));
        showImg.sprite = ImgSprite;
    }

    /// <summary>
    /// 读取文本
    /// </summary>
    /// <param name="path"></param>
    /// <param name="action"></param>
    /// <returns></returns>
    IEnumerator ReadData(string path, UnityAction<string> action)
    {
        Debug.Log(path);
        UnityWebRequest request = UnityWebRequest.Get(path);
        yield return request.SendWebRequest();
        if (request.isNetworkError || request.isHttpError)
        {
            Debug.Log(request.error);
        }
        else
        {
            Debug.Log(request.downloadHandler.text);
            action(request.downloadHandler.text);
        }
    }

    /// <summary>
    /// 加载文本
    /// </summary>
    /// <param name="text"></param>
    void LoadText(string text)
    {
        showText.text = text;
    }
}

注意:
clickSelectFileBtn函数用来调用Js函数,GetBase64函数用来接收数据。

2-3、打包WEBGL后修改

注意:我用的Unity2019.4.9f1版本。高版本打包出来的index.html以及Build里面的文件跟我的不一致,这个后面有空再说如何匹配高版本吧,先用2019版本演示。

打包之后:
在这里插入图片描述
在这里插入图片描述
修改index.html文件:

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | Demo_WEBGLLoadFile</title>
    <link rel="shortcut icon" href="TemplateData/favicon.ico">
    <link rel="stylesheet" href="TemplateData/style.css">
    <script src="TemplateData/UnityProgress.js"></script>
    <script src="Build/UnityLoader.js"></script>
    <script>
      var unityInstance = UnityLoader.instantiate("unityContainer", "Build/WEBGL_LoadFile.json", {onProgress: UnityProgress});
	  
	 function clickSelectFileBtn() {
        var tempFileLayout = document.getElementById('files');
        tempFileLayout.click();
     }
	      function fileImport() {
        //获取读取我文件的File对象
        var selectedFile = document.getElementById('files').files[0];
        if (selectedFile != null) {
            var reader = new FileReader();
            reader.readAsDataURL(selectedFile);
            reader.onload = function (e) {
                var base64Str = e.currentTarget.result.substring(e.currentTarget.result.indexOf(',') + 1);
                sendMessageToUnity(base64Str);
            }
        }
     }
	 
	  function sendMessageToUnity(s) {
        //发送给unity
		var file=document.getElementById('files').files[0];
        SetFileUrlByDragEnd(file);
     }

	  function SelectFile(){
            var file=document.getElementById('files').files[0];
            SetFileUrlByDragEnd(file);
        }
        function SetFileUrlByDragEnd(file) {
            unityInstance.SendMessage("Scripts", "GetBase64", file.name+"|"+URL.createObjectURL(file));
        }
    </script>
  </head>
  <body>
   <input type="file" id="files" style="display:none" onchange="fileImport()">
    <div class="webgl-content">
      <div id="unityContainer" style="width: 960px; height: 600px"></div>
      <div class="footer">
        <div class="webgl-logo"></div>
        <div class="fullscreen" onclick="unityInstance.SetFullscreen(1)"></div>
        <div class="title">Demo_WEBGLLoadFile</div>
      </div>
    </div>
  </body>
</html>

代码解释一下:
(1)首先是加了一个<input type="file" id="files" style="display:none" onchange="fileImport()">用来响应打开文件的操作。

(2)然后用Unity程序的Button响应clickSelectFileBtn函数,去触发这个组件。

(3)添加fileImport去处理打开的文件得到文件流。

(4)获得文件名和文件路径,通过unityInstance.SendMessage("Scripts", "GetBase64", file.name+"|"+URL.createObjectURL(file));传给Unity

(5)URL.createObjectURL(file)可以获得本地文件的缓存url,直接发给Unity,然后Unity用来加载。

2-4、效果演示

在这里插入图片描述

三、后记

如果觉得本篇文章有用别忘了点个关注,关注不迷路,持续分享更多Unity干货文章。


你的点赞就是对博主的支持,有问题记得留言:

博主主页有联系方式。

博主还有跟多宝藏文章等待你的发掘哦:

专栏方向简介
Unity3D开发小游戏小游戏开发教程分享一些使用Unity3D引擎开发的小游戏,分享一些制作小游戏的教程。
Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的路线,有C#和Unity的知识。
Unity3D之UGUIUGUIUnity的UI系统UGUI全解析,从UGUI的基础控件开始讲起,然后将UGUI的原理,UGUI的使用全面教学。
Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。
Unity3D之数据集合数据集合数组集合:数组、List、字典、堆栈、链表等数据集合知识分享。
Unity3D之VR/AR(虚拟仿真)开发虚拟仿真总结博主工作常见的虚拟仿真需求进行案例讲解。
Unity3D之插件插件主要分享在Unity开发中用到的一些插件使用方法,插件介绍等
Unity3D之日常开发日常记录主要是博主日常开发中用到的,用到的方法技巧,开发思路,代码分享等
Unity3D之日常BUG日常记录记录在使用Unity3D编辑器开发项目过程中,遇到的BUG和坑,让后来人可以有些参考。

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

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

相关文章

ElasticSearch内存占用率过高怎么办?

文章目录 1&#xff0c;先用top看看各个进程的内存占用情况2&#xff0c;不能简单的杀死进程&#xff0c;然后再重启。3&#xff0c;查看一下ElasticSearch进程的具体启动情况4&#xff0c;修改Elasticsearch 的Java堆内存 1&#xff0c;先用top看看各个进程的内存占用情况 先…

DC/AC并网逆变器模型与仿真MATLAB

DC/AC并网逆变器是一种将直流电&#xff08;DC&#xff09;转化为交流电&#xff08;AC&#xff09;&#xff0c;并将其与电网并联的设备。它的核心功能是实现直流电源&#xff08;如光伏电池板或储能电池&#xff09;与电网的有效连接&#xff0c;同时保证输出电能质量满足电网…

Flink三种集群部署模型

这里写自定义目录标题 Flink 集群剖析Flink 应用程序执行Flink Session 集群&#xff08;Session Mode&#xff09;Flink Job 集群&#xff08;以前称为per-job&#xff09;Flink Application 集群&#xff08;Application Mode&#xff09; 参考 Flink 集群剖析 Flink 运行时…

JVM实战—12.OOM的定位和解决

大纲 1.如何对系统的OOM异常进行监控和报警 2.如何在JVM内存溢出时自动dump内存快照 3.Metaspace区域内存溢出时应如何解决(OutOfMemoryError: Metaspace) 4.JVM栈内存溢出时应如何解决(StackOverflowError) 5.JVM堆内存溢出时应该如何解决(OutOfMemoryError: Java heap s…

一文读懂「LoRA」:大型语言模型的低秩适应

LoRA: Low-Rank Adaptation of Large Language Models 前言 LoRA作为大模型的微调框架十分实用&#xff0c;在LoRA出现以前本人都是通过手动修改参数、优化器或者层数来“炼丹”的&#xff0c;具有极大的盲目性&#xff0c;但是LoRA技术能够快速微调参数&#xff0c;如果LoRA…

IT面试求职系列主题-人工智能(一)

想成功求职&#xff0c;必要的IT技能一样不能少&#xff0c;再从人工智能基础知识来一波吧。 1&#xff09;您对人工智能的理解是什么&#xff1f; 人工智能是计算机科学技术&#xff0c;强调创造能够模仿人类行为的智能机器。这里智能机器可以定义为能够像人一样行动、像人一…

浏览器报错:您的连接不是私密连接,Kubernetes Dashboard无法打开

问题描述 部署完成Kubernetes Dashboard后&#xff0c;打开HTTPS的web页面&#xff0c;Chrome和Edge浏览器都无法正常加载页面&#xff0c;会提示您的连接不是私密连接的报错。 ​​​​​​​​​​​​ 原因&#xff1a; 浏览器不信任这些自签名的ssl证书&#xff0c;为了…

【Unity插件】解决移动端UI安全区问题 - Safe Area Helper

在移动端设计界面时&#xff0c;必须要考虑的就是UI的安全区。 Unity本身也提供了Safearea的API。 但在asset store时已经有人提供了免费的插件&#xff08;Safe Area Helper&#xff09;&#xff0c;我们可以直接使用。 插件链接&#xff1a; https://assetstore.unity.com/p…

ffmpeg7.0 aac转pcm

#pragma once #define __STDC_CONSTANT_MACROS #define _CRT_SECURE_NO_WARNINGSextern "C" { #include "libavcodec/avcodec.h" }//缓冲区大小&#xff08;缓存5帧数据&#xff09; #define AUDIO_INBUF_SIZE 40960 /*name depthu8 8s16 …

USRP X310 Windows 烧录镜像

说明 USRP-X 系列设备包含两个用于两个以太网通道的 SFP 端口。由于 SFP 端口支持 1 千兆 (SFP) 和 10 千兆 (SFP) 收发器&#xff0c;因此 UHD 附带了多个 FPGA 图像&#xff0c;以确定上述接口的行为。 注意&#xff1a;Aurora 图像需要从 FPGA 源代码手动构建。 FPGA 图像…

新型物联网智能断路器功能参数介绍

安科瑞刘鸿鹏 摘要 智能断路器作为现代配电系统的重要组成部分&#xff0c;以其实时监测、多重保护和远程操控的智能化功能&#xff0c;显著提升了电力系统的运行效率和安全性。本文以ASCB1系列智能断路器为例&#xff0c;探讨其技术特点和在工业、商业及民用建筑中的应用价…

119.使用AI Agent解决问题:Jenkins build Pipeline时,提示npm ERR! errno FETCH_ERROR

目录 1.Jenkins Build时的错误 2.百度文心快码AI智能体帮我解决 提问1&#xff1a;jenkins中如何配置npm的源 提问2&#xff1a;jenkins pipeline 类型为pipeline script from SCM时&#xff0c;如何配置npm源 3.最终解决方法-Jenkinsfile的修改 4.感触 1.Jenkins Build时…

pytest+allure 入门

使用allure如何生成自动化测试报​​​​​​告 &#xff1f;一文详解allure的使用 。_allure测试报告-CSDN博客 例子&#xff1a; import allure import pytest import osallure.epic("闹钟") allure.feature("闹钟增删") class TestSchedule():def setu…

【FPGA】时序约束与分析

设计约束 设计约束所处环节&#xff1a; 约束输入 分析实现结果 设计优化 设计约束分类&#xff1a; 物理约束&#xff1a;I/O接口约束&#xff08;例如引脚分配、电平标准设定等物理属性的约束&#xff09;、布局约束、布线约束以及配置约束 时序约束&#xff1a;设计FP…

【Vim Masterclass 笔记09】S06L22:Vim 核心操作训练之 —— 文本的搜索、查找与替换操作(第一部分)

文章目录 S06L22 Search, Find, and Replace - Part One1 从光标位置起&#xff0c;正向定位到当前行的首个字符 b2 从光标位置起&#xff0c;反向查找某个字符3 重复上一次字符查找操作4 定位到目标字符的前一个字符5 单字符查找与 Vim 命令的组合6 跨行查找某字符串7 Vim 的增…

win32汇编环境,窗口程序中对按钮控件常用操作的示例

;运行效果 ;win32汇编环境&#xff0c;窗口程序中对按钮控件常用操作的示例 ;常用的操作&#xff0c;例如创建按钮控件&#xff0c;使其无效&#xff0c;改变文本&#xff0c;得到文本等。 ;将代码复制进radasm软件里&#xff0c;直接就可以编译运行。重点部分加备注。 ;>&g…

继承(7)

大家好&#xff0c;今天我们继续来学习一下继承的知识&#xff0c;这方面需要大家勤动脑才能理解&#xff0c;那么我们来看。 1.9 protected关键字 在类和对象章节中&#xff0c;为了实现封装特性,java中引入访向限定符,主要限定:类或者类中成员能否在类外和其他包中被访问. …

基于RK3568/RK3588大车360度环视影像主动安全行车辅助系统解决方案,支持ADAS/DMS

产品设计初衷 HS-P2-2D是一款针对大车盲区开发的360度全景影像 安全行车辅助系统&#xff0c;通过车身四周安装的超广角像机&#xff0c;经算法合成全景鸟瞰图&#xff0c;通过鸟瞰图&#xff0c;司机非常清楚的看清楚车辆四周情况&#xff0c;大大降低盲区引发的交通事故。 产…

NVIDIA发布GeForce RTX 50 系列,售价549美元起

2025 CES消费电子展&#xff08;1月7日至10日&#xff0c;美国拉斯维加斯&#xff09;正式开幕。北京时间1月7日 (星期二)上午10:30&#xff0c;NVIDIA举办主题演讲&#xff0c;CEO黄仁勋担任主讲。正式发布了全新的RTX 50系列显卡&#xff01;一月下旬上市。同时公布了各版本的…

后端:Spring(IOC、AOP)

文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…