XNA를 사용하여 라인을 어떻게 그리나요?
문제
나는 XNA (그리고 다양한 버전)와 관련된 많은 튜토리얼을 읽었으며 여전히 프리미티브를 그리는 데 약간 혼란스러워합니다. 모든 것이 정말로 복잡해 보입니다.
누군가가 코드를 사용하여 화면에 하나 또는 두 줄을 그리는 가장 간단한 XNA 구현을 보여줄 수 있습니까? 아마도 간단한 설명 (보일러 플레이트 포함)이 있습니까?
나는 게임 프로그래머가 아니며 XNA 경험이 거의 없습니다. 나의 궁극적 인 목표는 화면에 선을 그리는 것입니다. 그러나이 첫 번째 단계를 위해 .. 나는 단순히 선을 그려야합니다! 나는 고대 Opengl 시절에 몇 가지 방법 호출이있는 줄을 그릴 때 상당히 간단한 것을 기억합니다. 관리되지 않는 DirectX 통화 사용으로 간단히 되돌아 가야합니까?
해결책
XNA로 작업 할 때는 3D 카드가 이해할 수있는 방식으로 모든 (2D 프리미티브)를 표현해야합니다. 즉, 선은 일련의 정점 일뿐입니다.
MSDN은 여기에 꽤 좋은 연습을 가지고 있습니다.
http://msdn.microsoft.com/en-us/library/bb196414.aspx#id2eef
텍스처 쿼드를 설정하는 것보다 원시 라인을 렌더링하는 데 더 많은 코드가 필요하고 본질적으로 라인을 렌더링 할 때 동일한 작업을 수행하기 때문에 회전합니다.
다른 팁
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);
데이터를 픽셀로 작성하는 방식은 텍스처의 표면 형식과 일치해야합니다. 텍스처가 RGB로 형식화되기 때문에 예제는 작동합니다. 회전은 Spritebatch.Draw에 다음과 같이 적용 할 수 있습니다.
this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
글쎄, 당신은 3D 끔찍한 벡터에 들어 가지 않고 매우 간단한 방법으로 할 수 있습니다.
예를 들어 빠른 질감을 만듭니다.
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
그런 다음 해당 텍스처를 사용하여 선을 그립니다.
this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);
이게 도움이 되길 바란다
그것에 대한 튜토리얼을 찾았습니다http://www.bit-101.com/blog/?p=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);
이것이 당신을 도와 주면 재미 있고 투표하십시오. 또한 내가 얻은 튜토리얼을 방문하십시오.
가장 간단한 가장 좋은 방법은 흰색 픽셀의 이미지를 얻은 다음 그 픽셀을 사각형으로 늘려 선처럼 보이는 것입니다.
나는 라인 클래스를 만들었고
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;
}
도움이 되었기를 바랍니다
CodePlex에서 "Manders"가 발표 한 "라운드 라인"코드도 있습니다.
여기에 대한 블로그 게시물은 다음과 같습니다.
폭발로 생성 된 광선을 디버그하고 물체가 교차하는 위치를 디버깅 할 수 있도록 광선을 그리고 싶었습니다. 이것은 두 지점 사이에 단일 픽셀 얇은 선을 그립니다. 이것이 내가 한 일입니다.
간단한 광선 데이터를 저장하는 클래스. XNA 기본 광선 클래스는 작동 할 수 있지만 광선의 길이를 교차로에 저장하지는 않습니다.
public class myRay
{
public Vector3 position, direction;
public float length;
}
그려야 할 광선을 저장하는 목록 :
List<myRay> DebugRays= new List<myRay>();
basiceffect를 만들고 loadContent 메소드에서 원하는 해상도를 사용하여 "matrix.createorthographic foffcenter"투영을 전달하십시오.
그런 다음이 점이 드로우 방법으로 실행합니다.
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);
}
그것은 매우 간단합니다 (아마도 그보다 더 복잡해 보일 수 있습니다). 다시 생각할 필요가없는 별도의 클래스에 넣기가 쉽습니다. 또한 한 번에 많은 줄을 그릴 수 있습니다.
다음은 시작 좌표, 엔드 좌표, 너비 및 색상을 지정하여 선을 만드는 데 사용하는 간단한 방법입니다.
참고 : 컨텐츠 디렉토리에 "dot"이라는 파일을 추가해야합니다 (줄은이 중 하나에서 만들어집니다).
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
}
}
나는이 문제를 내 자신을 만나고 Linebatch라는 수업을 만들기로 결정했습니다. Linebatch는 Spritebatch 또는 Dots없이 선을 그립니다. 수업은 아래에 있습니다.
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;
}
}
흰색 픽셀을 늘리십시오.
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();
}