Saya membuat game 2D XNA dan menemukan tutorial tentang menambahkan efek air (percikan) ke game XNA tetapi setelah menerapkannya ke dalam game saya, saya tidak bisa menurunkannya. Saat ini dibutuhkan seluruh layar.
Kelas Air terlihat seperti ini
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();
}
}
Kemudian di Game1.cs saya memiliki metode LoadContent berikut
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);
.
.
.
}
Dalam metode pembaruan saya, saya memiliki yang berikut (bersama dengan kode lain untuk permainan, saya hanya menunjukkan bagian air)
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;
}
Kemudian pada metode Draw saya memiliki yang berikut (ketika enum aktif adalah InGame)
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;
Masalah saya adalah ini jelas memakan seluruh layar. Saya menyadari mengapa hal itu menghabiskan seluruh layar tetapi saya tidak tahu bagaimana menurunkannya dan meletakkannya di lokasi tetap dalam permainan. Jika ada yang bisa membaca ini dan mengarahkan saya ke arah bagaimana saya akan berhasil menurunkan ini, saya akan sangat menghargainya.
Jawaban:
Nah, ada dua cara untuk melakukan ini:
Water
objek ke target render, lalu gambar target air akhir yang diskalakan dan pada posisi yang Anda inginkan. (ini adalah cara paling sederhana).Water
di area terbatas. Untuk ini, Anda perlu melakukan sesuatu seperti ini:Water
dengan menentukanRectangle
target sebagai di konstruktor.GraphicsDevice.Viewport
harus diganti dengan persegi panjang targetSplash
parameter metode (xPosition
) sehingga semuanya akan baik-baik saja ketika ditarik.PS: Kamu mengatakan sesuatu tentang
OutOfBoundsException
. Tolong beri kami detail lebih lanjut jika itu masalahnya lagi.sumber