Ich erstelle ein 2D-XNA-Spiel und bin auf ein Tutorial zum Hinzufügen von Wassereffekten (Spritzern) zu einem XNA-Spiel gestoßen, aber nachdem ich es in mein Spiel implementiert habe, kann ich es nicht verkleinern. Derzeit nimmt es den gesamten Bildschirm ein.
Die Wasserklasse sieht so aus
class Water
{
struct WaterColumn
{
public float TargetHeight;
public float Height;
public float Speed;
public void Update(float dampening, float tension)
{
float x = TargetHeight - Height;
Speed += tension * x - Speed * dampening;
Height += Speed;
}
}
PrimitiveBatch pb;
WaterColumn[] columns = new WaterColumn[201];
static Random rand = new Random();
public float Tension = 0.025f;
public float Dampening = 0.025f;
public float Spread = 0.25f;
RenderTarget2D metaballTarget, particlesTarget;
SpriteBatch spriteBatch;
AlphaTestEffect alphaTest;
Texture2D particleTexture;
private float Scale { get { return spriteBatch.GraphicsDevice.Viewport.Width / (columns.Length - 1f); } }
List<Particle> particles = new List<Particle>();
class Particle
{
public Vector2 Position;
public Vector2 Velocity;
public float Orientation;
public Particle(Vector2 position, Vector2 velocity, float orientation)
{
Position = position;
Velocity = velocity;
Orientation = orientation;
}
}
public Water(GraphicsDevice device, Texture2D particleTexture)
{
pb = new PrimitiveBatch(device);
this.particleTexture = particleTexture;
spriteBatch = new SpriteBatch(device);
metaballTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
particlesTarget = new RenderTarget2D(device, device.Viewport.Width, device.Viewport.Height);
alphaTest = new AlphaTestEffect(device);
alphaTest.ReferenceAlpha = 175;
var view = device.Viewport;
alphaTest.Projection = Matrix.CreateTranslation(-0.5f, -0.5f, 0) *
Matrix.CreateOrthographicOffCenter(0, view.Width, view.Height, 0, 0, 1);
for (int i = 0; i < columns.Length; i++)
{
columns[i] = new WaterColumn()
{
Height = 240,
TargetHeight = 240,
Speed = 0
};
}
}
// Returns the height of the water at a given x coordinate.
public float GetHeight(float x)
{
if (x < 0 || x > 800)
return 240;
return columns[(int)(x / Scale)].Height;
}
void UpdateParticle(Particle particle)
{
const float Gravity = 0.3f;
particle.Velocity.Y += Gravity;
particle.Position += particle.Velocity;
particle.Orientation = GetAngle(particle.Velocity);
}
public void Splash(float xPosition, float speed)
{
int index = (int)MathHelper.Clamp(xPosition / Scale, 0, columns.Length - 1);
for (int i = Math.Max(0, index - 0); i < Math.Min(columns.Length - 1, index + 1); i++)
columns[index].Speed = speed;
CreateSplashParticles(xPosition, speed);
}
private void CreateSplashParticles(float xPosition, float speed)
{
float y = GetHeight(xPosition);
if (speed > 120)
{
for (int i = 0; i < speed / 8; i++)
{
Vector2 pos = new Vector2(xPosition, y) + GetRandomVector2(40);
Vector2 vel = FromPolar(MathHelper.ToRadians(GetRandomFloat(-150, -30)), GetRandomFloat(0, 0.5f * (float)Math.Sqrt(speed)));
CreateParticle(pos, vel);
}
}
}
private void CreateParticle(Vector2 pos, Vector2 velocity)
{
particles.Add(new Particle(pos, velocity, 0));
}
private Vector2 FromPolar(float angle, float magnitude)
{
return magnitude * new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
}
private float GetRandomFloat(float min, float max)
{
return (float)rand.NextDouble() * (max - min) + min;
}
private Vector2 GetRandomVector2(float maxLength)
{
return FromPolar(GetRandomFloat(-MathHelper.Pi, MathHelper.Pi), GetRandomFloat(0, maxLength));
}
private float GetAngle(Vector2 vector)
{
return (float)Math.Atan2(vector.Y, vector.X);
}
public void Update()
{
for (int i = 0; i < columns.Length; i++)
columns[i].Update(Dampening, Tension);
float[] lDeltas = new float[columns.Length];
float[] rDeltas = new float[columns.Length];
// do some passes where columns pull on their neighbours
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
{
lDeltas[i] = Spread * (columns[i].Height - columns[i - 1].Height);
columns[i - 1].Speed += lDeltas[i];
}
if (i < columns.Length - 1)
{
rDeltas[i] = Spread * (columns[i].Height - columns[i + 1].Height);
columns[i + 1].Speed += rDeltas[i];
}
}
for (int i = 0; i < columns.Length; i++)
{
if (i > 0)
columns[i - 1].Height += lDeltas[i];
if (i < columns.Length - 1)
columns[i + 1].Height += rDeltas[i];
}
}
foreach (var particle in particles)
UpdateParticle(particle);
particles = particles.Where(x => x.Position.X >= 0 && x.Position.X <= 800 && x.Position.Y - 5 <= GetHeight(x.Position.X)).ToList();
}
public void DrawToRenderTargets()
{
GraphicsDevice device = spriteBatch.GraphicsDevice;
device.SetRenderTarget(metaballTarget);
device.Clear(Color.Transparent);
// draw particles to the metaball render target
spriteBatch.Begin(0, BlendState.Additive);
foreach (var particle in particles)
{
Vector2 origin = new Vector2(particleTexture.Width, particleTexture.Height) / 2f;
spriteBatch.Draw(particleTexture, particle.Position, null, Color.White, particle.Orientation, origin, 2f, 0, 0);
}
spriteBatch.End();
// draw a gradient above the water so the metaballs will fuse with the water's surface.
pb.Begin(PrimitiveType.TriangleList);
const float thickness = 20;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p1.X, p1.Y - thickness);
Vector2 p4 = new Vector2(p2.X, p2.Y - thickness);
pb.AddVertex(p2, Color.White);
pb.AddVertex(p1, Color.White);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p3, Color.Transparent);
pb.AddVertex(p4, Color.Transparent);
pb.AddVertex(p2, Color.White);
}
pb.End();
// save the results in another render target (in particlesTarget)
device.SetRenderTarget(particlesTarget);
device.Clear(Color.Transparent);
spriteBatch.Begin(0, null, null, null, null, alphaTest);
spriteBatch.Draw(metaballTarget, Vector2.Zero, Color.White);
spriteBatch.End();
// switch back to drawing to the backbuffer.
device.SetRenderTarget(null);
}
public void Draw()
{
Color lightBlue = new Color(0.2f, 0.5f, 1f);
// draw the particles 3 times to create a bevelling effect
spriteBatch.Begin();
spriteBatch.Draw(particlesTarget, -Vector2.One, new Color(0.8f, 0.8f, 1f));
spriteBatch.Draw(particlesTarget, Vector2.One, new Color(0f, 0f, 0.2f));
spriteBatch.Draw(particlesTarget, Vector2.Zero, lightBlue);
spriteBatch.End();
// draw the waves
pb.Begin(PrimitiveType.TriangleList);
Color midnightBlue = new Color(0, 15, 40) * 0.9f;
lightBlue *= 0.8f;
float bottom = spriteBatch.GraphicsDevice.Viewport.Height;
float scale = Scale;
for (int i = 1; i < columns.Length; i++)
{
Vector2 p1 = new Vector2((i - 1) * scale, columns[i - 1].Height);
Vector2 p2 = new Vector2(i * scale, columns[i].Height);
Vector2 p3 = new Vector2(p2.X, bottom);
Vector2 p4 = new Vector2(p1.X, bottom);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p2, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p1, lightBlue);
pb.AddVertex(p3, midnightBlue);
pb.AddVertex(p4, midnightBlue);
}
pb.End();
}
}
Dann habe ich in Game1.cs die folgende LoadContent-Methode
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
font = Content.Load<SpriteFont>("Font");
particleImage = Content.Load<Texture2D>("metaparticle");
backgroundImage = Content.Load<Texture2D>("sky");
rockImage = Content.Load<Texture2D>("rock");
water = new Water(GraphicsDevice, particleImage);
.
.
.
}
In meiner Update-Methode habe ich Folgendes (zusammen mit anderem Code für das Spiel zeige ich nur den Wasserteil)
protected override void Update(GameTime gameTime)
{
lastKeyState = keyState;
keyState = Keyboard.GetState();
lastMouseState = mouseState;
mouseState = Mouse.GetState();
water.Update();
Vector2 mousePos = new Vector2(mouseState.X, mouseState.Y);
// if the user clicked down, create a rock.
if (lastMouseState.LeftButton == ButtonState.Released && mouseState.LeftButton == ButtonState.Pressed)
{
rock = new Rock
{
Position = mousePos,
Velocity = (mousePos - new Vector2(lastMouseState.X, lastMouseState.Y)) / 5f
};
}
// update the rock if it exists
if (rock != null)
{
if (rock.Position.Y < 240 && rock.Position.Y + rock.Velocity.Y >= 240)
water.Splash(rock.Position.X, rock.Velocity.Y * rock.Velocity.Y * 5);
rock.Update(water);
if (rock.Position.Y > GraphicsDevice.Viewport.Height + rockImage.Height)
rock = null;
}
Dann habe ich in der Draw-Methode Folgendes (wenn die aktive Aufzählung InGame ist)
case ActiveScreen.InGame:
water.DrawToRenderTargets();
level.Draw(gameTime, spriteBatch);
DrawHud();
spriteBatch.Begin();
spriteBatch.Draw(backgroundImage, Vector2.Zero, Color.White);
if (rock != null)
rock.Draw(spriteBatch, rockImage);
spriteBatch.End();
water.Draw();
break;
Mein Problem ist, dass dies offensichtlich den gesamten Bildschirm einnimmt. Mir ist klar, warum es den gesamten Bildschirm einnimmt, aber ich kann nicht herausfinden, wie ich es verkleinern und an einem festen Ort im Spiel einstellen kann. Wenn jemand dies durchlesen und mich darauf hinweisen könnte, wie ich dies erfolgreich verkleinern würde, würde ich es sehr schätzen.
Antworten:
Nun, es gibt zwei Möglichkeiten, dies zu tun:
Water
Objekt auf ein Renderziel und dann das endgültige Wasserziel skaliert und an der gewünschten Position. (Dies ist der einfachste Weg).Water
in einem eingeschränkten Bereich. Dazu müssen Sie Folgendes tun:Water
indem SieRectangle
im Konstruktor ein als Ziel angeben.GraphicsDevice.Viewport
sollte durch das Zielrechteck ersetzt werdenSplash
Methodenparameter (xPosition
) die 'richtige' - reale - Position angeben, damit alles andere beim Zeichnen in Ordnung ist.PS: Du hast etwas darüber gesagt
OutOfBoundsException
. Bitte geben Sie uns weitere Details, wenn dies nicht mehr der Fall ist.quelle