Machen Sie Plätschern mit dynamischen 2D-Wassereffekten
() translation by (you can also view the original English article)
Sploosh! In dieser Lektion zeige ich Ihnen, wie Sie mit einfachen mathematischen, physikalischen und Partikeleffekten wunderschöne 2D-Wellen und Wassertröpfchen simulieren können.
Hinweis: Obwohl dieses Tutorial mit C# und XNA geschrieben wurde, sollten Sie die gleichen Methoden und Konzepte in fast jeder Spieleentwicklungsumgebung verwenden.
Vorschau Ergebnisse
Wenn Sie XNA haben, können Sie die Quelldateien herunterladen und die Demo kompilieren. Ansonsten schau dir das folgende Demo-Video an:
In der Wassersimulation gibt es grundsätzlich zwei unabhängige Teile. Zuerst werden wir Wellen mit einem Federmodell verwenden. Zweitens werden wir Partikeleffekte verwenden, um Plätschern hinzuzufügen.
Wellen erzeugen
Um Wellen zu erzeugen, werden wir die Wasseroberfläche als eine Reihe von vertikalen Federn simulieren, wie in diesem Diagramm gezeigt:



Dadurch können die Wellen steigen und fallen. Dann lassen wir Wasserteilchen zu benachbarten Teilchen ziehen, so dass sich die Wellen ausbreiten.
Federn und Hookes Gesetz
Eine der großen Eigenschaften von Federn ist, dass sie leicht nachzuahmen sind. Federn haben eine gewisse natürliche Länge; wenn Sie die Feder dehnen oder zusammendrücken, wird sie versuchen, zu dieser natürlichen Länge zurückzukehren.
Die Kraft, die von der Feder zur Verfügung gestellt wird, ist durch Hookes Gesetz gegeben:
\[
F = -kx
\]
F
ist die von der Feder erzeugte Kraft, k
- ist eine konstante Feder und x
- ist die Verschiebung der Federn aus ihrer natürlichen Länge. Ein negatives Vorzeichen zeigt an, dass die Kraft in die entgegengesetzte Richtung wirkt, in die die Feder vorgespannt ist; Wenn Sie die Feder drücken, wird sie gedrückt und umgekehrt.
Die Federkonstante k
bestimmt die Federsteifigkeit.
Um Federn zu simulieren, müssen wir herausfinden, wie man die Teilchen nach dem Hookes Gesetz bewegt. Dazu brauchen wir noch mehrere Formeln aus der Physik. Erstens, Newtons zweites Bewegungsgesetz:
\[
F = ma
\]
Hier ist F
die Kraft, m
ist die Masse und a
ist die Beschleunigung. Das bedeutet, dass sich die Kraft dem Objekt nähert und je leichter das Objekt ist, desto mehr beschleunigt es.
Die Kombination dieser beiden Formeln und Neuordnung gibt uns:
\[
a = - \ frac{k}{m} x
\]
Das gibt uns Beschleunigung für unsere Teilchen. Wir nehmen an, dass alle unsere Teilchen dieselbe Masse haben werden, so dass wir k/m
zu einer Konstanten kombinieren können.
Um die Position der Beschleunigung zu bestimmen, müssen wir eine numerische Integration durchführen. Wir werden die einfachste Form der numerischen Integration verwenden - für jeden Frame machen wir einfach folgendes:
1 |
|
2 |
Position += Velocity; |
3 |
Velocity += Acceleration; |
Dies wird als Euler-Methode bezeichnet. Das ist nicht die genaueste Art der numerischen Integration, aber sie ist schnell, einfach und für unsere Zwecke geeignet.
Durch die Kombination all dieser Elemente bilden unsere Partikel auf der Wasseroberfläche folgende Rahmen:
1 |
|
2 |
public float Position, Velocity; |
3 |
public void Update() |
4 |
{
|
5 |
const float k = 0.025f; // adjust this value to your liking |
6 |
float x = Height - TargetHeight; |
7 |
float acceleration = -k * x; |
8 |
|
9 |
Position += Velocity; |
10 |
Velocity += acceleration; |
11 |
}
|
Hier ist TargetHeight
die natürliche Position des oberen Teils der Feder, wenn sie sich nicht dehnt und nicht schrumpft. Sie müssen diesen Wert dort einstellen, wo die Wasseroberfläche sein soll. Für die Demo habe ich sie auf halber Höhe des Bildschirms auf 240 Pixel eingestellt.
Spannung und Feuchtigkeit
Ich habe bereits erwähnt, dass eine konstante Feder k
die Federsteifigkeit reguliert. Sie können diesen Wert anpassen, um die Eigenschaften des Wassers zu ändern. Eine niedrige Federkonstante bewirkt, dass sich die Feder löst. Das bedeutet, dass die Kraft dazu führt, dass große Wellen langsam oszillieren. Umgekehrt erhöht eine hohe konstante Feder die Spannung der Feder. Kräfte werden kleine Wellen erzeugen, die schnell schwanken. Eine hohe konstante Quelle lässt das Wasser wie eine Mischung aus Jello aussehen.
Achtung: Die Federkonstante nicht zu hoch einstellen. Sehr steife Federn verwenden sehr starke Kräfte, die in sehr kurzer Zeit stark variieren. Das ist nicht sehr gut mit der numerischen Integration, die die Federn simuliert, kombiniert als eine Reihe von diskreten Sprüngen mit regelmäßigen Zeitintervallen. Eine sehr starre Feder kann sogar eine Schwingungsdauer haben, die kürzer ist als Ihr Zeitschritt. Schlimmer noch, Eulers Integrationsmethode neigt dazu, die Energie zu erhöhen, da die Nachahmung weniger genau wird, was zur Explosion der starren Federn führt.
Es gibt immer noch ein Problem mit unserem Federmodell. Sobald die Feder zu schwingen beginnt, wird sie niemals aufhören. Um dieses Problem zu lösen, müssen wir etwas Befeuchtung anwenden. Die Idee ist, Kraft in die entgegengesetzte Richtung zu übertragen, so dass sich unsere Feder bewegt, um sie zu verlangsamen. Das erfordert eine kleine Anpassung unserer Federformel:
\[
a =-\frac{k}{m} x - dv
\]
Hier ist v
die Geschwindigkeit, d
ist der Dämpfungskoeffizient eine weitere Konstante, die Sie einstellen können, um das Gefühl von Wasser einzustellen. Das sollte ziemlich klein sein, wenn Sie möchten, dass Ihre Wellen schwanken. Die Demo verwendet einen Dämpfungsfaktor von 0,025. Ein hoher Dämpfungsfaktor lässt das Wasser dick wie Melasse aussehen, während ein niedriger Wert es ermöglicht, dass die Wellen lange schwingen.
Die Wellen propagieren lassen
Jetzt, wo wir den Feder machen können, benutzen wir sie, um Wasser zu modellieren. Wie im ersten Diagramm gezeigt, simulieren wir Wasser mit einer Reihe von parallelen vertikalen Federn. Natürlich, wenn die Federn alle unabhängig sind, werden sich die Wellen niemals wie echte Wellen ausbreiten.
Zuerst zeige ich den Code und dann gehe ich dahin:
1 |
|
2 |
for (int i = 0; i < springs.Length; i++) |
3 |
springs[i].Update(Dampening, Tension); |
4 |
|
5 |
float[] leftDeltas = new float[springs.Length]; |
6 |
float[] rightDeltas = new float[springs.Length]; |
7 |
|
8 |
// do some passes where springs pull on their neighbours
|
9 |
for (int j = 0; j < 8; j++) |
10 |
{
|
11 |
for (int i = 0; i < springs.Length; i++) |
12 |
{
|
13 |
if (i > 0) |
14 |
{
|
15 |
leftDeltas[i] = Spread * (springs[i].Height - springs [i - 1].Height); |
16 |
springs[i - 1].Speed += leftDeltas[i]; |
17 |
}
|
18 |
if (i < springs.Length - 1) |
19 |
{
|
20 |
rightDeltas[i] = Spread * (springs[i].Height - springs [i + 1].Height); |
21 |
springs[i + 1].Speed += rightDeltas[i]; |
22 |
}
|
23 |
}
|
24 |
|
25 |
for (int i = 0; i < springs.Length; i++) |
26 |
{
|
27 |
if (i > 0) |
28 |
springs[i - 1].Height += leftDeltas[i]; |
29 |
if (i < springs.Length - 1) |
30 |
springs[i + 1].Height += rightDeltas[i]; |
31 |
}
|
32 |
}
|
Dieser Code wird in jedem Frame Ihrer Update()
-Methode aufgerufen. Hier sind die Federn
eine Anordnung von Federn, die von links nach rechts angeordnet sind. leftDeltas
ist eine Anordnung von Schwimmern, die den Höhenunterschied zwischen jeder Feder und ihrem linken Nachbarn hält. rechtsDeltas
ist das Äquivalent für rechtshändige Nachbarn. Wir behalten alle diese Höhenunterschiede in den Arrays bei, da die letzten beiden if
-Anweisungen die Höhe der Federn ändern. Wir müssen den Höhenunterschied messen, bevor eine der Höhen geändert wird.
Der Code beginnt mit der Einführung des Hookeschen Gesetzes für jeden Feder, wie oben beschrieben. Dann betrachtet er den Höhenunterschied zwischen jeder Feder und ihren Nachbarn, und jede Feder zieht ihre benachbarten Federn zu sich selbst und verändert die Positionen und die Geschwindigkeit der Nachbarn. Der Schritt des Schrittes des Nachbarn wird achtmal wiederholt, so dass sich die Wellen schneller ausbreiten.
Es gibt noch einen konfigurierbaren Wert, hier Spread
genannt. Es steuert die Geschwindigkeit der Wellenausbreitung. Es kann Werte von 0 bis 0.5 annehmen, während größere Werte die Geschwindigkeit der Wellenausbreitung erhöhen.
Um die Bewegung der Wellen zu starten, fügen wir eine einfache Splash()
Methode hinzu.
1 |
|
2 |
public void Splash(int index, float speed) |
3 |
{
|
4 |
if (index >= 0 && index < springs.Length) |
5 |
springs[i].Speed = speed; |
6 |
}
|
Jedes Mal, wenn Sie Wellen schlagen möchten, rufen Sie Splash()
. Der Index
-Parameter bestimmt, auf welcher Feder der Splash erscheinen soll und der Speed
-Parameter bestimmt, wie groß die Wellen sein werden.
Rendern
Wir werden die XNA PrimitiveBatch
-Klasse aus dem XNA PrimitivesSample verwenden. Die PrimitiveBatch
-Klasse hilft uns, Linien und Dreiecke direkt mit dem Grafikprozessor zu zeichnen. Sie benutzen es so:
1 |
|
2 |
// in LoadContent()
|
3 |
primitiveBatch = new PrimitiveBatch(GraphicsDevice); |
4 |
|
5 |
// in Draw()
|
6 |
primitiveBatch.Begin(PrimitiveType.TriangleList); |
7 |
foreach (Triangle triangle in trianglesToDraw) |
8 |
{
|
9 |
primitiveBatch.AddVertex(triangle.Point1, Color.Red); |
10 |
primitiveBatch.AddVertex(triangle.Point2, Color.Red); |
11 |
primitiveBatch.AddVertex(triangle.Point3, Color.Red); |
12 |
}
|
13 |
primitiveBatch.End(); |
Es sollte beachtet werden, dass Sie standardmäßig die Eckpunkte des Dreiecks im Uhrzeigersinn zeigen müssen. Wenn Sie sie im Uhrzeigersinn hinzufügen, wird das Dreieck verworfen und Sie werden es nicht sehen.
Für jede Pixelbreite ist keine Feder erforderlich. In der Demo habe ich eine Quelle 201 verwendet, die sich entlang eines 800 Pixel breiten Fensters ausbreitet. Dss ergibt genau 4 Pixel zwischen jeder Feder, wobei die erste Feder 0 ist und die letzte 800 Pixel. Sie könnten wahrscheinlich noch weniger Federn verwenden und trotzdem glattes Wasser haben.
Wir wollen dünne dünne Trapeze zeichnen, die sich vom unteren Rand des Bildschirms bis zur Wasseroberfläche erstrecken und die Federn wie in diesem Diagramm gezeigt verbinden:

Da Grafikkarten Trapeze nicht direkt zeichnen, müssen wir jedes Trapez in Form von zwei Dreiecken zeichnen. Um es ein wenig besser aussehen zu lassen, machen wir das Wasser auch dunkler, wenn es tiefer wird, und malen die unteren Ecken in blau. Die GPU interpoliert automatisch die Farben zwischen den Scheitelpunkten.
1 |
|
2 |
primitiveBatch.Begin(PrimitiveType.TriangleList); |
3 |
Color midnightBlue = new Color(0, 15, 40) * 0.9f; |
4 |
Color lightBlue = new Color(0.2f, 0.5f, 1f) * 0.8f; |
5 |
|
6 |
var viewport = GraphicsDevice.Viewport; |
7 |
float bottom = viewport.Height; |
8 |
|
9 |
// stretch the springs' x positions to take up the entire window
|
10 |
float scale = viewport.Width / (springs.Length - 1f); // be sure to use float division |
11 |
|
12 |
for (int i = 1; i < springs.Length; i++) |
13 |
{
|
14 |
// create the four corners of our triangle.
|
15 |
Vector2 p1 = new Vector2((i - 1) * scale, springs[i - 1].Height); |
16 |
Vector2 p2 = new Vector2(i * scale, springs[i].Height); |
17 |
Vector2 p3 = new Vector2(p2.X, bottom); |
18 |
Vector2 p4 = new Vector2(p1.X, bottom); |
19 |
|
20 |
primitiveBatch.AddVertex(p1, lightBlue); |
21 |
primitiveBatch.AddVertex(p2, lightBlue); |
22 |
primitiveBatch.AddVertex(p3, midnightBlue); |
23 |
|
24 |
primitiveBatch.AddVertex(p1, lightBlue); |
25 |
primitiveBatch.AddVertex(p3, midnightBlue); |
26 |
primitiveBatch.AddVertex(p4, midnightBlue); |
27 |
}
|
28 |
|
29 |
primitiveBatch.End(); |
Hier ist das Ergebnis:
Erstellen Sie ein Spritzer
Die Wellen sehen gut aus, aber ich würde gerne einen Spritzer sehen, wenn der Stein ins Wasser fällt. Die Partikeleffekte sind dafür ideal.
Teileffekte
Der Partikeleffekt verwendet eine große Anzahl feiner Partikel, um einen visuellen Effekt zu erzeugen. Sie werden manchmal für Dinge wie Rauch oder Funken verwendet. Wir werden Partikel für Wassertropfen im Spray verwenden.
Das erste, was wir brauchen, ist eine Klasse von Teilchen:
1 |
|
2 |
class Particle |
3 |
{
|
4 |
public Vector2 Position; |
5 |
public Vector2 Velocity; |
6 |
public float Orientation; |
7 |
|
8 |
public Particle(Vector2 position, Vector2 velocity, float orientation) |
9 |
{
|
10 |
Position = position; |
11 |
Velocity = velocity; |
12 |
Orientation = orientation; |
13 |
}
|
14 |
}
|
Diese Klasse speichert einfach die Eigenschaften, die ein Partikel haben kann. Dann erstellen wir eine Liste von Partikeln.
1 |
|
2 |
List<Particle> particles = new List<Particle>(); |
In jedem Frame müssen wir Partikel aktualisieren und zeichnen.
1 |
|
2 |
void UpdateParticle(Particle particle) |
3 |
{
|
4 |
const float Gravity = 0.3f; |
5 |
particle.Velocity.Y += Gravity; |
6 |
particle.Position += particle.Velocity; |
7 |
particle.Orientation = GetAngle(particle.Velocity); |
8 |
}
|
9 |
|
10 |
private float GetAngle(Vector2 vector) |
11 |
{
|
12 |
return (float)Math.Atan2(vector.Y, vector.X); |
13 |
}
|
14 |
|
15 |
public void Update() |
16 |
{
|
17 |
foreach (var particle in particles) |
18 |
UpdateParticle(particle); |
19 |
|
20 |
// delete particles that are off-screen or under water
|
21 |
particles = particles.Where(x => x.Position.X >= 0 && x.Position.X <= 800 && x.Position.Y <= GetHeight(x.Position.X)).ToList(); |
22 |
}
|
In jedem Frame müssen wir Partikel aktualisieren und zeichnen. Dann entfernen wir alle Partikel, die sich außerhalb des Bildschirms oder unter Wasser befinden, kopieren alle Partikel, die wir in der neuen Liste behalten wollen, und Zuweisen von Partikeln. Dann zeichnen wir die Partikel.
1 |
|
2 |
void DrawParticle(Particle particle) |
3 |
{
|
4 |
Vector2 origin = new Vector2(ParticleImage.Width, ParticleImage.Height) / 2f; |
5 |
spriteBatch.Draw(ParticleImage, particle.Position, null, Color.White, particle.Orientation, origin, 0.6f, 0, 0); |
6 |
}
|
7 |
|
8 |
public void Draw() |
9 |
{
|
10 |
foreach (var particle in particles) |
11 |
DrawParticle(particle); |
12 |
}
|
Unten ist die Textur, die ich für Partikel verwendet habe.

Wenn wir jetzt einen Splash erstellen, erzeugen wir eine Menge Partikel.
1 |
|
2 |
private void CreateSplashParticles(float xPosition, float speed) |
3 |
{
|
4 |
float y = GetHeight(xPosition); |
5 |
|
6 |
if (speed > 60) |
7 |
{
|
8 |
for (int i = 0; i < speed / 8; i++) |
9 |
{
|
10 |
Vector2 pos = new Vector2(xPosition, y) + GetRandomVector2(40); |
11 |
Vector2 vel = FromPolar(MathHelper.ToRadians(GetRandomFloat(-150, -30)), GetRandomFloat(0, 0.5f * (float)Math.Sqrt(speed))); |
12 |
particles.Add(new Particle(pos, velocity, 0)); |
13 |
}
|
14 |
}
|
15 |
}
|
Sie können diese Methode über die Splash()
- Methode aufrufen, mit der wir Wellen erzeugen. Die Geschwindigkeit des Parameters ist die Geschwindigkeit, mit der der Stein ins Wasser gelangt. Wir werden große Spritzer machen, wenn sich der Stein schneller bewegt.
GetRandomVector2(40)
liefert einen Vektor mit einer zufälligen Richtung und einer Zufallslänge zwischen 0 und 40. Wir wollen einen Bit der Zufälligkeit der Positionen hinzuzufügen, die die Partikel müssen nicht alle an der gleichen Stelle erscheinen. FromPolar()
gibt Vector2
mit der angegebenen Richtung und Länge zurück.
Hier ist das Ergebnis:
Metabälle als Partikel verwenden
Unsere Sprays sehen ziemlich anständig aus, und einige großartige Spiele, wie zum Beispiel World of Goo, haben Explosionen von Partikeleffekten, die genau wie unsere aussehen. Ich werde Ihnen jedoch die Technik zeigen, um das Spray flüssiger aussehen zu lassen. Die Technik verwendet Metabasen, organische Blasen, von denen ich bereits ein Lehrbuch geschrieben habe. Wenn Sie an Details zu den Metaballs und deren Funktionsweise interessiert sind, lesen Sie dieses Tutorial. Wenn Sie nur wissen möchten, wie Sie sie auf unsere Spritzer anwenden können, lesen Sie weiter.
Metaballs sehen wie Flüssigkeit aus, weil sie miteinander verschmelzen, wodurch sie sich für unsere flüssige Spritzer macht. Um metabally zu machen, müssen wir eine neue Klasse Variablen hinzuzufügen:
1 |
|
2 |
RenderTarget2D metaballTarget; |
3 |
AlphaTestEffect alphaTest; |
Was wir initialisieren wie folgt aus:
1 |
|
2 |
var view = GraphicsDevice.Viewport; |
3 |
metaballTarget = new RenderTarget2D(GraphicsDevice, view.Width, view.Height); |
4 |
alphaTest = new AlphaTestEffect(GraphicsDevice); |
5 |
alphaTest.ReferenceAlpha = 175; |
6 |
|
7 |
alphaTest.Projection = Matrix.CreateTranslation(-0.5f, -0.5f, 0) * |
8 |
Matrix.CreateOrthographicOffCenter(0, view.Width, view.Height, 0, 0, 1); |
Dann ziehen wir metaballs:
1 |
|
2 |
GraphicsDevice.SetRenderTarget(metaballTarget); |
3 |
GraphicsDevice.Clear(Color.Transparent); |
4 |
|
5 |
Color lightBlue = new Color(0.2f, 0.5f, 1f); |
6 |
|
7 |
spriteBatch.Begin(0, BlendState.Additive); |
8 |
foreach (var particle in particles) |
9 |
{
|
10 |
Vector2 origin = new Vector2(ParticleImage.Width, ParticleImage.Height) / 2f; |
11 |
spriteBatch.Draw(ParticleImage, particle.Position, null, lightBlue, particle.Orientation, origin, 2f, 0, 0); |
12 |
}
|
13 |
spriteBatch.End(); |
14 |
|
15 |
GraphicsDevice.SetRenderTarget(null); |
16 |
device.Clear(Color.CornflowerBlue); |
17 |
spriteBatch.Begin(0, null, null, null, null, alphaTest); |
18 |
spriteBatch.Draw(metaballTarget, Vector2.Zero, Color.White); |
19 |
spriteBatch.End(); |
20 |
|
21 |
// draw waves and other things
|
Die Wirkung des Metaballs hängt von der Anwesenheit einer Teilchenstruktur ab, die verschwindet, wenn Sie sich weiter vom Zentrum entfernen. Hier ist, wie es aussieht:Die Wassertröpfchen verschmelzen nun zusammen, wenn sie in der Nähe sind.

So sieht es aus:
Sie sind jedoch nicht mit der Wasseroberfläche verschmelzen. Sie verschmelzen jedoch nicht mit der Wasseroberfläche. Wir können dies beheben, indem wir der Oberfläche des Wassers einen Gradienten hinzufügen, der es allmählich verschwinden lässt und es zu unserem Ziel macht, einen Metaball zu erzeugen.
Fügen Sie der obigen Methode den folgenden Code in Zeile 9 hinzuGraphicsDevice.SetRendertarget(null)
:
1 |
|
2 |
primitiveBatch.Begin(PrimitiveType.TriangleList); |
3 |
|
4 |
const float thickness = 20; |
5 |
float scale = GraphicsDevice.Viewport.Width / (springs.Length - 1f); |
6 |
for (int i = 1; i < springs.Length; i++) |
7 |
{
|
8 |
Vector2 p1 = new Vector2((i - 1) * scale, springs[i - 1].Height); |
9 |
Vector2 p2 = new Vector2(i * scale, springs[i].Height); |
10 |
Vector2 p3 = new Vector2(p1.X, p1.Y - thickness); |
11 |
Vector2 p4 = new Vector2(p2.X, p2.Y - thickness); |
12 |
|
13 |
primitiveBatch.AddVertex(p2, lightBlue); |
14 |
primitiveBatch.AddVertex(p1, lightBlue); |
15 |
primitiveBatch.AddVertex(p3, Color.Transparent); |
16 |
|
17 |
primitiveBatch.AddVertex(p3, Color.Transparent); |
18 |
primitiveBatch.AddVertex(p4, Color.Transparent); |
19 |
primitiveBatch.AddVertex(p2, lightBlue); |
20 |
}
|
21 |
|
22 |
primitiveBatch.End(); |
Nun werden Partikel mit der Wasseroberfläche verschmelzen
Hinzufügen eines Abschrägungseffekt
Wasserteilchen schauen ein wenig flach, und es wäre schön, wenn sie etwas Schatten zu geben. Im Idealfall wäre, können Sie es im Shader getan. Damit dieses Tutorial einfach ist, verwenden wir einen schnellen und einfachen Trick: wir werden die Partikel jetzt dreimal mit verschiedenen Schattierungen und Verschiebungen zeichnen, wie in dem Diagramm unten gezeigt.



Dazu wollen wir Partikel metaballa neues Rendering-Ziel erfassen. Dann werden wir dieses Ziel einmal für jede Farbwiedergabe zeichnen.
Zuerst erklärt eine neue RenderTarget2D
sowie für metaballs:
1 |
|
2 |
particlesTarget = new RenderTarget2D(GraphicsDevice, view.Width, view.Height); |
Dann wird anstelle des Zeichnens metaballsTarget
direkt in den Pufferpuffer, wollen wir es auf particleTarget
ziehen. Wechseln Sie dazu zu der Methode, in der wir die Metabälle zeichnen, und ändern Sie einfach diese Zeilen:
1 |
|
2 |
GraphicsDevice.SetRenderTarget(null); |
3 |
device.Clear(Color.CornflowerBlue); |
... damit:
1 |
|
2 |
GraphicsDevice.SetRenderTarget(particlesTarget); |
3 |
device.Clear(Color.Transparent); |
Dann verwenden Sie den folgenden Code, um die Teilchen dreimal mit verschiedenen Schattierungen und Offsets zu ziehen:
1 |
|
2 |
Color lightBlue = new Color(0.2f, 0.5f, 1f); |
3 |
|
4 |
GraphicsDevice.SetRenderTarget(null); |
5 |
device.Clear(Color.CornflowerBlue); |
6 |
|
7 |
spriteBatch.Begin(); |
8 |
spriteBatch.Draw(particlesTarget, -Vector2.One, new Color(0.8f, 0.8f, 1f)); |
9 |
spriteBatch.Draw(particlesTarget, Vector2.One, new Color(0f, 0f, 0.2f)); |
10 |
spriteBatch.Draw(particlesTarget, Vector2.Zero, lightBlue); |
11 |
spriteBatch.End(); |
12 |
|
13 |
// draw waves and other stuff
|
Schlussfolgerung
Das ist für die Basis 2D Wasser. Um zu demonstrieren, habe ich einen Stein, die Sie ins Wasser werfen. Ich zeichne Wasser mit etwas Transparenz auf die Oberseite des Felsens, so dass es aussieht, als wäre es unter Wasser und verlangsamt es, wenn es aufgrund der Wasserbeständigkeit unter Wasser ist.
Um die Demo ein wenig schöner zu machen, ging ich auf und fand opengameart.org Bild für Felsen und Himmel Hintergrund. Sie können einen Stein und Himmel bei http://opengameart.org/content/rocks zu finden und opengameart.org/content/sky-backdrop entsprechend.