thirdparty_libraries_1_featured

en Tutoriales, UE4

Usando librerías de terceros en nuestro proyecto de UE4

En ocasiones necesitamos incluir librerías de terceros para añadir soporte a una nueva característica o simplemente para facilitar el desarrollo. Con este tutorial vamos a explicar algunos detalles de como configurar y ser capaces de utilizar una librería de terceros con un proyecto de UE4. También vamos a prepararlo para el supuesto de querer exportarlo para la plataforma Android.

Parte 1: Configurando las rutas del proyecto
Parte 2: Usando la librería
Parte 3: Espectro de Frecuencias
Parte 4: Visualizador de espectro para UE4
Parte 5: Algoritmo detector de pulsos
Parte 6: Visualizador de pulso para UE4

Para este tutorial vamos a integrar la librería FMOD (solo vamos a utilizar el API de bajo nivel) en un proyecto centrado en las plataformas Win64 y Android.

FMOD es un motor de efectos de sonido y una herramienta de autoría para videojuegos y aplicaciones desarrollado por Firelight Technologies, que reproduce y mezcla sonidos de diversos formatos y para múltiples sistemas operativos.

En la sección de descargas FMOD podemos seleccionar el .zip correspondiente a nuestra version de motor UE4.

fmod_download

Para este ejemplo solo necesitamos el API de bajo nivel por lo que solo serán necesarios unos pocos binarios de este .zip.

Podemos empezar creando una carpeta para las librerías de terceros, si queremos compartir estas librerías con más de un proyecto podemos crear esta carpeta fuera del proyecto y configurar las rutas relativas a esta carpeta. En nuestro ejemplo vamos a crear la carpeta Thirdparty dentro del proyecto.

thirdparty_folder

la siguiente imagen muestra los archivos que vamos a necesitar, donde se pueden encontrar dentro del .zip y su destino final dentro de nuestra carpeta de Thirdparty (hay que tener en cuenta que estos archivos pueden cambiar en posteriores versiones de la librería)

thirdparty_tree

Para poder incluir los binarios de las librería en un despliegue para Android tendremos que crear un archivo .xml en APL, Este archivo tiene la información para configurar y copiar los binarios necesarios durante el proceso de empaquetado.

<?xml version="1.0" encoding="utf-8"?>
<!--GearVR plugin additions-->
<root xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- init section is always evaluated once per architecture -->
	<init>
		<log text="FMOD APL init (shipping build)"/>
	</init>

	<!-- optional additions to proguard -->
	<proguardAdditions>
		<insert>
			-keep class org.fmod.** {
			*;
			}
			-dontwarn org.fmod.**
		</insert>
	</proguardAdditions>

	<!-- optional files or directories to copy to Intermediate/Android/APK -->
	<resourceCopies>
		<log text="FMOD APL copying files for $S(Architecture)"/>
		<copyFile src="$S(PluginDir)/Libraries/Android/$S(Architecture)/libfmod.so"
					dst="$S(BuildDir)/libs/$S(Architecture)/libfmod.so" />
		<copyFile src="$S(PluginDir)/Libraries/Android/fmod.jar"
					dst="$S(BuildDir)/libs/fmod.jar" />
	</resourceCopies>

	<!-- optional additions to GameActivity onCreate in GameActivity.java -->
	<gameActivityOnCreateAdditions>
		<insert>
		// Initialize FMOD jar file
		org.fmod.FMOD.init(this);
		</insert>
	</gameActivityOnCreateAdditions>

	<!-- optional additions to GameActivity onDestroy in GameActivity.java -->
	<gameActivityOnDestroyAdditions>
		<insert>
		// Shutdown FMOD jar file
		org.fmod.FMOD.close();
		</insert>
	</gameActivityOnDestroyAdditions>

	<!-- optional libraries to load in GameActivity.java before libUE4.so -->
	<soLoadLibrary>
		<log text="FMOD APL adding loadLibrary references"/>
		<loadLibrary name="fmod" failmsg="libfmod not loaded and required!" />
	</soLoadLibrary>
</root>

Ahora podemos abrir el archivo {PROJECT_NAME}.Build.cs de nuestra solución. Este archivo declara una clase que hereda de ModuleRules, y establece las propiedades que controlan como debe ser generado por su constructor. Este archivo es compilado por la herramienta UnrealBuildTool y construida para determinar el entorno de compilación general.

using UnrealBuildTool;

public class Tutorial_spectrum : ModuleRules
{
	public Tutorial_spectrum(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
		PrivateDependencyModuleNames.AddRange(new string[] {  });

		// Uncomment if you are using Slate UI
		// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
		
		// Uncomment if you are using online features
		// PrivateDependencyModuleNames.Add("OnlineSubsystem");

		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
	}
}

En este constructor tenemos que establecer las rutas a las librerias de terceros para cada una de las plataformas. Para obtener la ruta de este archivo build.cs podemos utilizar la función GetDirectoryName del namespace System.IO.Path

var basePath = Path.GetDirectoryName(RulesCompiler.GetFileNameFromType(GetType()));

Para alcanzar la carpeta Thirdparty podemos utilizar Combine para modificar la ruta base y ascender por el árbol de directorio hasta la carpeta

string thirdPartyPath = Path.Combine(basePath, "..", "..", "Thirdparty");

Ahora ya podemos comenzar a añadir las rutas de la librería, la primera de ellas es la relacionada con los archivos .h, esta le dice al visual studio donde encontrar los archivos de inclusión. Para ello utilizaremos PublicIncludePaths, esta carpeta es común a todas las plataformas.

PublicIncludePaths.Add(Path.Combine(thirdPartyPath, "FMOD", "Includes"));

Para añadir los binarios de la libraría (.lib/.dll/.so) tenemos que hacer distinción según la plataforma. Para seleccionar la plataforma actual podemos comprobar el valor de la variable Target.Platform y realizar las siguientes acciones según la plataforma.

if (Target.Platform == UnrealTargetPlatform.Win64)
{
}
else if (Target.Platform == UnrealTargetPlatform.Android)
{
}
else
{
//Unsupported platform
}

Para incluir la ruta a los binarios tenemos que utilizar PublicAdditionalLibraries para indicar el nombre de la librería.
Para la plataforma Win64 tendremos que establecer también la ruta a las dependencias en tiempo de ejecución (.dll) utilizando RuntimeDependencies, puesto que el .dll debe estar en la misma carpeta que el ejecutable .exe de nuestro juego, tendremos que copiarla a la carpeta donde se genera la Build para poder utilizar la librería durante el desarrollo con el editor. Por último podemos utilizar PublicDelayLoadDLLs para activar el retraso de la carga de la librería, la librería será cargada automáticamente la primera vez que sea necesaria.

PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Win64","fmod_vc.lib"));
string fmodDllPath = Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Win64", "fmod.dll");
RuntimeDependencies.Add(fmodDllPath);

string binariesDir = Path.Combine(basePath,"..","..", "Binaries", "Win64");
if (!Directory.Exists(binariesDir))
    System.IO.Directory.CreateDirectory(binariesDir);

string fmodDllDest = System.IO.Path.Combine(binariesDir, "fmod.dll");
CopyFile(fmodDllPath, fmodDllDest);
PublicDelayLoadDLLs.AddRange(new string[] { "fmod.dll" });

Para la plataforma Android tendremos que añadir la ruta al anterior archivo _APL.xml con AdditionalPropertiesForReceipt.

PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Android", "armeabi-v7a","libfmod.so"));
PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Android", "arm64-v8a","libfmod.so"));
string RelAPLPath = Utils.MakePathRelativeTo(System.IO.Path.Combine(thirdPartyPath, "FMOD", "FMOD_APL.xml"), Target.RelativeEnginePath);
AdditionalPropertiesForReceipt.Add("AndroidPlugin", RelAPLPath);

Poniendo todo junto el build.cs debería quedar así:

using UnrealBuildTool;
using System.IO;

public class Tutorial_spectrum : ModuleRules
{
	public Tutorial_spectrum(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
	
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
		PrivateDependencyModuleNames.AddRange(new string[] {  });

		var basePath = Path.GetDirectoryName(RulesCompiler.GetFileNameFromType(GetType()));
        string thirdPartyPath = Path.Combine(basePath, "..", "..", "Thirdparty");

        
        //FMOD
        PublicIncludePaths.Add(Path.Combine(thirdPartyPath, "FMOD", "Includes"));
   
		if (Target.Platform == UnrealTargetPlatform.Win64)
		{
                PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Win64","fmod_vc.lib"));
                string fmodDllPath = Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Win64", "fmod.dll");
                RuntimeDependencies.Add(fmodDllPath);

                string binariesDir = Path.Combine(basePath,"..","..", "Binaries", "Win64");
                if (!Directory.Exists(binariesDir))
                    System.IO.Directory.CreateDirectory(binariesDir);

                string fmodDllDest = System.IO.Path.Combine(binariesDir, "fmod.dll");
                CopyFile(fmodDllPath, fmodDllDest);
                PublicDelayLoadDLLs.AddRange(new string[] { "fmod.dll" });
		}
        else if (Target.Platform == UnrealTargetPlatform.Android)
		{
                PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Android", "armeabi-v7a","libfmod.so"));
                PublicAdditionalLibraries.Add(Path.Combine(thirdPartyPath, "FMOD", "Libraries", "Android", "arm64-v8a","libfmod.so"));
                string RelAPLPath = Utils.MakePathRelativeTo(System.IO.Path.Combine(thirdPartyPath, "FMOD", "FMOD_APL.xml"), Target.RelativeEnginePath);
                AdditionalPropertiesForReceipt.Add("AndroidPlugin", RelAPLPath);
		}
		else
		{
                //throw new System.Exception(System.String.Format("Unsupported platform {0}", Target.Platform.ToString()));
        }
    }


    private void CopyFile(string source, string dest)
    {
        System.Console.WriteLine("Copying {0} to {1}", source, dest);
        if (System.IO.File.Exists(dest))
        {
            System.IO.File.SetAttributes(dest, System.IO.File.GetAttributes(dest) & ~System.IO.FileAttributes.ReadOnly);
        }
        try
        {
            System.IO.File.Copy(source, dest, true);
        }
        catch (System.Exception ex)
        {
            System.Console.WriteLine("Failed to copy file: {0}", ex.Message);
        }
    }
}

Tendremos que hacer un Rebuild de nuestro proyecto para hacer efectivos los cambios en el archivo build.cs durante la sesión actual de desarrollo. Ahora ya podemos ponernos con la clase que utilizará la librería. En este caso vamos a crear un SoundManager que reproducirá un sonido utilizando la librería fmod.

Parte 2: Usando la librería

2020/06/22 – Actualizado a Unreal Engine 4.24

Ayudanos con este blog!

El último año he estado dedicando cada vez más tiempo a la creación de tutoriales, en su mayoria sobre desarrollo de videojuegos. Si crees que estos posts te han ayudado de alguna manera o incluso inspirado, por favor considera ayudarnos a mantener este blog con alguna de estas opciones. Gracias por hacerlo posible!

Escribe un comentario

Comentario