Your project manager is amazed with the shower of dozens of meteors in the background. However, he wants to add a more realistic background.
He shows you a water simulation sample using Farseer Physics Engine. He wants you to use the wave simulation capabilities offered by this powerful physics simulator to create an asteroids belt.
First, we are going to create a new class to define a fluid model capable of setting the initial parameters and updating a wave controller provided by the physics simulator.
We will use Farseer Physics Engine's wave controller to add real-time fluids with movement for our games. The following code is based on the Silverlight water sample offered with the physics simulator. However, in this case, we are not interested in collision detection capabilities because we are going to create an asteroid belt in the background.
using System;
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Controllers;
using FarseerGames.FarseerPhysics.Mathematics;
public WaveController WaveController { get; private set; }
public float WaveGeneratorMax { get; set; }
public float WaveGeneratorMin { get; set; }
public float WaveGeneratorStep { get; set; }
public FluidModel()
{
// Assign the initial values for the wave generator parameters
WaveGeneratorMax = 0.20f;
WaveGeneratorMin = -0.15f;
WaveGeneratorStep = 0.025f;
}
public void Initialize(PhysicsSimulator physicsSimulator)
{
// The wave controller controls how the waves move
// It defines how big and how fast is the wave
// It is represented as set of points equally spaced
horizontally along the width of the wave.
WaveController = new WaveController();
WaveController.Position = ConvertUnits.ToSimUnits(-20, 5);
WaveController.Width = ConvertUnits.ToSimUnits(30);
WaveController.Height = ConvertUnits.ToSimUnits(3);
// The number of vertices that make up the surface of the wave
WaveController.NodeCount = 40;
// Determines how quickly the wave will dissipate
WaveController.DampingCoefficient = .95f;
// Establishes how fast the wave algorithm runs (in seconds)
WaveController.Frequency = .16f;
//The wave generator parameters simply move an end-point of the
WaveController.WaveGeneratorMax = WaveGeneratorMax;
WaveController.WaveGeneratorMin = WaveGeneratorMin;
WaveController.WaveGeneratorStep = WaveGeneratorStep;
WaveController.Initialize();
}
public void Update(TimeSpan elapsedTime)
{
WaveController.Update((float) elapsedTime.TotalSeconds);
}
We now have a FluidModel class that creates, configures, and updates a WaveController instance according to an associated physics simulator. As we are going to work with different gravitational forces, we are going to use another independent physics simulator to work with the FluidModel instance in our game.
The wave controller offers many parameters to represent a set of points equally spaced horizontally along the width of one or many waves. The waves can be:
The wave controller's parameters allow us to determine the number of vertices that make up the surface of the wave assigning a value to its NodeCount property. In this case, we are going to create waves with 40 nodes and each point is going to be represented by an asteroid:
WaveController.NodeCount = 40;
The Initialize method defines the position, width, height and other parameters for the wave controller. We have to convert our position values to the simulator values. Thus, we use the ConvertUnits.ToSimUnits method. For example, this line defines the 2D Vector for the wave's upper left corner (X = -20 and Y = 5):
WaveController.Position = ConvertUnits.ToSimUnits(-20, 5);
The best way to understand each parameter is changing its values and running the example using these new values. Using a wave controller we can create amazing fluids with movement.
Now, we are going to create a specialized subclass of Actor (Balder.Core.Runtime. Actor) to load, create an update a fluid with waves. This class will enable us to encapsulate an independent asteroid belt and add it to the game. In this case, it is a 3D character composed of many models (many instances of Mesh).
public class FluidWithWaves : Actor
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
// BALDER
using Balder.Core;
using Balder.Core.Geometries;
using Balder.Core.Math;
using Balder.Core.Runtime;
// FARSEER PHYSICS
using FarseerGames.FarseerPhysics;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Mathematics;
// LISTS
using System.Collections.Generic;
protected RealTimeGame _game;
protected Scene _scene;
private FluidModel _fluidModel;
private PointCollection _points;
private List<Mesh> _meshList;
public FluidWithWaves(RealTimeGame game, Scene scene,
PhysicsSimulator physicsSimulator)
{
_game = game;
_scene = scene;
_fluidModel = new FluidModel();
_fluidModel.Initialize(physicsSimulator);
int count = _fluidModel.WaveController.NodeCount;
_points = new PointCollection();
for (int i = 0; i < count; i++)
{
_points.Add(new Point(ConvertUnits.ToDisplayUnits
(_fluidModel.WaveController.XPosition[i]),
ConvertUnits.ToDisplayUnits
(_fluidModel.WaveController.CurrentWave[i])));
}
}
public override void LoadContent()
{
base.LoadContent();
_meshList = new List<Mesh>(_points.Count);
for (int i = 0; i < _points.Count; i++)
{
Mesh mesh = _game.ContentManager.Load<Mesh>("meteor.ase");
_meshList.Add(mesh);
_scene.AddNode(mesh);
mesh.Position.X = (float) _points[i].X;
mesh.Position.Y = (float) _points[i].Y;
mesh.Position.Z = 0;
}
}
public override void Update()
{
base.Update();
// Update the fluid model with the real-time game elapsed time
_fluidModel.Update(_game.ElapsedTime);
_points.Clear();
for (int i = 0; i < _fluidModel.WaveController.NodeCount; i++)
{
Point p = new Point(ConvertUnits.ToDisplayUnits
(_fluidModel.WaveController.XPosition[i]),
ConvertUnits.ToDisplayUnits
(_fluidModel.WaveController.CurrentWave[i])
+ConvertUnits.ToDisplayUnits
(_fluidModel.WaveController.Position.Y));
_points.Add(p);
}
// Update the positions for the meshes that define the wave's points
for (int i = 0; i < _points.Count; i++)
{
_meshList[i].Position.X = (float)_points[i].X;
_meshList[i].Position.Y = (float)_points[i].Y;
}
}