Внедрение водных эффектов (брызг) в игру XNA 4.0 [закрыто]


8

Я создаю 2D игру XNA и наткнулся на учебник по добавлению водных эффектов (брызг) в игру XNA, но после его внедрения в мою игру я не могу заставить его уменьшать масштаб. В настоящее время он занимает весь экран.

Класс воды выглядит следующим образом

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();
    }
}

Затем в Game1.cs у меня есть следующий метод LoadContent

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);
        .
        .
        .
    }

В моем методе обновления у меня есть следующее (наряду с другим кодом для игры, я просто показываю часть воды)

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;
        }

Затем в методе Draw у меня есть следующее (когда активным перечислением является 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;

Моя проблема в том, что это, очевидно, занимает весь экран. Я понимаю, почему он занимает весь экран, но не могу понять, как уменьшить его и установить в фиксированном месте в игре. Если бы кто-нибудь мог прочитать это и направить меня к тому, как я буду успешно сокращать это, я был бы очень благодарен.


4
Я написал довольно подробный ответ на аналогичный вопрос раньше. Пример кода может помочь вам. Какая часть вашего кода является проблемой? Как вы пытались это исправить?
Анко,

Я не уверен ... в Water.cs я пытаюсь изменить масштаб, так как в настоящее время он рисуется, чтобы покрыть ширину экрана. Каждый раз, когда я пытаюсь изменить это, я выхожу за пределы исключений.
UserBruiser

1
Этот вопрос кажется не по теме, потому что он слишком локализован. По сути, вы просите кого-то переписать этот учебный код для вас, чтобы он работал в вашей игре.
MichaelHouse

Ответы:


0

Ну, есть два способа сделать это:

  1. Нарисуйте весь Waterобъект до цели рендера, затем нарисуйте конечную водную цель в масштабе и в нужной позиции. (это самый простой способ).
  2. Нарисуйте Waterв ограниченной области. Для этого вам нужно сделать что-то вроде этого:
    • Создайте Water, указав в Rectangleкачестве цели в конструкторе.
    • Каждая ссылка на GraphicsDevice.Viewportдолжна быть заменена целевым прямоугольником
    • Я предполагаю, что вы дадите «правильную» - реальную - позицию в качестве Splashпараметра метода ( xPosition), так что все остальное будет хорошо при рисовании.

PS: Вы сказали что-то о OutOfBoundsException. Пожалуйста, дайте нам больше подробностей, если это так.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.