How to improve this code? (too many if) - if-statement

I want to print the border of the square...
It may print only one side, or more sides of the square, so I wrote this method
printBorder(N, E, S, W) {
if (N) {
square.printBorder(0,0,0,10);
}
if (E) {
square.printBorder(0,10,10,10);
}
if (S) {
square.printBorder(10,0,10,10);
}
if (W) {
square.printBorder(0,0,10,0);
}
}
It can work fine, but I think it is not so elegant, it is too many if, and all statement is more or less the same. I think there must be have a way to simplify this codes, any suggestions?

One way of simplifying it... make calls even if you don't need them, but conditionalise the implementation:
printBorder(N, E, S, W){
square.printBorder(n, 0,0,0,10);
square.printBorder(e, 0,10,10,10);
square.printBorder(s, 10,0,10,10);
square.printBorder(w, 0,0,10,0);
}
Then in Square (or whatever):
printBorder(condition, top, left, bottom, right) {
if (!condition) {
return;
}
printBorder(top, left, bottom, right);
}
A similar alternative would be to keep the conditional printBorder with the original function:
printBorder(N, E, S, W){
printBorder(n, 0,0,0,10);
printBorder(e, 0,10,10,10);
printBorder(s, 10,0,10,10);
printBorder(w, 0,0,10,0);
}
printBorder(condition, top, left, bottom, right) {
if (!condition) {
return;
}
square.printBorder(top, left, bottom, right);
}

I wouldn't care about the ifs. I'd just make it more readable:
printBorder(N, E, S, W){
if(N) square.printBorder( 0, 0, 0, 10);
if(E) square.printBorder( 0, 10, 10, 10);
if(S) square.printBorder(10, 0, 10, 10);
if(W) square.printBorder( 0, 0, 10, 0);
}

Personally, I really like binary comparisons.
const uint NORTH = 1;
const uint SOUTH = 2;
const uint EAST = 4;
const uint WEST = 8;
// ... some code ...
printBorder(NORTH + EAST);
// ... some other code ...
printBorder(uint Sides)
{
if((NORTH & Sides) > 0) square.printBorder(0, 0, 0, 10);
if((SOUTH & Sides) > 0) square.printBorder(0, 10, 10, 10);
if((EAST & Sides) > 0) square.printBorder(10, 0, 10, 10);
if((WEST & Sides) > 0) square.printBorder(0, 0, 10, 0);
}
Some might say that this makes the code inside the function less readable. However, my thinking is there is only a single occurrence of this function whereas you will be calling this function all over the place. If you're running through some code you haven't looked at in awhile which is more readable?
printBorder(true, false, true, true);
or
printBorder(NORTH + SOUTH + EAST);
Just my opinion. :)

First you are doing ok, this does exactly what it expresses, don't worry about the space that you are using, most of the solutions here just muddy the water.
If you really want to 'do' something look if you can't move the Border parameter into the square. you could move the border padding (10 in your example into the square), possibly also the State which border should be shown, and then just call square.printBorders(). That depends a lot on the context where you are using this.

How about:
square.printBorder(N|E|W?0:10, N|S|W?0:10, N?0:10, N|E|S?10:0);

you did not specify which programming language.
if it were java, enums can provide good readable syntax,type safety, as well as take advantage of the efficient bit-fiddling capabilities of the EnumSet implementation.
alternatively you could also provide a varargs method signature, but then you cannot be sure that your method will be called with printBorder(N,N), which does not really make sense. using the EnumSet interface you have this guarantee.
public class PrintBorder {
//this is your method without the if's
public static void printBorder(EnumSet<Sides> sides) {
for (Sides side : sides) {
side.print(square);
}
}
//use it like this
public static void main(String[] args) {
printBorder(EnumSet.of(N, E)); //static import here
}
//declare an enum for the sides.
public enum Sides {
N(0, 0, 0, 10),
E(0, 10, 10, 10),
S(10, 0, 10, 10),
W(0, 0, 10, 0);
private final int x1;
private final int y1;
private final int x2;
private final int y2;
Sides(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
//this method could as well be in the Square class, would be cleaner
public void print(Square s) {
s.printBorder(x1, y1, x2, y2);
}
}
//boilerplate here
private static final Square square = new Square();
private static class Square {
public void printBorder(int x1, int y1, int x2, int y2) {
//do something..
}
}
}

Related

Fl_Button callback does not get called

I'm coding some custom widgets in FLTK, one is a single digit spinner for touch screen which is a value with one arrow above and another below.
class Spinner: public Fl_Group
{
private:
::std::unique_ptr<Fl_Button> button_up_;
::std::unique_ptr<Fl_Button> button_down_;
::std::unique_ptr<Fl_Output> value_;
public:
static unsigned int const WIDTH = 36;
static unsigned int const HEIGHT = 100;
...
};
Spinner::Spinner(int x, int y, size_t val)
:Fl_Group(x, y, WIDTH, HEIGHT)
,button_up_(new Fl_Button(x + 2, y + 1, 32, 17))
,button_down_(new Fl_Button(x + 2, y + 82, 32, 17))
,value_(new Fl_Output(x + 2, y + 18, 32, 64))
{
char v[] = {'0', 0};
if (val > 9) val = 9;
v[0] = val + '0';
button_up_->callback(reinterpret_cast<Fl_Callback*>(&Spinner::upcbk), this);
button_down_->callback(reinterpret_cast<Fl_Callback*>(&Spinner::downcbk), this);
button_up_->image(up_arrow);
button_down_->image(down_arrow);
button_up_->color(FL_BLACK);
button_down_->color(FL_BLACK);
button_up_->box(FL_FLAT_BOX);
button_down_->box(FL_FLAT_BOX);
value_->box(FL_BORDER_BOX);
value_->color(FL_BLACK);
value_->labelcolor(FL_RED);
value_->selection_color (FL_RED);
value_->textsize(46);
value_->textcolor(FL_RED);
value_->value(v);
value_->redraw();
end();
}
void Spinner::upcbk(Fl_Widget*, Spinner* spnr)
{
cout << "Spinner::upcbk()" << endl;
spnr->increment();
}
void Spinner::downcbk(Fl_Widget*, Spinner* spnr)
{
cout << "Spinner::downcbk()" << endl;
spnr->decrement();
}
When I instantiate one Spinner object in the window it works ok and each callback gets called when its corresponding arrow is clicked.
The other custom widget is a Spinner agregation:
class UintSpinner: public Fl_Group
{
private:
uint16_t value_;
uint16_t const max_;
uint16_t const min_;
uint16_t const order_;
char fmt_[6];
::std::vector< ::std::unique_ptr <Spinner> > spinners_;
...
};
UintSpinner::UintSpinner(int x, int y, uint16_t minval, uint16_t maxval
,uint16_t val)
:Fl_Group(x, y, Spinner::WIDTH * static_cast<uint16_t>(log10(max_)) + 1, Spinner::HEIGHT)
,value_(val < minval ? minval : (val > maxval ? maxval : val))
,max_(maxval)
,min_(minval)
,order_(static_cast<uint16_t>(log10(max_)) + 1)
{
strncpy(fmt_, "%00hu", 6);
fmt_[2] = static_cast<char>(order_) + '0';
char buffer[6];
snprintf(buffer, 6, fmt_, val);
for (ssize_t i = order_ - 1; i >= 0; i--) {
spinners_.emplace_back(new Spinner(x + i * Spinner::WIDTH, y, buffer[i] - '0'));
spinners_.back()->callback(&UintSpinner::spinner_cb, this);
}
}
void UintSpinner::spinner_cb(Fl_Widget* cb, void* p)
{
cout << "UintSpinner::spinner_cb()" << endl;
reinterpret_cast<UintSpinner*>(p)->spinnercb();
}
In this case If I istantiate an object of UintSpinner class with max value of 244 it correctly renders three individual spinners but neither Spinner::upcbk nor Spinner::downcbk get called when I hit one of the arrows.
I tried to use stack variables for Fl_Button and Fl_Output inside the Spinner class and the behavior is the same. I also tried to create a vector of Spinners instead of a vector of pointers but it does not compile because the Fl_Group seems not moveable.
Am I doing something wrong? Why using the exactly the same code in Spinner class their internal members callbacks works when using that instance directly and not when using the instance inside another custom widget?
Best regards and thank you for your help.
Ok, I found the solution.
In the UintSpinner constructor, when calling the base Fl_Group constructor, I pass a width parameter with an undefined value (a copy-paste error). According to Erco's FLTK Cheat Page -> Fl_Group Event vs. Draw Clipping when the children of a Fl_Group expands outside its bounding box, by default the render is not clipped but its mouse events are. So changing the constructor to:
UintSpinner::UintSpinner(int x, int y, uint16_t minval, uint16_t maxval
,uint16_t val)
:Fl_Group(x, y, Spinner::WIDTH * static_cast<uint16_t>(log10(maxval) + 1), Spinner::HEIGHT)
,value_(val < minval ? minval : (val > maxval ? maxval : val))
,max_(maxval)
,min_(minval)
,order_(static_cast<uint16_t>(log10(max_)) + 1)
{
...
solve the issue.

Using a function inside class

I have one class defined as example:
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area (void);
} ;
Suppose that the area calculation is complex an in order to be clean and readable i want to use functions like display_result(); or order_items(int x, int y);. How can i do this ? The general question is how to use functions inside classes in c++.
My particular case:
bool AlphaBetaAI::computeMove (PangScenario *ps, int playerNumber, int *move)
{
int alpha=-Number_MAX_VALUE;
int beta= Number_MAX_VALUE;
float childValue;
int score;
vector<int> legalMovements;
legalMovements.push_back(1); //Move right
legalMovements.push_back(2); //Move left
legalMovements.push_back(3); //stop
legalMovements.push_back(4); //Shoot
vector< pair<float,int> > lista;
PangScenario *pangCopy = new PangScenario(*ps);
//score=myPacman->getPoints(); Si quiero arrastrar los puntos ya conseguidos por pacman antes de tomar decision
score=0; //Si quiero empezar desde 0 a partir del momento que calculo la decisión.
lista.clear();
for ( int i=0; i < legalMovements.size();i++)
{
if ( legalMovements[i] != 0)
{
switch (legalMovements[i])
{
case 1: //North
pangCopy->characterPlayerOne->moveRight();
childValue = minimaxAlphaBeta(pangCopy, alpha, beta, score, 1, depth-1, jugador+1);
lista.push_back(make_pair(childValue,1));
break;
case 2: //South
CharactersLocationsMaze[playerRow]=sr+1;
CharactersLocationsMaze[playerColumn]=sc;
childValue= minimaxAlphaBeta(mazeTemp,CharactersLocationsMaze, alpha, beta, score, 2, depth-1, jugador+1);
lista.push_back(make_pair(childValue,2));
break;
case 3: //West
CharactersLocationsMaze[playerRow]=sr;
CharactersLocationsMaze[playerColumn]=sc-1;
childValue= minimaxAlphaBeta(mazeTemp,CharactersLocationsMaze, alpha, beta, score, 3, depth-1, jugador+1);
lista.push_back(make_pair(childValue,3));
break;
case 4: //East
CharactersLocationsMaze[playerRow]=sr;
CharactersLocationsMaze[playerColumn]=sc+1;
childValue = minimaxAlphaBeta(mazeTemp,CharactersLocationsMaze, alpha, beta, score, 4, depth-1, jugador+1);
lista.push_back(make_pair(childValue,4));
break;
}
}
}//for
//more code not relevant to the question
}
I compute the move of some kind of pang game. To calculate the best movement id have to call minimaxAlphabeta recursively using a depth of 5. So i have to declare and define minimaxAlphabeta and use it inside the class computemove.
Thanks in advance
If I understand correctly, your question is not about using methods but about optimizing heavy calculations.
Let take your Rectangle example:
class Rectangle {
int width, height;
public:
void set_values (int,int);
int area (void);
} ;
Supposing that calculating area is not that heavy, it may be done in the method itself and that is a clear, straight and easy example of method. However, if the calculation is complex, we need some optimization.
The simplest solution is to only calculate the area when it make sense, and not always. For this purpose, we could save a boolean indicating the need of updating the value:
class Rectangle {
float width=0.0, height=0.0;
bool area_to_update = true;
float area_last_value = 0.0;
public:
void set_values (float, float);
float area ();
} ;
float Rectangle::area()
{
if (area_to_update)
{
// ... Make complex calculation here, save it to area_last_value
area_to_update = with*height;
// save that the area is now correct.
area_to_update = false;
}
// return the new result
return area_to_update;
}
By default, the value need to be updated, so the first call to area will trigger the calculation, and following calls will just return the same value until an update is required.
When any of the values is changed, the area require to update again:
void Rectangle::set_values(float w, float h)
{
// ... Update values here
bool value_changed = (w!=width || h!=height);
width= w;
height = h;
// Check if the value has really changed, and set area to be updated.
if ( value_changed) area_to_update = true;
}
Using functions in a class is straightforward. Example:
class Foo
{
int x;
public:
void bar();
void baz();
};
void Foo::bar()
{
x = 3;
}
void Foo::baz()
{
bar(); // inside another class function (of same class), you just call it
}
int main()
{
Foo f;
f.baz(); // outside you need an instance, call with dot
}
A note regarding the line
PangScenario *pangCopy = new PangScenario(*ps);
Please don't use new, a regular variable will suffice here,
since pangCopy presumably don't need to outlive AlphaBetaAI::computeMove.
Further, in that function, prefer to take ps by reference.

Cairo memory leak on draw

I've written a drawCircle function below but as more circles are drawn/redrawn, memory usage increases greatly, so I'm assuming theres a memory leak somewhere but I can't seem to figure it out. I've tried deleting the instance at the end of the function but that doesn't help.
void drawCircle(cairo_surface_t *container, int x, int y, int radius, float r, float g, float b, float a)
{
cairo_t *cairoInstance;
cairoInstance = cairo_create(container);
cairo_set_source_rgba(cairoInstance, r, g, b, a);
cairo_arc(cairoInstance, x, y, radius, 0, 2*M_PI);
cairo_stroke_preserve(cairoInstance);
cairo_fill_preserve(cairoInstance);
//delete cairoInstance;
gtk_widget_queue_draw_area(GTK_WIDGET(frame2), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}
Any ideas?
Thanks in advance.
A few points:
cairo_ts are reference counted. Call cairo_destroy when you are done with your cairo_t*. Alternatively, you don't need to create and destroy cairo_t's for each circle - refactor the function by pulling out the call to cairo_create.
Unless you need them, prefer the cairo_X functions to the cairo_X_preserve ones. In your code cairo_fill_preserve should probably be cairo_preserve. (The stroke should be preserved though, so that the following fill works.)
The call to invalidate a rectangle of your gtk widget with gtk_widget_queue_draw_area can be refactored out as well, and only done once per draw.
gtk_widget_queue_draw_area invalidates a rectangular region of the widgets window - you may be fine with just gdk_window_invalidate_rect - see the documentation
Psuedo-code after refactoring (with a hypothetical Circle type):
void drawCircle(cairo_t *cr, int x, int y, int radius, float r, float g, float b, float a) {
cairo_set_source_rgba(cr, r, g, b, a);
cairo_arc(cr, x, y, radius, 0, 2*M_PI);
cairo_stroke_preserve(cr); // keep the arc so that we can call cairo_fill
cairo_fill(cr);
}
void functionThatDrawsCircles(cairo_surface_t* surface, Circle* circles, int num) {
cairo_t* cr = cairo_create(surface);
for(int i = 0; i < num; i++) {
drawCircle(cr, circles[i].x, circles[i].y, 10, circles[i].r, circles[i].g, circles[i].b, 1.0);
}
cairo_destroy(cr);
gtk_widget_queue_draw_area(GTK_WIDGET(frame2), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
}

c++: a class accessing an std::vector of main class by pointers goes wrong

I'm building my C++ project with VS2012 Express, Platform Toolset v100, and with openFrameworks 0.7.4.
I have a class called NameRect and this is part of the .h file:
void config(int cx, int cy, int cw, int ch, std::string cname) {
x = cx;
y = cy;
w = cw;
h = ch;
name = cname;
dead = false;
}
void branch(int iterants, std::vector<NameRect> *nrs) {
for (int i = 0; i < iterants; i++) {
NameRect nnr;
nnr.config(x + w, y - iterants * h / 2 + i * h, w, h, "cb");
children.push_back(nnr);
nrs->push_back(nnr);
}
}
void render() {
if (!dead) {
ofSetColor(ofRandom(0, 255), ofRandom(0, 255), ofRandom(0, 255), 0);
ofRect(x, y, w, h);
}
}
And there's the code in my testApp.cpp:
//--------------------------------------------------------------
void testApp::setup(){
ofSetWindowShape(800, 600);
nr.config(0, 300, 50, 10, "cb");
nrs.push_back(nr);
}
//--------------------------------------------------------------
void testApp::update(){
if (ofRandom(0, 50) <= 1 && nrs.size() < 100) {
for (int cnri = 0; cnri < nrs.size(); cnri++) {
if (ofRandom(0, nrs.size() - cnri) <= 1) {
nrs[cnri].branch(2, &nrs);
}
}
}
}
//--------------------------------------------------------------
void testApp::draw(){
for (int di = 0; di < nrs.size(); di++) {
nrs[di].render();
}
}
And when I actually build (succeeds) this project and run it, it gives me such an error:
I take a look at the local variables watch and it shows such large integer values!
What is the problem?
branch() is modifying the vector array which is passed in as the second parameter.
This means when you call nrs[cnri].branch(2, &nrs) from testApp::update() the underlying array structure is modified. This will lead to unpredictable results and will surely cause your access violation.
your problem #1 is nrs[cnri].branch(2, &nrs);, you may be reallocating memory where nrs[cnri] is residing from inside branch() when doing push_back()
your problem #2, which you'll face once you have started including your header to multiple cpp files, is the way you define functions. If you define them in the header put "inline" otherwise you'll have the same function defined in multiple places.

How do I draw lines using XNA?

I've read a bunch of tutorials involving XNA (and it's various versions) and I still am a little confused on drawing primitives. Everything seems to be really convoluted.
Can someone show me, using code, the simplest XNA implementation of drawing one or two lines on to the screen? Perhaps with a brief explanation (including the boilerplate)?
I'm not a games programmer and I have little XNA experience. My ultimate goal is to draw some lines onto the screen which I will eventually transform with rotations, etc (by hand). However, for this first step.. I need to simply draw the lines! I remember back in my ancient OpenGL days it was fairly straightforward when drawing a line with a few method calls. Should I simply revert to using unmanaged directx calls?
When working with XNA, everything (even 2d primitives) have to be expressed in a way that a 3d card can understand, which means that a line is just a set of vertices.
MSDN has a pretty good walkthrough here:
http://msdn.microsoft.com/en-us/library/bb196414.aspx#ID2EEF
You'll find that it takes more code to render a primitive line than it would take to just setup a textured quad and rotate that, since in essence, your doing the same thing when rendering a line.
Following NoHayProblema's answer (I cannot comment yet).
That answer, although the correct one for this old question, is incomplete. Texture2D constructor returns an uninitialized texture, which is never painted on screen.
In order to use that approach, you need to set the texture's data like this:
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);
Take into account that the way you write the data into pixel must be consistent with the texture's SurfaceFormat. The example works because the texture is being formatted as RGB.
Rotations can be applied in spriteBatch.Draw like this:
this.spriteBatch.Draw (SimpleTexture, new Rectangle(0, 0, 100, 1), null,
Color.Blue, -(float)Math.PI/4, new Vector2 (0f, 0f), SpriteEffects.None, 1f);
found a tutorial for that
http://www.bit-101.com/blog/?p=2832
its using a BasicEffect (shader)
and the built in draw user primitive in XNA 4.0
some code samples i find helpful:
load content method
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);   
draw method
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);
have fun and vote up if this helped you. also pay a visit to the tutorial i got this from.
Well, you can do it in a very simple way without getting into the 3D horrible vector stuff.
Just create a quick texture, for example:
Texture2D SimpleTexture = new Texture2D(GraphicsDevice, 1, 1, false, SurfaceFormat.Color);
And then just draw a line using that texture:
this.spriteBatch.Draw(SimpleTexture, new Rectangle(100, 100, 100, 1), Color.Blue);
I hope this helps
The simplest best way, I think, is to get the image of just a white pixel then stretch that pixel in a rectangle to look like a line
I made a Line class,
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;
}
Hope this helps
There is also the "round line" code that "manders" has released on CodePlex:
http://roundline.codeplex.com/
Here is the blog post about it:
XNA RoundLine Code Released on CodePlex
Just stretch a white pixel.
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();
}
I wanted to draw rays so that I could debug rays created by explosions and where they intersect objects. This will draw a single pixel thin line between two points. This is what I did:
Class to store some simple ray data. The XNA default ray class could work, but it doesn't store the length of the ray to intersection.
public class myRay
{
public Vector3 position, direction;
public float length;
}
A list to store the rays that are to be drawn:
List<myRay> DebugRays= new List<myRay>();
Create a BasicEffect and pass it a "Matrix.CreateOrthographicOffCenter" projection with your desired resolution in the LoadContent method.
Then run this in the draw method:
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();
}
So when an explosion happens in my game it does this (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);
}
It's pretty simple (It possibly looks way more complicated than it is) and it'd be easy to put it into a separate class that you never have to think about again. It also lets you draw a whole lot of lines at once.
I encountered this problem my self and decided to make a class called LineBatch.
LineBatch will draw lines without needing a spriteBatch or dots.
The class is below.
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;
}
}
Here is a simple way that I use to make lines by specifying a start coordinate, an end coordinate, width, and color of them:
NOTE: you must add a file named "dot" to the content directory (the line will be made out of these).
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 Distance(int x1, int x2, int y1, int y2)
{
return (int)(Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)));
}
#endregion
}
}