I am working on game. I want to highlight a spot on the screen when something happens.
I created a class to do this for me, and found a bit of code to draw the rectangle:
static private Texture2D CreateRectangle(int width, int height, Color colori)
{
Texture2D rectangleTexture = new Texture2D(game.GraphicsDevice, width, height, 1, TextureUsage.None,
SurfaceFormat.Color);// create the rectangle texture, ,but it will have no color! lets fix that
Color[] color = new Color[width * height];//set the color to the amount of pixels in the textures
for (int i = 0; i < color.Length; i++)//loop through all the colors setting them to whatever values we want
{
color[i] = colori;
}
rectangleTexture.SetData(color);//set the color data on the texture
return rectangleTexture;//return the texture
}
The problem is that the code above is called every update, (60 times a second), and it was not written with optimization in mind. It needs to be extremely fast (the code above freezes the game, which has only skeleton code right now).
Any suggestions?
Note: Any new code would be great (WireFrame/Fill are both fine). I would like to be able to specify color.
The SafeArea demo on the XNA Creators Club site has code to do specifically that.
You don't have to create the Texture every frame, just in LoadContent. A very stripped down version of the code from that demo:
public class RectangleOverlay : DrawableGameComponent
{
SpriteBatch spriteBatch;
Texture2D dummyTexture;
Rectangle dummyRectangle;
Color Colori;
public RectangleOverlay(Rectangle rect, Color colori, Game game)
: base(game)
{
// Choose a high number, so we will draw on top of other components.
DrawOrder = 1000;
dummyRectangle = rect;
Colori = colori;
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
dummyTexture = new Texture2D(GraphicsDevice, 1, 1);
dummyTexture.SetData(new Color[] { Color.White });
}
public override void Draw(GameTime gameTime)
{
spriteBatch.Begin();
spriteBatch.Draw(dummyTexture, dummyRectangle, Colori);
spriteBatch.End();
}
}
This is how I did it. It is probably not the fastest or the best solution, but it works.
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Engine
{
/// <summary>
/// An extended version of the SpriteBatch class that supports line and
/// rectangle drawing.
/// </summary>
public class ExtendedSpriteBatch : SpriteBatch
{
/// <summary>
/// The texture used when drawing rectangles, lines and other
/// primitives. This is a 1x1 white texture created at runtime.
/// </summary>
public Texture2D WhiteTexture { get; protected set; }
public ExtendedSpriteBatch(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
this.WhiteTexture = new Texture2D(this.GraphicsDevice, 1, 1);
this.WhiteTexture.SetData(new Color[] { Color.White });
}
/// <summary>
/// Draw a line between the two supplied points.
/// </summary>
/// <param name="start">Starting point.</param>
/// <param name="end">End point.</param>
/// <param name="color">The draw color.</param>
public void DrawLine(Vector2 start, Vector2 end, Color color)
{
float length = (end - start).Length();
float rotation = (float)Math.Atan2(end.Y - start.Y, end.X - start.X);
this.Draw(this.WhiteTexture, start, null, color, rotation, Vector2.Zero, new Vector2(length, 1), SpriteEffects.None, 0);
}
/// <summary>
/// Draw a rectangle.
/// </summary>
/// <param name="rectangle">The rectangle to draw.</param>
/// <param name="color">The draw color.</param>
public void DrawRectangle(Rectangle rectangle, Color color)
{
this.Draw(this.WhiteTexture, new Rectangle(rectangle.Left, rectangle.Top, rectangle.Width, 1), color);
this.Draw(this.WhiteTexture, new Rectangle(rectangle.Left, rectangle.Bottom, rectangle.Width, 1), color);
this.Draw(this.WhiteTexture, new Rectangle(rectangle.Left, rectangle.Top, 1, rectangle.Height), color);
this.Draw(this.WhiteTexture, new Rectangle(rectangle.Right, rectangle.Top, 1, rectangle.Height + 1), color);
}
/// <summary>
/// Fill a rectangle.
/// </summary>
/// <param name="rectangle">The rectangle to fill.</param>
/// <param name="color">The fill color.</param>
public void FillRectangle(Rectangle rectangle, Color color)
{
this.Draw(this.WhiteTexture, rectangle, color);
}
}
}
This is probably not the best solution, but you should be able to use a 1x1 pixel texture stretched to fit the rectangle.
Related
Hello I am trying to make a paint program using XNA and I followed the guide found in here as much as possible: How to create Paint-like app with XNA?
And it worked all great so far however there is one issue: the drawn rectangles dont connect together to form a line. I am out of ideas and I would appreciate any help offered. Heres my code below. Please also take a look at the image attached to get a better understanding.
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
namespace ProfAnas
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D canvas;
Vector2 brushPos;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1920; // set this value to the desired width of your window
graphics.PreferredBackBufferHeight = 1080; // set this value to the desired height of your window
graphics.ApplyChanges();
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
base.Initialize();
Color[] pixel = new Color[1920 * 1080];
for (int i = 0; i < pixel.Length; i++)
{
pixel[i] = Color.White;
}
pixel[1919] = Color.Red;
spriteBatch = new SpriteBatch(GraphicsDevice);
canvas = new Texture2D(this.GraphicsDevice, 1920, 1080);
canvas.SetData<Color>(pixel);
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
MouseState state = Mouse.GetState();
Color[] pixel = new Color[1920 * 1080];
canvas.GetData<Color>(pixel);
if (state.LeftButton == ButtonState.Pressed)
{
brushPos.X = state.X;
brushPos.Y = state.Y;
double piOn4 = Math.PI / 4;
int xComponent;
int yComponent;
int screenWidth = 1920;
int screenHeight = 1080;
int regionXHalfed = (int)Math.Ceiling((10.0 * Math.Cos(piOn4) + 30.0 * Math.Cos(piOn4))/2);
int regionYHalfed = (int)Math.Ceiling((10.0 * Math.Sin(piOn4) + 30.0 * Math.Sin(piOn4))/2);
double angle;
double centerToBoundary;
double pixelToCenter;
List<int> boundedPixel = new List<int>();
for (int row=(state.Y-regionYHalfed); row < (state.Y + regionYHalfed); row++)
{
for (int column= (state.X - regionXHalfed); column < (state.X + regionXHalfed); column++)
{
xComponent=column - state.X+1;
yComponent=row - state.Y+1;
if (xComponent == 0)
{
pixelToCenter = Math.Sqrt((double)xComponent*xComponent + (double)yComponent*yComponent);
if (Math.Abs(pixelToCenter) <= 5*Math.Sqrt(2))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
continue;
}
angle=Math.Atan( (double)yComponent / (double)xComponent);
if (angle>= (piOn4 - Math.Atan(1.0/3)) && angle<= (piOn4 + Math.Atan(1.0/3)))
{
centerToBoundary = 15 / Math.Cos(angle - piOn4);
pixelToCenter= xComponent / Math.Cos(angle);
if( Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row)* screenWidth + column)+1);
}
}
if (angle >= (piOn4 + Math.Atan(1.0 / 3)) && angle <= Math.PI/2)
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
if (angle >= 0.0 && angle <= (piOn4 - Math.Atan(1.0 / 3)))
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
if (angle >= -Math.PI / 2 && angle <= 0.0)
{
centerToBoundary = 5 / Math.Cos(angle + piOn4);
pixelToCenter = xComponent / Math.Cos(angle);
if (Math.Abs(pixelToCenter) <= Math.Abs(centerToBoundary))
{
boundedPixel.Add(((row) * screenWidth + column) + 1);
}
}
}
}
foreach (int i in boundedPixel)
{
if(i>=0)
pixel[i] = Color.Red;
}
}
canvas.SetData<Color>(pixel);
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
spriteBatch.Draw(canvas, new Vector2(0, 0));
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Thank you :)
As #adv12 suggested, the Update method of XNA games isn't as fast as the one of MSPaint's, and that's why you'll never have a line if drawing pixel-by-pixel (or in your case rectangle-by-rectangle) if you move your mouse fast across the canvas.
The possible solution is to draw new rectangles when you release left mouse button, between the rectangles that were created when left mouse button was pressed. That will give you twice -1 the rectangles you are currently drawing, and you will have a line, no matter how fast the Update is called.
I'm drawing cylinders using the primitives provided by XNA, and what I want to do is, when the mouse hovers on a cylinder, I detect it. I tried using bounding spheres but it didn't work, as it wasn't accurate. I don't know what to do. Any help?
Summary of what I want to do.
1- Create bounding Box for this Cylinder
2- Rotate this bounding Box.
CODE TO DRAW CYLINDER can be found here
http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives_3d
#region File Description
//-----------------------------------------------------------------------------
// CylinderPrimitive.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace TheProteinBundle
{
/// <summary>
/// Geometric primitive class for drawing cylinders.
/// </summary>
public class CylinderPrimitive : GeometricPrimitive
{
/// <summary>
/// Constructs a new cylinder primitive, using default settings.
/// </summary>
public CylinderPrimitive(GraphicsDevice graphicsDevice)
: this(graphicsDevice, 1, 1, 32)
{
}
/// <summary>
/// Constructs a new cylinder primitive,
/// with the specified size and tessellation level.
/// </summary>
public CylinderPrimitive(GraphicsDevice graphicsDevice,
float height, float diameter, int tessellation)
{
if (tessellation < 3)
throw new ArgumentOutOfRangeException("tessellation");
height /= 2;
float radius = diameter / 2;
// Create a ring of triangles around the outside of the cylinder.
for (int i = 0; i < tessellation; i++)
{
Vector3 normal = GetCircleVector(i, tessellation);
AddVertex(normal * radius + Vector3.Up * height, normal);
AddVertex(normal * radius + Vector3.Down * height, normal);
AddIndex(i * 2);
AddIndex(i * 2 + 1);
AddIndex((i * 2 + 2) % (tessellation * 2));
AddIndex(i * 2 + 1);
AddIndex((i * 2 + 3) % (tessellation * 2));
AddIndex((i * 2 + 2) % (tessellation * 2));
}
// Create flat triangle fan caps to seal the top and bottom.
CreateCap(tessellation, height, radius, Vector3.Up);
CreateCap(tessellation, height, radius, Vector3.Down);
InitializePrimitive(graphicsDevice);
base.boundingSphere.Center = Vector3.Zero;
if (height > diameter)
base.boundingSphere.Radius = height;
else
base.boundingSphere.Radius = diameter;
}
/// <summary>
/// Helper method creates a triangle fan to close the ends of the cylinder.
/// </summary>
void CreateCap(int tessellation, float height, float radius, Vector3 normal)
{
// Create cap indices.
for (int i = 0; i < tessellation - 2; i++)
{
if (normal.Y > 0)
{
AddIndex(CurrentVertex);
AddIndex(CurrentVertex + (i + 1) % tessellation);
AddIndex(CurrentVertex + (i + 2) % tessellation);
}
else
{
AddIndex(CurrentVertex);
AddIndex(CurrentVertex + (i + 2) % tessellation);
AddIndex(CurrentVertex + (i + 1) % tessellation);
}
}
// Create cap vertices.
for (int i = 0; i < tessellation; i++)
{
Vector3 position = GetCircleVector(i, tessellation) * radius +
normal * height;
AddVertex(position, normal);
}
}
/// <summary>
/// Helper method computes a point on a circle.
/// </summary>
static Vector3 GetCircleVector(int i, int tessellation)
{
float angle = i * MathHelper.TwoPi / tessellation;
float dx = (float)Math.Cos(angle);
float dz = (float)Math.Sin(angle);
return new Vector3(dx, 0, dz);
}
}
}
Using this class
#region File Description
//-----------------------------------------------------------------------------
// GeometricPrimitive.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace TheProteinBundle
{
/// <summary>
/// Base class for simple geometric primitive models. This provides a vertex
/// buffer, an index buffer, plus methods for drawing the model. Classes for
/// specific types of primitive (CubePrimitive, SpherePrimitive, etc.) are
/// derived from this common base, and use the AddVertex and AddIndex methods
/// to specify their geometry.
/// </summary>
public abstract class GeometricPrimitive : IDisposable
{
#region Fields
// During the process of constructing a primitive model, vertex
// and index data is stored on the CPU in these managed lists.
List<VertexPositionNormal> vertices = new List<VertexPositionNormal>();
List<ushort> indices = new List<ushort>();
// Once all the geometry has been specified, the InitializePrimitive
// method copies the vertex and index data into these buffers, which
// store it on the GPU ready for efficient rendering.
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
BasicEffect basicEffect;
public BoundingSphere boundingsphere2;
public BoundingSphere boundingSphere;
public Matrix world;
#endregion
#region Initialization
/// <summary>
/// Adds a new vertex to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddVertex(Vector3 position, Vector3 normal)
{
vertices.Add(new VertexPositionNormal(position, normal));
}
/// <summary>
/// Adds a new index to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddIndex(int index)
{
if (index > ushort.MaxValue)
throw new ArgumentOutOfRangeException("index");
indices.Add((ushort)index);
}
/// <summary>
/// Queries the index of the current vertex. This starts at
/// zero, and increments every time AddVertex is called.
/// </summary>
protected int CurrentVertex
{
get { return vertices.Count; }
}
/// <summary>
/// Once all the geometry has been specified by calling AddVertex and AddIndex,
/// this method copies the vertex and index data into GPU format buffers, ready
/// for efficient rendering.
protected void InitializePrimitive(GraphicsDevice graphicsDevice)
{
// Create a vertex declaration, describing the format of our vertex data.
// Create a vertex buffer, and copy our vertex data into it.
vertexBuffer = new VertexBuffer(graphicsDevice,
typeof(VertexPositionNormal),
vertices.Count, BufferUsage.None);
vertexBuffer.SetData(vertices.ToArray());
// Create an index buffer, and copy our index data into it.
indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
indices.Count, BufferUsage.None);
indexBuffer.SetData(indices.ToArray());
// Create a BasicEffect, which will be used to render the primitive.
basicEffect = new BasicEffect(graphicsDevice);
basicEffect.EnableDefaultLighting();
}
/// <summary>
/// Finalizer.
/// </summary>
~GeometricPrimitive()
{
Dispose(false);
}
/// <summary>
/// Frees resources used by this object.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Frees resources used by this object.
/// </summary>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (vertexBuffer != null)
vertexBuffer.Dispose();
if (indexBuffer != null)
indexBuffer.Dispose();
if (basicEffect != null)
basicEffect.Dispose();
}
}
#endregion
public void TransformBoundingSphere(Matrix TransformToWorld)
{
boundingSphere.Transform(ref TransformToWorld, out boundingsphere2);
}
public bool CheckRayIntersection(Ray ray)
{
if (ray.Intersects(boundingsphere2).HasValue) return true;
return false;
}
#region Draw
/// <summary>
/// Draws the primitive model, using the specified effect. Unlike the other
/// Draw overload where you just specify the world/view/projection matrices
/// and color, this method does not set any renderstates, so you must make
/// sure all states are set to sensible values before you call it.
/// </summary>
public void Draw(Effect effect)
{
GraphicsDevice graphicsDevice = effect.GraphicsDevice;
// Set our vertex declaration, vertex buffer, and index buffer.
graphicsDevice.SetVertexBuffer(vertexBuffer);
graphicsDevice.Indices = indexBuffer;
foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
{
effectPass.Apply();
int primitiveCount = indices.Count / 3;
graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
vertices.Count, 0, primitiveCount);
}
}
/// <summary>
/// Draws the primitive model, using a BasicEffect shader with default
/// lighting. Unlike the other Draw overload where you specify a custom
/// effect, this method sets important renderstates to sensible values
/// for 3D model rendering, so you do not need to set these states before
/// you call it.
/// </summary>
public void Draw(Matrix world, Matrix view, Matrix projection, Color color)
{
// Set BasicEffect parameters.
basicEffect.World = world;
basicEffect.View = view;
basicEffect.Projection = projection;
basicEffect.DiffuseColor = color.ToVector3();
basicEffect.Alpha = color.A / 255.0f;
GraphicsDevice device = basicEffect.GraphicsDevice;
device.DepthStencilState = DepthStencilState.Default;
if (color.A < 255)
{
// Set renderstates for alpha blended rendering.
device.BlendState = BlendState.AlphaBlend;
}
else
{
// Set renderstates for opaque rendering.
device.BlendState = BlendState.Opaque;
}
// Draw the model, using BasicEffect.
Draw(basicEffect);
}
#endregion
}
}
Can anybody provide a basic tutorial on how to override the paint event in C#? I dont have much experience in C# and this is some part that I dont get easily as I cannot follow some tutorials and tricks as I dont get the concept of overriding in C#.
This is a very basic example, which will (should!) draw a red 'X':
public class FacadeControl : Control
{
private Pen invalidPen;
public FacadeControl()
{
invalidPen = new Pen(Color.Red, 2);
SetStyle(ControlStyles.ResizeRedraw, true); // make sure the control is redrawn every time it is resized
}
protected override void OnPaint(PaintEventArgs pe)
{
// get the graphics object to use to draw
Graphics g = pe.Graphics;
g.DrawLine(invalidPen, 0, 0, Width, Height);
g.DrawLine(invalidPen, 0, Height, Width, 0);
}
}
}
For example :
public class FirstControl : Control{
public FirstControl() {}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
e.Graphics.DrawString(Text, Font, new SolidBrush(ForeColor), ClientRectangle);
}
}
just don't forget to call the base paint handler before writing yours
this is the button
I would like to share my code:
The button class:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
namespace Controles.Buttons
{
/// <summary>
/// Clase personalizada button.
/// Jorge Arturo Avilés Nuñez
/// Zapopan, Jalisco, México
/// 18-DIC-2017
/// </summary>
public class SansationRoundButton : Button
{
#region members
private TextRenderingHint _hint = TextRenderingHint.AntiAlias;
private const int FlagMouseOver = 0x0001;
private const int FlagMouseDown = 0x0002;
private int state = 0;
#endregion
#region Constructor
public SansationRoundButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.Font = new System.Drawing.Font("Sansation", 21.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
this.UseVisualStyleBackColor = true;
this.Cursor = Cursors.Hand;
}
#endregion
#region Internal methods and properties
internal bool OwnerDraw
{
get
{
return FlatStyle != FlatStyle.System;
}
}
internal bool MouseIsOver
{
get
{
return GetFlag(FlagMouseOver);
}
}
#endregion
#region Private methods
private bool GetFlag(int flag)
{
return ((state & flag) == flag);
}
private void SetFlag(int flag, bool value)
{
bool oldValue = ((state & flag) != 0);
if (value)
state |= flag;
else
state &= ~flag;
if (OwnerDraw && (flag & FlagMouseDown) != 0 && value != oldValue)
AccessibilityNotifyClients(AccessibleEvents.StateChange, -1);
}
#endregion
#region Overrides
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
SetFlag(FlagMouseOver, true);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
SetFlag(FlagMouseOver, false);
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
e.Graphics.Clear(Color.White);
e.Graphics.TextRenderingHint = this.TextRenderingHint;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Color backColor = this.MouseIsOver ? this.BackColorMouseOver : this.BackColor;
Color borderColor = this.MouseIsOver ? this.BorderColorMouseOver : this.BorderColor;
e.Graphics.DrawRoundedRectangle(new Pen(borderColor), 0, 0, this.Width - 1, this.Height - 1, 10);
e.Graphics.FillRoundedRectangle(new SolidBrush(backColor), 0, 0, this.Width - 1, this.Height - 1, 10);
StringFormat sr = BaseControl.CreateStringFormat(this, this.TextAlign, false, this.UseMnemonic);
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(ForeColor), ClientRectangle, sr);
}
#endregion
#region public properties
public TextRenderingHint TextRenderingHint
{
get { return this._hint; }
set { this._hint = value; }
}
public new bool ShowKeyboardCues
{
get
{
return base.ShowKeyboardCues;
}
}
public Color BackColorMouseOver { get; set; } = Color.Red;
public Color BorderColor { get; set; } = Color.Black;
public Color BorderColorMouseOver { get; set; } = Color.Black;
#endregion
}
}
this class BaseControl creates a new instance of StringFormat object that is needed to call the DrawString method from the Graphics object.
using System.Drawing;
using System.Windows.Forms;
namespace Controles.Buttons
{
public class BaseControl
{
private static readonly ContentAlignment anyRight = ContentAlignment.TopRight | ContentAlignment.MiddleRight | ContentAlignment.BottomRight;
private static readonly ContentAlignment anyBottom = ContentAlignment.BottomLeft | ContentAlignment.BottomCenter | ContentAlignment.BottomRight;
private static readonly ContentAlignment anyCenter = ContentAlignment.TopCenter | ContentAlignment.MiddleCenter | ContentAlignment.BottomCenter;
private static readonly ContentAlignment anyMiddle = ContentAlignment.MiddleLeft | ContentAlignment.MiddleCenter | ContentAlignment.MiddleRight;
static StringAlignment TranslateAlignment(ContentAlignment align)
{
StringAlignment result;
if ((align & anyRight) != 0)
result = StringAlignment.Far;
else if ((align & anyCenter) != 0)
result = StringAlignment.Center;
else
result = StringAlignment.Near;
return result;
}
static StringAlignment TranslateLineAlignment(ContentAlignment align)
{
StringAlignment result;
if ((align & anyBottom) != 0)
{
result = StringAlignment.Far;
}
else if ((align & anyMiddle) != 0)
{
result = StringAlignment.Center;
}
else
{
result = StringAlignment.Near;
}
return result;
}
static StringFormat StringFormatForAlignment(ContentAlignment align)
{
StringFormat output = new StringFormat();
output.Alignment = TranslateAlignment(align);
output.LineAlignment = TranslateLineAlignment(align);
return output;
}
public static StringFormat CreateStringFormat(SansationRoundButton ctl, ContentAlignment textAlign, bool showEllipsis, bool useMnemonic)
{
StringFormat stringFormat = StringFormatForAlignment(textAlign);
// Adjust string format for Rtl controls
if (ctl.RightToLeft == RightToLeft.Yes)
{
stringFormat.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
}
if (showEllipsis)
{
stringFormat.Trimming = StringTrimming.EllipsisCharacter;
stringFormat.FormatFlags |= StringFormatFlags.LineLimit;
}
if (!useMnemonic)
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
}
else if (ctl.ShowKeyboardCues)
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Show;
}
else
{
stringFormat.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.Hide;
}
if (ctl.AutoSize)
{
stringFormat.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces;
}
return stringFormat;
}
}
}
finally this class is used to create and fill the rectangles.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
namespace Plasmoid.Extensions
{
static class GraphicsExtension
{
private static GraphicsPath GenerateRoundedRectangle(
this Graphics graphics,
RectangleF rectangle,
float radius)
{
float diameter;
GraphicsPath path = new GraphicsPath();
if (radius <= 0.0F)
{
path.AddRectangle(rectangle);
path.CloseFigure();
return path;
}
else
{
if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
return graphics.GenerateCapsule(rectangle);
diameter = radius * 2.0F;
SizeF sizeF = new SizeF(diameter, diameter);
RectangleF arc = new RectangleF(rectangle.Location, sizeF);
path.AddArc(arc, 180, 90);
arc.X = rectangle.Right - diameter;
path.AddArc(arc, 270, 90);
arc.Y = rectangle.Bottom - diameter;
path.AddArc(arc, 0, 90);
arc.X = rectangle.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
}
return path;
}
private static GraphicsPath GenerateCapsule(
this Graphics graphics,
RectangleF baseRect)
{
float diameter;
RectangleF arc;
GraphicsPath path = new GraphicsPath();
try
{
if (baseRect.Width > baseRect.Height)
{
diameter = baseRect.Height;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 90, 180);
arc.X = baseRect.Right - diameter;
path.AddArc(arc, 270, 180);
}
else if (baseRect.Width < baseRect.Height)
{
diameter = baseRect.Width;
SizeF sizeF = new SizeF(diameter, diameter);
arc = new RectangleF(baseRect.Location, sizeF);
path.AddArc(arc, 180, 180);
arc.Y = baseRect.Bottom - diameter;
path.AddArc(arc, 0, 180);
}
else path.AddEllipse(baseRect);
}
catch { path.AddEllipse(baseRect); }
finally { path.CloseFigure(); }
return path;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.DrawPath(pen, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius
/// for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
/// <param name="width">Width of the rectangle to draw.</param>
/// <param name="height">Height of the rectangle to draw.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void DrawRoundedRectangle(
this Graphics graphics,
Pen pen,
int x,
int y,
int width,
int height,
int radius)
{
graphics.DrawRoundedRectangle(
pen,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
/// <summary>
/// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
/// and the radius for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="width">Width of the rectangle to fill.</param>
/// <param name="height">Height of the rectangle to fill.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void FillRoundedRectangle(
this Graphics graphics,
Brush brush,
float x,
float y,
float width,
float height,
float radius)
{
RectangleF rectangle = new RectangleF(x, y, width, height);
GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
SmoothingMode old = graphics.SmoothingMode;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.FillPath(brush, path);
graphics.SmoothingMode = old;
}
/// <summary>
/// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
/// and the radius for the arcs that make the rounded edges.
/// </summary>
/// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
/// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
/// <param name="width">Width of the rectangle to fill.</param>
/// <param name="height">Height of the rectangle to fill.</param>
/// <param name="radius">The radius of the arc used for the rounded edges.</param>
public static void FillRoundedRectangle(
this Graphics graphics,
Brush brush,
int x,
int y,
int width,
int height,
int radius)
{
graphics.FillRoundedRectangle(
brush,
Convert.ToSingle(x),
Convert.ToSingle(y),
Convert.ToSingle(width),
Convert.ToSingle(height),
Convert.ToSingle(radius));
}
}
}
Enjoy!
I've been experimenting with drawing lines in XNA. I've got no trouble drawing lines in the X and Y directions, but whenever I try and add Z data to the points, there seems to be no effect.
Here's what I was hoping I could do by adding Z information (which I've simulated here by altering the Y and averaging the X points with the midpoint)
And here's what I'm actually getting (I've translated the 2nd line upwards to verify that there are, in fact, two lines getting drawn - when I only change the Z, the two lines get drawn on top of each other)
Am I messing up something basic with my perspective matrix? Skipping an important step? Code below is for the 2nd picture.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace WindowsGame3
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
BasicEffect baseEffect;
VertexPositionColor[] vertices;
VertexPositionColor[] verticesTop;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
float AspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
baseEffect = new BasicEffect(graphics.GraphicsDevice);
baseEffect.World = Matrix.Identity;
baseEffect.View = Matrix.Identity;
baseEffect.VertexColorEnabled = true;
baseEffect.Projection = Matrix.CreateOrthographicOffCenter
(0, graphics.GraphicsDevice.Viewport.Width, // left, right
graphics.GraphicsDevice.Viewport.Height, 0, // bottom, top
-100, 100); // near, far plane
vertices = new VertexPositionColor[7];
verticesTop = new VertexPositionColor[7];
vertices[0].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 1/8, graphics.GraphicsDevice.Viewport.Height * 5/7, 0);
vertices[0].Color = Color.Black;
vertices[1].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 2/8, graphics.GraphicsDevice.Viewport.Height * 5/7, 1/8);
vertices[1].Color = Color.Red;
vertices[2].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 3 / 8, graphics.GraphicsDevice.Viewport.Height * 5 / 7, -2/8);
vertices[2].Color = Color.Black;
vertices[3].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 4 / 8, graphics.GraphicsDevice.Viewport.Height * 5 / 7, 3/8);
vertices[3].Color = Color.Red;
vertices[4].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 5 / 8, graphics.GraphicsDevice.Viewport.Height * 5 / 7, -4/8);
vertices[4].Color = Color.Black;
vertices[5].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 6 / 8, graphics.GraphicsDevice.Viewport.Height * 5 / 7, 5/8);
vertices[5].Color = Color.Red;
vertices[6].Position = new Vector3(graphics.GraphicsDevice.Viewport.Width * 7 / 8, graphics.GraphicsDevice.Viewport.Height * 5 / 7, -6/8);
vertices[6].Color = Color.Black;
for (int i = 0; i < 7; i++)
{
verticesTop[i] = vertices[i];
verticesTop[i].Position.Y -= 200; // remove this line once perspective is working
verticesTop[i].Position.Z += 100;
}
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
// TODO: use this.Content to load your game content here
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
baseEffect.CurrentTechnique.Passes[0].Apply();
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, vertices, 0, 6);
graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, verticesTop, 0, 6);
base.Draw(gameTime);
}
}
}
The function you are using Matrix.CreateOrthographicOffCenter() creates an orthogonal projection matrix which have no perspective.
Try using Matrix.CreatePerspectiveOffCenter() or Matrix.CreatePerspectiveFieldOfView() instead.
baseEffect.Projection = Matrix.CreatePerspectiveFieldOfView (
1.57f, // 90 degrees field of view
width / height, // aspect ratio
1.0f, // near plane, you want that as far as possible
10000.0f); // far plane, you want that as near as possible
I have some sort of a problem. I'm new to XNA and want to draw a polygon shape that looks something like this (In the end, I want these point to be random):
So I read some articles and this is what I ended up with:
private VertexPositionColor[] vertices;
public TextureClass()
{
setupVertices();
}
public override void Render(SpriteBatch spriteBatch)
{
Texture2D texture = createTexture(spriteBatch);
spriteBatch.Draw(texture, new Rectangle((int)vertices[0].Position.X, (int)vertices[0].Position.Y, 30, 30), Color.Brown);
}
private Texture2D createTexture(SpriteBatch spriteBatch)
{
Texture2D texture = new Texture2D(spriteBatch.GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
texture.SetData<Color>(new Color[] { Color.Brown });
return texture;
}
When I call Render it's starts drawing some squares as if it where in a loop. I'm just guessing I'm doing it all wrong. I would love it if someones points me into the right direction. Just creating a polygon and drawing it. It seemed so simple...
Here it what I have right now.
A class that generates a BasicEffect with some desired asignments.
public class StandardBasicEffect : BasicEffect
{
public StandardBasicEffect(GraphicsDevice graphicsDevice)
: base(graphicsDevice)
{
this.VertexColorEnabled = true;
this.Projection = Matrix.CreateOrthographicOffCenter(
0, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height, 0, 0, 1);
}
public StandardBasicEffect(BasicEffect effect)
: base(effect) { }
public BasicEffect Clone()
{
return new StandardBasicEffect(this);
}
}
Here is my PolygonShape class
/// <summary>
/// A Polygon object that you will be able to draw.
/// Animations are being implemented as we speak.
/// </summary>
/// <param name="graphicsDevice">The graphicsdevice from a Game object</param>
/// <param name="vertices">The vertices in a clockwise order</param>
public PolygonShape(GraphicsDevice graphicsDevice, VertexPositionColor[] vertices)
{
this.graphicsDevice = graphicsDevice;
this.vertices = vertices;
this.triangulated = false;
triangulatedVertices = new VertexPositionColor[vertices.Length * 3];
indexes = new int[vertices.Length];
}
/// <summary>
/// Triangulate the set of VertexPositionColors so it will be drawn correcrly
/// </summary>
/// <returns>The triangulated vertices array</returns>}
public VertexPositionColor[] Triangulate()
{
calculateCenterPoint();{
setupIndexes();
for (int i = 0; i < indexes.Length; i++)
{
setupDrawableTriangle(indexes[i]);
}
triangulated = true;
return triangulatedVertices;
}
/// <summary>
/// Calculate the center point needed for triangulation.
/// The polygon will be irregular, so this isn't the actual center of the polygon
/// but it will do for now, as we only need an extra point to make the triangles with</summary>
private void calculateCenterPoint()
{
float xCount = 0, yCount = 0;
foreach (VertexPositionColor vertice in vertices)
{
xCount += vertice.Position.X;
yCount += vertice.Position.Y;
}
centerPoint = new Vector3(xCount / vertices.Length, yCount / vertices.Length, 0);
}
private void setupIndexes()
{
for (int i = 1; i < triangulatedVertices.Length; i = i + 3)
{
indexes[i / 3] = i - 1;
}
}
private void setupDrawableTriangle(int index)
{
triangulatedVertices[index] = vertices[index / 3]; //No DividedByZeroException?...
if (index / 3 != vertices.Length - 1)
triangulatedVertices[index + 1] = vertices[(index / 3) + 1];
else
triangulatedVertices[index + 1] = vertices[0];
triangulatedVertices[index + 2].Position = centerPoint;
}
/// <summary>
/// Draw the polygon. If you haven't called Triangulate yet, I wil do it for you.
/// </summary>
/// <param name="effect">The BasicEffect needed for drawing</param>
public void Draw(BasicEffect effect)
{
try
{
if (!triangulated)
Triangulate();
draw(effect);
}
catch (Exception exception)
{
throw exception;
}
}
private void draw(BasicEffect effect)
{
effect.CurrentTechnique.Passes[0].Apply();
graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
PrimitiveType.TriangleList, triangulatedVertices, 0, vertices.Length);
}
Sorry, it's kind of alot. Now for my next quest. Animation my polygon.
Hope it helped fellow people with the same problem.
this code is useful to draw 2D lines, some calcs can be done into an initilization call, but i prefer for this example to keep all together.
public void DrawLine(VertexPositionColor[] Vertices)
{
Game.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
Vector2 center;
center.X = Game.GraphicsDevice.Viewport.Width * 0.5f;
center.Y = Game.GraphicsDevice.Viewport.Height * 0.5f;
Matrix View = Matrix.CreateLookAt( new Vector3( center, 0 ), new Vector3( center, 1 ), new Vector3( 0, -1, 0 ) );
Matrix Projection = Matrix.CreateOrthographic( center.X * 2, center.Y * 2, -0.5f, 1 );
Effect EffectLines = Game.Content.Load<Effect>( "lines" );
EffectLines.CurrentTechnique = EffectLines.Techniques["Lines"];
EffectLines.Parameters["xViewProjection"].SetValue( View * Projection );
EffectLines.Parameters["xWorld"].SetValue( Matrix.Identity );
foreach ( EffectPass pass in EffectLines.CurrentTechnique.Passes )
{
pass.Apply( );
Game.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>
( PrimitiveType.LineList, Vertices, 0, Vertices.Length/2 );
}
}
LINES.FX
uniform float4x4 xWorld;
uniform float4x4 xViewProjection;
void VS_Basico(in float4 inPos : POSITION, in float4 inColor: COLOR0, out float4 outPos: POSITION, out float4 outColor:COLOR0 )
{
float4 tmp = mul (inPos, xWorld);
outPos = mul (tmp, xViewProjection);
outColor = inColor;
}
float4 PS_Basico(in float4 inColor:COLOR) :COLOR
{
return inColor;
}
technique Lines
{
pass Pass0
{
VertexShader = compile vs_2_0 VS_Basico();
PixelShader = compile ps_2_0 PS_Basico();
FILLMODE = SOLID;
CULLMODE = NONE;
}
}
I worked with XNA in the past on a physics simulation where I had to draw bounding boxes with GraphicsDevice.DrawIndexedPrimitives (You should google or MSDN for this function for more worked examples.)
The below code is what I used in my project for drawing a 3D geometry.
/// <summary>
/// Draw the primitive.
/// </summary>
/// <param name="world">World Matrix</param>
/// <param name="view">View Matrix</param>
/// <param name="projection">Projection Matrix</param>
/// <param name="color">Color of the primitive</param>
public void Draw(Matrix world, Matrix view, Matrix projection, Color color)
{
_mGraphicsDevice.VertexDeclaration = _mVertexDeclaration;
_mGraphicsDevice.Vertices[0].SetSource(_mVertexBuffer, 0, VertexPositionNormal.SizeInBytes);
_mGraphicsDevice.Indices = _mIndexBuffer;
_mBasicEffect.DiffuseColor = color.ToVector3();
_mBasicEffect.World = _mTransform * world;
_mBasicEffect.View = view;
_mBasicEffect.Projection = projection;
int primitiveCount = _mIndex.Count / 3;
_mBasicEffect.Begin();
foreach (EffectPass pass in _mBasicEffect.CurrentTechnique.Passes)
{
pass.Begin();
_mGraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _mVertex.Count, 0, primitiveCount);
pass.End();
}
_mBasicEffect.End();
}
This function is a member method of a geometry object (class) and is called from the Game class' Draw(GameTime) method