I've written a working tetris clone but it has a pretty messy layout. Could I please get feedback on how to restructure my classes to make my coding better. I focuses on making my code as generic as possible, trying to make it more an engine for games only using blocks.
Each block is created seperately in the game.
My game has 2 BlockLists (linked lists): StaticBlocks and Tetroid.
StaticBlocks is obviously the list of all non-moving blocks, and tetroid are the 4 blocks of the current tetroid.
In main a World is created.
First a new tetroid (4 blocks in a list Tetroid) is created by (NewTetroid)
Collision is detected by the (***Collide) functions, by comparing each of Tetroid with all of the StaticBlocks using the (If*****) functions.
When the tetroid stops (hits the bottom/blocks), it is copied (CopyTetroid) to the StaticBlocks and Tetroid is made empty, then tests are made for complete lines, blocks are destroyed/dropped etc by searching StaticBlocks with (SearchY).
A new tetroid is created.
(TranslateTetroid) and (RotateTetroid) perform operations on each block in the Tetroid list one by one ( I think this is bad practise).
(DrawBlockList) just goes through a list, running the Draw() function for each block.
Rotation is controlled by setting rotation axis relative to the first block in Tetroid when (NewTetroid) is called. My rotation function (Rotate) for each block rotates it around the axis, using an input +-1 for left/right rotation. RotationModes and States are for blocks that rotate in 2 or 4 different ways, defining what state they are currently in, and whether they should be rotated left or right. I am not happy with how these are defined in "World", but I don't know where to put them, whilst still keeping my (Rotate) function generic for each block.
My classes are as follows
class World
{
public:
/* Constructor/Destructor */
World();
~World();
/* Blocks Operations */
void AppendBlock(int, int, BlockList&);
void RemoveBlock(Block*, BlockList&);;
/* Tetroid Operations */
void NewTetroid(int, int, int, BlockList&);
void TranslateTetroid(int, int, BlockList&);
void RotateTetroid(int, BlockList&);
void CopyTetroid(BlockList&, BlockList&);
/* Draw */
void DrawBlockList(BlockList&);
void DrawWalls();
/* Collisions */
bool TranslateCollide(int, int, BlockList&, BlockList&);
bool RotateCollide(int, BlockList&, BlockList&);
bool OverlapCollide(BlockList&, BlockList&); // For end of game
/* Game Mechanics */
bool CompleteLine(BlockList&); // Test all line
bool CompleteLine(int, BlockList&); // Test specific line
void ColourLine(int, BlockList&);
void DestroyLine(int, BlockList&);
void DropLine(int, BlockList&); // Drops all blocks above line
int rotationAxisX;
int rotationAxisY;
int rotationState; // Which rotation it is currently in
int rotationModes; // How many diff rotations possible
private:
int wallX1;
int wallX2;
int wallY1;
int wallY2;
};
class BlockList
{
public:
BlockList();
~BlockList();
Block* GetFirst();
Block* GetLast();
/* List Operations */
void Append(int, int);
int Remove(Block*);
int SearchY(int);
private:
Block *first;
Block *last;
};
class Block
{
public:
Block(int, int);
~Block();
int GetX();
int GetY();
void SetColour(int, int, int);
void Translate(int, int);
void Rotate(int, int, int);
/* Return values simulating the operation (for collision purposes) */
int IfTranslateX(int);
int IfTranslateY(int);
int IfRotateX(int, int, int);
int IfRotateY(int, int, int);
void Draw();
Block *next;
private:
int pX; // position x
int pY; // position y
int colourR;
int colourG;
int colourB;
};
Sorry if this is a bit unclear or long winded, I'm just looking for some help restructuring.
What is the single responsibility of the World class?
It's just a blob containing practically every kind of functionality. That's not good design. One obvious responsibility is "represent the grid onto which blocks are placed". But that has nothing to do with creating tetroids or manipulating block lists or drawing. In fact, most of that probably doesn't need to be in a class at all. I would expect the World object to contain the BlockList you call StaticBlocks so it can define the grid on which you're playing.
Why do you define your own Blocklist? You said you wanted your code to be generic, so why not allow any container to be used? Why can't I use a std::vector<Block> if I want to? Or a std::set<Block>, or some home-brewed container?
Use simple names that don't duplicate information or contradict themselves. TranslateTetroid doesn't translate a tetroid. It translates all the blocks in a blocklist. So it should be TranslateBlocks or something. But even that is redundant. We can see from the signature (it takes a BlockList&) that it works on blocks. So just call it Translate.
Try to avoid C-style comments (/*...*/). C++-style (//..)behaves a bit nicer in that if you use a C-style comment out an entire block of code, it'll break if that block also contained C-style comments. (As a simple example, /*/**/*/ won't work, as the compiler will see the first */ as the end of the comment, and so the last */ won't be considered a comment.
What's with all the (unnamed) int parameters? It's making your code impossible to read.
Respect language features and conventions. The way to copy an object is using its copy constructor. So rather than a CopyTetroid function, give BlockList a copy constructor. Then if I need to copy one, I can simply do BlockList b1 = b0.
Rather than void SetX(Y) and Y GetX() methods, drop the redundant Get/Set prefix and simply have void X(Y) and Y X(). We know it's a getter because it takes no parameters and returns a value. And we know the other one is a setter because it takes a parameter and returns void.
BlockList isn't a very good abstraction. You have very different needs for "the current tetroid" and "the list of static blocks currently on the grid". The static blocks can be represented by a simple sequence of blocks as you have (although a sequence of rows, or a 2D array, may be more convenient), but the currently active tetroid needs additional information, such as the center of rotation (which doesn't belong in the World).
A simple way to represent a tetroid, and to ease rotations, might be to have the member blocks store a simple offset from the center of rotation. That makes rotations easier to compute, and means that the member blocks don't have to be updated at all during translation. Just the center of rotation has to be moved.
In the static list, it isn't even efficient for blocks to know their location. Instead, the grid should map locations to blocks (if I ask the grid "which block exists in cell (5,8), it should be able to return the block. but the block itself doesn't need to store the coordinate. If it does, it can become a maintenance headache. What if, due to some subtle bug, two blocks end up with the same coordinate? That can happen if blocks store their own coordinate, but not if the grid holds a list of which block is where.)
this tells us that we need one representation for a "static block", and another for a "dynamic block" (it needs to store the offset from the tetroid's center). In fact, the "static" block can be boiled down to the essentials: Either a cell in the grid contains a block, and that block has a colour, or it does not contain a block. There is no further behavior associated with these blocks, so perhaps it is the cell into which it is placed that should be modelled instead.
and we need a class representing a movable/dynamic tetroid.
Since a lot of your collision detection is "predictive" in that it deals with "what if I moved the object over here", it may be simpler to implement non-mutating translation/rotation functions. These should leave the original object unmodified, and a rotated/translated copy returned.
So here's a first pass on your code, simply renaming, commenting and removing code without changing the structure too much.
class World
{
public:
// Constructor/Destructor
// the constructor should bring the object into a useful state.
// For that, it needs to know the dimensions of the grid it is creating, does it not?
World(int width, int height);
~World();
// none of thes have anything to do with the world
///* Blocks Operations */
//void AppendBlock(int, int, BlockList&);
//void RemoveBlock(Block*, BlockList&);;
// Tetroid Operations
// What's wrong with using BlockList's constructor for, well, constructing BlockLists? Why do you need NewTetroid?
//void NewTetroid(int, int, int, BlockList&);
// none of these belong in the World class. They deal with BlockLists, not the entire world.
//void TranslateTetroid(int, int, BlockList&);
//void RotateTetroid(int, BlockList&);
//void CopyTetroid(BlockList&, BlockList&);
// Drawing isn't the responsibility of the world
///* Draw */
//void DrawBlockList(BlockList&);
//void DrawWalls();
// these are generic functions used to test for collisions between any two blocklists. So don't place them in the grid/world class.
///* Collisions */
//bool TranslateCollide(int, int, BlockList&, BlockList&);
//bool RotateCollide(int, BlockList&, BlockList&);
//bool OverlapCollide(BlockList&, BlockList&); // For end of game
// given that these functions take the blocklist on which they're operating as an argument, why do they need to be members of this, or any, class?
// Game Mechanics
bool AnyCompleteLines(BlockList&); // Renamed. I assume that it returns true if *any* line is complete?
bool IsLineComplete(int line, BlockList&); // Renamed. Avoid ambiguous names like "CompleteLine". is that a command? (complete this line) or a question (is this line complete)?
void ColourLine(int line, BlockList&); // how is the line supposed to be coloured? Which colour?
void DestroyLine(int line, BlockList&);
void DropLine(int, BlockList&); // Drops all blocks above line
// bad terminology. The objects are rotated about the Z axis. The x/y coordinates around which it is rotated are not axes, just a point.
int rotationAxisX;
int rotationAxisY;
// what's this for? How many rotation states exist? what are they?
int rotationState; // Which rotation it is currently in
// same as above. What is this, what is it for?
int rotationModes; // How many diff rotations possible
private:
int wallX1;
int wallX2;
int wallY1;
int wallY2;
};
// The language already has perfectly well defined containers. No need to reinvent the wheel
//class BlockList
//{
//public:
// BlockList();
// ~BlockList();
//
// Block* GetFirst();
// Block* GetLast();
//
// /* List Operations */
// void Append(int, int);
// int Remove(Block*);
// int SearchY(int);
//
//private:
// Block *first;
// Block *last;
//};
struct Colour {
int r, g, b;
};
class Block
{
public:
Block(int x, int y);
~Block();
int X();
int Y();
void Colour(const Colour& col);
void Translate(int down, int left); // add parameter names so we know the direction in which it is being translated
// what were the three original parameters for? Surely we just need to know how many 90-degree rotations in a fixed direction (clockwise, for example) are desired?
void Rotate(int cwSteps);
// If rotate/translate is non-mutating and instead create new objects, we don't need these predictive collision functions.x ½
//// Return values simulating the operation (for collision purposes)
//int IfTranslateX(int);
//int IfTranslateY(int);
//int IfRotateX(int, int, int);
//int IfRotateY(int, int, int);
// the object shouldn't know how to draw itself. That's building an awful lot of complexity into the class
//void Draw();
//Block *next; // is there a next? How come? What does it mean? In which context?
private:
int x; // position x
int y; // position y
Colour col;
//int colourR;
//int colourG;
//int colourB;
};
// Because the argument block is passed by value it is implicitly copied, so we can modify that and return it
Block Translate(Block bl, int down, int left) {
return bl.Translate(down, left);
}
Block Rotate(Block bl, cwSteps) {
return bl.Rotate(cwSteps);
}
Now, let's add some of the missing pieces:
First, we'll need to represent the "dynamic" blocks, the tetroid owning them, and the static blocks or cells in a grid.
(We'll also add a simple "Collides" method to the world/grid class)
class Grid
{
public:
// Constructor/Destructor
Grid(int width, int height);
~Grid();
// perhaps these should be moved out into a separate "game mechanics" object
bool AnyCompleteLines();
bool IsLineComplete(int line);
void ColourLine(int line, Colour col);Which colour?
void DestroyLine(int line);
void DropLine(int);
int findFirstInColumn(int x, int y); // Starting from cell (x,y), find the first non-empty cell directly below it. This corresponds to the SearchY function in the old BlockList class
// To find the contents of cell (x,y) we can do cells[x + width*y]. Write a wrapper for this:
Cell& operator()(int x, int y) { return cells[x + width*y]; }
bool Collides(Tetroid& tet); // test if a tetroid collides with the blocks currently in the grid
private:
// we can compute the wall positions on demand from the grid dimensions
int leftWallX() { return 0; }
int rightWallX() { return width; }
int topWallY() { return 0; }
int bottomWallY { return height; }
int width;
int height;
// let this contain all the cells in the grid.
std::vector<Cell> cells;
};
// represents a cell in the game board grid
class Cell {
public:
bool hasBlock();
Colour Colour();
};
struct Colour {
int r, g, b;
};
class Block
{
public:
Block(int x, int y, Colour col);
~Block();
int X();
int Y();
void X(int);
void Y(int);
void Colour(const Colour& col);
private:
int x; // x-offset from center
int y; // y-offset from center
Colour col; // this could be moved to the Tetroid class, if you assume that tetroids are always single-coloured
};
class Tetroid { // since you want this generalized for more than just Tetris, perhaps this is a bad name
public:
template <typename BlockIter>
Tetroid(BlockIter first, BlockIter last); // given a range of blocks, as represented by an iterator pair, store the blocks in the tetroid
void Translate(int down, int left) {
centerX += left;
centerY += down;
}
void Rotate(int cwSteps) {
typedef std::vector<Block>::iterator iter;
for (iter cur = blocks.begin(); cur != blocks.end(); ++cur){
// rotate the block (*cur) cwSteps times 90 degrees clockwise.
// a naive (but inefficient, especially for large rotations) solution could be this:
// while there is clockwise rotation left to perform
for (; cwSteps > 0; --cwSteps){
int x = -cur->Y(); // assuming the Y axis points downwards, the new X offset is simply the old Y offset negated
int y = cur->X(); // and the new Y offset is the old X offset unmodified
cur->X(x);
cur->Y(y);
}
// if there is any counter-clockwise rotation to perform (if cwSteps was negative)
for (; cwSteps < 0; --cwSteps){
int x = cur->Y();
int y = -cur->X();
cur->X(x);
cur->Y(y);
}
}
}
private:
int centerX, centerY;
std::vector<Block> blocks;
};
Tetroid Translate(Tetroid tet, int down, int left) {
return tet.Translate(down, left);
}
Tetroid Rotate(Tetroid tet, cwSteps) {
return tet.Rotate(cwSteps);
}
and we'll need to re-implement the speculative collision checks. Given the non-mutating Translate/Rotate methods, that is simple: We just create rotated/translated copies, and test those for collision:
// test if a tetroid t would collide with the grid g if it was translated (x,y) units
if (g.Collides(Translate(t, x, y))) { ... }
// test if a tetroid t would collide with the grid g if it was rotated x times clockwise
if (g.Collides(Rotate(t, x))) { ... }
I would personally ditch the static blocks and deal with them as rows. Having a static block you are keeping a lot more information than you need.
A world is made of rows, which is an array of single squares. The squares can be either empty, or a color (or extend it if you have special blocks).
The world also owns a single active block, as you have now. The class should have a rotate and translate method. The block will obviously need to maintain a reference to the world to determine if it will collide with existing bricks or the edge of the board.
When the active block goes out of play, it will call something like world.update() which will add the pieces of the active block to the appropriate rows, clear all full rows, decide if you have lost, etc, and finally create a new active block if needed.
Related
This question already has answers here:
What is a debugger and how can it help me diagnose problems?
(2 answers)
What can cause segmentation faults in C++? [closed]
(9 answers)
Closed 2 years ago.
So I am writing a c++ OpenGl application that should test if two figures layed out with matches are identical. I have written an own class: the "figure" class. This class makes use of the two classes "Line" and "Point". Here you can see my implementation in the figure.h header:
#define SHINT short int
class Point
{
public:
std::pair<SHINT, SHINT> coord;
Point()
{
coord = std::pair<SHINT, SHINT>{ 0, 0 };
}
Point(int x, int y)
{
coord = std::pair<SHINT, SHINT>{ (short)x, (short)y };
};
};
class Line
{
public:
std::pair<Point, Point> pts;
Line()
{
pts = std::pair<Point, Point>{ Point(), Point() };
}
Line(Point p1, Point p2)
{
pts = std::pair<Point, Point>{ (Point)p1, (Point)p2 };
};
};
class Figure
{
public:
Figure() {};
private:
std::vector<Line> lines;
Point p1;
Point p2;
char* renderPath = FILEPATH;
public:
void DrawComponents();
void Clear();
void RemoveLine(int index);
void AddLine();
void SetWorking(int x, int y, bool segment); //true seg1, false seg2
void Render();
};
The implementation of the problematic functions are as follows:
void Figure::AddLine()
{
Line l1 = Line(p1, p2);
lines.push_back(l1);
}
void Figure::SetWorking(int x, int y, bool segment)
{
if (segment)
{
p1 = Point(x, y);
}
p2 = Point(x, y);
}
I have a fig*, that can be either set to the first figure or to the second Figure. You can do that with the help of an ImGui overlay. If you press your mouse button, a set of function is triggered:
selected->SetWorking(posX, posY, m_Select); //
if (m_Select)
selected->AddLine();
posX and posY are calculated relative to the mouse position and that is working properly.
We than call the SetWorking() function on the figure pointer, which again calls a constructor in the Point and Sets the working Point to what posX and Y are. I have to Points that are used in turns. So if I click the first time, the first Point is set, if I press a second time, the second Point is set, and with two Points set, we push a new line into the vector. This alteration is achieved by the "segment" bool int the SetWorking() function. Points are saved as std::pairs<> that hold short ints, and Lines are saved as std::pairs<> that hold two points. But if we land there, an error occurs: "Exception thrown: write access violation.
this was 0x14"
The error is thrown in the utility file of the c++ STL: (line 292)
pair& operator=(_Identity_t<_Myself&&> _Right) noexcept(
conjunction_v<is_nothrow_move_assignable<_Ty1>, is_nothrow_move_assignable<_Ty2>>) /* strengthened */ {
first = _STD forward<_Ty1>(_Right.first); // <---- here
second = _STD forward<_Ty2>(_Right.second);
return *this;
}
And honestly, at this point I don't have the slightest clue as of what is going on there. My educated guess is, that something with the std::pair is going wrong, but I am to unskilled with c++ yet, so I don't really now how to solve these kinds of error
"this was 0x14": The error occured in a member function (operator=()) of pair. The pair object that executes operator=() has a value of 0x14 for its this pointer, which is highly unlikely to be correct.
Usually this means that the pair is member of an object with this pointer == nullptr, and the pair object is located at offset 0x14 relative to the class.
I suspect that selected is a nullpointer and the assignment of p1 results in the access violation.
I really need help on this one cause I am extremely stuck and have no idea what to do.
Edit:
A lot of you guys are saying that I need to use the debugger but let me be clear I have not used C++ for an extremely long time and I've used visual studio for a grand total of 2 weeks so I do not know all the cool stuff it can do with the debugger.
I am a student at university at the beginning of my second year who is trying to work out how to do something mostly by failing.
I AM NOT a professional coder and I don't have all the knowledge that you people have when it comes to these issues and that is why I am asking this question. I am trying my best to show my issue so yes my code contains a lot of errors as I only have a very basic understanding of a lot of C++ principles so can you please keep that in mind when commenting
I'm only posting this here because I can don't know who else to ask right now.
I have a function called world that is suppose to call my render class to draw all the objects inside of its vector to the screen.
#include "C_World.h"
C_World::C_World()
{
// creates an instance of the renderer class to render any drawable objects
C_Renderer *render = new C_Renderer;
}
C_World::~C_World()
{
delete[] render;
}
// adds an object to the world vector
void C_World::addToWorld(C_renderable* a)
{
world_list.push_back(a);
}
void C_World::World_Update()
{
render->ClearScreen();
World_Render();
}
void C_World::World_Render() {
for (int i = 0; i < 1; i++)
{
//render->DrawSprite(world_list[i]->getTexture(), world_list[i]->get_X, world_list[i]->get_Y());
render->DrawSprite(1, 1, 1);
}
}
While testing I commented out the Sprites get functions in order to check if they were causing the issue.
the renderer sprites are added to the vector list in the constructor through the create sprite function
C_Renderer::C_Renderer()
{
// test sprite: Id = 1
CreateSprite("WhiteBlock.png", 250, 250, 1);
}
I thought this might of been the issue so I had it in other functions but this didn't solve anything
Here are the Draw and create Sprite functions
// Creates a sprite that is stored in the SpriteList
// Sprites in the spriteList can be used in the drawSprite function
void C_Renderer::CreateSprite(std::string texture_name,
unsigned int Texture_Width, unsigned int Texture_height, int spriteId)
{
C_Sprite *a = new C_Sprite(texture_name,Texture_Width,
Texture_height,spriteId);
SpriteList.push_back(a);
size_t b = SpriteList.size();
HAPI.DebugText(std::to_string(b));
}
// Draws a sprite to the X and Y co-ordinates
void C_Renderer::DrawSprite(int id,int x,int y)
{
Blit(screen, _screenWidth, SpriteList[id]->get_Texture(),
SpriteList[id]->getTexture_W(), SpriteList[id]->getTexture_H(), x, y);
}
I even added some test code into the create sprite function to check to see if the sprite was being added too the vector list. It returns 1 so I assume it is.
Exception thrown: read access violation.
std::_Vector_alloc<std::_Vec_base_types<C_Sprite *,
std::allocator<C_Sprite *> > >::_Mylast(...) returned 0x8.
that is the full error that I get from the compiler
I'm really really stuck if there is anymore information you need just say and ill post it straight away
Edit 2:
#pragma once
#include <HAPI_lib.h>
#include <vector>
#include <iostream>
#include "C_renderable.h"
#include "C_Renderer.h"
class C_World
{
public:
C_World();
~C_World();
C_Renderer *render = nullptr;
void World_Update();
void addToWorld(C_renderable* a);
private:
std::vector<C_renderable*> world_list;
void C_World::World_Render();
};
#pragma once
#include <HAPI_lib.h>
#include "C_renderable.h"
#include "C_Sprite.h"
#include <vector>
class C_Renderer
{
public:
C_Renderer();
~C_Renderer();
// gets a pointer to the top left of screen
BYTE *screen = HAPI.GetScreenPointer();
void Blit(BYTE *destination, unsigned int destWidth,
BYTE *source, unsigned int sourceWidth, unsigned int sourceHeight,
int posX, int posY);
void C_Renderer::BlitBackground(BYTE *destination,
unsigned int destWidth, unsigned int destHeight, BYTE *source,
unsigned int sourceWidth, unsigned int sourceHeight);
void SetPixel(unsigned int x,
unsigned int y, HAPI_TColour col,BYTE *screen, unsigned int width);
unsigned int _screenWidth = 1750;
void CreateSprite(std::string texture_name,
unsigned int Texture_Width,unsigned int Texture_height, int spriteId);
void DrawSprite(int id, int x, int y);
void ClearScreen();
private:
std::vector<C_Sprite*> SpriteList;
};
I don't say this lightly, but the code you've shown is absolutely terrible. You need to stop and go back several levels in your understanding of C++.
In all likeliness, your crash is the result of a simple "shadowing" issue in one or more of your functions:
C_World::C_World()
{
// creates an instance of the renderer class to render any drawable objects
C_Renderer *render = new C_Renderer;
}
C_World::~C_World()
{
delete[] render;
}
There are multiple things wrong here, and you don't show the definition of C_World but if this code compiles we can deduce that it has a member render, and you have fallen into a common trap.
C_Renderer *render = new C_Renderer;
Because this line starts with a type this is a definition of a new, local variable, render. Your compiler should be warning you that this shadows the class-scope variable of the same name.
What these lines of code
C_World::C_World()
{
// creates an instance of the renderer class to render any drawable objects
C_Renderer *render = new C_Renderer;
}
do is:
. assign an undefined value to `this->render`,
. create a *local* variable `render`,
. construct a dynamic `C_Renderer` presumably on the heap,
. assign that to the *local* variable `render`,
. exit the function discarding the value of `render`.
So at this point the memory is no-longer being tracked, it has been leaked, and this->render is pointing to an undefined value.
You repeat this problem in several of your functions, assigning new results to local variables and doing nothing with them. It may not be this specific instance of the issue that's causing the problem.
Your next problem is a mismatch of new/delete vs new[]/delete[]:
C_World::~C_World()
{
delete[] render;
}
this would result in undefined behavior: this->render is undefined, and delete[] on a non-new[] allocation is undefined.
Most programmers use a naming convention that distinguishes a member variable from a local variable. Two common practices are an m_ prefix or an _ suffix for members, e.g.
class C_World
{
public:
C_Foo* m_foo; // option a
C_Renderer* render_; // option b
// ...
}
Perhaps you should consider using modern C++'s concept of smart pointers:
#include <memory>
class C_World {
// ...
std::unique_ptr<C_Renderer> render_;
// ...
};
C_World::C_World()
: render_(new C_Renderer) // initializer list
{}
But it's unclear why you are using a dynamic allocation here in the first place. It seems like an instance member would be better:
class C_World {
C_Renderer render_;
};
C_World::C_World() : render_() {}
EDIT: To initialize the position array m_pos[3] I set all it's values to 0 in the constructor and then I call from the main function another function called SetPos() which only sets the position of the planet in the 3D map:
void SetPos(float x, float z);
void Planet::SetPos(float x, float z)
{
m_pos[0]=x;
m_pos[1]=0;
m_pos[2]=y;
}
Thus, the constructor takes the form:
Planet::Planet()
{
m_pos[0]=0;
m_pos[1]=0;
m_pos[2]=0;
}
Is that a bad way to do it? (by need, i can't set the position directly through the constructor).
ORIGINAL:
I've created a class called Planet which controles a series of planets (Planet object) in a map. Each object has an array pos[3] which stores the coordinates where the planet must be drawn.
The planets also own a function called DrawConnections() which is in charge of drawing lines representing the connections between the actual planet and the other planets. The planets that one planet is connected to are stored in a vector, std::vector<Planet> connections.
Since attributes are encapsulated, there's a function in the Planet class which returns the position of the planet, called GetPos(float* pos), where *pos is a pointer to an array capable of storing the position of the planet.
First things first, those are the prototypes and variable declarations from Planet.h file:
public:
void DrawConnections(float radius);
void GetPos(float* position);
private:
float m_pos[3];
std::vector<Planet> m_connection;
The function DrawConnections() from Planet.cpp looks like this:
void Planet::DrawConnections(float radius) //parameter radius controls width of lines
{
float position[3]={0.0f,0.0f,0.0f}; //array storing the position of the planets
//which we are connecting to
//various OpenGl calls go here
glBegin(GL_LINES); //begins drawing the lines
for(int i=0;i<m_connection.size();i++) //for each planet we are connected to, draw a
//line
{
glVertex3f(m_pos[0],m_pos[1],m_pos[2]); //draws the first point of the line in the
//actual planet
m_connection[i].GetPos(position); //Gets the position of the planet we connect to
glVertex3f(position[0],position[1],position[2]); //draws the second point of the
//in the planet we connect to
}
glEnd(); //ends drawing
//some other OpenGl calls
}
The function GetPos() from Planet.cpp looks like this:
void Planet::GetPos(float* position)
{
position[0]=m_pos[0]; //copies the data from the position array to
position[1]=m_pos[1]; //the given pointer
position[2]=m_pos[2];
}
Any planet has x, neither z, 0 coordinate. Each one of them has a set of (x,y,z) coordinates, with x and z always different to 0.
However, some of the calls to GetPos() return x and z equal to 0, while others work properly.
This results in many lines going from the planets to the bottom left corner of the screen, without representing any connection. From what I've figured out I think the problem is in the GetPos(). However, other similar drawing functions also use GetPos() and work perfectly when they're called before the DrawConnection() function, but seem to be affected when they're called once DrawConnections() has been called It is as if that one was modifying the values of the position array when called and thus disturbing everything else which has to be with the position, including herself.
As an additional information, I'm working with Codeblocks and the MinGW GNU GCC compiler. I appreciate any help that you could give me.
Why not do?
public:
void DrawConnections(float radius);
const std::vector<float>& GetPos() const {return m_pos;};
private:
std::vector<float> m_pos;
std::vector<Planet> m_connection;
I'm designing a robot simulator for a university project and I've hit a big issue for some collision detection. Here is my robot.h header file:
#ifndef robot_h
#define robot_h
#include <vector>
enum direction
{
UP,DOWN,LEFT,RIGHT
};
enum motor
{
STOP,SLOW,FAST
};
class robot
{
public:
robot();
char bot; // The bot onscreen
int getX(); // The X position of robot
int getY(); // The Y position of robot
int dir; // The direction the robot is going
bool touchSensor; // Boolean value if collision
int lightSensor; // light sensor between 10-100
int motorA; // Motor A between 0, 1 and 2
int motorB; // Motor A between 0, 1 and 2
void detection(int x, int y);
void getReturnObject();
bool returnObjectDash;
bool returnObjectLine;
void move(); // Moving the robot
void draw(); // Drawing the robot on screen
void update(); // Updating the robot position
private:
int positionX; // Starting X value
int positionY; // Starting Y value
};
#endif
Basically, I have two boolean values being used:
returnObjectDash;and returnObjectLine. I have this code in my matrix.cpp file:
void matrix::detection(int x, int y)
{
if(vector2D[x][y]=='-')
{
returnObjectDash=true;
system("pause");
}
else
{
returnObjectDash=false;
}
if(vector2D[x][y]=='|')
{
returnObjectLine=true;
}
else
{
returnObjectLine=false;
}
}
Inside my robot.cpp I have this code which gets the two boolean values and then outputs to the console:
void robot::getReturnObject()
{
if(returnObjectDash==true)
{
std::cout<<"Dash\n";
//dir=DOWN;
}
if(returnObjectLine==true)
{
std::cout<<"Line\n";
//dir=DOWN;
}
}
This is my main.cpp
int main()
{
robot r;
while(true)
{
matrix m;
m.robotPosition(r.getX(), r.getY());
m.update(); // vector2D init and draw
m.detection(m.getX(), m.getY());
r.update();
Sleep(250);
}
}
I'm setting the default value of my two boolean variables in my matrix.cpp default constructor to false. When I hit the pause button and debug I seem to be getting two different returns. For my matrix it is returning false, though for my robot it is returning true, it is like my program is making two different variables. If someone could shed some light on this weird behaviour, then please do tell! Thank you
Your program is making two different values because it has two different values. The matrix class apparently has its own Boolean variable, matrix::returnObjectDash, and the robot class has its own variable, robot::returnObjectDash. Setting the variable in one instance of one class has no impact on the variables in any other classes (or any other instances).
You have not provided code to you matrix class, however, judging from void matrix::detection(int x, int y) I assume that you have a method in there, called detection, and that you have declared the same fields returnObjectLine and returnObjectDash.
You are correct in assuming that there are 2 versions of those variables: One of those versions is inside your matrix object, and the other one is inside your robot object.
Not only that, you can (and usually do!) have more than one matrix/robot object. Each of those will have its own separate copy of those variables, and changing one of them will not impact the other ones.
I have a university assignment and I am completely confused on how to pass the array correctly to prevent the array from being passed as a single array and not a 2D array.
We are to create a random maze generator that will allow us to play that maze too. We are using a specialized windows code to display the maze, but that's not were the problem is so Ill leave that out
My lecturer gave us the skeleton code to work from. What must I change to get it to work?
We have not learnt dynamic memory location or vectors. We have to use an array. Please Help!!?
Here is his code:
I have used the same and just added all the function parameters. I have not changed anything with 'maze' though
class MazeSquare
{
public:
bool leftWall, rightWall, bottomWall, topWall;
bool visited;
int steps;
MazeSquare() // constructor
{
Initialise();
}
void Initialise(void) // reinitialise a square for a new maze
{
leftWall = true; // create the maze square with all the walls
rightWall = true;
bottomWall = true;
topWall = true;
visited = false; // the robot has not visited the square yet
steps = 256; // greater than maximum possible number of steps
}
};
// constants
const int MAZE_SIZE = 16;
// function prototypes
void CreateMaze(MazeSquare maze[MAZE_SIZE][MAZE_SIZE]);
void SolveMaze(MazeSquare maze[MAZE_SIZE][MAZE_SIZE]);
void RestartMaze(MazeSquare maze[MAZE_SIZE][MAZE_SIZE]);
void MoveRobot(MazeSquare maze[MAZE_SIZE][MAZE_SIZE], int &x, int &y, Point click);
void DrawWindow(MazeSquare maze[MAZE_SIZE][MAZE_SIZE], int x, int y);
int ccc_win_main() // main function for a graphics program
{
MazeSquare maze[MAZE_SIZE][MAZE_SIZE]; // maze design
int x = 0, y = 0; // robot position
bool exit = false; // flag to control end of program
// initialise the random number generator
srand((unsigned int)(time(NULL)));
/* initialise the window coordinates here */
CreateMaze(maze); // create a new maze
DrawWindow(maze); // draw the image in the GUI window
do
{
// get a mouse click
Point click = cwin.get_mouse("Click a button or move the robot");
// handle the different types of mouse clicks
if (/* new button is clicked */)
{
CreateMaze(maze);
x = 0;
y = 0;
}
if (/* solve button is clicked */)
{
SolveMaze(maze);
}
if (/* restart button is clicked */)
{
RestartMaze(maze);
x = 0;
y = 0;
}
if (/* exit button is clicked */)
{
exit = true;
}
// handle robot moves
if (/* maze is clicked */)
{
MoveRobot(maze, x, y);
}
DrawWindow(maze);
} while (!exit);
return 0;
}
Confusion number one - You cannot pass arrays to functions in C++.
Confusion number two - You cannot declare arrays as function parameters in C++
Confusion number three - 2D arrays are single arrays, a 2D array is an arrays of arrays, therefore it's a 'single array` also. I guess I'm saying the term single array doesn't have much meaning.
Arrays are a confusing topic in C++. You cannot do everything you might expect to be able to do with them. Instead everything is done with pointers. The relationship beween arrays and pointers in C++ is another confusing topic. You really need to read a book. Any specific questions, ask again.
But on the bright side I don't see anything particularly wrong with your code. You're certainly are not passing single arrays to your functions, as you are worried about.
EDIT:
Perhaps I should make this a little clearer. On point two, this code
void CreateMaze(MazeSquare maze[MAZE_SIZE][MAZE_SIZE]);
certainly looks like you are declaring a function with an array parameter. But it doesn't. Instead the compiler takes the code and converts it into the equivalent code that uses pointers.
On point one, this code
CreateMaze(maze); // create a new maze
certainly looks like you are passing an array to a function, but again you are not. Given that code the compiler passes a pointer to the first element of your maze array, it doesn't pass the array itself.