Related
I decided to make a multiplayer game in sfml, the map is a text variable
one step lower,! - block with a collider, # - ground.
It looks something like this:
"## !;
! # !;
###;"
I have a special class "Qardrat" that represents a block with a texture, that is, an alternative to a sprite.
class Quardrat {
public:
void spriteSetPosition(int Vx, int Vy) {
sprite.setPosition(Vx, Vy);
}
void LoadTexture(String textureName) {
texture.loadFromFile(textureName);
// RectangleShape tmpSprite(texture);
sprite.setScale(sizeX, sizeY);
sprite.setTexture(&texture);
}
void ShapeMove(int Vx, int Vy) {
sprite.move(Vx, Vy);
//sprite.setPosition(sprite.getPosition().x+Vx, sprite.getPosition().y+Vy);
std::cout << "Сдвинулся на " << Vx << "По x" << std::endl;
}
Quardrat(float x = 0, float y = 0, float sx = 1, float sy = 1, String textureName = "player.png") {
LoadTexture(textureName);
sizeX = sx;
sizeY = sy;
sprite.setPosition(x, y);
sprite.setSize(Vector2f(sx, sy));
}
sf::RectangleShape GetShape() {
return sprite;
}
void DrawShape() {
::window.draw(sprite);
}
float GetSizeX() {
return sizeX;
}float GetSizeY() {
return sizeY;
}
private:
Texture texture;
std::string texutreName = "player.png";
float sizeX = 10;
float sizeY = 10;
//Sprite sprite;
RectangleShape sprite;
};
It is declared as follows: x position, y position, height, width, texture name.
To draw objects, 3 cities are used, which are placed in the square classes:
sloi0, sloi1, player /
here is the drawing code
void DrawOnDisplay(const std::vector<std::reference_wrapper<Quardrat>>& a) {//РИСОВАНИЕ
for (Quardrat i : sloi0)
i.DrawShape();
//::window.draw(i.GetShape());
for (Quardrat i : sloi1)
i.DrawShape();
// ::window.draw(i.GetShape());
for (Quardrat i : a) {
i.DrawShape();
// ::window.draw(i.GetShape());
}
}
And I'll leave the card reading code here, just in case:
for (char block : map) {
if (block == '#') {
Quardrat bl = Quardrat(BlockPosX * StandartBlockSize, BlockPosY * StandartBlockSize, StandartBlockSize, StandartBlockSize);
sloi0.push_back(bl); BlockPosX++;
// }
if (block == '!') {
Quardrat bl = Quardrat(BlockPosX * StandartBlockSize, BlockPosY * StandartBlockSize,
StandartBlockSize / 10, StandartBlockSize / 10, "block.png");
sloi0.push_back(bl); BlockPosX++;
collisions.push_back(bl);
}
if (block == ';') {
BlockPosY++;
BlockPosX = 0;
}
}
And here's the problem - from all the blocks of the map, a texture appears only for one, for all the rest - a white square.
I'm trying to implement collision detection in sfml using the separating axis theorem but my function to get the min translation vector (getMtv()) is always returning that there is a collision (MTV::IsValid()). I was following a tutorial here
but I can't see where I went wrong.
#include <iostream>
#include <math.h>
#include <SFML/Graphics.hpp>
#include <gtest/gtest.h>
typedef sf::Vector2f Vector2;
typedef sf::Vector2f Axis;
typedef sf::Vector2f Projection;
typedef std::vector<Axis> AxesVec;
class MTV
{
private:
bool _valid;
Axis _axis;
float _overlap;
public:
MTV(Axis axis, float overlap, bool valid)
{
_valid = valid;
_overlap = overlap;
_axis = axis;
}
bool IsValid() const
{
return _valid;
}
};
Vector2 perpendicular(Vector2 v)
{
return Vector2(v.y, -v.x);
}
float dot(Vector2 vec1, Vector2 vec2)
{
return (vec1.x * vec2.x) + (vec1.y * vec2.y);
}
float magnitude(Vector2 v)
{
return std::sqrt(dot(v,v));
}
class Polygon : public sf::ConvexShape
{
public:
const AxesVec& GetAxes() const
{
return axes;
}
AxesVec axes;
void generateAxes()
{
for (int i = 0; i < getPointCount(); i++)
{
// get the current vertex
Vector2 p1 = getPoint(i); //shape.vertices[i];
// get the next vertex
Vector2 p2 = getPoint(i + 1 == getPointCount() ? 0 : i + 1);
// subtract the two to get the edge vector
Vector2 edge = p1 - p2;
// get either perpendicular vector
Vector2 normal = perpendicular(edge);
// the perp method is just (x, y) => (-y, x) or (y, -x)
axes.push_back(normal / magnitude(normal));
}
};
float cross(Vector2 vec1, Vector2 vec2)
{
return (vec1.x * vec2.y) - (vec1.y * vec2.x);
}
Vector2 project(Polygon p, Axis axis)
{
float min = dot(axis, p.getPoint(0)); //axis.dot(shape.vertices[0]);
float max = min;
for (int i = 1; i < p.getPointCount(); i++)
{
// NOTE: the axis must be normalized to get accurate projections
float prj = dot(axis, p.getPoint(i));//axis.dot(shape.vertices[i]);
if (prj < min)
{
min = prj;
}
else if (prj > max)
{
max = prj;
}
}
//Projection proj = new Projection(min, max);
return Projection(min, max);
}
class Collison
{
private:
Vector2 mtv;
Polygon a;
Polygon b;
};
bool overlap(Projection a, Projection b)
{
// x = min & y = max
return !(a.x > b.y || a.x > b.y);
}
float getOverlap(Projection a, Projection b)
{
// x = min & y = max
return (a.y < b.y) ? a.y - b.x : b.y - a.x;
}
MTV getMtv(Polygon a, Polygon b)
{
float overlapMax = std::numeric_limits<float>::infinity();// really large value;
float Overlap;
Axis smallest;// = null;
AxesVec axesA = a.GetAxes();
AxesVec axesB = b.GetAxes();
// loop over the axes1
for (auto&& axis : axesA) //for (int i = 0; i < axes1.length; i++)
{
//Axis axis = axes1[i];
// project both shapes onto the axis
Projection pA = project(a, axis);
Projection pB = project(b, axis);
// do the projections overlap?
if (!overlap(pA, pB)) //(!p1.overlap(p2))
{
// then we can guarantee that the shapes do not overlap
return MTV(smallest, 0, false);
}
else
{
// get the overlap
float o = getOverlap(pA, pB); //p1.getOverlap(p2);
// check for minimum
if (o < overlapMax)
{
// then set this one as the smallest
Overlap = o;
smallest = axis;
}
}
}
for (auto&& axis : axesB) //for (int i = 0; i < axes1.length; i++)
{
//Axis axis = axes1[i];
// project both shapes onto the axis
Projection pA = project(a, axis);
Projection pB = project(b, axis);
// do the projections overlap?
if (!overlap(pA, pB)) //(!p1.overlap(p2))
{
// then we can guarantee that the shapes do not overlap
return MTV(smallest, 0, false);
}
else
{
// get the overlap
double o = getOverlap(pA, pB); //p1.getOverlap(p2);
// check for minimum
if (o < overlapMax)
{
// then set this one as the smallest
Overlap = o;
smallest = axis;
}
}
}
//MTV mtv = new MTV(smallest, overlap);
// if we get here then we know that every axis had overlap on it
// so we can guarantee an intersection
return MTV(smallest, Overlap, true);
}
int main(int argc, char **argv)
{
Polygon polygon;
polygon.setPointCount(3);
polygon.setPoint(0, sf::Vector2f(500, 100));
polygon.setPoint(1, sf::Vector2f(250, 500));
polygon.setPoint(2, sf::Vector2f(750, 500));
polygon.setFillColor(sf::Color::Red);
polygon.generateAxes();
Polygon polygon2;
polygon2.setPointCount(3);
polygon2.setPoint(0, sf::Vector2f(100, 0));
polygon2.setPoint(1, sf::Vector2f(50, 150));
polygon2.setPoint(2, sf::Vector2f(150, 150));
polygon2.generateAxes();
//polygon2.setPoint(0, sf::Vector2f(100, 0));
//polygon2.setPoint(1, sf::Vector2f(500, 150));
//polygon2.setPoint(2, sf::Vector2f(250, 150));
polygon2.setFillColor(sf::Color::Green);
sf::RenderWindow window(sf::VideoMode(800, 600), "My window");
// run the program as long as the window is open
while (window.isOpen())
{
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
}
// clear the window with black color
window.clear(sf::Color::Black);
// draw everything here...
window.draw(polygon);
window.draw(polygon2);
std::cout << getMtv(polygon, polygon2).IsValid() << std::endl;
// end the current frame
window.display();
}
return 0;
}
I'm trying to implement the A* algorithm for finding the fastest path to the destination. I can find the path when the destination is not far away from the player. But it's freezes when I take a position that is far away from the player. Can anyone help me out with this? Can't find it anyway.
int Controller::findPathfindingNodes(int xdes, int ydes)
{
std::vector<std::shared_ptr<Tile>> tiles = model->getAllLimitTiles(); //all the tiles where we can move
std::shared_ptr<Protagonist> prota = model->getAllProtagonist(); //get data from protagonist
int currentX = prota->getXPos(); //get xpos of prota
int currentY = prota->getYPos(); //get ypos of prota
openList.clear();
closedList.clear();
mylist.clear();
//return 50;
std::shared_ptr<Tile> endPoint = model->getCoordinate(QPointF(xdes,ydes));
std::shared_ptr<Tile> startPoint = model->getCoordinate(QPointF(currentX,currentY));
int sumtotal = abs((currentX - xdes) + (currentY - ydes));
//we insert our start position, we have no parrent yet
PathFinding* start = new PathFinding(startPoint,currentX, currentY, 0, sumtotal,nullptr);
//this value we insert in our openList
openList.insert(start->getXCoord() + (model->getCols()+1)*start->getYCoord(), start);
bool pathFound = false;
int i = 0;
int pathcost;
//do whil path is found
while(!pathFound){
QHashIterator<int, PathFinding*> iterator(openList);
PathFinding* parent;
iterator.next();
parent = iterator.value();
//we take the next tile, and we take the one with the lowest value
while(iterator.hasNext()){
iterator.next();
//checking lowest f value
if((iterator.value()->getGcost() + iterator.value()->getHcost()) < (parent->getGcost() + parent->getHcost())){
parent = iterator.value();
}
}
//here we check if we are at the destionation. if we are we return our pathcost.
if(atDestionation(parent,endPoint)){
pathFound = true;
while(parent->hasParent()){
mylist.append(parent);
parent = parent->getParent();
}
//we calculate what the pathcost is and return it
pathcost = calculatePathCost(mylist);
return pathcost;
}else{
int parent_x = parent->getXCoord();
int parent_y = parent->getYCoord();
i++;
clearLists(parent);
filllists(parent,endPoint);
}
}
}
To put the values of the nodes in the open & closed list I do with this:
void Controller::filllists(PathFinding *parent,std::shared_ptr<Tile> endPoint)
{
int xPosNode = parent->getXPos();
int yPosNode = parent->getYPos();
//while found location
for(int x = -1; x < 2; x++) {
for(int y = -1; y <2; y++) {
int p_x = xPosNode + x;
int p_y = yPosNode + y;
// Is this coordinate within the world?
if(p_x >= 0 && p_x < model->getCols() && p_y >= 0 && p_y < model->getRows()) {
//we check if the tile exist (if it's no infinity tile)
if(model->tileExist(QPointF(p_x,p_y))){
// Already in open list > Check if current node is a better parent
if((!openList.value(p_x))+ (model->getCols() + 1)*(p_y)){
// Not in open and not in closed list > Possible candidate
if((!closedList.value(p_x))+ (model->getCols() + 1)*(p_y)){
int h_value = calculateHvalue(parent->getXCoord(),parent->getYCoord(),endPoint->getXPos(),endPoint->getYPos());
//int g_value = parent->getGcost() + calculateTileCost(parent->getXCoord(),parent->getYCoord(),p_x,p_y);
int g_value = calculateGvalue(parent, p_x, p_y);
std::shared_ptr<Tile> tile = model->getCoordinate(QPointF(p_x,p_y));
PathFinding* move = new PathFinding(tile,p_x, p_y, g_value, h_value, parent);
int number = move->getXCoord() + (model->getCols()+1)*move->getYCoord();
openList.insert(move->getXCoord() + (model->getCols()+1)*move->getYCoord(), move);
}
}
}
}
}
}
}
My header file looks like:
class Controller : public QObject
{
Q_OBJECT
public:
Controller(std::shared_ptr<Model> &modelPtr,std::shared_ptr<View> &viewPtr);
// void checkTile(QString position,PathFinding *parent, std::shared_ptr<Tile> endPoint, int pos);
void checkAboveTile(PathFinding *parent, std::shared_ptr<Tile> endPoint, int pos);
void checkBelowTile(PathFinding *parent, std::shared_ptr<Tile> endPoint, int pos);
void checkLeftTile(PathFinding *parent, std::shared_ptr<Tile> endPoint, int pos);
void checkRightTile(PathFinding *parent, std::shared_ptr<Tile> endPoint, int pos);
bool atDestionation(PathFinding *parent, std::shared_ptr<Tile> endPoint);
float calculateTileCost(int xposnew,int yposnew, int xpos, int ypos);
int calculateHvalue(int p_x,int p_y, int des_x, int des_y);
int calculateGvalue(PathFinding *parent, int x, int y);
void filllists(PathFinding *parent, std::shared_ptr<Tile> endPoint);
//QPair<QList<QPointF>, float> generatePath(Tile* endPoint);
//Openlist contains the nodes to be examined
QHash <int, PathFinding *> openList;
//ClosedList has the nodes that are already examined
QHash <int, PathFinding *> closedList;
QList<QPointF> pathlist;
void clearLists(PathFinding *parent);
QList<PathFinding*> mylist;
int calculatePathCost(QList<PathFinding*> mylist);
int findPathfindingNodes(int xdes, int ydes);
private:
std::shared_ptr<Model> model;
std::shared_ptr<View> view;
int heurCost;
signals:
void atDestination(int xPos,int yPos);
void gotoview(int xPos,int yPos);
public slots :
void goToDestination(int xDestination, int yDestination);
};
#endif // CONTROLLER_H
When I try to output the path which is travelled the I can see that it put sometimes the same position in mylist.
X1 value : 2 Y1 value : 3
X1 value : 1 Y1 value : 3
X1 value : 0 Y1 value : 3
X1 value : 1 Y1 value : 3
X1 value : 1 Y1 value : 3
X1 value : 2 Y1 value : 3
X1 value : 2 Y1 value : 4
X1 value : 3 Y1 value : 4
X1 value : 3 Y1 value : 4
X1 value : 3 Y1 value : 5
X1 value : 2 Y1 value : 6
X1 value : 2 Y1 value : 6
X1 value : 1 Y1 value : 6
X1 value : 0 Y1 value : 6
X1 value : 0 Y1 value : 7
X1 value : 0 Y1 value : 6
X1 value : 0 Y1 value : 5
This is how the player walked, can see that he sometimes uses the same parent over again.
I can't see anywhere that you move visited nodes from the open list to the closed list.
This would account for visiting the same place multiple times.
if((!openList.value(p_x)) + (model->getCols() + 1)*(p_y)) is very likely to be true.
(Adding 0 or 1 to (model->getCols() + 1)*p_y is probably non-zero.)
I believe you meant if(!openList.value(p_x + (model->getCols() + 1)*p_y)).
(Same issue with the closed list.)
This would also account for multiple visits, as you repeatedly insert the same nodes in the open list.
I would suggest abstracting out the index calculation:
int index(shared_ptr<Model> model, int x, int y)
{
return x * (model->getCols() + 1)* y;
}
and using that instead of error-prone code reproduction, e.g.
openList.insert(index(model, start->getXCoord(), start->getYCoord()), start);
// ...
if(!openList.value(index(model, p_x, p_y)))
It's also a good idea to add tracing output along the way.
I'm a bit of a newcomer to programming and C++, and learning how to program games with Allegro 5. One of the projects I've set for myself is to clean up a tutorial source code of Pong that I found here: http://www.cppgameprogramming.com/newforums/viewtopic.php?f=5&t=1991
However, I've run into a problem. The compiler generates an error I don't entirely understand, and none of my research is panning out. The error is:
insufficient contextual information to determine type
The closest I found on the internet was this page: http://bytes.com/topic/c/answers/138897-error-insufficient-contextual-information-determine-type which helped me narrow down that the problem is in the class declarations. However, the solution provided there doesn't exactly apply to me, since the class constructors here take parameters. The question previously asked here doesn't seem to apply to my situation, either, since it used file output and templates, neither of which I'm using.
I've posted a large chunk of my program below, with the error-generating parts marked out with commented stars to make them easy to find, I hope. However, I've left in a lot of the rest of the code on the off-chance that it's somewhere else.
As a heads up: there might be bits of code you don't recognize from allegro 5, such as alObject.paint(255,255,255). That's me consolidating some of the allegro objects and functions into their own class to make it a bit easier on me, the source of which I'm not including here since the compiler doesn't generate errors with them.
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include "allegro.h"
struct CBox{
CBox(int _x, int _y, int _w, int _h):x(_x),y(_y),w(_w),h(_h){}
CBox(const CBox& other){
x = other.x;
y = other.y;
w = other.w;
h = other.h;
}
bool collides(const CBox& other){
return not(other.x + other.w < x or other.y + other.h < y or other.x > x + y or other.y > y + h);
}
int x;
int y;
int w;
int h;
};
class CPlayer{
private:
int score;
CBox box;
ALLEGRO_COLOR color;
double mov_y;
void testBounds(CBox&);
public:
CPlayer(CBox p, ALLEGRO_COLOR col):box(p),color(col){mov_y = 0.0;}
void setScore(int new_s){score=new_s;}
int getScore(){return score;}
void setBox(const CBox& b){box=b;}
CBox getBox(){return box;}
void setXYMovement(double new_my){mov_y=new_my;}
double getXYMovement(){return mov_y;}
void move(CBox& bounds);
void draw(){
al_draw_filled_rectangle(box.x, box.y, box.x + box.w, box.y + box.h, color);
}
};
class CBall{
private:
CBox box;
ALLEGRO_COLOR color;
double mov_y;
double mov_x;
int last_touch;
void testCollision(CBox&, const CBox&, CPlayer*);
int testBounds(CBox&, const CBox&);
public:
CBall(CBox p, ALLEGRO_COLOR col):box(p),color(col),last_touch(3){}
void setXYMovement(double new_mx, double new_my){
mov_x = new_mx;
mov_y = new_my;
}
void move(const CBox& bounds, CPlayer* plys);
void draw(){
al_draw_filled_circle(box.x + box.w/2, box.y + box.h/2, box.w/2, color);
}
};
class GameLoop{
private:
CBox fieldbox(int, int, int, int);
/************************************************/
/***********ERROR HERE?? ERROR HERE??************/
/************************************************/
CBall ball(const CBox&, ALLEGRO_COLOR);
CPlayer player1(const CBox&, ALLEGRO_COLOR);
CPlayer player2(const CBox&, ALLEGRO_COLOR);
/*************************************************/
/*************************************************/
public:
GameLoop(Allegro&);
void GameStart(Allegro&);
void runTimerChecks(ALLEGRO_EVENT&, Allegro&);
void runExit(ALLEGRO_EVENT&, Allegro&, bool&);
void playerInput(ALLEGRO_EVENT&, bool&);
void endPlayerInput(ALLEGRO_EVENT&);
};
void CPlayer::move(CBox& bounds){
//make sure the player doesn't go off-bounds
testBounds(bounds);
box.y+=(int)mov_y;
//Players can't move horizontally, so no bounds checking in that matter
}
void CPlayer::testBounds(CBox& bounds){
if((mov_y < 0) && (box.y + mov_y < bounds.y)){
box.y = bounds.y;
mov_y = 0;
}
else if((mov_y > 0) && (box.y + box.h > bounds.y + bounds.h)){
box.y = bounds.y + bounds.h - box.h;
mov_y = 0;
}
}
//ghostbox is the precalculated ball's trajectory
void CBall::move(const CBox& bounds, CPlayer* plys){
CBox ghostbox(box.x+(int)mov_y, box.y+(int)mov_y, box.w, box.h);
// test collision for box players
testCollision(ghostbox, bounds, plys);
testBounds(ghostbox, bounds);
}
void CBall::testCollision(CBox& ghostbox, const CBox& bounds, CPlayer* plys){
for(int i = 0; i < 2; i++){
//a player cannot touch the ball twice in a row
if(i != last_touch){
CBox other = plys[i].getBox();
if(ghostbox.collides(other)){
//set the last touch to this player
last_touch = i;
//negate the "ghost movement" in x axis
ghostbox.x -= (int)mov_x;
//bounce horizontally
mov_x = -mov_x;
//bounce vertically to change the ball's trajectory
mov_y = (((box.y+box.h/2.0)-(other.y+other.h/2.0))/other.h)*10;
break;
}
}
}
}
int CBall::testBounds(CBox& ghostbox, const CBox& bounds){
if(ghostbox.y < bounds.y){
ghostbox.y = bounds.y;
mov_y = -mov_y;
}
else if(ghostbox.y + ghostbox.h > bounds.y + bounds.h){
ghostbox.y = (bounds.y + bounds.h - ghostbox.h);
mov_y = -mov_y;
}
if(ghostbox.x + ghostbox.w < bounds.x){
box.x = bounds.x + bounds.w/2 - bounds.w/2;
box.y = bounds.y + bounds.h/2 - bounds.h/2;
return 2;
}
else if(ghostbox.x > bounds.x + bounds.w){
box.x = bounds.x + bounds.w/2 - box.w/2;
box.y = bounds.y + bounds.h/2 - box.h/2;
return 1;
}
box = ghostbox;
return 0;
}
GameLoop::GameLoop(Allegro& alObject){
// This box is our playfield (covers the whole screen)
fieldbox(0,0,alObject.getWidth(), alObject.getHeight());
//we setup the ball at the center of the screen with a white color
ball(CBox(alObject.getWidth()/2-10,alObject.getHeight()/2-10,20,20),alObject.paint(255,255,255));
//red player on the left
player1(CBox(10,alObject.getHeight()/2-80/2,20,80), alObject.paint(255,0,0));
//blue player on the right
player2(CBox(alObject.getWidth()-10-20,alObject.getHeight()/2-80/2,20,80), alObject.paint(0,0,255));
}
void GameLoop::GameStart(Allegro& alObject){
/*
when this variable is set to true the program will quit the main loop
and free the allocated resources before quitting.
*/
bool exit = false;
//we tell the ball to move to the left
/***********************************************/
/***********************************************/
ball.setXYMovement(-5.0,5.0); // GENERATES THE ERROR
/***********************************************/
/***********************************************/
while(!exit){
al_wait_for_event(alObject.eventq, NULL);
while(al_get_next_event(alObject.eventq, &alObject.event)){
if(alObject.event.type == ALLEGRO_EVENT_TIMER){
runTimerChecks(alObject.event, alObject);
}
else if(alObject.event.type == ALLEGRO_EVENT_DISPLAY_CLOSE){
// quit if the user tries to close the window
runExit(alObject.event, alObject, exit);
}
else if(alObject.event.type == ALLEGRO_EVENT_KEY_DOWN){
playerInput(alObject.event, exit);
}
else if(alObject.event.type == ALLEGRO_EVENT_KEY_UP){
endPlayerInput(alObject.event);
}
}
}
}
void GameLoop::runTimerChecks(ALLEGRO_EVENT& event, Allegro& alObject){
if(alObject.event.timer.source == alObject.getTimer()){
//fill the screen with black
al_clear_to_color(alObject.paint(0,0,0));
//move and draw our two players
/**************************************************/
/**************************************************/
player1.move(fieldbox); // GENERATES THE ERROR
player1.draw(); // GENERATES THE ERROR
player2.move(fieldbox); // GENERATES THE ERROR
player2.draw(); // GENERATES THE ERROR
/**************************************************/
/**************************************************/
}
}
void GameLoop::runExit(ALLEGRO_EVENT& event, Allegro& alObject, bool& exit){
if(event.display.source == alObject.getDisplay()){
exit = true;
}
}
void GameLoop::playerInput(ALLEGRO_EVENT& event, bool& exit){}
void GameLoop::endPlayerInput(ALLEGRO_EVENT& event){}
Yes, the error is here:
CBall ball(const CBox&, ALLEGRO_COLOR);
CPlayer player1(const CBox&, ALLEGRO_COLOR);
CPlayer player2(const CBox&, ALLEGRO_COLOR);
This doesn't declare member variables called ball, player1 and player2, as you think it does (according to code like: player1.draw();). Instead, what you've written is a declaration of member functions with these names, taking in argument the parameters you've specified. Instead, you should do:
CBall ball;
CPlayer player1;
CPlayer player2;
Then in the constructor of GameLoop, initialize them with whatever value you want, using initialization lists:
GameLoop::GameLoop(Allegro& alObject) :
ball(/* ... */),
player1(/* ... */),
player2(/* ... */)
{
// ....
}
Never seen this linker error in my life until I started programming PSP homebrew applications. Anyway, I am making an allegro game and I need to fire projectiles or missiles in this case and I need to use a dynamic and generic array. But my linker complains that there is an undefined reference to the new operator. Below will be the whole source code, makefile, and error details.
Error Details:
1>------ Build started: Project: PSP Asteroids, Configuration: Debug Win32 ------
1> psp-g++ -I. -IC:/pspsdk/psp/sdk/include -O2 -G0 -Wall -I. -IC:/pspsdk/psp/sdk/include -O2 -G0 -Wall -fno-exceptions -fno-rtti -D_PSP_FW_VERSION=150 -c -o main.o main.cpp
1> psp-gcc -I. -IC:/pspsdk/psp/sdk/include -O2 -G0 -Wall -D_PSP_FW_VERSION=150 -L. -LC:/pspsdk/psp/sdk/lib main.o -lalleg -lpspgu -lpspirkeyb -lm -lpsppower -lpspaudio -lpsprtc -lpspdebug -lpspdisplay -lpspge -lpspctrl -lpspsdk -lc -lpspnet -lpspnet_inet -lpspnet_apctl -lpspnet_resolver -lpsputility -lpspuser -lpspkernel -o main.elf
1> main.o: In function `std::vector<Missile*, std::allocator<Missile*> >::_M_insert_aux(__gnu_cxx::__normal_iterator<Missile**, std::vector<Missile*, std::allocator<Missile*> > >, Missile* const&)':
1> main.cpp:(.text._ZNSt6vectorIP7MissileSaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_[_ZNSt6vectorIP7MissileSaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_]+0xb8): undefined reference to `operator new(unsigned int)'
1> main.cpp:(.text._ZNSt6vectorIP7MissileSaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_[_ZNSt6vectorIP7MissileSaIS1_EE13_M_insert_auxEN9__gnu_cxx17__normal_iteratorIPS1_S3_EERKS1_]+0x124): undefined reference to `operator delete(void*)'
1> C:\pspsdk\bin\make: *** [main.elf] Error 1
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Makefile
TARGET = main
OBJS = main.o
CFLAGS = -O2 -G0 -Wall
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = PSP Asteroids
LIBS = -lalleg -lpspgu -lpspirkeyb -lm -lpsppower -lpspaudio -lpsprtc
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
main.cpp
#define ALLEGRO_NO_MAGIC_MAIN
#define WIDTH 480
#define HEIGHT 272
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <allegro.h>
#include <math.h>
#include <vector>
PSP_MODULE_INFO("PSP Asteroids", 0, 1, 1);
int check_bb_collision ( BITMAP* spr1, BITMAP* spr2, int x1, int y1, int x2, int y2)
{
int b1_x = x1;
int b2_x = x2;
int b1_y = y1;
int b2_y = y2;
int b1_w = spr1->w;
int b2_w = spr2->w;
int b1_h = spr1->h;
int b2_h = spr2->h;
if ( ( b1_x > b2_x + b2_w - 1 ) || // is b1 on the right side of b2?
( b1_y > b2_y + b2_h - 1 ) || // is b1 under b2?
( b2_x > b1_x + b1_w - 1 ) || // is b2 on the right side of b1?
( b2_y > b1_y + b1_h - 1 ) ) // is b2 under b1?
{
// no collision
return 0;
}
// collision
return 1;
}
//Pass 2 Allegro bitmaps and their respective positions and this function
//returns true if there is a collision and false if theres not.
//The 2 bitmaps must be memory bitmaps of the same color depth.
int check_pp_collision_normal(BITMAP *spr1, BITMAP *spr2, int x1, int y1, int x2, int y2)
{
int dx1, dx2, dy1, dy2; //We will use this deltas...
int fx,fy,sx1,sx2; //Also we will use this starting/final position variables...
int maxw, maxh; //And also this variables saying what is the maximum width and height...
int depth; //This will store the color depth value...
char CHARVAR; //We will use these to store the transparent color for the sprites...
short SHORTVAR;
long LONGVAR;
if( !check_bb_collision(spr1, spr2, x1,y1, x2,y2) ) return 0; //If theres not a bounding box collision, it is impossible to have a pixel perfect collision right? So, we return that theres not collision...
//First we need to see how much we have to shift the coordinates of the sprites...
if(x1>x2) {
dx1=0; //don't need to shift sprite 1.
dx2=x1-x2; //shift sprite 2 left. Why left? Because we have the sprite 1 being on the right of the sprite 2, so we have to move sprite 2 to the left to do the proper pixel perfect collision...
} else {
dx1=x2-x1; //shift sprite 1 left.
dx2=0; //don't need to shift sprite 2.
}
if(y1>y2) {
dy1=0;
dy2=y1-y2; //we need to move this many rows up sprite 2. Why up? Because we have sprite 1 being down of sprite 2, so we have to move sprite 2 up to do the proper pixel perfect collision detection...
} else {
dy1=y2-y1; //we need to move this many rows up sprite 1.
dy2=0;
}
//Then, we have to see how far we have to go, we do this seeing the minimum height and width between the 2 sprites depending in their positions:
if(spr1->w-dx1 > spr2->w-dx2) {
maxw=spr2->w-dx2;
} else {
maxw=spr1->w-dx1;
}
if(spr1->h-dy1 > spr2->h-dy2) {
maxh=spr2->h-dy2;
} else {
maxh=spr1->h-dy1;
}
maxw--;
maxh--;
fy=dy1;
fx=dx1;
dy1+=maxh;
dy2+=maxh;
sx1=dx1+maxw;
sx2=dx2+maxw;
depth=bitmap_color_depth(spr1); //Get the bitmap depth...
if(depth==8) {
CHARVAR=bitmap_mask_color(spr1); //Get the transparent color of the sprites...
for(; dy1>=fy; dy1--,dy2--) { //Go through lines...
for(dx1=sx1,dx2=sx2; dx1>=fx; dx1--,dx2--) { //Go through the X axis...
if((spr1->line[dy1][dx1]!=CHARVAR) && (spr2->line[dy2][dx2]!=CHARVAR)) return 1; //Both sprites don't have transparent color in that position, so, theres a collision and return collision detected!
}
}
} else {
if(depth==16 || depth==15) {
SHORTVAR=bitmap_mask_color(spr1); //Get the transparent color of the sprites...
for(; dy1>=fy; dy1--,dy2--) { //Go through lines...
for(dx1=sx1,dx2=sx2; dx1>=fx; dx1--,dx2--) { //Go through the X axis...
if(( ((short *)spr1->line[dy1])[dx1]!=SHORTVAR) && ( ((short *)spr2->line[dy2])[dx2]!=SHORTVAR)) return 1; //Both sprites don't have transparent color in that position, so, theres a collision and return collision detected!
}
}
} else {
if(depth==32) {
LONGVAR=bitmap_mask_color(spr1); //Get the transparent color of the sprites...
for(; dy1>=fy; dy1--,dy2--) { //Go through lines...
for(dx1=sx1,dx2=sx2; dx1>=fx; dx1--,dx2--) { //Go through the X axis...
if(( ((long *)spr1->line[dy1])[dx1]!=LONGVAR) && ( ((long *)spr2->line[dy2])[dx2]!=LONGVAR)) return 1; //Both sprites don't have transparent color in that position, so, theres a collision and return collision detected!
}
}
} else {
if(depth==24) {
CHARVAR=bitmap_mask_color(spr1)>>16; //if the order is RGB, this will contain B...
SHORTVAR=bitmap_mask_color(spr1)&0xffff; //if the order is RGB, this will contain GR...
for(; dy1>=fy; dy1--,dy2--) { //Go through lines...
for(dx1=sx1,dx2=sx2; dx1>=fx; dx1--,dx2--) { //Go through the X axis...
if( (*((short *)(spr1->line[dy1]+(dx1)*3))!=SHORTVAR) && (spr1->line[dy1][(dx1)*3+2]!=CHARVAR) && (*((short *)(spr2->line[dy2]+(dx2)*3))!=SHORTVAR) && (spr2->line[dy2][(dx2)*3+2]!=CHARVAR) ) return 1; //Both sprites don't have transparent color in that position, so, theres a collision and return collision detected!
//I have tryed to avoid the above multiplications but it seems that GCC optimizes better than I :-))
}
}
}
}
}
}
//If we have reached here it means that theres not a collision:
return 0; //Return no collision.
}
//Finds the magnitude from a point in 2d.
double magnitude(int x, int y)
{
return sqrt((x * x) + (y* y));
}
char* itoa(int val, int base){
static char buf[32] = {0};
int i = 30;
for(; val && i ; --i, val /= base)
buf[i] = "0123456789abcdef"[val % base];
return &buf[i+1];
}
// static class that contain special game constants
class Constants
{
public:
static const double PI = 3.14159265358979323846;
static const double PIOVER4 = (3.14159265358979323846 / 4);
static const double TWOPI = (2 * 3.14159265358979323846);
static const double PIOVER2 = (3.14159265358979323846 / 2);
static const unsigned int MAXBULLETS = 5;
};
// Clamp
inline float clamp(float x, float min, float max)
{
return x < min ? min : (x > max ? max : x);
}
// The ship class
class Ship
{
public:
double X;
static const double Y = (272 - 64);
double angle;
void Init(int x)
{
angle = 0;
X = x;
}
void MoveLeft()
{
X -= 2;
}
void MoveRight()
{
X += 2;
}
void Draw(BITMAP* buffer, BITMAP* sprite, int frame)
{
X = clamp(X, 0, 480);
draw_sprite(buffer, sprite, X, Y);
}
};
class Missile
{
private:
static const double angle = (3.14159265358979323846 / 2);
public:
bool Alive;
static const int V = 5;
double X;
double Y;
void Init(bool alive, int x, int y)
{
Alive = alive;
X = x;
Y = y;
}
void Update()
{
X += V * cos(angle);
Y += V * sin(angle);
}
void Kill()
{
Alive = false;
}
void Draw(BITMAP* buffer, BITMAP* sprite)
{
draw_sprite(buffer, sprite, X, Y);
}
};
std::vector<Missile*>* bullets = (std::vector<Missile*>*)malloc(1);
void FireBullet(Ship* s)
{
if (bullets->size() < Constants::MAXBULLETS)
{
Missile* missile = (Missile*)malloc(1);
missile->Init(true, s->X, s->Y);
bullets->push_back(missile);
}
}
void CleanUp()
{
for(unsigned int index = 0; index < bullets->size(); index++)
{
if (bullets->at(index)->Alive == false)
{
bullets->erase(bullets->begin() + index);
}
}
}
void UpdateBullets()
{
for(unsigned int index = 0; index < bullets->size(); index++)
{
if (bullets->at(index)->Y < 0)
{
bullets->at(index)->Update();
}
else
{
bullets->at(index)->Kill();
}
}
}
void DrawBullets(BITMAP* buffer, BITMAP* sprite)
{
for(unsigned int index = 0; index < bullets->size(); index++)
{
if (bullets->at(index)->Alive == true)
{
bullets->at(index)->Draw(buffer, sprite);
}
}
}
//Entry point of the application
int main(void)
{
Ship* s = (Ship*)malloc(1);
int x = (WIDTH / 2) - 64;
allegro_message("Initialzing ship class");
s->Init(x);
int frame = 0;
BITMAP* buffer = NULL;
BITMAP* background = NULL;
BITMAP* ship = NULL;
SceCtrlData pad;
bool done = false;
allegro_message("Initializing Game...");
int rval = allegro_init();
if (allegro_init() != 0)
{
allegro_message("Error initializing Game Because it returned: %i", rval);
return 1;
}
allegro_message("Setting Graphics Mode...Press X To Begin Game");
set_color_depth(32);
int ret = set_gfx_mode(GFX_AUTODETECT,480,272,0,0);
if (ret != 0)
{
allegro_message("Error setting grahpic mode! Because of it returned: %i", ret);
return ret;
}
background = load_bmp("background.bmp", NULL);
ship = load_bmp("ship.bmp", NULL);
BITMAP* m = load_bmp("missile.bmp", NULL);
if (background == NULL || ship == NULL || m != NULL){
allegro_message("Couldn't load one or more sprites...");
return 0;
}
buffer = create_bitmap(WIDTH, HEIGHT);
if (buffer == NULL)
{
allegro_message("Couldn't create buffer!");
return 0;
}
int previousx = 0;
int previousy = 0;
while(!done)
{
sceCtrlReadBufferPositive(&pad, 1);
if (pad.Buttons & PSP_CTRL_START)
{
done = true;
}
else if (pad.Buttons & PSP_CTRL_CROSS)
{
FireBullet(s);
}
else if (pad.Buttons & PSP_CTRL_LEFT)
{
s->MoveLeft();
}
else if (pad.Buttons & PSP_CTRL_RIGHT)
{
s->MoveRight();
}
UpdateBullets();
CleanUp();
clear(buffer);
draw_sprite(buffer, background, 0, 0);
s->Draw(buffer, ship, frame);
DrawBullets(buffer, ship);
masked_blit(buffer, screen, 0, 0, 0, 0, WIDTH, HEIGHT);
if (frame == (60 * 10))
{
frame = 0;
}
frame++;
vsync();
previousx = pad.Lx;
previousy = pad.Ly;
}
allegro_message("Clearing resources!");
clear(buffer);
clear(ship);
clear(background);
clear(screen);
allegro_message("Thank you for playing!");
return 0;
}
END_OF_MAIN()
If this error cannot be fixed is there a work around. Like what can I use that doesn't use the new operator. I mean I use malloc for dynamically allocating everything but the vector template class is somehow using the new operator.
It seems like you are using gcc as a linker wrapper and not g++. Thus, standard C++ runtime is not linked in, and operator new could not be found. You have to either use g++ to link, or specify C++ runtime yourself, usually it is -lstdc++.