Monogame 和 C#,将具有相同图像的多个对象加载到列表中,都获得相同的属性

Monogame and C#, loading multiple objects with same image into a list, all get same properties

我刚开始使用 MonoGame 和 C#,我 运行 遇到了一个问题,试图将 1 个图像用于对象列表。

我正在尝试用 1 张图片制作 20 个气球,但它们应该都有不同的位置和计时器。它们在循环 0-19 中被添加到一个列表中,在每次迭代中我创建一个新的气球(图像),构造函数给它随机的起始坐标和计时器,图像是从参数中获取的。

我遇到的问题是列表中的所有气球都获得了循环结束后添加的最后一个气球的值,如果它们都是同一个对象,情况就会如此,但我正在创建一个每次都是新的所以不知道这里发生了什么?

气球 class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Game1
{
    public class Balloon
{
    private const int SCREEN_WIDTH = 640;
    private const int SCREEN_HEIGHT = 480;
    private const int MIN_VELOCITY = 30;

    //picture from contents
    private Texture2D image;
    //balloons current position
    private Vector2 position;
    //how fast its moving
    private Vector2 velocity;
    private bool moving;
    //timer, so they can pop up at a diferent time, number 1-9
    private int timer;


    public Balloon(Texture2D image)
    {
        this.image = image;
        setRandomPosition();
        setRandomTimer();
        //no balloon is moving when initialsied
        moving = false;
        //start with 0 speed
        velocity = new Vector2(0.0f, 0.0f);
    }

    //sets random X for the balloon, with Y just out of screen visible area
    public void setRandomPosition()
    {
        position = new Vector2(new Random().Next(SCREEN_WIDTH), SCREEN_HEIGHT + 1);
    }

    //number 1-9 , balloon starts moving when seconds elapsed % timer is 0
    public void setRandomTimer()
    {
        timer = new Random().Next(1, 10);
    }

    private void calculateRandomVelocity()
    {
        velocity = new Vector2(velocity.X, new Random().Next(50) + MIN_VELOCITY);
    }

    //starts moving the balloon by subtracting the Y
    public void go(GameTime gameTime)
    {
        moving = true;
        calculateRandomVelocity();
        //subtract Y so balloon goes up
        position -= velocity * (float)gameTime.ElapsedGameTime.TotalSeconds;
    }

    //stops the balloon and sets random X with Y outside visible area
    public void stop()
    {
        moving = false;
        setRandomPosition();
    }

    //--------Getters and Setters------------//

    public Vector2 getPosition()
    {
        return position;
    }

    public void setMoving(bool moving)
    {
        this.moving = moving;
    }

    public bool isMoving()
    {
        return moving;
    }

    public Texture2D getImage()
    {
        return image;
    }

    public void setImage(Texture2D image)
    {
        this.image = image;
    }

    public int getTimer()
    {
        return timer;
    }
}

}

游戏class:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Game1
{

public class Game1 : Game
{
    const int NUM_BALLOONS = 20;

    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    List<Balloon> balloons;

    public Game1()
    {
        Content.RootDirectory = "Content";
        graphics = new GraphicsDeviceManager(this);  
    }

    protected override void Initialize()
    {
        base.Initialize();
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);

        loadBalloonsIntoList();           
    }

    public void loadBalloonsIntoList()
    {
        balloons = new List<Balloon>();

        Texture2D image = Content.Load<Texture2D>("balloon");
        //load 20 balloons into the list  
        //after loading here it ends up with same properties - exact same object for all the elements
        //they all take the values from the last one added
        for (int i = 0; i < NUM_BALLOONS; i++)
        {
            Balloon b = new Balloon(image);
            balloons.Add(b);
        }   
    }

    public void moveBalloons(GameTime gameTime)
    {
        foreach(Balloon b in balloons)
        {
            //cant be 0 because all balloons would start on first iteration, start moving a balloon that is not moving already
            //and its time has come
            if (gameTime.ElapsedGameTime.Seconds > 0 && gameTime.ElapsedGameTime.Seconds % b.getTimer() == 0 && !b.isMoving())
            {
                b.go(gameTime);
            }
            //stop the balloon when it goes out of visible area
            if (b.getPosition().Y <= 0)
            {
                b.stop();
            }
        }
    }

    protected override void UnloadContent()
    {

    }

    protected override void Update(GameTime gameTime)
    {
        moveBalloons(gameTime);
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.White);

        spriteBatch.Begin();

        foreach(Balloon b in balloons)
        {
            spriteBatch.Draw(b.getImage(), b.getPosition(), Color.White);    
        }

        spriteBatch.End();

    }
}
}

您的 setRandomPosition 方法会在每次调用时创建 Random class 的新实例。由于 20 个气球正在快速连续实例化(即在一个循环中),每个新的随机 class 实例很可能以相同的种子值结束,因此输出相同的 "random" 值。因此,所有气球对象最终都具有相同的坐标。尝试在 class 级别创建一个 static Random 实例。

来自https://msdn.microsoft.com/en-us/library/h343ddh9(v=vs.110).aspx

The default seed value is derived from the system clock and has finite resolution. As a result, different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers.