Hola a todo el mundo,
Después de la aparición de
RailWorks y de la última actualización (
v97.6a), he empezado a investigar las nuevas posibilidades.
1.-Activación de efectos de vapor, según los valores de ciertos controles. Por ejemplo, el vapor del silbato
que sólo se emite cuando lo hacemos sonar. Esto es sencillo y tan sólo requiere el siguiente código dentro de la función
OnControlValueChange ( name, index, value ) del archivo
lua Engine Script, el que está en la propia carpeta de la máquina (
Engine)
- Código: Seleccionar todo
if name == "Horn" then
if value > 0 then
Call( "Vapor_Sirena:SetEmitterActive", 1 );
else
Call( "Vapor_Sirena:SetEmitterActive", 0 );
end
end
Previamente, dentro de la función
Initialise () de ese mismo archivo, hay que hacer que ese
emitter esté inactivo al empezar a jugar. Eso se consigue con la instrucción
- Código: Seleccionar todo
Call( "Vapor_Sirena:SetEmitterActive", 0 );
Un buen efecto es que, con el regulador abierto, se escape algo de vapor por los prensaestopas de los cilindros:
La inicialización en la función
Initialise () del archivo
lua Engine Script para un
emitter en cada cilindro es
- Código: Seleccionar todo
Call( "Vapor_cilindre_dreta:SetEmitterActive", 0 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 0 );
y en la función
OnControlValueChange ( name, index, value ) de ese mismo archivo el código es
- Código: Seleccionar todo
if name == "Regulator" then
if value > 0.05 then
Call( "Vapor_cilindre_dreta:SetEmitterActive", 1 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 1 );
.......................
.......................
El caso de las purgas es algo más complejo, porque se depende de los valores de
dos controles, a saber, el mando de las purgas (
CylinderCock) y el regulador (
Regulator). En efecto, con las purgas abiertas, si se cierra el regulador, el vapor debe dejar de salir y, con el regulador cerrado, al abrir las purgas no debe salir vapor. Contando con cuatro emisores de vapor, el código a incluir en la función
OnControlValueChange ( name, index, value ) del archivo
lua Engine Script es éste:
- Código: Seleccionar todo
if name == "CylinderCock" then
if value > 0 then
if Call( "*:ControlExists", "Regulator", 0 ) ~= zero then
rgValue = Call( "*:GetControlValue", "Regulator", 0 );
if rgValue > 0 then
Call( "Vapor_purga_dreta1:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 1 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 1 );
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
if name == "Regulator" then
if value > 0.05 then
Call( "Vapor_cilindre_dreta:SetEmitterActive", 1 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 1 );
if Call( "*:ControlExists", "CylinderCock", 0 ) ~= zero then
purgValue = Call( "*:GetControlValue", "CylinderCock", 0 );
if purgValue > 0 then
Call( "Vapor_purga_dreta1:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 1 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 1 );
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
else
Call( "Vapor_cilindre_dreta:SetEmitterActive", 0 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
y, como antes, en la función
Initialise () de ese mismo archivo, hay que hacer que esos
emitters empiecen inactivos con el código
- Código: Seleccionar todo
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
El efecto es éste:
Todo el archivo
lua Engine Script queda así:
- Código: Seleccionar todo
--------------------------------------------------------------------------------------
-- KUJU / Rail Simulator
--------------------------------------------------------------------------------------
-- INITIALISE
function Initialise ()
-- All emitters are active by default so turn off some of them
--Call( "Fum_xemeneia:SetEmitterActive", 0 );
Call( "Vapor_Sirena:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
Call( "Vapor_cilindre_dreta:SetEmitterActive", 0 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 0 );
-- call("BeginUpdate");
end
------------------------------------------------------------
-- OnControlValueChange
------------------------------------------------------------
-- Called when a cab control is modified
------------------------------------------------------------
-- Parameters:
-- name = Name of the control
-- index = Index of the control
-- value = Modified control value
------------------------------------------------------------
function OnControlValueChange ( name, index, value )
local rgValue = 0;
local purgValue = 0;
local increment = 0;
local zero = 0
if Call( "*:ControlExists", name, index ) then
Call( "*:SetControlValue", name, index, value );
end
if name == "Horn" then
if value > 0 then
Call( "Vapor_Sirena:SetEmitterActive", 1 );
else
Call( "Vapor_Sirena:SetEmitterActive", 0 );
end
end
if name == "CylinderCock" then
if value > 0 then
if Call( "*:ControlExists", "Regulator", 0 ) ~= zero then
rgValue = Call( "*:GetControlValue", "Regulator", 0 );
if rgValue > 0 then
Call( "Vapor_purga_dreta1:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 1 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 1 );
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
if name == "Regulator" then
if value > 0.05 then
Call( "Vapor_cilindre_dreta:SetEmitterActive", 1 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 1 );
if Call( "*:ControlExists", "CylinderCock", 0 ) ~= zero then
purgValue = Call( "*:GetControlValue", "CylinderCock", 0 );
if purgValue > 0 then
Call( "Vapor_purga_dreta1:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 1 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 1 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 1 );
else
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
else
Call( "Vapor_cilindre_dreta:SetEmitterActive", 0 );
Call( "Vapor_cilindre_esquerra:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta1:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra1:SetEmitterActive", 0 );
Call( "Vapor_purga_dreta2:SetEmitterActive", 0 );
Call( "Vapor_purga_esquerra2:SetEmitterActive", 0 );
end
end
end
Naturalmente, esos
emitters han de estar declarados como
S Child en el
blueprint de la máquina, en la sección
Container component y puestos en su sitio con el previsualizador del
Asset Editor.
2.-Animaciones exteriores de controles que no son de tipo
Sí/No: el regulador, la palanca de cambio de marchas, etc. Se trata que, desde el exterior, se vea moverse el regulador, la palanca de cambio de marchas y su timonería, los frenos con su cilindro timonería y zapatas, etc. de modo que las posiciones de la animación exterior
sigan fielmente las posiciones del control de dentro de la cabina.
Por ejemplo, para la palanca y timonería del cambio de marchas tenemos la posición inicial (0). Nótese el movimiento del contrapeso de la distribución, señalado con una flecha roja:
marcha adelante a fondo:
marcha atrás a fondo:
y una posición intermedia (41%):
Para ello, la palanca, timonería y contrapeso, es decir, todo lo que se va a mover,
no debe formar parte del fichero
IGS del modelo, sinó que todo ese conjunto móvil debe tener sus ficheros
IGS y
IA propios. Con ellos se hace un
Anim procedural scenery blueprint en el que se declaran los ficheros
IGS y
IA. Ese
blueprint se declara como
S Child en el
blueprint de la máquina, en la sección
Container component y se pone en su sitio con el previsualizador del
Asset Editor. Esa animacion
no hay que declararla en la sección
Render component -> Anim set del
blueprint principal de la máquina.
El fichero a manipular ahora es el
lua Simulation Script, el que está en la carpeta (
Simulation) de la máquina. La función que maneja el proceso es
AddTime (Container componentID, AnimationID, time).
En mi máquina, el nombre que le he puesto al
Container component es
Node_Palanca_marxes y, en
su blueprint, el nombre de la animación es
Anim_palanca_marxes. Así pues, el uso de la función será
- Código: Seleccionar todo
Call("Node_Palanca_marxes:AddTime", "Anim_palanca_marxes", rvIncrement );
(
rvincrement es el tiempo que damos a la animación para que se mueva durante el mismo)
Empezamos por definir una variable global,
gPrevReverserValue, y poner la palanca justo en su punto medio:
- Código: Seleccionar todo
gPrevReverserValue = 0
if Call( "*:ControlExists", "Reverser", 0 ) ~= zero then
Call("Node_Palanca_marxes:AddTime", "Anim_palanca_marxes", 0.29 );
end
Ese código va dentro de la función
Setup () del fichero
lua Simulation Script. El valor 0.29 del tiempo se encuentra por experimentación...
El movimiento se controla mediante este código en la función
Update (interval) de ese mismo script:
- Código: Seleccionar todo
io.output("sortida.txt")
local rvIncrement = 0;
-- Reverser
if Call( "*:ControlExists", "Reverser", 0 ) ~= zero then
controlValue = Call( "*:GetControlValue", "Reverser", 0 )
if controlValue ~= gPrevReverserValue then
rvIncrement = (controlValue - gPrevReverserValue)*0.333333;
-- io.write( "DEBUG: gPrevReverserValue = ", gPrevReverserValue, ", ReverserValue = ", controlValue, ", rvIncrement = ", rvIncrement, "\n" );
Call("Node_Palanca_marxes:AddTime", "Anim_palanca_marxes", rvIncrement );
Call( "*:SetEngineValue", "Reverser", controlValue )
gPrevReverserValue = controlValue;
end
end
La instrucción
io.output("sortida.txt") abre el fichero
sortida.txt, que aparecerá en la carpeta raíz de
RailWorks y la instrucción
io.write( "DEBUG: gPrevReverserValue = ", gPrevReverserValue, ", ReverserValue = ", controlValue, ", rvIncrement = ", rvIncrement, "\n" ); escribe en este fichero, con el propósito de
debuggear el
script. Cuando estemos satisfechos con los resultados, podemos
comentar (las dos rayitas -- del principio de la línea) esa línea para que no se gasten recursos en el funcionamiento del juego.
El freno de mano también puede animarse del mismo modo:
Posición de desfrenado:
y su correspondencia en la vista interior de la cabina:
Posición de frenado:
y esa posición en la cabina:
Una posición intermedia:
El fichero
script de simulación, queda así:
- Código: Seleccionar todo
------------------------------------------------------------
-- Ficher de simulació per a les locomotores CGFC 27-42, "Bergues"
------------------------------------------------------------
------------------------------------------------------------
-- Setup
------------------------------------------------------------
-- Called when the engine script is initialised
------------------------------------------------------------
function Setup ()
DEBUG = true
io.output("sortida.txt")
SG_STATE_STOPPED = 0
SG_STATE_FORWARD = 1
SG_STATE_BACKWARD = 2
mStopGoState = SG_STATE_STOPPED
gPrevFireBoxDoorValue = -1 -- Forces 1st frame update
gPrevReverserValue = 0
gPrevRegulatorValue = 0
gPrevHornValue = 0
gPrevHandBrakeValue = 0
if Call( "*:ControlExists", "Reverser", 0 ) ~= zero then
Call("Node_Palanca_marxes:AddTime", "Anim_palanca_marxes", 0.29 );
end
end
------------------------------------------------------------
-- Update
------------------------------------------------------------
-- Called every frame to update the simulation
------------------------------------------------------------
-- Parameters:
-- interval = time since last update
------------------------------------------------------------
function Update (interval)
local fbValue = 0;
local sgValue = 0;
local hbValue = 0;
local zero = 0
local hbIncrement = 0;
local rvIncrement = 0;
local hbValue = 0;
local que = 0
if Call( "*:ControlExists", "HandBrake", 0 ) ~= zero then
hbValue = Call( "*:GetControlValue", "HandBrake", 0 );
if hbValue ~= gPrevHandBrakeValue then
hbIncrement = (hbValue - gPrevHandBrakeValue)*0.5;
que=que+hbIncrement;
-- io.write( "DEBUG: gPrevHandBrakeValue = ", gPrevHandBrakeValue, ", HandBrakeValue = ", hbValue, ", hbIncrement = ", hbIncrement, " acumulat = ", que, "\n" );
Call("Node_Palanca_i_espiga_fre_manual:AddTime", "Anim_palanca_i_espiga_fre_manual", hbIncrement );
Call("Node_Timoneria_frens:AddTime", "Anim_timoneria_frens", hbIncrement );
gPrevHandBrakeValue = hbValue
end
end
if Call( "*:ControlExists", "Firebox door", 0 ) ~= zero then
fbValue = Call( "*:GetControlValue", "Firebox door", 0 );
Call( "*:SetEmitterColour", fbValue, fbValue, fbValue );
if fbValue ~= gPrevFireBoxDoorValue then
Call( "ControlSound:SetParameter", "FireBoxDoorValue", fbValue )
gPrevFireBoxDoorValue = fbValue
end
end
-- Stop go controls...
if Call( "*:ControlExists", "StopGoForward", 0 ) ~= zero then
sgValue = Call( "*:GetControlValue", "StopGoForward", 0 );
if sgValue > zero then
mStopGoState = SG_STATE_FORWARD
elseif sgValue < zero then
mStopGoState = SG_STATE_BACKWARD
end
end
if Call( "*:ControlExists", "StopGoStop", 0 ) ~= zero then
sgValue = Call( "*:GetControlValue", "StopGoStop", 0 );
if sgValue ~= zero then
Call( "*:SetControlValue", "StopGoForward", zero, zero );
mStopGoState = SG_STATE_STOP
end
end
Call("SetFailureValue", "ElectricSim", "ForceMultiplierDueToFailure", 0.5)
local controlValue = 0;
-- Reverser
if Call( "*:ControlExists", "Reverser", 0 ) ~= zero then
controlValue = Call( "*:GetControlValue", "Reverser", 0 )
if controlValue ~= gPrevReverserValue then
rvIncrement = (controlValue - gPrevReverserValue)*0.333333;
-- io.write( "DEBUG: gPrevReverserValue = ", gPrevReverserValue, ", ReverserValue = ", controlValue, ", rvIncrement = ", rvIncrement, "\n" );
Call("Node_Palanca_marxes:AddTime", "Anim_palanca_marxes", rvIncrement );
Call( "*:SetEngineValue", "Reverser", controlValue )
gPrevReverserValue = controlValue;
end
end
-- Regulator
if Call( "*:ControlExists", "Regulator", 0 ) ~= zero then
controlValue = Call( "*:GetControlValue", "Regulator", 0 )
if controlValue ~= gPrevRegulatorValue then
Call( "*:SetEngineValue", "Regulator", controlValue )
gPrevRegulatorValue = controlValue
end
end
-- Horn
if Call( "*:ControlExists", "Horn", 0 ) ~= zero then
controlValue = Call( "*:GetControlValue", "Horn", 0 )
-- KJM Print("controlValue = ", controlValue);
if controlValue ~= gPrevHornValue then
--Call( "*:SetEngineValue", "Horn", controlValue )
Call( "ControlSound:SetParameter", "HornValue", controlValue )
gPrevHornValue = controlValue
end
end
end
function GetStopGoState()
return mStopGoState
end
Espero haber ayudado a avanzar en la simulación.
Saludos,
Carles Romero,
"Brill"