En la meva darrera publicació vaig parlar sobre la diferència entre fer servir Lumen o il·luminació tradicional i per què m'interessa l'opció tradicional.
A més, vaig crear un Blueprint de configuració d'escena per controlar el PostProcessVolume i canviar entre Lumen i la il·luminació tradicional usant l'editor Unreal Engine i dins del joc.
Ara aprofundiré en els meus Blueprints de llums per prendre algunes notes importants sobre l'herència i les instàncies dels components perquè hi ha alguns conceptes confusos que vull aclarir.
HERÈNCIA
En el desenvolupament de programari aquest és un concepte molt important en la Programació Orientada a Objectes i Unreal Engine pot aplicar-lo a Blueprints, cosa que és molt útil.
Imagina que vols fer vehicles terrestres de diferents tipus, per exemple, una moto, un cotxe i un autobús, i vols fer-ne la lògica i propietats. Podries fer el codi per a cada tipus de vehicle, però el teu programa es tornarà un infern perquè estàs triplicant la feina necessària per fer-los (i la feina necessària per fer millores i canvis més endavant en aquestes lògiques...)
Aquí és on cal l'herència. Amb l'herència, podeu especificar un objecte base, per exemple, un "vehicle terrestre", i derivar-ne totes les variacions que necessitareu: la motocicleta, l'automòbil i l'autobús.
En aquest objecte base “vehicle terrestre” podem especificar algunes lògiques i variables bàsiques, per exemple, cada vehicle terrestre pot transportar persones, tenir un motor, moure's, tenir llums, etc. Però cada tipus de vehicles poden tenir algunes característiques especials, variables i lògiques que no s'aplicarien a la resta de tipus de vehicles: per exemple, per anar amb moto cal portar casc, un cotxe i un autobús tenen portes, l'autobús té màquina validadora de bitllets, etc.
Llavors tenim alguna cosa com això:
Objecte base:
- Vehicle terrestre -> pot transportar persones, té un motor, llums que es poden encendre o apagar, es poden moure, etc.
Objectes secundaris derivats:
- Moto -> ha de fer servir casc, pot tenir un bloqueig de roda, etc.
- Cotxe -> té portes, té finestres, etc.
- Autobús -> té portes, té finestres, té màquina validadora de butlletes, etc.
Com funciona a Unreal Engine? Vegem-ne un exemple amb el meu sistema de llums.
DIFERENTS TIPUS DE LLUMS
Vull un Blueprint per controlar un llum a l'escenari, però després necessitaré diferents tipus de llums. De moment en tinc dues: un llum rodó i un llum allargat. El llum verd és el mateix que la rodona només que canviant el color, després ho veurem.
La lògica és la mateixa per a cadascuna: necessito poder canviar la llum i configurar-la per fer servir Lumen o no.
Llavors he de poder canviar el xassís del llum i el seu color i intensitat per personalitzar-ne la llum. En un futur afegiré opcions perquè parpellegin i tirin espurnes.
Com podeu veure en aquest diagrama, tindré un Blueprint amb el llum base i dos derivats:
A "BP_Base_Lamp" hi haurà gairebé tota la lògica i les propietats i després a "BP_Protoype_LongLamp" i "BP_Prototype_PointLamp" només hi haurà algunes lògiques de configuració i la malla.
Cada llum tindrà:
- Un "LightComponent" que serà la llum principal que tindrà el llum.
- Un o més "LightComponents" que seran els llums ambientals que projecten la llum de cada llum per al mode d'il·luminació tradicional (Lumen no els necessitarà).
- Una "Mesh" que serà la bombeta amb un material especial que brilla en el color de la llum principal.
Per aconseguir això, també tindrà aquestes propietats públiques:
- LightColor: una variable "LinearColor" que tindrà el color de la llum.
- LightOnIntensity: una variable "Float" amb la intensitat que tindrà la llum.
- LightOffIntensity: una altra variable "Float" amb la intensitat quan la llum està apagada. En general, serà zero, però potser necessiteu algunes làmpades que encara funcionin amb baixa intensitat quan estiguin apagades per il·luminar una mica un escenari fosc.
- IsActive: una variable "boolean" per saber si aquesta llum està encesa o apagada. A l'editor podem encendre o no la llum usant aquesta variable i veure'n el resultat.
- HideAmbientLight: aquest és un altre "booleà" i serà útil només amb fins de prova. Ocultarà tots els llums ambientals en el mode d'il·luminació tradicional per ajudar-me en el procés d'il·luminació de l'escenari.
A més, hi haurà algunes variables privades que seran útils a la lògica interna d'aquest Blueprint, però les revisarem mentre explorem la seva lògica.
INICIALITZACIÓ DE LÀMPADA BASE
Primer, necessitem el llum base que contindrà la lògica i les variables principals: el nostre Blueprint base anomenat "BP_Base_Lamp".
A l'interior no farem res a la finestra "Viewport" ja que aquest Blueprint no necessita cap representació gràfica.
Necessitem totes aquestes variables:
És important fer-les públiques o privades, ja que només veurem les públiques a l'editor d'Unreal Engine i, en el nostre cas, només necessitem fer públiques les variables de configuració per personalitzar els Blueprints instanciats en el nivell.
Llavors necessitem algunes funcions que anomenarem dins de la nostra lògica de llum. En primer lloc, la funció "InitializeLamp":
Aquesta funció rep tres paràmetres:
- Una "Malla" anomenada "Bulb" que serà la bombeta on trobar el material brillant especial.
- Un component de llum principal anomenat "MainLight"
- Una col·lecció de components de llums anomenada "AmbientLights".
Necessitem aquesta funció per inicialitzar el Blueprint derivat.
El primer és establir aquests paràmetres en variables privades, ja que necessitarem recuperar-los en alguns punts dins de la lògica d'aquest llum.
És important aclarir que aquests paràmetres rebran una referència a objectes que hi ha al joc, però estem treballant amb un element abstracte que realment no en té. Necessitem instàncies vives d'aquesta bombeta i components de llum per jugar amb les seves propietats que afecten el món que els envolta, per aconseguir-ho, els hem d'inicialitzar des de fora al constructor del Blueprint derivat perquè és on aquests elements cobren vida i podem assignar-los dins del Blueprint base usant aquesta funció.
Després obtenim la malla de la bombeta i del seu material creem una "DynamicMaterialInstance" i la guardem en una variable privada per usar-la més tard, aquest material té dues propietats amb què podem jugar: el color i la brillantor.
El següent pas és configurar les propietats del llum, aquesta és una funció anomenada "SetLampProperties" que desenvoluparem més endavant i en la que configurem el color i la intensitat de la llum del llum. Si el llum ara està activa, configurem "LightOnIntensity", altrament configurarem "LightOffIntensity".
I el pas final és obtenir "BP_Scene_Config" del nivell i configurar lús de Lumen en aquest llum. Per configurar Lumen fem servir una altra funció que pots veure a continuació anomenada "ConfigLumen":
"ConfigLumen" rep un booleà per saber si està actiu o no. Després hi ha un "ForEachLoop" on desactivem cada llum ambiental del llum si Lumen està actiu o viceversa.
Ja tenim el llum configurat. Aquesta lògica funciona cada cop que canviem una variable a l'editor d'Unreal Engine oa l'inici del joc. Més endavant veurem on llençar la funció "InitializeLamp".
Una cosa més sobre la inicialització: a "BP_Scene_Config" tenim dos esdeveniments per activar o desactivar Lumen, ia "EventGraph" de "BP_Base_Lamp" els fem servir per actualitzar la configuració de Lumen. Això ajuda a actualitzar l'estat de Lumen per a cada llum en el joc.
CONFIGURAR LES PROPIETATS DE LA LÀMPADA I LA LLUM
Per configurar les propietats del llum tenim una funció anomenada "SetLampProperties".
Primer, configurem algunes variables locals per modificar aquesta llum. Una variable local és una variable que només existeix dins una funció. En aquest cas, configurem "ActualColor" i "ActualIntensity" per treballar-hi fàcilment dins d'aquesta funció.
Configurem les propietats de llum de "MainLight" usant la variable privada que configurem anteriorment a "InitializationLamp". Aquí fem servir una altra funció personalitzada anomenada "SetLightProperties" on establim el color i la intensitat de la llum, en aquest cas el nostre color i intensitat de les nostres variables locals.
Després configurem el material de la bombeta perquè brilli amb el nostre color i una petita fracció de la nostra intensitat de llum. Per aconseguir-ho, fem servir "SetVectorParameterValue" per al color de la llum i "SetScalarParameterValue" per a la intensitat sobre la nostra variable privada "LightBulbMaterial" que configurem anteriorment a la funció "InitializeLamp". Això requereix un material especial amb paràmetres per establir el color i la intensitat de la seva brillantor, després ho veurem.
Després apliquem les propietats de la llum sobre tots els llums ambientals usant la mateixa funció "SetLightProperties" però dividint la intensitat perquè la llum ambiental brilla menys que la llum principal. En cas que estiguem usant Lumen o vulguem amagar aquests llums posem la intensitat a zero. Tenim tots els llums ambientals en una matriu a les nostres variables privades gràcies a la funció "InitializationLamp".
La funció "SetLightProperties" és aquesta que podeu veure a continuació:
És més simple del que sembla. Si la intensitat de la llum és més gran que zero, establim el color i la intensitat i establim aquesta llum com a visible, en cas contrari ocultem aquesta llum, perquè no volem fer feina extra mostrant llums que són inútils.
MATERIAL DE LA BOMBETA
Anteriorment hem parlat sobre un material especial que brilla. És aquest: el material "M_BulbLight".
És força simple. Creem un material amb dos paràmetres anomenats "LightColor" i "LightIntensity" que configuren l'efecte de brillantor de forma dinàmica en el joc i l'editor d'Unreal Engine. D'aquest material fem una instància de material que és la que farem servir a les nostres bombetes als nostres llums.
DIFERENTS LÀMPADES
Finalment, podem fer diferents llums usant la nostra base "BP_Base_Lamp". A l'editor de Blueprints podem configurar aquí la classe base per heretar-ho tot: funcions, variables, malles, etc:
Aleshores, als Blueprints de làmpades secundàries configurarem la mateixa "BP_Base_Lamp" i després farem servir la màgia de l'herència per configurar dues làmpades diferents.
Al "BP_Prototype_LongLamp" he fet una malla de cub amb forma rectangular i una altra de més petita per dins amb el material de la bombeta "M_BulbLight". Per fer llums d'ambient poso tres punts de llum i per a la llum principal poso una llum rectangular. Després podria haver configurat les variables per al color i la intensitat, però les vaig deixar en els seus valors predeterminats.
El més important és que fem servir el constructor per inicialitzar el llum, aquí és on anomenem la famosa funció "InitializeLamp".
Com podeu veure, configurem els llums i la bombeta a través de la funció "InitializeLamp" perquè aquí tenim les instàncies d'aquests elements.
I per al llum rodó una mica semblant però amb diferent malla i menys llums d'ambient:
Puc fer més variacions, aquesta canviant el color de la llum quan instàncies un "BP_Prototype_PointLight" al nivell:
I el resultat és el que heu vist a l'inici d'aquest post: