I am building a location based game in unity as a WebGL build. The file was building before I added my script changes but the location input was not working on site (Hosted on itch.io). I then added a javascript file and some changes to the location services (The location input script was imported from Mapbox api) using chatgpt. Now the file itself is not building.
javascript file:
var EXPORTED_FUNCTIONS = ['_requestWebGLLocation'];
mergeInto(LibraryManager.library, {
requestWebGLLocation: function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
function(position) {
unityInstance.SendMessage('LocationProviderFactory',
'OnLocationReceived',
position.coords.latitude + ',' +
position.coords.longitude);
},
function(error) {
console.error('Error obtaining location: ', error);
}
);
} else {
console.error('Geolocation is not supported by this browser.');
}
}
})
Location Input code:
#if !UNITY_EDITOR
#define NOT_UNITY_EDITOR
#endif
namespace Mapbox.Unity.Location
{
using UnityEngine;
using Mapbox.Unity.Map;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
/// <summary>
/// Singleton factory to allow easy access to various LocationProviders.
/// This is meant to be attached to a game object.
/// </summary>
public class LocationProviderFactory : MonoBehaviour
{
[SerializeField]
public AbstractMap mapManager;
[SerializeField]
[Tooltip("Provider using Unity's builtin 'Input.Location' service")]
AbstractLocationProvider _deviceLocationProviderUnity;
[SerializeField]
[Tooltip("Custom native Android location provider. If this is not set above
provider is used")]
DeviceLocationProviderAndroidNative _deviceLocationProviderAndroid;
[SerializeField]
AbstractLocationProvider _editorLocationProvider;
[SerializeField]
AbstractLocationProvider _transformLocationProvider;
[SerializeField]
bool _dontDestroyOnLoad;
[DllImport("__Internal")]
private static extern void requestWebGLLocation();
/// <summary>
/// The singleton instance of this factory.
/// </summary>
private static LocationProviderFactory _instance;
public static LocationProviderFactory Instance
{
get
{
return _instance;
}
private set
{
_instance = value;
}
}
ILocationProvider _defaultLocationProvider;
/// <summary>
/// The default location provider.
/// Outside of the editor, this will be a <see
cref="T:Mapbox.Unity.Location.DeviceLocationProvider"/>.
/// In the Unity editor, this will be an <see
cref="T:Mapbox.Unity.Location.EditorLocationProvider"/>
/// </summary>
/// <example>
/// Fetch location to set a transform's position:
/// <code>
/// void Update()
/// {
/// var locationProvider =
LocationProviderFactory.Instance.DefaultLocationProvider;
/// transform.position =
Conversions.GeoToWorldPosition(locationProvider.Location,
///
MapController.ReferenceTileRect.Center,
///
MapController.WorldScaleFactor).ToVector3xz();
/// }
/// </code>
/// </example>
public ILocationProvider DefaultLocationProvider
{
get
{
return _defaultLocationProvider;
}
set
{
_defaultLocationProvider = value;
}
}
/// <summary>
/// Returns the serialized <see
cref="T:Mapbox.Unity.Location.TransformLocationProvider"/>.
/// </summary>
public ILocationProvider TransformLocationProvider
{
get
{
return _transformLocationProvider;
}
}
/// <summary>
/// Returns the serialized <see
cref="T:Mapbox.Unity.Location.EditorLocationProvider"/>.
/// </summary>
public ILocationProvider EditorLocationProvider
{
get
{
return _editorLocationProvider;
}
}
/// <summary>
/// Returns the serialized <see
cref="T:Mapbox.Unity.Location.DeviceLocationProvider"/>
/// </summary>
public ILocationProvider DeviceLocationProvider
{
get
{
return _deviceLocationProviderUnity;
}
}
/// <summary>
/// Create singleton instance and inject the DefaultLocationProvider upon
Initialization of this component.
/// </summary>
protected virtual void Awake()
{
if (Instance != null)
{
DestroyImmediate(gameObject);
return;
}
Instance = this;
if (_dontDestroyOnLoad)
{
DontDestroyOnLoad(gameObject);
}
InjectEditorLocationProvider();
InjectDeviceLocationProvider();
}
/// <summary>
/// Injects the editor location provider.
/// Depending on the platform, this method and calls to it will be stripped
during compile.
/// </summary>
[System.Diagnostics.Conditional("UNITY_EDITOR")]
void InjectEditorLocationProvider()
{
Debug.LogFormat("LocationProviderFactory: Injected EDITOR Location Provider
- {0}", _editorLocationProvider.GetType());
DefaultLocationProvider = _editorLocationProvider;
}
/// <summary>
/// Injects the device location provider.
/// Depending on the platform, this method and calls to it will be stripped
during compile.
/// </summary>
[System.Diagnostics.Conditional("NOT_UNITY_EDITOR")]
void InjectDeviceLocationProvider()
{
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
Debug.Log("Using WebGL Geolocation.");
requestWebGLLocation();
}
else
{
int AndroidApiVersion = 0;
var regex = new Regex(@"(?<=API-)-?\d+");
Match match = regex.Match(SystemInfo.operatingSystem);
if (match.Success) { int.TryParse(match.Groups[0].Value, out
AndroidApiVersion); }
Debug.LogFormat("{0} => API version: {1}", SystemInfo.operatingSystem,
AndroidApiVersion);
if (Application.platform == RuntimePlatform.Android &&
AndroidApiVersion >= 24)
{
DefaultLocationProvider = _deviceLocationProviderAndroid;
}
else
{
DefaultLocationProvider = _deviceLocationProviderUnity;
}
}
}
public void OnLocationReceived(string locationData)
{
string[] coordinates = locationData.Split(',');
if (coordinates.Length == 2)
{
float latitude = float.Parse(coordinates[0]);
float longitude = float.Parse(coordinates[1]);
Debug.Log($"WebGL Location Received: Latitude: {latitude}, Longitude:
{longitude}");
// You can further process this location data as required
}
}
}
}
Build errors:
Library\Bee\artifacts\WebGL\build\debug_WebGL_wasm\build.js: undefined symbol: requestWebGLLocation (referenced by top-level compiled C/C++ code) UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
Library\Bee\artifacts\WebGL\build\debug_WebGL_wasm\build.js: Aborting compilation due to previous errors UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
Building Library\Bee\artifacts\WebGL\build\debug_WebGL_wasm\build.js failed with output:
error: undefined symbol: requestWebGLLocation (referenced by top-level compiled C/C++ code)
warning: Link with -s LLD_REPORT_UNDEFINED
to get more information on undefined symbols
warning: To disable errors for undefined symbols use -s ERROR_ON_UNDEFINED_SYMBOLS=0
warning: _requestWebGLLocation may need to be added to EXPORTED_FUNCTIONS if it arrives from a system library
Error: Aborting compilation due to previous errors
emcc: error: '"C:/Program Files/Unity/Hub/Editor/2022.3.46f1/Editor/Data/PlaybackEngines/WebGLSupport/BuildTools/Emscripten/node/node.exe" "C:\Program Files\Unity\Hub\Editor\2022.3.46f1\Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\Emscripten\emscripten\src\compiler.js" C:\Users\Nifty\AppData\Local\Temp\tmpmbvsgucj.json' failed (returned 1)
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
tl;dr: Change the file type from .js
to .jslib
The error itself is simple: The _requestWebGLLocation
method/function is missing on the JavaScript side.
You said that your file is a plain normal .js
file. Unity will treat this one basically as any other text file asset and drop it as it isn't referenced anywhere.
For the Unity WebGL build process to actually recognize it as a plug-in and include your JavaScript code into the webassembly it needs to rather be a proper .jslib
plug-in file!
See https://docs.unity3d.com/Manual/web-interacting-browser-js.html
The
var EXPORTED_FUNCTIONS = ['_requestWebGLLocation'];
shouldn't be required actually
Also instead of using SendMessage
you can rather pass in a callback and directly call it which I find way cleaner and less error prone and it is also more efficient as you directly pass on the method pointer rather than have the engine search for the according object and method by name
See https://docs.unity3d.com/Manual/web-interacting-browser-example.html