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

推荐阅读

  • 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和坑,让后来人可以有些参考。

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

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

相关文章

《安富莱嵌入式周报》第348期:开源低功耗测试仪,开源创意万用表,续航100-300小时,开源PCB电机,自制shell和网络协议栈,开源水培自动化系统

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 视频版&#xff1a; https://www.bilibili.com/video/BV1Tzr9Y3EQ7/ 《安富莱嵌入式周报》第348期&#xff1a;开源低功…

音频合成的常见问题

使用了1年多的音频合成&#xff0c;有些常见的问题分享给大家 。 一、音质问题 噪声 背景噪声&#xff1a;在音频合成过程中&#xff0c;可能会引入背景噪声。这可能是由于原始音频素材本身质量不佳&#xff0c;比如录制环境嘈杂&#xff0c;包含电脑风扇声、外界交通声等。当…

用AI技术提升Flutter开发效率:ScriptEcho的力量

引言 在当今快速发展的技术时代&#xff0c;Flutter作为一种跨平台开发框架&#xff0c;正在越来越多的开发者中崭露头角。它不仅能够为开发者提供一套代码同时部署到iOS和Android平台的解决方案&#xff0c;还能帮助企业节省人力成本和开发时间。然而&#xff0c;对于新手开发…

SmartAIChain荣获重要认可

2024年12月21日,洛杉矶尔湾市——在今年尔湾举办的交流会上,黄荣先生的SmartAIChain项目获得了重要认可。此次活动汇聚了来自各地的杰出人才以及社区代表,共同庆祝这一创新性艺术的时刻。 在活动中,核桃市议员伍立伦(Allen Wu)的代表黄冬平女士发言,并为黄荣先生颁发了荣誉证书…

EFT信号测试和电源测试经验笔记

1. 什么是EFT 标准&#xff1a;perlEC 61000-4-4 eft设备将群脉冲干扰加到信号或者电源上&#xff0c;常见的频率是 5K 100K 两个频率 电压 电源3k&#xff0c;信号2k -----电网设备 电源4K -------------------空调设备 大概就是下图这样的周期性脉冲 1.1 电源eft 通过信…

web前端学习总结(一)

web前端使用三项技术:html、css、javascript. 一、html:超文本标记语言&#xff0c;用于展示网页的框架。 <html> <head><title> </title></head><body><div> </div> <!--用于布局&#xff0c;占1行 --><span&g…

【web靶场】之upload-labs专项训练(基于BUUCTF平台)

前言 该靶场&#xff0c;是通过平台BUUCTF在线评测中的靶场进行的&#xff0c;基于linux搭建的 当然若是想要该靶场&#xff0c;可以采用github上的醒目&#xff0c;点击后面文字即可访问c0ny1/upload-labs: 一个想帮你总结所有类型的上传漏洞的靶场 或者本人分享在网盘中&a…

ES 的倒排索引

目录 什么是 elasticSearch。 什么是倒排索引 Term Index 是什么 Stored Fields 是什么 Doc Values 是什么 segment lucene 是什么 高性能 高扩展性 高可用 Node 角色分化 去中心化 ES 是什么? ES 和 Kafka 的架构差异 ES 的写入流程 ES 的搜索流程 查询阶段…

微服务电商平台课程六:后端代码框架认识

本地环境搭建好,大家可以进行调试,并能够修改其中代码。 后端技术栈 Spring Boot是伴随着Spring4.0共同诞生的,它的目的就是简化spring的配置及开发,并协助开发人员可以整体管理应用程序的配置而不再像以前那样需要做大量的配置工作,它提供了很多开发组件,并且内嵌了we…

Unity教程(二十)战斗系统 角色反击

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

Garnet:微软官方基于.Net 8开源缓存系统,可无需任何改动直接替代Redis,而且还更高性能!

近日微软官方开源了一个开源缓存系统&#xff0c;可完全替代Redis。 01 项目简介 Garnet是微软官方基于.Net 8开发的、开源高性能远程缓存存储系统&#xff0c;支持单节点、集群。 Garnet使用的是当前流行的 RESP 协议&#xff0c;使得当前大多数编程语言现成的 Redis 客户端…

java项目之网上点餐系统源码(springboot+mysql+vue)

大家好我是风歌&#xff0c;曾担任某大厂java架构师&#xff0c;如今专注java毕设领域。今天要和大家聊的是一款基于springboot的网上点餐系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 网上点餐系统的主要使用者分为管理员登录…

深入Android架构(从线程到AIDL)_23 活用IBinder接口于近程通信01

1、 在同一进程里活用IBinder接口 议题 1. myActivity对象是谁创建的呢? 2. myService对象是谁创建的呢? 3. 当myService类里有个f1()函数&#xff0c;如何去调用它呢? 4. 必须先取得myService对象的指针&#xff0c;才能调用f1()函数去存取对象的属性(Attribute)值。 …

拥抱HarmonyOS之高效使用DevEco

拥抱HarmonyOS之高效使用DevEco 前半年对公司的IM SDK进行了鸿蒙化&#xff0c;半年过去系统已经伴随APP上线应用市场&#xff0c;总结一些适配过程中的经验技巧&#xff0c;希望对大家有用。 1.应用AI翻译代码 IM SDK和普通应用的最大差异是逻辑性代码比较多&#xff0c;使…

RT-DETR代码详解(官方pytorch版)——参数配置(1)

前言 RT-DETR虽然是DETR系列&#xff0c;但是它的代码结构和之前的DETR系列代码不一样。 它是通过很多的yaml文件进行参数配置&#xff0c;和之前在train.py的parser argparse.ArgumentParser()去配置所有参数不同&#xff0c;所以刚开始不熟悉代码的时候可能不知道在哪儿修…

Halcon在linux及ARM上的安装及c++工程化

一、HALCON下载 建议到HALCON官方下载页选择linux版本下载,压缩包名为MVTec_HALCON_Progress-18.11.0.1-linux(x64-aarch64-armv7a)-FullVersion.tar.gz。下载前需要登录HALCON帐号,如果没有请自行注册,填写一些基本信息然后激活邮件,操作方便简易。 下载许可证文件 该许…

单通道串口服务器(三格电子)

一、产品介绍 1.1 功能简介 SG-TCP232-110 是一款用来进行串口数据和网口数据转换的设备。解决普通 串口设备在 Internet 上的联网问题。 设备的串口部分提供一个 232 接口和一个 485 接口&#xff0c;两个接口内部连接&#xff0c;同 时只能使用一个口工作。 设 备 的网 口…

Figma如何装中文字体-PingFang苹方字体、Alibaba PuHuiTi阿里普惠

**写在前面&#xff1a; 工具类软件更新迭代如此快的世界&#xff0c;不能靠历史知识来做操作反应。需要着眼于当下工具的形态来思考用法。另外&#xff0c;有人说&#xff0c;当前的用户越来越少发教程类的图文消息了&#xff08;转去了视频&#xff09;&#xff0c;现在很多…

JVM实战—13.OOM的生产案例

大纲 1.每秒仅上百请求的系统为何会OOM(RPC超时时间设置过长导致QPS翻几倍) 2.Jetty服务器的NIO机制如何导致堆外内存溢出(S区太小 禁NIO的显式GC) 3.一次微服务架构下的RPC调用引发的OOM故障排查实践(MAT案例) 4.一次没有WHERE条件的SQL语句引发的OOM问题排查实践(使用MA…

Photoshop PS批处理操作教程(批量修改图片尺寸、参数等)

前言 ‌Photoshop批处理的主要作用‌是通过自动化处理一系列相似的操作来同时应用于多张图片&#xff0c;从而节省时间和精力&#xff0c;提高工作效率。批处理功能特别适用于需要批量处理的任务&#xff0c;如图像尺寸调整、颜色校正、水印添加等‌。 操作步骤 1.创建动作 …