Algo que hechamos en falta en la nueva actualización de Silverlight 5 es la clase equivalente a Game de XNA. ¿Cual es el equivalente en Silverlight?
Después de instalar el Silverlight Toolkit, creamos un nuevo proyecto usando la plantilla Silverlight 3D. Abrimos MainPage.xaml y vemós la declaración del objeto DrawingSurface y cómo está trucado el evento Draw. Ahora vamso a ver MainPage.xaml.cs y vemos la implementación del evento myDrawingsurface_Draw. Después de llamar a scene.Draw(), se llama a e.InvalidateSurface(), que le dice a Silverlight que este objeto debe ser repintado. Normalmente Silverlight pinta el el objeto una vez al inicio, y sólo se repinta cuando algo ha cambiado (quizás porque se ha redimensionado, o porque se ha movido algo). Pero cuando el manejador invalida la superficie, hace que se vuelva a pintar, lo que la invalida de nuevo, y hace que se vuelva a pintar, y así sucesivamente. Esto crea un bucle infinito, con la superficie que se pinta contínuamente, justo lo que queremos para un bucle de juego.
Fijáos que el método UserControl_Loaded en MainPage.xaml.cs, está enlazado al evento UserControl.Loaded. Esto es el equivalente a los métodos Initialize y LoadContent de XNA.
Bueno vale, ¿pero cómo montamos un juego encima de esto?. Mirad el juego XNA Platformer en c:/Program Files (x86)/Microsoft SDKs/Silverlight/v5.0/Toolkit/Sep11/Source/Sample source code.zip/Xna). Vamos a ver el método myDrawingSurface_Draw de MainPage.xaml.cs. Es muy sencillo:
private void myDrawingSurface_Draw(object sender, DrawEventArgs e)
{
platformerGame.Update(e);
platformerGame.Draw(e);
e.InvalidateSurface();
}
Aquí tenemos el bucle de juego. Gracias a la llamada InvalidateSurface, se llama al update y al draw lo más rápido posible.
Si preferís usar un sistema con tiempo, podéis hacer lo siguiente:
readonly TimeSpan TargetElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 60);
readonly TimeSpan MaxElapsedTime = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / 10);
TimeSpan accumulatedTime;
private void myDrawingSurface_Draw(object sender, DrawEventArgs e)
{
TimeSpan elapsedTime = e.DeltaTime;
if (elapsedTime > MaxElapsedTime)
{
elapsedTime = MaxElapsedTime;
}
accumulatedTime += elapsedTime;
while (accumulatedTime >= TargetElapsedTime)
{
Update();
accumulatedTime -= TargetElapsedTime;
}
Draw();
e.InvalidateSurface();
}
Espero que os guste.
Juan María Laó Ramos.