C++ Dangling pointer issue - c++

I am using the Raylib GUI framework for a project that displays all the nodes within an iteration of the Collatz Conjecture. In my program, I have a class for a Node object that acts simply as a circle with a labelled number. The variable text in my draw method, however, has an issue; C26815: The pointer is dangling because it points at a temporary instance which was destroyed. I'm new to C++ and don't have access to any books to teach me at the moment, hence I'm not entirely sure what a "dangling" pointer is or what it means, but I'm fairly certain it's the reason I can't display any text on my nodes. Here's my Node class:
class Node {
private:
int x;
int y;
int value;
int radius;
public:
Vector2 get_pos() {
return Vector2{ (float)x, (float)-value * 25 };
}
void set_radius(int newRadius) {
radius = newRadius;
}
void set_value(int newValue) {
value = newValue;
}
void set_x(int newX) {
x = newX;
}
void set_y(int newY) {
y = newY;
}
void draw() {
if (value) {
const char* text = std::to_string(value).c_str();
Vector2 size = MeasureTextEx(GetFontDefault(), text, 32.0f, 0.0f);
Vector2 pos = get_pos();
DrawCircle(pos.x, pos.y, radius, WHITE);
DrawText(text, pos.x - size.x / 2, pos.y - size.y / 2, 32, BLACK);
}
}
};
Any help or explanation as to what's going on would be appreciated.
EDIT: Other people have had similar issues on other questions but none of the answers made sense to me or felt applicable to my situation.

In this line
const char* text = std::to_string(value).c_str();
You are calling c_str() which returns a pointer to the buffer of the temporary returned by std::to_string(value). This temporaries lifetime ends at the end of this line. The pointer returned from c_str is only valid as long as the string is still alive.
If DrawText copies the string (rather than just copying the pointer you pass) you can fix it via
std::string text = std::to_string(value);
Vector2 size = MeasureTextEx(GetFontDefault(), text, 32.0f, 0.0f);
Vector2 pos = get_pos();
DrawCircle(pos.x, pos.y, radius, WHITE);
DrawText(text.c_str(), pos.x - size.x / 2, pos.y - size.y / 2, 32, BLACK);

Related

how to parent object to another object and affect its position through rotation (make object rotate around other object)

For context, I'm making a top down shooter game where the player always rotates/faces itself to the mouse cursor. That can be easily done, but now I'm stuck in positioning the weapon that the player hold (I separate the weapon entity and the player entity because I want the player to be able to switch weapons). I have to make the weapon also rotates to the same angle as the player (which is also easily done by just getting the player's rotation angle and applying that to the weapon as well). Then the part where I'm really stuck is to always position the weapon like it's revolving around the player (with a bit offset).
With no further ado, here's the code:
class Player
{
public:
Player(string skin)
{
this->skin.loadFromFile("gfx/skins/" + skin + ".png");
player.setTexture(this->skin);
player.setOrigin(Vector2f(7, 6.5f));
}
void SetScale(float x, float y)
{
player.setScale(x, y);
}
void SetPosition(float x, float y)
{
x_pos = x;
y_pos = y;
}
Vector2f GetScale()
{
return player.getScale();
}
Vector2f GetPosition()
{
return Vector2f(x_pos, y_pos);
}
float GetRotation()
{
return rotate_angle;
}
void Update(float delta_time, Vector2f mouse_pos)
{
if (Keyboard::isKeyPressed(Keyboard::A) || Keyboard::isKeyPressed(Keyboard::D))
{
if (Keyboard::isKeyPressed(Keyboard::A))
{
vel_x = smoothMotion(-185.f, vel_x, delta_time);
}
if (Keyboard::isKeyPressed(Keyboard::D))
{
vel_x = smoothMotion(185.f, vel_x, delta_time);
}
}
else
vel_x = smoothMotion(0.f, vel_x, delta_time);
if (Keyboard::isKeyPressed(Keyboard::W) || Keyboard::isKeyPressed(Keyboard::S))
{
if (Keyboard::isKeyPressed(Keyboard::W))
{
vel_y = smoothMotion(-185.f, vel_y, delta_time);
}
if (Keyboard::isKeyPressed(Keyboard::S))
{
vel_y = smoothMotion(185.f, vel_y, delta_time);
}
}
else
vel_y = smoothMotion(0.f, vel_y, delta_time);
x_pos += vel_x * delta_time;
y_pos += vel_y * delta_time;
player.setPosition(x_pos, y_pos);
player_mouse_distance = Vector2f(mouse_pos.x - x_pos, mouse_pos.y - y_pos);
rotate_angle = radToDeg(atan2(player_mouse_distance.y, player_mouse_distance.x));
player.setRotation(rotate_angle);
}
void Draw(RenderWindow& window)
{
window.draw(player);
}
public:
Vector2f player_mouse_distance;
private:
Sprite player;
Texture skin;
float x_pos, y_pos;
float vel_x = 0.f, vel_y = 0.f;
float rotate_angle;
};
class Weapon
{
public:
Weapon(string weapon_name)
{
weapon_texture.loadFromFile("gfx/weapons/" + weapon_name + ".png");
weapon.setTexture(weapon_texture);
}
void SetScale(float x, float y)
{
weapon.setScale(x, y);
}
void SetPosition(float x, float y)
{
x_pos = x;
y_pos = y;
}
void Update(Player player, float delta_time)
{
SetPosition((player.GetScale().x * (9 - 7)) /* <- offset */ * cos(player.GetRotation()) + player.GetPosition().x, (player.GetScale().y * (6.5 - 5)) * sin(player.GetRotation()) + player.GetPosition().y);
weapon.setPosition(x_pos, y_pos);
weapon.setRotation(player.GetRotation());
}
void Draw(RenderWindow& window)
{
window.draw(weapon);
}
private:
Sprite weapon;
Texture weapon_texture;
float x_pos, y_pos;
float vel_x = 0.f, vel_y = 0.f;
float rotate_angle;
};
I'm using C++ and SFML 2.5.1 by the way, but any answer using other language or other graphics library (like Pygame, etc) can be accepted too (since the physics uses the same math formulas anyways).
I watched tutorials about this, but most of them uses game engines like Unity and Godot. They simply just parents the player entity to the weapon entity so that the weapon can also change position when player is rotating.
I figured out that cosine and sine function must be the key formula to implement that, but if I'm wrong please correct me.
Any help is appreciated :]
First, in Player.Update(), the formula for rotation angle should be atan2(y,x), do not convert it to degrees as sin and cos take radians as input.
If other parts of your project rely on Player.rotate_angle to be in degrees, you should convert them back to radians in Weapon.Update(). However, I recommend using radians as all of the C++ base trig functions take radians as input.
In Weapon.Update(), you are applying different offset multipliers to the x and y arguments for SetPosition: (9 - 7) to the x coordinate and (6.5 - 5) to the y coordinates. These should be singular constants instead of expressions like that, and they have to be the same unless you want the Weapon to have an elliptical orbit. Replace those expressions with a constant variable defined somewhere in the Weapon class.
Additionally, player.GetScale() could have different x and y values, so you can replace player.GetScale().x and player.GetScale().y with some new method like Player.GetScaleMagnitude() that returns the length of the vector from player.GetScale() as a float. However, player.GetScale() contributing to an elliptical orbit could be visually beneficial depending on how you want the game to look.
I totally agree with Pablo's answer, but I would go a step further :
Implement a parenting system!
Once you implement his solution, you will already be adding a transformation on top of another one : the weapon's final tranformation will be a composition of its own transformation (offset from the player) and the player transformation (its position+orientation).
I won't describe the exact formulas involved in composing the transformations, Pablo already gave a good answer on that. I'll describe here the architecture of a parentable system :
class TransformationNode
{
public :
TransformationNode(TransformationNode* _parent = nullptr)
: parent(_parent)
{
}
void SetPosition(const float x, const float y)
{
localX = x;
localY = y;
}
void SetAngle(const float angle)
{
localAngle = angle;
}
void computeGlobalCoords()
{
if (parent)
{
globalX = transformFormulaHere(parent->GetGlobalPosition(), parent->GetGlobalAngle());
globalY = transformFormulaHere(parent->GetGlobalPosition(), parent->GetGlobalAngle());
globalAngle = localAngle + parent->GetGlobalAngle();
}
else
{
globalX = localX;
globalY = localY;
globalAngle = localAngle;
}
}
private :
float localX, localY, localAngle;
float globalX, globalY, globalAngle;
TransformationNode* parent;
};
And then you'll have both Player and Weapon inherit from TransformNode. I haven't compiled the code, it's just to get the idea.
By the way, I strongly recommend you to look at Transformation matrices. They are better to use than individual positions and angles.

Access violation for object

I have an Entity.h like this:
using namespace physx;
class Entity
{
public:
Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id);
virtual ~Entity(void);
virtual void update(Ogre::Real dt);
virtual void init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity=PxVec3(0, 0, 0));
protected:
Ogre::Entity* mOgreEntity = NULL;
Ogre::SceneNode* mOgreNode = NULL;
Ogre::Vector3 mPosition;
Ogre::Vector3 mDimensions;
std::string mMesh;
std::string mId;
PxRigidDynamic* mActor;
PxMaterial* mMaterial;
};
And here is my Entity source:
#include "Entity.h"
Entity::Entity(Ogre::Vector3 dims, Ogre::Vector3 pos, std::string mesh, std::string id)
{
mDimensions = dims;
mPosition = pos;
mMesh = mesh;
mId = id;
mActor = NULL;
mMaterial = NULL;
}
Entity::~Entity(void)
{
}
void Entity::update(Ogre::Real dt)
{
PxVec3 pos = mActor->getGlobalPose().p;
Ogre::Real r = 0;
mOgreNode->setPosition(Ogre::Vector3(pos.x + r, pos.y + r, pos.z + r));
}
void Entity::init(Ogre::SceneManager* sceneMgr, PxPhysics* physics, PxScene* scene, PxVec3 velocity)
{
// Create an Entity
mOgreEntity = sceneMgr->createEntity(mId, mMesh);
mOgreEntity->setCastShadows(true);
// Create a SceneNode and attach the Entity to it
mOgreNode = sceneMgr->getRootSceneNode()->createChildSceneNode(mId + "Node");
Ogre::AxisAlignedBox box = mOgreEntity->getBoundingBox();
Ogre::Vector3 realSizes = box.getSize();
mOgreNode->setPosition(mPosition);
mOgreNode->attachObject(mOgreEntity);
Ogre::Vector3 scaler = Ogre::Vector3(mDimensions.x / realSizes.x, mDimensions.y / realSizes.y, mDimensions.z / realSizes.z);
mOgreNode->scale(scaler);
mMaterial = physics->createMaterial(1.5f, 1.5f, 1.0f);
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
} else {
// geometry = NULL;
}
PxTransform transform = PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z));
mActor = PxCreateDynamic(*physics, transform, *geometry, *mMaterial, PxReal(.1));
// if(!mActor) {
// MessageBox( NULL, "no actor", "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
// return;
// }
mActor->setLinearVelocity(velocity);
// And add the actor to a scene:
scene->addActor(*mActor);
}
Now, if I create a single entity and initialize it works. Even wirh a second entity var it works as well. Now with an array:
Entity *mEntities[20];
for(int i = 0 ; i < 20 ; i++ ){
ostringstream nameStream;
nameStream << "Sphere_" << i;
string name = nameStream.str();
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6), Ogre::Vector3(i*5, i*4.5, i*6), "sphere.mesh", name);
sphere->init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
mEntities[i] = sphere;
}
I got Access violation. W/ the just-in-time debugger, it turned out that mActorwas null as well as mMaterial
EDIT:
This code does not work either:
mEntity = Entity(Ogre::Vector3(50.0f, 50.0f, 50.0f), Ogre::Vector3(50.0f, 40.5f, 60.0f), "sphere.mesh", "sphere");
mEntity.init(mSceneMgr, mPhysics, gScene, PxVec3(-10.0f, 0, 0));
1)
Entity* sphere = new Entity(Ogre::Vector3(i*5, i*4.5, i*6),
Ogre::Vector3(i*5, i*4.5, i*6),
"sphere.mesh",
"Sphere_"+i);
Look at the "Sphere_"+i
If the i is larger then length of ”Sphere_” you are passing pointer to some random memory. I assume that you wanted to create a string with i at the end.
Use sprintf or std::string for that.
2)
If you change the loop range from 20 to let's say 3 it will probably work. The problem is that your names will be:
Sphere_, phere_, here_
Because by doing "Sphere_"+i you are not adding integer to the string.
This is "equal" to:
char *string = "String";
string += 3;
3)
This code will generate string that you need:
std::ostringstream newStringStream;
newStringStream << "Sphere_" << i;
std::string newString = newStringStream.str();
Here is another issue:
PxGeometry* geometry = NULL;
if(mMesh == "sphere.mesh")
{
geometry = &PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
The problem with this is that you are assigning to geometry the address of a temporary value. Once that line of code is completed, that temporary is gone.
The probable fix for this is to do this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
geometry = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
}
//...
mActor = PxCreateDynamic(*physics, transform, geometry, *mMaterial, PxReal(.1));
Now geometry is no longer a pointer, and you're assigning geometry to the value returned, (not address-of the value returned).
I am reading the documentation here:
http://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/apireference/files/classPxSphereGeometry.html
So PxSphereGeometry(x) is a constructor call. So you need to assign the return value to a PxShpereGeometry, not a PxSphereGeometry*.
Edit: Your latest changes also do not have the desired effect:
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = &g;
}
The g is local to the if() block. You assign the address of this g to geometry. Then when that block exits, g is gone, and now you have geometry pointing to something that no longer exists.
The difference between your edited code and the answer I gave is that my answer assigns the return value to an existing object. So I created a duplicate of the return value. What your doing in the edited code is not creating a duplicate, but pointing to a local object, which as explained, won't exist after it leaves scope.
So if you were to write code that follows the pattern of your edited code, and have it be valid, the change would look like this:
PxGeometry geometry;
if(mMesh == "sphere.mesh")
{
PxGeometry g = PxSphereGeometry(mDimensions.x / 2); // Because it's a radius
geometry = g;
}
However, this does extraneous work. The original answer is sufficient.
I tried the alternate way to create a rigid body and it worked!!!!
mActor = physics->createRigidDynamic(PxTransform(PxVec3(mPosition.x, mPosition.y, mPosition.z)));
PxShape* shape = mActor->createShape(PxSphereGeometry(mDimensions.x / 2), *mMaterial);
PxRigidBodyExt::updateMassAndInertia(*mActor, 0.4f);

World to screen space coordinates in OpenSceneGraph

So I've got a class Label that inherits from osg::Geode which I draw in the world space in OpenSceneGraph. After displaying each frame, I then want to read the screen space coordinates of
each Label, so I can find out how much they overlap in the screen space. To this end, I created a class ScreenSpace which should calculate this (the interesting function is calc_screen_coords.)
I wrote a small subroutine that dumps each frame with some extra information, including the ScreenSpace box which represents what the program thinks the screen space coordinates are:
Now in the above picture, there seems to be no problem; but if I rotate it to the other side (with my mouse), then it looks quite different:
And that is what I don't understand.
Is my world to screen space calculation wrong?
Or am I getting the wrong BoundingBox from the Drawable?
Or maybe it has something to do with the setAutoRotateToScreen(true) directive that I give the osgText::Text object?
Is there a better way to do this? Should I try to use a Billboard instead? How would I do that though? (I tried and it totally didn't work for me — I must be missing something...)
Here is the source code for calculating the screen space coordinates of a Label:
struct Pixel {
// elided methods...
int x;
int y;
}
// Forward declarations:
pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam);
void rearange(Pixel& left, Pixel& right);
class ScreenSpace {
public:
ScreenSpace(const Label* label, const osg::Camera* cam)
{
BoundingBox box = label->getDrawable(0)->computeBound();
tie(bottom_left_, upper_right_) = calc_screen_coords(box, cam);
rearrange(bottom_left_, upper_right_);
}
// elided methods...
private:
Pixel bottom_left_;
Pixel upper_right_;
}
pair<Pixel, Pixel> calc_screen_coords(const osg::BoundingBox& box, const osg::Camera* cam)
{
Vec4d vec (box.xMin(), box.yMin(), box.zMin(), 1.0);
Vec4d veq (box.xMax(), box.yMax(), box.zMax(), 1.0);
Matrixd transmat
= cam->getViewMatrix()
* cam->getProjectionMatrix()
* cam->getViewport()->computeWindowMatrix();
vec = vec * transmat;
vec = vec / vec.w();
veq = veq * transmat;
veq = veq / veq.w();
return make_pair(
Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y())),
Pixel(static_cast<int>(veq.x()), static_cast<int>(veq.y()))
);
}
inline void swap(int& v, int& w)
{
int temp = v;
v = w;
w = temp;
}
inline void rearrange(Pixel& left, Pixel& right)
{
if (left.x > right.x) {
swap(left.x, right.x);
}
if (left.y > right.y) {
swap(left.y, right.y);
}
}
And here is the construction of Label (I tried to abridge it a little):
// Forward declaration:
Geometry* createLeader(straph::Point pos, double height, Color color);
class Label : public osg::Geode {
public:
Label(font, fontSize, text, color, position, height, margin, bgcolor, leaderColor)
{
osgText::Text* txt = new osgText::Text;
txt->setFont(font);
txt->setColor(color.vec4());
txt->setCharacterSize(fontSize);
txt->setText(text);
// Set display properties and height
txt->setAlignment(osgText::TextBase::CENTER_BOTTOM);
txt->setAutoRotateToScreen(true);
txt->setPosition(toVec3(position, height));
// Create bounding box and leader
typedef osgText::TextBase::DrawModeMask DMM;
unsigned drawMode = DMM::TEXT | DMM::BOUNDINGBOX;
drawMode |= DMM::FILLEDBOUNDINGBOX;
txt->setBoundingBoxColor(bgcolor.vec4());
txt->setBoundingBoxMargin(margin);
txt->setDrawMode(drawMode);
this->addDrawable(txt);
Geometry* leader = createLeader(position, height, leaderColor);
this->addDrawable(leader);
}
// elided methods and data members...
}
Geometry* createLeader(straph::Point pos, double height, Color color)
{
Geometry* leader = new Geometry();
Vec3Array* array = new Vec3Array();
array->push_back(Vec3(pos.x, pos.y, height));
array->push_back(Vec3(pos.x, pos.y, 0.0f));
Vec4Array* colors = new Vec4Array(1);
(*colors)[0] = color.vec4();
leader->setColorArray(colors);
leader->setColorBinding(Geometry::BIND_OVERALL);
leader->setVertexArray(array);
leader->addPrimitiveSet(new DrawArrays(PrimitiveSet::LINES, 0, 2));
LineWidth* lineWidth = new osg::LineWidth();
lineWidth->setWidth(2.0f);
leader->getOrCreateStateSet()->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
return leader;
}
Any pointers or help?
I found a solution that works for me, but is also unsatisfying, so if you have a better solution, I'm all ears.
Basically, I take different points from the Label that I know will be at certain points,
and I calculate the screen space by combining this. For the left and right sides, I take
the bounds of the regular bounding box, and for the top and bottom, I calculate it with the
center of the bounding box and the position of the label.
ScreenSpace::ScreenSpace(const Label* label, const osg::Camera* cam)
{
const Matrixd transmat
= cam->getViewMatrix()
* cam->getProjectionMatrix()
* cam->getViewport()->computeWindowMatrix();
auto topixel = [&](Vec3 v) -> Pixel {
Vec4 vec(v.x(), v.y(), v.z(), 1.0);
vec = vec * transmat;
vec = vec / vec.w();
return Pixel(static_cast<int>(vec.x()), static_cast<int>(vec.y()));
};
// Get left right coordinates
vector<int> xs; xs.reserve(8);
vector<int> ys; ys.reserve(8);
BoundingBox box = label->getDrawable(0)->computeBound();
for (int i=0; i < 8; i++) {
Pixel p = topixel(box.corner(i));
xs.push_back(p.x);
ys.push_back(p.y);
};
int xmin = *min_element(xs.begin(), xs.end());
int xmax = *max_element(xs.begin(), xs.end());
// Get up-down coordinates
int ymin = topixel(dynamic_cast<const osgText::Text*>(label->getDrawable(0))->getPosition()).y;
int center = topixel(box.center()).y;
int ymax = center + (center - ymin);
bottom_left_ = Pixel(xmin, ymin);
upper_right_ = Pixel(xmax, ymax);
z_ = distance_from_camera(label, cam);
}

Why do I get an EXC_BAD_ACCESS when reading back a private class variable?

I have a Rectangle class shown below:
Header:
class Rectangle: public Polygon {
private:
float _width, _height;
public:
Rectangle(float width, float height);
float getWidth(float* width) const;
float getHeight(float* height) const;
bool isCollidingWith(Rectangle* other) const;
};
Selected Implementation:
Rectangle::Rectangle(float width, float height) : Polygon(explodeRect(width, height, new struct vertex[4]), 4) {
printf("creating rect %f x %f\n", width, height);
_width = width;
_height = height;
printf("set _width to %f\n", _width);
}
float Rectangle::getWidth(float* width) const {
printf("_w: %f\n", _width);
*width = _width;
return *width;
//return (*width = _width);
}
float Rectangle::getHeight(float* height) const {
return (*height = _height);
}
I initialize an instance of the Rectangle class, and the output indicates that the _width variable is being correctly assigned. However, when I later try to read the variable using the getWidth method, I get an EXC_BAD_ACCESS error on the line:
printf("_w: %f\n", _width);
Why can I no longer read this variable? I get the same problem with the _height variable as well.
EDIT: I would also like to note that if I skip reading the width, I get an error trying to read public variables directly from the object, e.g. when I try to read its x position with obj->x.
EDIT 2: Could this be from the fact that the object is an instance of a subclass of Rectangle, and this subclass is defined in a different file than Rectangle is? I am also reading the values from a third file.
EDIT 3: More code below.
I am trying to re-create Tetris with OpenGL. In my display method, I have this code to draw the rectangles:
if(fallingBlock != nullptr) {
printf("drawing falling block at (%f, %f)\n", fallingBlock->x, fallingBlock->y);
draw(fallingBlock);
}
fallingBlock is defined as a global variable at the top of my file:
Block* fallingBlock;
From my main, I call an initVars method that subsequently calls a startDroppingBlock method. Here it is:
void startDroppingBlock() {
Block* block = availableBlocks[random() % numAvailableBlocks].copy();
block->x = 0.5;
block->y = SCREEN_TOP;
block->dy = -0.01f;
//printf("copied block is at (%f, %f)\n", block->x, block->y);
fallingBlock = block;
}
And here is my block drawing method:
void draw(Block* obj) {
bool shape[3][3];
obj->getShape(shape);
//printf("got shape: {%d, %d, %d}, {%d, %d, %d}, {%d, %d, %d}\n", shape[0][0], shape[0][1], shape[0][2], shape[1][0], shape[1][1], shape[1][2], shape[2][0], shape[2][1], shape[2][2]);
/*float pieceWidth;
obj->getWidth(&pieceWidth);
pieceWidth /= 3.0f;*/
float pieceWidth = obj->getWidth();
for(unsigned int i=0; i<3; i++) {
for(unsigned int j=0; j<3; j++) {
if(shape[i][j]) {
Square rect = Square(pieceWidth);
rect.x = obj->x + pieceWidth * j;
rect.y = obj->y + pieceWidth * i;
rect.color = obj->color;
draw(&rect);
}
}
}
}
I get an EXC_BAD_ACCESS error on the line [...]. Why can I no longer read this variable? I get the same problem with the _height variable as well. [later...] I have tried both float pieceWidth; obj->getWidth(&pieceWidth); and obj->getWidth(new float) - the actual error is on the line where I read _width, before I even use the passed in pointer. [later...] I modified the getWidth and getHeight methods to just simply return _width and _height. Now I just get an error on return _width;
In this case I see you are using a Rectangle* pointer as obj->getWidth which can as well lead to a bad access error if obj is not a valid pointer.
It is to note that I don't quite understand your getter method at all. A simplified (and possibly standard) version of it might be:
float Rectangle::getWidth() const {
return _width;
}
With the only difference that when you used:
// float a;
// float b;
a = rect.getWidth(&b);
you can now do:
// float a;
// float b;
a = b = rect.getWidth();
which is possibly cleaner and will surely don't cause such an error. A good rule of thumb is never to use pointers when possible. If you need to modify a variable inside a function just use a reference.

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