Как рисовать линии, используя XNA? - PullRequest
44 голосов
/ 06 ноября 2008

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

Может ли кто-нибудь показать мне, используя код, простейшую реализацию XNA для рисования одной или двух линий на экране? Возможно с кратким объяснением (включая шаблон)?

Я не программист, и у меня мало опыта в XNA. Моя конечная цель - нарисовать на экране несколько линий, которые я в конечном итоге преобразую вращениями и т. Д. (Вручную). Тем не менее, для этого первого шага .. Мне нужно просто нарисовать линии! Я помню, что в древние времена OpenGL было довольно просто нарисовать линию несколькими вызовами методов. Должен ли я просто вернуться к использованию неуправляемых вызовов DirectX?

Ответы [ 10 ]

19 голосов
/ 06 ноября 2008

При работе с XNA все (даже 2d-примитивы) должны быть выражены таким образом, чтобы 3d-карта могла их понять, что означает, что линия - это просто набор вершин.

MSDN имеет довольно хорошее прохождение здесь:

http://msdn.microsoft.com/en-us/library/bb196414.aspx#ID2EEF

Вы обнаружите, что для рендеринга примитивной строки требуется больше кода, чем просто для настройки текстурированного квадрата и его поворота, поскольку, по сути, вы делаете то же самое при рендеринге линии.

17 голосов
/ 15 января 2011

После ответа NoHayProblema (пока не могу комментировать).

Этот ответ, хотя и правильный для этого старого вопроса, является неполным. Конструктор Texture2D возвращает неинициализированную текстуру, которая никогда не отображается на экране. Чтобы использовать этот подход, вам нужно установить данные текстуры следующим образом:

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

Учтите, что способ записи данных в пиксель должен соответствовать SurfaceFormat текстуры. Пример работает, потому что текстура форматируется как RGB. Вращения могут быть применены в spriteBatch. Рисуем так:

this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
    Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
7 голосов
/ 12 ноября 2012

нашел учебник для этого http://www.bit -101.com / блог /? Р = 2832

с использованием BasicEffect (шейдер) и встроенный пользовательский примитив draw в XNA 4.0

некоторые примеры кода, которые я считаю полезными:

метод загрузки содержимого

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

метод рисования

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

повеселись и проголосуй, если это тебе помогло. также посетите учебник, из которого я получил это.

7 голосов
/ 19 сентября 2010

Ну, вы можете сделать это очень простым способом, не вдаваясь в ужасные трехмерные векторные вещи.

Просто создайте быструю текстуру, например:

Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);

А затем просто нарисуйте линию, используя эту текстуру:

this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);

Надеюсь, это поможет

4 голосов
/ 13 ноября 2013

Я думаю, что самый простой способ получить изображение белого пикселя, а затем растянуть его в прямоугольнике, чтобы он выглядел как линия

Я сделал класс Line,

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

Надеюсь, это поможет

2 голосов
/ 23 февраля 2017

Я хотел нарисовать лучи так, чтобы я мог отлаживать лучи, созданные взрывами, и там, где они пересекаются с объектами. Это нарисует тонкую линию в один пиксель между двумя точками. Вот что я сделал:

Класс для хранения простых данных лучей. Класс луча XNA по умолчанию может работать, но он не хранит длину луча до пересечения.

public class myRay
{
    public Vector3 position, direction;
    public float length;
}   

Список для хранения лучей, которые должны быть нарисованы:

List<myRay> DebugRays= new List<myRay>();

Создайте BasicEffect и передайте ему проекцию Matrix.CreateOrthographicOffCenter с нужным разрешением в методе LoadContent.

Затем запустите это в методе рисования:

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

Поэтому, когда в моей игре происходит взрыв, он делает это (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);
}

Это довольно просто (возможно, выглядит намного сложнее, чем на самом деле), и было бы легко поместить его в отдельный класс, о котором вам больше никогда не придется думать. Он также позволяет рисовать сразу много линий.

2 голосов
/ 06 июля 2009

Существует также код "круглой линии", который "manders" выпустил в CodePlex:


Вот сообщение в блоге об этом:

0 голосов
/ 08 июня 2016

Просто растяните белый пиксель.

        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();
        }
0 голосов
/ 19 сентября 2014

Я сам столкнулся с этой проблемой и решил создать класс под названием LineBatch. LineBatch будет рисовать линии без необходимости spriteBatch или точек. Класс ниже.

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;
    }
}
0 голосов
/ 04 декабря 2013

Вот простой способ, который я использую для создания линий путем указания начальной координаты, конечной координаты, ширины и цвета:

ПРИМЕЧАНИЕ. Вы должны добавить файл с именем «точка» в каталог содержимого (из них будет сделана строка).

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



    }
}
...