WebGL改造笔记

Posted stalendp

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGL改造笔记相关的知识,希望对你有一定的参考价值。

这段时间需要把原有的一个老项目改造成WebGL版本,遇到了一些困难,这里将记录改造的一些情况。

用Node.js来管理游戏内容

var express = require('express');
var app = express();
app.get('/', function (req, res) 
  res.send('Hello World!');
);

app.use(express.static('tttt'));
var server = app.listen(8080, function () 
  var host = server.address().address;
  var port = server.address().port;
  console.log('Example app listening at http://%s:%s', host, port);
);

下面是webSocket的通信相关。

由于安全性问题,WebGL不支持TCPSocket,但是支持WebSockets;下面是几个socket的写法。

TCP Socket,用node.js运行:

var net = require('net');

var HOST = 'ipaddr';
var PORT = 123;

var b64encode = data => Buffer.from(data).toString('base64');  
var b64decode = data => Buffer.from(data, 'base64');  

var login1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
var login2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";

var client = new net.Socket();
client.connect(PORT, HOST, function() 

    console.log('CONNECTED TO: ' + HOST + ':' + PORT);
    // Write a message to the socket as soon as the client is connected, the server will receive it as message from the client 
    client.write(b64decode(login1));
    client.write(b64decode(login2));
);

// Add a 'data' event handler for the client socket
// data is what the server sent to this socket
client.on('data', function(data) 
    
    console.log('DATA: ' + b64encode(data));
    // Close the client socket completely
    client.destroy();
);

// Add a 'close' event handler for the client socket
client.on('close', function() 
    console.log('Connection closed');
);

WebSocket, 运行在浏览器里面:

hello websocket!!  Please see console to check the result!
<script>
(function(r)if(typeof exports==="object"&&typeof module!=="undefined")module.exports=r()else if(typeof define==="function"&&define.amd)define([],r)elsevar e;if(typeof window!=="undefined")e=windowelse if(typeof global!=="undefined")e=globalelse if(typeof self!=="undefined")e=selfelsee=thise.base64js=r())(function()var r,e,n;return function()function r(e,n,t)function o(i,a)if(!n[i])if(!e[i])var u=typeof require=="function"&&require;if(!a&&u)return u(i,!0);if(f)return f(i,!0);var d=new Error("Cannot find module '"+i+"'");throw d.code="MODULE_NOT_FOUND",dvar c=n[i]=exports:;e[i][0].call(c.exports,function(r)var n=e[i][1][r];return o(n?n:r),c,c.exports,r,e,n,t)return n[i].exportsvar f=typeof require=="function"&&require;for(var i=0;i<t.length;i++)o(t[i]);return oreturn r()("/":[function(r,e,n)"use strict";n.byteLength=c;n.toByteArray=v;n.fromByteArray=s;var t=[];var o=[];var f=typeof Uint8Array!=="undefined"?Uint8Array:Array;var i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";for(var a=0,u=i.length;a<u;++a)t[a]=i[a];o[i.charCodeAt(a)]=ao["-".charCodeAt(0)]=62;o["_".charCodeAt(0)]=63;function d(r)var e=r.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4")return r[e-2]==="="?2:r[e-1]==="="?1:0function c(r)return r.length*3/4-d(r)function v(r)var e,n,t,i,a;var u=r.length;i=d(r);a=new f(u*3/4-i);n=i>0?u-4:u;var c=0;for(e=0;e<n;e+=4)t=o[r.charCodeAt(e)]<<18|o[r.charCodeAt(e+1)]<<12|o[r.charCodeAt(e+2)]<<6|o[r.charCodeAt(e+3)];a[c++]=t>>16&255;a[c++]=t>>8&255;a[c++]=t&255if(i===2)t=o[r.charCodeAt(e)]<<2|o[r.charCodeAt(e+1)]>>4;a[c++]=t&255else if(i===1)t=o[r.charCodeAt(e)]<<10|o[r.charCodeAt(e+1)]<<4|o[r.charCodeAt(e+2)]>>2;a[c++]=t>>8&255;a[c++]=t&255return afunction l(r)return t[r>>18&63]+t[r>>12&63]+t[r>>6&63]+t[r&63]function h(r,e,n)var t;var o=[];for(var f=e;f<n;f+=3)t=(r[f]<<16&16711680)+(r[f+1]<<8&65280)+(r[f+2]&255);o.push(l(t))return o.join("")function s(r)var e;var n=r.length;var o=n%3;var f="";var i=[];var a=16383;for(var u=0,d=n-o;u<d;u+=a)i.push(h(r,u,u+a>d?d:u+a))if(o===1)e=r[n-1];f+=t[e>>2];f+=t[e<<4&63];f+="=="else if(o===2)e=(r[n-2]<<8)+r[n-1];f+=t[e>>10];f+=t[e>>4&63];f+=t[e<<2&63];f+="="i.push(f);return i.join(""),],,[])("/"));

// ===================================================================
var b64encode = data => base64js.fromByteArray(data);    
var b64decode = data => base64js.toByteArray(data);  

var login1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
var login2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";

var addr = 'ws://127.0.0.1:811';
var ws = new WebSocket(addr);
ws.onopen = function()
    console.log('Connected to: ' + addr);
    console.log('Try to login!!!');
    ws.send(b64decode(login1));
    ws.send(b64decode(login2));

ws.addEventListener("message", function(event) 
    console.log('Chrome Received: ' + b64encode(event.data));
    alert("Login OK!!");
);
</script>

webSocket Server,用Node.js运行:

const WebSocket = require('ws');

const wss = new WebSocket.Server( port: 811 );
wss.on('connection', function connection(ws) 
  ws.on('message', function incoming(msg) 
    console.log('received: %s', msg);
    setInterval(() => 
      ws.send(msg);
    , 1500);
  );
);

C# 端的TCP socket例子:

string serverIP = "192.168.1.1";
int port = 12345;
Socket clientSocket = null;
private IEnumerator oldSendLogin() 
    Debug.Log("try to connect!!");
    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    clientSocket.Blocking = true;
    var address = new IPEndPoint(IPAddress.Parse(serverIP), port);

    IAsyncResult result = clientSocket.BeginConnect(address, null, null);
    bool success = result.AsyncWaitHandle.WaitOne(5000);
    if (success) 
        clientSocket.EndConnect(result);
    
    clientSocket.Blocking = false;

    yield return StartCoroutine(trySocketSend());
    yield return StartCoroutine(trySocketReceive());


string login_param1 = "MwAAAAEAAAAHAAAAMTA5NzU5AAIAAAA5ABEAAAA5ZmYzZjA1MzcyOWQ0ZmE0AAEAAAAA";
string login_param2 = "sgAAACMAAABJAAAAAQYAMTA5NzU5NpZHUC/B+SHjMCVg5U/R0BQzxFqpNRyv0JlnhMMzBXK+x7nT6RiAuuxrijfvanzYHySvXnCPToRHuenl1t/k8iEAAAA0NTY5MjcxRkM2REMyOTJBNUMwNzQxOTcwREMwQjk3OAA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";

private bool isSended = false;
IEnumerator trySocketSend() 
    Debug.Log(">>>>>>> try socketSend!!!!");

    while (true && !isSended) 
        yield return null;
        var ret = clientSocket.Poll(0, SelectMode.SelectWrite);
        if (ret) 
            var buff = Convert.FromBase64String(login_param1);
            int n = clientSocket.Send(buff, 0, buff.Length, SocketFlags.None);
            Debug.Log("Send Data Byte: " + n);
            break;
        
    

    while (true && !isSended) 
        yield return null;
        var ret = clientSocket.Poll(0, SelectMode.SelectWrite);
        if (ret) 
            var buff = Convert.FromBase64String(login_param2);
            int n = clientSocket.Send(buff, 0, buff.Length, SocketFlags.None);
            Debug.Log("Send Data Byte: " + n);
            break;
        
    
    isSended = true;



IEnumerator trySocketReceive() 
    Debug.Log(">>>>>>> try trySocketReceive!!!!");

    while (true) 
        yield return null;
        var ret = clientSocket.Poll(0, SelectMode.SelectRead);
        if (ret) 
            var recvStream = new MemoryStream(64 * 1024);
            var n = clientSocket.Receive(recvStream.GetBuffer(), (int)recvStream.Position, 64 * 1024, SocketFlags.None);
            Debug.Log("Receive Data Byte: " + n);
        
    

C#端WebSocket的登陆逻辑,使用插件:Simple Web Sockets for Unity WebGL

public IEnumerator webSocketLogin()

    var login1 = Convert.FromBase64String(login_param1);
    var login2 = Convert.FromBase64String(login_param2);

    WebSocket w = new WebSocket(new Uri("ws://192.168.1.1:1234"));
    yield return StartCoroutine(w.Connect());

    w.Send(login1);
    w.Send(login2);

    int i = 0;
    while (true) 
        var reply = w.Recv();
        if (reply != null) 
            Debug.LogFormat("Received: 0 ==> 1", reply.Length, Convert.ToBase64String(reply));
            //   w.SendString("Hi there" + i++);
        
        if (w.error != null) 
            Debug.LogError("Error: " + w.error);
            break;
        
        yield return 0;
    
    w.Close();

一个带CORS和https功能的server

var path = require('path');
var express = require('express');

var fs = require('fs');
var http = require('http');
var https = require('https');
var privateKey  = fs.readFileSync('key.pem', 'utf8');
var certificate = fs.readFileSync('cert.pem', 'utf8');

var app = express();
app.get('/', function (req, res) 
  res.send('<a href="/index.html">Enter Game</a>');
);


app.use(express.static(
    path.join('.', "WebBuild"),
    
        setHeaders: (res) => 
             res.setHeader("Access-Control-Allow-Origin", "*");
				res.setHeader("Access-Control-Allow-Credentials", "*");
				res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT");
				res.setHeader("Access-Control-Allow-Headers", "Content-Type,Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time");
        
    
))


var credentials = key: privateKey, cert: certificate;

// your express configuration here

var httpServer = http.createServer(app);
var httpsServer = https.createServer(credentials, app);

httpServer.listen(8080);
httpsServer.listen(8443);

console.log(`Server Started, http on 8080, https on 8443`);

一个webSocket转socket的服务:

const net = require('net');
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');

// ============ proxy服务器地址  ===============
var HOST = 'xxxxxx';
var PORT = 123;

var WS_PORT = 811;   // webSocket 的监听端口

var SSL_CERT = "./cert.pem";
var SSL_KEY = "./key.pem";
// ============  proxy服务器地址 ===============

var port = parseInt(process.argv[2]);  
if(port>0) 
    WS_PORT = port;
 else 
    console.log(`port is not set or not a number, use default $WS_PORT`);


var b64encode = data => Buffer.from(data).toString('base64');
var b64decode = data => Buffer.from(data, 'base64');

// 日志相关
var curDay = -1;
Log = msg => 
    var date = new Date();
    var hour = date.getHours();
    var min  = date.getMinutes();
    var sec  = date.getSeconds();
    var mi = date.getMilliseconds();
    var rt = `[$hour >= 10 ? hour : "0" + hour:$min >= 10 ? min : "0" + min:$sec >= 10 ? sec : "0" + sec $mi >= 100 ? mi : ((mi >= 10 ? "0" : "00") + mi)]`;
   
    var day = date.getDate();
	if(curDay != day)   // 隔天,要显示日期信息
        curDay = day;
		var year = date.getFullYear();
		var month = date.getMonth() + 1;
		rt = `************** $year-$month >= 10 ? month : "0" + month-$day >= 10 ? day : "0" + day *****************\\r\\n` + rt;
	
    console.log(`$rt $msg`);


// ============ 下面是TcpSocket,辅助连接Proxy服务器和客户端  ===========

// referTo https://nodejs.org/api/net.html
var TcpSocket = function()   
    var _userId = 0;
    var _wsocket = null;
    var _socket = null;
    var _pendingSend = null;
    var _attachCnt = 0;
    var _alive = false;

    // 进行连接
    function init() 
        _alive = false;
        _socket = new net.Socket();

        _socket.on("connect", () => 
            _alive = true;
            if(_pendingSend!=null)   // 处理链接期间到来的数据
                while(_pendingSend.length > 0) 
                    _socket.write(_pendingSend.shift());  // TODO 判断状态,并进行异常处理
                
            
        );

        _socket.on('data', data => 
            if(_wsocket!=null && _wsocket.readyState === WebSocket.OPEN) 
                _wsocket.send(data); 
             
        );

        _socket.on('close', had_err => 
            _alive = false;
        );
    
        _socket.on('error', err =>  
            console.log("Error :: " + err);
        );
    ;
    init();

    var _close = () => 
        _attachCnt--;
        if(_attachCnt<=0) 
            if(_socket!=null) 
                _socket.end();  // 通知服务器关闭socket
                _socket = null;
            
            if(_wsocket!=null) 
                _wsocket.close();   
                _wsocket = null;    
            
            if(tcpDict[_userId]!=null) 
                delete tcpDict[_userId];
            
        
    ;

    var _trySend = data => 
        if(_socket == null) 
            return;
        
        if(_alive) 
            _socket.write(data);   //TODO 进行异常处理
         else 
            if(!_socket.connecting)   // 尝试恢复连接
                _socket.connect(PORT, HOST);
            
            if(_pendingSend == null) 
                _pendingSend = [];
            
            if(_pendingSend.length < 10)  // 10条之内的消息,做缓存
                _pendingSend.push(data);
             else 
                console.log("cache is full!!");
            
        
    ;

    return 
        close : _close,
        send : _trySend,
        get uid() 
            return _userId;
        ,
        attach: (ws, uid) => 
            _attachCnt++;
            if(_wsocket!=null) 
                _wsocket.terminate();
            
            _wsocket = ws;
            _userId = uid;
            if(_socket == null) 
                init();
            
            if(!_alive && !_socket.connecting) 
                _socket.connect(PORT, HOST);
            
        
    ;


var tcpDict = ;

// ============ 下面是WebSocket,负责和客户端连接 ================
// https://github.com/websockets/ws
var idReg = /\\/(\\d+)/;
const server = new https.createServer(
    cert: fs.readFileSync(SSL_CERT),
    key: fs.readFileSync(SSL_KEY)
  );
var wss = new WebSocket.Server( server );
wss.on('connection', (ws, req) => 
    var uid = parseInt(req.url.replace(idReg, "$1"));
    Log(`$wss.clients.size @ <$uid> $req.connection.remoteAddress`);  // Connected!
    
    if(uid <= 0)   // uid is not correct!
        console.log("userid is not correct!!!");
        ws.terminate();
        return;
     

    var tcp = tcpDict[uid];
    if(tcp==null) 
        tcp = new TcpSocket();
     
    tcpDict[uid] = tcp;
    tcp.attach(ws, uid);

    ws.on('message', msg =>  
        tcp.send(msg);
    );
    
    ws.on("close", () => 
        tcp.close();
        Log(`$wss.clients.size ~ <$tcp.uid> $req.connection.remoteAddress`);  // Disconnect
    );
);

server.listen(WS_PORT);
Log(`WebSocket Server listen on : $WS_PORT`);

===================下面是浏览器中相关编程 ==========================

从网路下载文本的方式

var url = "https://192.168.1.194:8443/StreamingAssets/AssetsBundles/LocalBundleMap.txt";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
xhr.responseType = 'json';
xhr.onload = function (evt) 
  if (xhr.status == 200) 
    var json = xhr.response;
    if (json) 
        console.log(json);
     
   else 
    console.error("error:",  xhr.status);
  
;
xhr.send();



var url = "https://192.168.1.194:8443/StreamingAssets/AssetsBundles/LocalBundleMap.txt";
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
xhr.responseType = 'blob';
xhr.onload = function (evt) 
  if (xhr.status == 200) 
    var val = xhr.response;
    if (val) 
        var reader = new FileReader();
        reader.addEventListener("loadend", function() 
            console.log(reader.result);
        );
        reader.readAsText(val);  // 读取成text
     
   else 
    console.error("Error: ",  xhr.status);
  
;
xhr.send();

IndexDB相关操作

参考:https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB

// md5 function
!function (n)  "use strict"; function t(n, t)  var r = (65535 & n) + (65535 & t); return (n >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r  function r(n, t)  return n << t | n >>> 32 - t  function e(n, e, o, u, c, f)  return t(r(t(t(e, n), t(u, f)), c), o)  function o(n, t, r, o, u, c, f)  return e(t & r | ~t & o, n, t, u, c, f)  function u(n, t, r, o, u, c, f)  return e(t & o | r & ~o, n, t, u, c, f)  function c(n, t, r, o, u, c, f)  return e(t ^ r ^ o, n, t, u, c, f)  function f(n, t, r, o, u, c, f)  return e(r ^ (t | ~o), n, t, u, c, f)  function i(n, r)  n[r >> 5] |= 128 << r % 32, n[14 + (r + 64 >>> 9 << 4)] = r; var e, i, a, d, h, l = 1732584193, g = -271733879, v = -1732584194, m = 271733878; for (e = 0; e < n.length; e += 16)i = l, a = g, d = v, h = m, g = f(g = f(g = f(g = f(g = c(g = c(g = c(g = c(g = u(g = u(g = u(g = u(g = o(g = o(g = o(g = o(g, v = o(v, m = o(m, l = o(l, g, v, m, n[e], 7, -680876936), g, v, n[e + 1], 12, -389564586), l, g, n[e + 2], 17, 606105819), m, l, n[e + 3], 22, -1044525330), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 4], 7, -176418897), g, v, n[e + 5], 12, 1200080426), l, g, n[e + 6], 17, -1473231341), m, l, n[e + 7], 22, -45705983), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 8], 7, 1770035416), g, v, n[e + 9], 12, -1958414417), l, g, n[e + 10], 17, -42063), m, l, n[e + 11], 22, -1990404162), v = o(v, m = o(m, l = o(l, g, v, m, n[e + 12], 7, 1804603682), g, v, n[e + 13], 12, -40341101), l, g, n[e + 14], 17, -1502002290), m, l, n[e + 15], 22, 1236535329), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 1], 5, -165796510), g, v, n[e + 6], 9, -1069501632), l, g, n[e + 11], 14, 643717713), m, l, n[e], 20, -373897302), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 5], 5, -701558691), g, v, n[e + 10], 9, 38016083), l, g, n[e + 15], 14, -660478335), m, l, n[e + 4], 20, -405537848), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 9], 5, 568446438), g, v, n[e + 14], 9, -1019803690), l, g, n[e + 3], 14, -187363961), m, l, n[e + 8], 20, 1163531501), v = u(v, m = u(m, l = u(l, g, v, m, n[e + 13], 5, -1444681467), g, v, n[e + 2], 9, -51403784), l, g, n[e + 7], 14, 1735328473), m, l, n[e + 12], 20, -1926607734), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 5], 4, -378558), g, v, n[e + 8], 11, -2022574463), l, g, n[e + 11], 16, 1839030562), m, l, n[e + 14], 23, -35309556), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 1], 4, -1530992060), g, v, n[e + 4], 11, 1272893353), l, g, n[e + 7], 16, -155497632), m, l, n[e + 10], 23, -1094730640), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 13], 4, 681279174), g, v, n[e], 11, -358537222), l, g, n[e + 3], 16, -722521979), m, l, n[e + 6], 23, 76029189), v = c(v, m = c(m, l = c(l, g, v, m, n[e + 9], 4, -640364487), g, v, n[e + 12], 11, -421815835), l, g, n[e + 15], 16, 530742520), m, l, n[e + 2], 23, -995338651), v = f(v, m = f(m, l = f(l, g, v, m, n[e], 6, -198630844), g, v, n[e + 7], 10, 1126891415), l, g, n[e + 14], 15, -1416354905), m, l, n[e + 5], 21, -57434055), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 12], 6, 1700485571), g, v, n[e + 3], 10, -1894986606), l, g, n[e + 10], 15, -1051523), m, l, n[e + 1], 21, -2054922799), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 8], 6, 1873313359), g, v, n[e + 15], 10, -30611744), l, g, n[e + 6], 15, -1560198380), m, l, n[e + 13], 21, 1309151649), v = f(v, m = f(m, l = f(l, g, v, m, n[e + 4], 6, -145523070), g, v, n[e + 11], 10, -1120210379), l, g, n[e + 2], 15, 718787259), m, l, n[e + 9], 21, -343485551), l = t(l, i), g = t(g, a), v = t(v, d), m = t(m, h); return [l, g, v, m]  function a(n)  var t, r = "", e = 32 * n.length; for (t = 0; t < e; t += 8)r += String.fromCharCode(n[t >> 5] >>> t % 32 & 255); return r  function d(n)  var t, r = []; for (r[(n.length >> 2) - 1] = void 0, t = 0; t < r.length; t += 1)r[t] = 0; var e = 8 * n.length; for (t = 0; t < e; t += 8)r[t >> 5] |= (255 & n.charCodeAt(t / 8)) << t % 32; return r  function h(n)  return a(i(d(n), 8 * n.length))  function l(n, t)  var r, e, o = d(n), u = [], c = []; for (u[15] = c[15] = void 0, o.length > 16 && (o = i(o, 8 * n.length)), r = 0; r < 16; r += 1)u[r] = 909522486 ^ o[r], c[r] = 1549556828 ^ o[r]; return e = i(u.concat(d(t)), 512 + 8 * t.length), a(i(c.concat(e), 640))  function g(n)  var t, r, e = ""; for (r = 0; r < n.length; r += 1)t = n.charCodeAt(r), e += "0123456789abcdef".charAt(t >>> 4 & 15) + "0123456789abcdef".charAt(15 & t); return e  function v(n)  return unescape(encodeURIComponent(n))  function m(n)  return h(v(n))  function p(n)  return g(m(n))  function s(n, t)  return l(v(n), v(t))  function C(n, t)  return g(s(n, t))  function A(n, t, r)  return t ? r ? s(t, n) : C(t, n) : r ? m(n) : p(n)  "function" == typeof define && define.amd ? define(function ()  return A ) : "object" == typeof module && module.exports ? module.exports = A : n.md5 = A (this);

// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction ||  READ_WRITE: "readwrite" ; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

if (!window.indexedDB) 
    window.alert("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
 else 
    console.log("the browser is OK!! --- ");


const DB_NAME = '/idbfs';
const DB_STORE = "FILE_DATA";

var db;
var req = window.indexedDB.open(DB_NAME);
var baseDir = `/idbfs/$md5(window.location.href.replace(/(https?.*)\\/[^\\/]*/, "$1"))`;
console.log(baseDir);

function bin2Str(array) 
    var result = "";
    for (var i = 0; i < array.length; ++i) 
        result += (String.fromCharCode(array[i]));
    
    return result;


req.onsuccess = function () 
    // Better use "this" than "req" to the result to 
    // avoid problems with garbage collection
    db = this.result;
    var fn = `$baseDir/AssetsBundles/LocalBundleMap.txt`;
    console.log("==> " + fn);
    db.transaction(DB_STORE).objectStore(DB_STORE).get(fn).onsuccess = evt => 
        var obj = evt.target.result;
        var d = JSON.parse(bin2Str(obj.contents));
        //     console.log(JSON.stringify(d));
    ;
;
req.onerror = evt => 
    console.error("Error openDB:", evt.target.errorCode);
;

req.onupgradeneeded = evt => 
    console.log("openDb.onupgradeneeded");
    // var store = evt.currentTarget.result.createObjectStore(
    //     DB_STORE, keyPath: 'id', autoIncrement: true );
    // store.createIndex('title', 'title',  unique: false );
;

javascript中的协程的用法:

参考:https://x.st/javascript-coroutines/

function coroutine(f) 
    var o = f(); // instantiate the coroutine
    o.next(); // execute until the first yield
    return function(x) 
        o.next(x);
    


var doJob = coroutine(function*()
    console.log("init");
    var x = yield;
    console.log("First I got : " + x);
    var y = yield;
    console.log("Then I got: " + y);
);

doJob('a dog');
doJob('a cat');

JavaScript的枚举:

    var JobStatus = Object.freeze(
        UnInited:1,  
        DBReady:2, 
        BundleMapReady:3,
        LocalResReady: 4,
        );

Promise的用法 :

function get(url) 
  // Return a new promise.
  return new Promise(function(resolve, reject) 
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() 
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) 
        // Resolve the promise with the response text
        resolve(req.response);
      
      else 
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      
    ;

    // Handle network errors
    req.onerror = function() 
      reject(Error("Network Error"));
    ;

    // Make the request
    req.send();
  );

//Now let's use it:

get('story.json').then(function(response) 
  console.log("Success!", response);
, function(error) 
  console.error("Failed!", error);
)

Promise的动态串联:

getJson('story.json').then(function (story) 
    addHtmlToPage(story.heading);

    return story.chapterUrls.reduce(function (chain, chapterUrl) 
        // Once the last chapter's promise is done…
        return chain.then(function () 
            // …fetch the next chapter
            return getJson(chapterUrl);
        ).then(function (chapter) 
            // and add it to the page
            addHtmlToPage(chapter.html);
        );
    , Promise.resolve());
).then(function () 
    // And we're all done!
    addTextToPage("All done");
).catch(function (err) 
    // Catch any error that happened along the way
    addTextToPage("Argh, broken: " + err.message);
).then(function () 
    // Always hide the spinner
    document.querySelector('.spinner').style.display = 'none';
);

async函数的基本写法

参考:点击打开链接

// async的写法:
async function logFetch(url) 
  try 
    const response = await fetch(url);
    console.log(await response.text());
  
  catch (err) 
    console.log('fetch failed', err);
  

// 相对于的promise的写法
function logFetch(url) 
  return fetch(url)
    .then(response => response.text())
    .then(text => 
      console.log(text);
    ).catch(err => 
      console.error('fetch failed', err);
    );

 

批量下载例子:

 

参考:https://developers.google.com/web/fundamentals/primers/async-functions

In following examples, the URLs are fetched and read in parallel, but the "smart" reduce bit is replaced with a standard, boring, readable for-loop.

 

// 方法1:
function logInOrder(urls) 
  // fetch all the URLs
  const textPromises = urls.map(url => 
    return fetch(url).then(response => response.text());
  );

  // log them in order
  textPromises.reduce((chain, textPromise) => 
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  , Promise.resolve());


// 方法2:
async function logInOrder(urls) 
  // fetch all the URLs in parallel
  const textPromises = urls.map(async url => 
    const response = await fetch(url);
    return response.text();
  );

  // log them in sequence
  for (const textPromise of textPromises) 
    console.log(await textPromise);
  

 

================

 

C# 和  JavaScript的交互中的回调函数 

参考: emscripten代码 emscripten文档  unity论坛   一个日文的文章    官方解决方案(SendMessage)

C#端:

// C#端对javascript的申明
[DllImport("__Internal")]
public static extern void callFuncStalendp(string strEx, System.Action<System.IntPtr> func);

// 回调函数
[MonoPInvokeCallback(typeof(System.Action))]
public static void callback(System.IntPtr ptr) 
    var val = Marshal.PtrToStringAuto(ptr);
    Debug.LogError("The function is called!! " + val);


// 调用
FacebookHelper.callFuncStalendp("C#端", callback);

jslib端:

var LibraryFackbookHelper = 
    
    callFuncStalendp : function(msgEx, callback) 
        var msg = Pointer_stringify(msgEx);
        var hello = "hello world! 你好!";
        if (callback) 
            var stack = Runtime.stackSave();
            Runtime.dynCall('vi', callback, [allocate(intArrayFromString(msg + hello), 'i8', ALLOC_STACK)]);
            Runtime.stackRestore(stack);
        
    

;

mergeInto(LibraryManager.library, LibraryFackbookHelper);

关于Jslib的注意点,

var LibraryWonHelper = 
    $WonCommon: 
        str2Ptr : function(str) 
            return str && allocate(intArrayFromString(str), 'i8', ALLOC_STACK) || null;
        ,
        invoke: function (callback, methodId, isOK, returnVal) 
            var stack = Runtime.stackSave();
            Runtime.dynCall('viii', callback, [methodId, isOK, WonCommon.str2Ptr(returnVal)]);
            Runtime.stackRestore(stack);
        
    ,
    wbgl_won_login: function (wonId, callback)  // TYPE 0
        WonWrapper.login(Pointer_stringify(wonId)).then(function (id, token) 
            WonCommon.invoke(callback, 0, 1, JSON.stringify([id, token]));
        ).catch(function (err) 
            WonCommon.invoke(callback, 0, 0);
            console.error(err);
        );
    ,
    wbgl_won_token: function () 
        return WonCommon.str2Ptr(WonWrapper.getToken());
    
;
autoAddDeps(LibraryWonHelper, '$WonCommon');
mergeInto(LibraryManager.library, LibraryWonHelper);

函数的声明,

1. 都要使用function(...) 的形式,不能用 (param1, parram2) => 这种形式。

2. 导出的函数(在c#中 用[DllImport("__Internal")] 申明的),会在js代码中生成对应的函数(但是函数名前面加了"_")。比如wbgl_won_tokens生成的函数名是 _wbgl_won_tokens。这个代码是生成在mudule中的,在javascript不能够被直接调用。如果想直接调用,在合适的时机,把这些函数绑定到全局变量中。没有被导出的函数,是不会生成js代码的。

关于合适的时机,可以写一个函数,在unity启动的时候调用,并进行代码的注册。

3. jslib中的公用代码,参考上面例子中的 $WonCommon(注意要用autoAddDeps注册一下)

==============

Cookie的辅助类

let CookieHelper = 
    setCookie : (cname, cvalue, exdays) => 
        let d = new Date();
        d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
        document.cookie = `$cname=$cvalue;expires=$d.toUTCString();path=/`;
    , 
    getCookie : cname => 
        return document.cookie.replace(new RegExp(`(?:(?:^|.*;\\\\s*)$cname\\\\s*=\\\\s*([^;]*).*$)|^.*$`, 'i'), "$1");
    ,
    delCookie : cname => 
        document.cookie = `$cname=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
    
;

=============

异步加载类

let loadScript = (src, id) => 
    return new Promise(function (resolve, reject) 
        if (document.getElementById(id)) 
            reject("already loaded");
          else 
            let s = document.createElement('script');
            s.id = id;
            s.src = src;
            s.type = 'text/javascript';
            s.onload = resolve;
            s.onerror = reject;
            document.head.appendChild(s);
        
    );

===========

单例模式

var UnityLoader = UnityLoader ||  .... 

 

以上是关于WebGL改造笔记的主要内容,如果未能解决你的问题,请参考以下文章

Webgl 学习笔记-术语集锦

[笔记]《webGL编程指南》- WebGL入门

WebGL《WebGL编程指南》读书笔记——第五章

WebGL《WebGL编程指南》读书笔记——第六章

WebGL《WebGL编程指南》读书笔记——第四章

Webgl笔记