Comment puis-je dessiner des lignes en utilisant XNA?
J'ai lu un tas de tutoriels impliquant XNA (et ses différentes versions) et je suis toujours un peu confus sur le dessin des primitives. Tout semble être vraiment compliqué.
Quelqu'un peut-il me montrer, en utilisant du code, l'implémentation XNA la plus simple de dessiner une ou deux lignes sur l'écran? Peut-être avec une brève explication (y compris la passe-partout)?
Je ne suis pas un programmeur de jeux et j'ai peu D'expérience XNA. Mon but ultime est de dessiner quelques lignes sur l'écran que je vais éventuellement transformer avec des rotations, etc (à la main). Cependant, pour cette première étape.. Je dois simplement tracer les lignes! Je me souviens de mes anciens jours OpenGL, c'était assez simple lors du dessin d'une ligne avec quelques appels de méthode. Devrais - je simplement revenir à l'utilisation d'appels directx non gérés?
10 réponses
Lorsque vous travaillez avec XNA, tout (même les primitives 2d) doit être exprimé d'une manière qu'une carte 3d peut comprendre, ce qui signifie qu'une ligne est juste un ensemble de Sommets.
MSDN a une assez bonne procédure pas à pas ici:
Http://msdn.microsoft.com/en-us/library/bb196414.aspx#ID2EEF
Vous constaterez qu'il faut plus de code pour rendre une ligne primitive que pour simplement configurer un quad texturé et faire pivoter cela, car en substance, vous faites la même chose quand le rendu d'une ligne.
Suite à la réponse de NoHayProblema (Je ne peux pas encore commenter).
Cette réponse, bien que correcte pour cette ancienne question, est incomplète. Texture2D constructor renvoie une texture non initialisée, qui n'est jamais peinte à l'écran. Pour utiliser cette approche, vous devez définir les données de la texture comme ceci:
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false,
SurfaceFormat.Color);
Int32[] pixel = {0xFFFFFF}; // White. 0xFF is Red, 0xFF0000 is Blue
SimpleTexture.SetData<Int32> (pixel, 0, SimpleTexture.Width * SimpleTexture.Height);
// Paint a 100x1 line starting at 20, 50
this.spriteBatch.Draw(SimpleTexture, new Rectangle(20, 50, 100, 1), Color.Blue);
Tenez compte du fait que la façon dont vous écrivez les données dans pixel doit être cohérente avec le SurfaceFormat de la texture. L'exemple fonctionne parce que la texture est formaté en RVB. Les Rotations peuvent être appliquées dans spriteBatch.Dessinez comme ceci:
this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
Eh bien, vous pouvez le faire d'une manière très simple sans entrer dans les trucs vectoriels horribles 3D.
Il suffit de créer une texture rapide, par exemple:
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
Et puis dessinez simplement une ligne en utilisant cette texture:
this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);
J'espère que cela aide
Trouvé un tutoriel pour cela http://www.bit-101.com/blog/?p=2832
Son utilisation D'un BasicEffect (shader) et la primitive utilisateur draw intégrée dans XNA 4.0
Quelques exemples de code que je trouve utiles:
Méthode de contenu de charge
basicEffect = new BasicEffect(GraphicsDevice);
basicEffect.VertexColorEnabled = true;
basicEffect.Projection = Matrix.CreateOrthographicOffCenter
(0, GraphicsDevice.Viewport.Width, // left, right
GraphicsDevice.Viewport.Height, 0, // bottom, top
0, 1);
Méthode De Tirage
basicEffect.CurrentTechnique.Passes[0].Apply();
var vertices = new VertexPositionColor[4];
vertices[0].Position = new Vector3(100, 100, 0);
vertices[0].Color = Color.Black;
vertices[1].Position = new Vector3(200, 100, 0);
vertices[1].Color = Color.Red;
vertices[2].Position = new Vector3(200, 200, 0);
vertices[2].Color = Color.Black;
vertices[3].Position = new Vector3(100, 200, 0);
vertices[3].Color = Color.Red;
GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, vertices, 0, 2);
Amusez - vous et votez si cela vous a aidé. faites également une visite au tutoriel que j'ai obtenu de.
La meilleure façon la plus simple, je pense, est d'obtenir l'image d'un pixel blanc puis d'étirer ce pixel dans un rectangle pour ressembler à une ligne
J'ai fait une classe de ligne,
class Line
{
Texture pixel = ((set this to a texture of a white pixel with no border));
Vector2 p1, p2; //this will be the position in the center of the line
int length, thickness; //length and thickness of the line, or width and height of rectangle
Rectangle rect; //where the line will be drawn
float rotation; // rotation of the line, with axis at the center of the line
Color color;
//p1 and p2 are the two end points of the line
public Line(Vector2 p1, Vector2 p2, int thickness, Color color)
{
this.p1 = p1;
this.p2 = p2;
this.thickness = thickness;
this.color = color;
}
public void Update(GameTime gameTime)
{
length = (int)Vector2.Distance(p1, p2); //gets distance between the points
rotation = getRotation(p1.X, p1.Y, p2.X, p2.Y); //gets angle between points(method on bottom)
rect = new Rectangle((int)p1.X, (int)p1.Y, length, thickness)
//To change the line just change the positions of p1 and p2
}
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
spriteBatch.Draw(pixel, rect, null, color, rotation, new Vector2.Zero, SpriteEffects.None, 0.0f);
}
//this returns the angle between two points in radians
private float getRotation(float x, float y, float x2, float y2)
{
float adj = x - x2;
float opp = y - y2;
float tan = opp / adj;
float res = MathHelper.ToDegrees((float)Math.Atan2(opp, adj));
res = (res - 180) % 360;
if (res < 0) { res += 360; }
res = MathHelper.ToRadians(res);
return res;
}
J'espère que cela aide
Il y a aussi le code" round line "que" manders " a publié sur CodePlex:
Voici le billet de blog à ce sujet:
Je voulais dessiner des rayons pour pouvoir déboguer les rayons créés par les explosions et où ils croisent les objets. Cela tracera une ligne mince de pixel unique entre deux points. C'est ce que j'ai fait:
Classe pour stocker des données de rayon simples. La classe de rayon par défaut XNA peut fonctionner, mais elle ne stocke pas la longueur du rayon à l'intersection.
public class myRay
{
public Vector3 position, direction;
public float length;
}
Une liste pour stocker les rayons à dessiner:
List<myRay> DebugRays= new List<myRay>();
Créez un effet de base et passez-lui une " matrice.CreateOrthographicOffCenter" projection avec la résolution souhaitée dans la méthode LoadContent.
Ensuite, exécutez ceci dans la méthode draw:
private void DrawRays()
{
spriteBatch.Begin();
foreach (myRay ray in DebugRays)
{
//An array of 2 vertices - a start and end position
VertexPositionColor[] Vertices = new VertexPositionColor[2];
int[] Indices = new int[2];
//Starting position of the ray
Vertices[0] = new VertexPositionColor()
{
Color = Color.Orange,
Position = ray.position
};
//End point of the ray
Vertices[1] = new VertexPositionColor()
{
Color = Color.Orange,
Position = ray.position + (ray.direction * ray.length)
};
Indices[0] = 0;
Indices[1] = 1;
foreach (EffectPass pass in BasicEffect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawUserIndexedPrimitives(PrimitiveType.LineStrip, Vertices, 0, 2, Indices, 0, 1, VertexPositionColorTexture.VertexDeclaration);
}
}
spriteBatch.End();
}
Donc, quand une explosion se produit dans mon jeu, il fait ceci (Psuedocode):
OnExplosionHappened()
{
DebugRays.Clear()
myRay ray = new myRay()
{
position = explosion.Position,
direction = GetDirection(explosion, solid),
//Used GetValueOrDefault here to prevent null value errors
length = explosionRay.Intersects(solid.BoundingBox).GetValueOrDefault()
};
DebugRays.Add(ray);
}
C'est assez simple (cela semble peut-être beaucoup plus compliqué qu'il ne l'est) et il serait facile de le mettre dans une classe séparée à laquelle vous n'aurez plus jamais à penser. Il vous permet également de dessiner beaucoup de lignes à la fois.
Voici un moyen simple que j'utilise pour créer des lignes en spécifiant une coordonnée de début, une coordonnée de fin, une largeur et une couleur d'entre elles:
Remarque: Vous devez ajouter un fichier nommé " dot " au répertoire de contenu (la ligne sera faite à partir de ceux-ci).
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 Xna.LineHelper
{
public class LineManager
{
int loopCounter;
int lineLegnth;
Vector2 lineDirection;
Vector2 _position;
Color dotColor;
Rectangle _rectangle;
List<Texture2D> _dots = new List<Texture2D>();
FunctionsLibrary functions = new FunctionsLibrary();
public void CreateLineFiles(Vector2 startPosition, Vector2 endPosition, int width, Color color, ContentManager content)
{
dotColor = color;
_position.X = startPosition.X;
_position.Y = startPosition.Y;
lineLegnth = functions.Distance((int)startPosition.X, (int)endPosition.X, (int)startPosition.Y, (int)endPosition.Y);
lineDirection = new Vector2((endPosition.X - startPosition.X) / lineLegnth, (endPosition.Y - startPosition.Y) / lineLegnth);
_dots.Clear();
loopCounter = 0;
_rectangle = new Rectangle((int)startPosition.X, (int)startPosition.Y, width, width);
while (loopCounter < lineLegnth)
{
Texture2D dot = content.Load<Texture2D>("dot");
_dots.Add(dot);
loopCounter += 1;
}
}
public void DrawLoadedLine(SpriteBatch sb)
{
foreach (Texture2D dot in _dots)
{
_position.X += lineDirection.X;
_position.Y += lineDirection.Y;
_rectangle.X = (int)_position.X;
_rectangle.Y = (int)_position.Y;
sb.Draw(dot, _rectangle, dotColor);
}
}
}
public class FunctionsLibrary
{
//Random for all methods
Random Rand = new Random();
#region math
public int TriangleArea1(int bottom, int height)
{
int answer = (bottom * height / 2);
return answer;
}
public double TriangleArea2(int A, int B, int C)
{
int s = ((A + B + C) / 2);
double answer = (Math.Sqrt(s * (s - A) * (s - B) * (s - C)));
return answer;
}
public int RectangleArea(int side1, int side2)
{
int answer = (side1 * side2);
return answer;
}
public int SquareArea(int side)
{
int answer = (side * side);
return answer;
}
public double CircleArea(int diameter)
{
double answer = (((diameter / 2) * (diameter / 2)) * Math.PI);
return answer;
}
public int Diference(int A, int B)
{
int distance = Math.Abs(A - B);
return distance;
}
#endregion
#region standardFunctions
public int RollDice(int sides)
{
int result = (Rand.Next(1, sides + 1));
return result;
}
public void ConsoleWelcomeMessage(string gameName, string playerName = "\b")
{
Console.WriteLine("Welcome " + playerName + " to " + gameName + "!");
}
public string ConsoleGetName()
{
Console.WriteLine();
Console.Write("Type your name: ");
string name = Console.ReadLine();
Console.WriteLine("Your name will be: " + name);
return name;
}
public int ConsoleGetDifficulty(int min, int max)
{
bool done = false;
int difficulty = 1;
Console.WriteLine();
Console.Write("Choose your difficulty from " + min + " to " + max + ": ");
while (done == false)
{
try
{
string input = Console.ReadLine();
difficulty = int.Parse(input);
if (difficulty < max + 1 && difficulty > min - 1)
{
done = true;
}
else
{
//Ends the try block with an impossible action (bool.Parse)
bool tester = bool.Parse(input);
}
}
catch
{
Console.Write("Enter a valid number: ");
}
}
Console.WriteLine("Your difficulty will be: " + difficulty);
return difficulty;
}
public int Distance(int x1, int x2, int y1, int y2)
{
return (int)(Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
}
public void Test()
{
}
#endregion
}
}
J'ai rencontré ce problème moi-même et j'ai décidé de créer une classe appelée LineBatch. LineBatch tracera des lignes sans avoir besoin d'un spriteBatch ou des points. La classe est ci-dessous.
public class LineBatch
{
bool cares_about_begin_without_end;
bool began;
GraphicsDevice GraphicsDevice;
List<VertexPositionColor> verticies = new List<VertexPositionColor>();
BasicEffect effect;
public LineBatch(GraphicsDevice graphics)
{
GraphicsDevice = graphics;
effect = new BasicEffect(GraphicsDevice);
Matrix world = Matrix.Identity;
Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
effect.World = world;
effect.View = view;
effect.VertexColorEnabled = true;
effect.Projection = projection;
effect.DiffuseColor = Color.White.ToVector3();
cares_about_begin_without_end = true;
}
public LineBatch(GraphicsDevice graphics, bool cares_about_begin_without_end)
{
this.cares_about_begin_without_end = cares_about_begin_without_end;
GraphicsDevice = graphics;
effect = new BasicEffect(GraphicsDevice);
Matrix world = Matrix.Identity;
Matrix view = Matrix.CreateTranslation(-GraphicsDevice.Viewport.Width / 2, -GraphicsDevice.Viewport.Height / 2, 0);
Matrix projection = Matrix.CreateOrthographic(GraphicsDevice.Viewport.Width, -GraphicsDevice.Viewport.Height, -10, 10);
effect.World = world;
effect.View = view;
effect.VertexColorEnabled = true;
effect.Projection = projection;
effect.DiffuseColor = Color.White.ToVector3();
}
public void DrawAngledLineWithRadians(Vector2 start, float length, float radians, Color color)
{
Vector2 offset = new Vector2(
(float)Math.Sin(radians) * length, //x
-(float)Math.Cos(radians) * length //y
);
Draw(start, start + offset, color);
}
public void DrawOutLineOfRectangle(Rectangle rectangle, Color color)
{
Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y), color);
Draw(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.X, rectangle.Y + rectangle.Height), color);
Draw(new Vector2(rectangle.X + rectangle.Width, rectangle.Y), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
Draw(new Vector2(rectangle.X, rectangle.Y + rectangle.Height), new Vector2(rectangle.X + rectangle.Width, rectangle.Y + rectangle.Height), color);
}
public void DrawOutLineOfTriangle(Vector2 point_1, Vector2 point_2, Vector2 point_3, Color color)
{
Draw(point_1, point_2, color);
Draw(point_1, point_3, color);
Draw(point_2, point_3, color);
}
float GetRadians(float angleDegrees)
{
return angleDegrees * ((float)Math.PI) / 180.0f;
}
public void DrawAngledLine(Vector2 start, float length, float angleDegrees, Color color)
{
DrawAngledLineWithRadians(start, length, GetRadians(angleDegrees), color);
}
public void Draw(Vector2 start, Vector2 end, Color color)
{
verticies.Add(new VertexPositionColor(new Vector3(start, 0f), color));
verticies.Add(new VertexPositionColor(new Vector3(end, 0f), color));
}
public void Draw(Vector3 start, Vector3 end, Color color)
{
verticies.Add(new VertexPositionColor(start, color));
verticies.Add(new VertexPositionColor(end, color));
}
public void End()
{
if (!began)
if (cares_about_begin_without_end)
throw new ArgumentException("Please add begin before end!");
else
Begin();
if (verticies.Count > 0)
{
VertexBuffer vb = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), verticies.Count, BufferUsage.WriteOnly);
vb.SetData<VertexPositionColor>(verticies.ToArray());
GraphicsDevice.SetVertexBuffer(vb);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, verticies.Count / 2);
}
}
began = false;
}
public void Begin()
{
if (began)
if (cares_about_begin_without_end)
throw new ArgumentException("You forgot end.");
else
End();
verticies.Clear();
began = true;
}
}
Il suffit d'étirer un pixel blanc.
point = game.Content.Load<Texture2D>("ui/point");
public void DrawLine(Vector2 start, Vector2 end, Color color)
{
Vector2 edge = end - start;
float angle = (float)Math.Atan2(edge.Y, edge.X);
spriteBatch.Begin();
spriteBatch.Draw(point,
new Rectangle((int)start.X, (int)start.Y, (int)edge.Length(), 1),
null,
color,
angle,
new Vector2(0, 0),
SpriteEffects.None,
0);
spriteBatch.End();
}