I'm working with 3D graphics in Monogame and I'm creating 100 instances of a Rectangle class I made:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace BlockMonglerFixed1
{
public class Rectangle
{
/// <summary>
/// Width of the rectangle
/// </summary>
public int Width { get; set; }
/// <summary>
/// Height of the rectangle
/// </summary>
public int Height { get; set; }
/// <summary>
/// Length of the rectangle
/// </summary>
public int Length { get; set; }
/// <summary>
/// Position of the rectangle
/// </summary>
public Vector3 Position { get; set; }
/// <summary>
/// Rotation vector3 of the rectangle. X = pitch. Y = yaw. Z = roll
/// </summary>
public Vector3 Rotation { get; set; }
/// <summary>
/// GraphicsDevice used to draw the rectangle
/// </summary>
public GraphicsDevice GraphicsDevice { get; set; }
/// <summary>
/// BasicEffect used to to draw the rectangle
/// </summary>
public BasicEffect Effect { get; set; }
/// <summary>
/// Color of the rectangle. Not used if a texture is being used
/// </summary>
public Color Color { get; set; }
/// <summary>
/// Texture of the rectangle. Not used if a color is being used.
/// </summary>
public Texture3D Texture { get; set; }
/// <summary>
/// Determines if the rectangle should move based on its rotation
/// </summary>
public bool MoveBasedOnRotation { get; set; }
int _vertexLength;
VertexBuffer _buffer;
/// <summary>
/// Cube constructor. Generates a new rectangle
/// </summary>
/// <param name="width">Width of rectangle</param>
/// <param name="height">Height of rectangle</param>
/// <param name="length">Length of rectangle</param>
/// <param name="position">Position of the rectangle></param>
/// <param name="rotation">Rotation of the rectangle. This value rotates the rectangle</param>
/// <param name="graphicsDevice">GraphicsDevice used to draw the rectangle</param>
/// <param name="effect">Effect of rectangle</param>
/// <param name="color">Color of rectangle</param>
/// <param name="moveBasedOnRotation">Determines if the rectangle should move based on its rotation</param>
public Rectangle(int width, int height, int length, Vector3 position,
Vector3 rotation, GraphicsDevice graphicsDevice, BasicEffect effect, Color color, bool moveBasedOnRotation)
{
//Dimensions of the rectangle
Width = width;
Height = height;
Length = length * 2;
//Position setup
Position = position;
//Rotation setup
Rotation = rotation;
MoveBasedOnRotation = moveBasedOnRotation;
//Effect setup
Effect = effect;
//Color setup
Color = color;
//Graphics device setup.
GraphicsDevice = graphicsDevice;
//Cube setup
VertexPositionColor[] vertexPositionColors =
{
//Face 1
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
//Face 2
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
//Side 1
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
//Side 2
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
//Bottom
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
//Top
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
};
//Length setup
_vertexLength = vertexPositionColors.Length;
//Buffer setup
_buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), _vertexLength,
BufferUsage.WriteOnly);
//Set data of the buffer to the vertexList
_buffer.SetData(vertexPositionColors);
}
public Rectangle(int width, int height, int length, Matrix worldMatrix, Matrix projectionMatrix,
Matrix viewMatrix,
GraphicsDevice graphicsDevice, BasicEffect effect, Texture3D texture)
{
throw new NotImplementedException();
}
/// <summary>
/// Sees if the rectangle collides with another object
/// </summary>
/// <param name="otherRectangle">Rectangle to detect collision with</param>
public bool IsColliding(Rectangle otherRectangle)
{
//Get the minimum values of our position
float minX = Position.X - Width;
float minY = Position.Y - Height;
float minZ = Position.Z - Length / 2f;
//Get the maximum values of our position
float maxX = Position.X + Width;
float maxY = Position.Y + Height;
float maxZ = Position.Z + Length / 2f;
//Get the minimum values of the other rectangle's position
float otherMinX = otherRectangle.Position.X - otherRectangle.Width;
float otherMinY = otherRectangle.Position.Y - otherRectangle.Height;
float otherMinZ = otherRectangle.Position.Z - otherRectangle.Length / 2f;
//Get the maximum values of the other rectangle's positions
float otherMaxX = otherRectangle.Position.X + otherRectangle.Width;
float otherMaxY = otherRectangle.Position.Y + otherRectangle.Height;
float otherMaxZ = otherRectangle.Position.Z + otherRectangle.Length / 2f;
//We can calculate if one rectangle is in another by seeing if one of our sides is inside another rectangle
return minX <= otherMaxX && maxX >= otherMinX &&
minY <= otherMaxY && maxY >= otherMinY &&
minZ <= otherMaxZ && maxZ >= otherMinZ;
}
/// <summary>
/// Draws the rectangle to the screen
/// </summary>
public void Draw()
{
//Hold the current position
Vector3 tempPosition = Position;
//Create a new matrix with the position and rotation
Matrix effectWorldMatrix = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up) *
Matrix.CreateTranslation(-new Vector3(0, 0, -Length / 2f)) *
Matrix.CreateRotationX(Rotation.X) *
Matrix.CreateRotationY(Rotation.Y) *
Matrix.CreateRotationZ(Rotation.Z) *
Matrix.CreateTranslation(Position);
//If MoveBasedOnRotation is true, set the translation of our new matrix to the previous translation
//if (!MoveBasedOnRotation) effectWorldMatrix.Translation = tempPosition;
//Set the effect to our respective matrices;
Effect.World = effectWorldMatrix;
Effect.View = Camera.ViewMatrix;
Effect.Projection = Camera.ProjectionMatrix;
//Apply our vertex buffer to the graphics device
GraphicsDevice.SetVertexBuffer(_buffer);
//Disable culling so that we can see our object on both sides
RasterizerState rasterizerState = new RasterizerState {CullMode = CullMode.None};
GraphicsDevice.RasterizerState = rasterizerState;
foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
{
//Apply the pass
pass.Apply();
//Use the graphics device to draw the triangle
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, _vertexLength);
}
}
}
}
The problem I am having is that the program runs smoothly at 60 fps, but then the fps randomly drops to near 0. The frequency of frame drops seems to be related to the number of rectangles.
Here is the Draw code that is called every frame (blocks is the rectangle array)
/// <summary>
/// Draw logic. Activates every frame.
/// </summary>
/// <param name="gameTime">How long the program has been running for</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Chocolate);
foreach (var a in blocks)
a.Draw();
fps++;
if (gameTime.TotalGameTime.Milliseconds % 1000 == 0 && enableFpsCounter)
{
Console.WriteLine(fps);
fps = 0;
}
base.Draw(gameTime);
}
The problem only happens when there is a sizable amount of rectangles (~25 rectangles)
Here is is a gif that showcases this problem: https://gyazo.com/ec41cacd6c89a41c330e151108590050
Thank you for reading and I appreciate any help I can get.
I managed to solve this problem by making it so that the RasterizerState is initiated inside the constructor rather than the Draw method:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace BlockMonglerFixed1
{
public class Rectangle
{
/// <summary>
/// Width of the rectangle
/// </summary>
public int Width { get; set; }
/// <summary>
/// Height of the rectangle
/// </summary>
public int Height { get; set; }
/// <summary>
/// Length of the rectangle
/// </summary>
public int Length { get; set; }
/// <summary>
/// Position of the rectangle
/// </summary>
public Vector3 Position { get; set; }
/// <summary>
/// Rotation vector3 of the rectangle. X = pitch. Y = yaw. Z = roll
/// </summary>
public Vector3 Rotation { get; set; }
/// <summary>
/// GraphicsDevice used to draw the rectangle
/// </summary>
public GraphicsDevice GraphicsDevice { get; set; }
/// <summary>
/// BasicEffect used to to draw the rectangle
/// </summary>
public BasicEffect Effect { get; set; }
/// <summary>
/// Color of the rectangle. Not used if a texture is being used
/// </summary>
public Color Color { get; set; }
/// <summary>
/// Texture of the rectangle. Not used if a color is being used.
/// </summary>
public Texture3D Texture { get; set; }
/// <summary>
/// Determines if the rectangle should move based on its rotation
/// </summary>
public bool MoveBasedOnRotation { get; set; }
int _vertexLength;
VertexBuffer _buffer;
RasterizerState _rasterizerState;
/// <summary>
/// Cube constructor. Generates a new rectangle
/// </summary>
/// <param name="width">Width of rectangle</param>
/// <param name="height">Height of rectangle</param>
/// <param name="length">Length of rectangle</param>
/// <param name="position">Position of the rectangle></param>
/// <param name="rotation">Rotation of the rectangle. This value rotates the rectangle</param>
/// <param name="graphicsDevice">GraphicsDevice used to draw the rectangle</param>
/// <param name="effect">Effect of rectangle</param>
/// <param name="color">Color of rectangle</param>
/// <param name="moveBasedOnRotation">Determines if the rectangle should move based on its rotation</param>
public Rectangle(int width, int height, int length, Vector3 position,
Vector3 rotation, GraphicsDevice graphicsDevice, BasicEffect effect, Color color, bool moveBasedOnRotation)
{
//Dimensions of the rectangle
Width = width;
Height = height;
Length = length * 2;
//Position setup
Position = position;
//Rotation setup
Rotation = rotation;
MoveBasedOnRotation = moveBasedOnRotation;
//Effect setup
Effect = effect;
//Color setup
Color = color;
//Graphics device setup.
GraphicsDevice = graphicsDevice;
//Cube setup
VertexPositionColor[] vertexPositionColors =
{
//Face 1
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
//Face 2
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
//Side 1
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
//Side 2
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
//Bottom
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, -Height, 0), Color),
//Top
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(-Width, Height, 0), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
new VertexPositionColor(new Vector3(-Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, -Length), Color),
new VertexPositionColor(new Vector3(Width, Height, 0), Color),
};
//Length setup
_vertexLength = vertexPositionColors.Length;
//Buffer setup
_buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), _vertexLength,
BufferUsage.WriteOnly);
//Set data of the buffer to the vertexList
_buffer.SetData(vertexPositionColors);
_rasterizerState = new RasterizerState {CullMode = CullMode.None};
GraphicsDevice.RasterizerState = _rasterizerState;
}
public Rectangle(int width, int height, int length, Matrix worldMatrix, Matrix projectionMatrix,
Matrix viewMatrix,
GraphicsDevice graphicsDevice, BasicEffect effect, Texture3D texture)
{
throw new NotImplementedException();
}
/// <summary>
/// Sees if the rectangle collides with another object
/// </summary>
/// <param name="otherRectangle">Rectangle to detect collision with</param>
public bool IsColliding(Rectangle otherRectangle)
{
//Get the minimum values of our position
float minX = Position.X - Width;
float minY = Position.Y - Height;
float minZ = Position.Z - Length / 2f;
//Get the maximum values of our position
float maxX = Position.X + Width;
float maxY = Position.Y + Height;
float maxZ = Position.Z + Length / 2f;
//Get the minimum values of the other rectangle's position
float otherMinX = otherRectangle.Position.X - otherRectangle.Width;
float otherMinY = otherRectangle.Position.Y - otherRectangle.Height;
float otherMinZ = otherRectangle.Position.Z - otherRectangle.Length / 2f;
//Get the maximum values of the other rectangle's positions
float otherMaxX = otherRectangle.Position.X + otherRectangle.Width;
float otherMaxY = otherRectangle.Position.Y + otherRectangle.Height;
float otherMaxZ = otherRectangle.Position.Z + otherRectangle.Length / 2f;
//We can calculate if one rectangle is in another by seeing if one of our sides is inside another rectangle
return minX <= otherMaxX && maxX >= otherMinX &&
minY <= otherMaxY && maxY >= otherMinY &&
minZ <= otherMaxZ && maxZ >= otherMinZ;
}
/// <summary>
/// Draws the triangle to the screen
/// </summary>
public void Draw()
{
//Hold the current position
Vector3 tempPosition = Position;
//Create a new matrix with the position and rotation
Matrix effectWorldMatrix = Matrix.CreateWorld(Vector3.Zero, Vector3.Forward, Vector3.Up) *
Matrix.CreateTranslation(-new Vector3(0, 0, -Length / 2f)) *
Matrix.CreateRotationX(Rotation.X) *
Matrix.CreateRotationY(Rotation.Y) *
Matrix.CreateRotationZ(Rotation.Z) *
Matrix.CreateTranslation(Position);
//If MoveBasedOnRotation is true, set the translation of our new matrix to the previous translation
//if (!MoveBasedOnRotation) effectWorldMatrix.Translation = tempPosition;
//Set the effect to our respective matrices;
Effect.World = effectWorldMatrix;
Effect.View = Camera.ViewMatrix;
Effect.Projection = Camera.ProjectionMatrix;
//Apply our vertex buffer to the graphics device
GraphicsDevice.SetVertexBuffer(_buffer);
foreach (EffectPass pass in Effect.CurrentTechnique.Passes)
{
//Apply the pass
pass.Apply();
//Use the graphics device to draw the triangle
GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, _vertexLength);
}
}
}
}