Compute objects moving with arrows and mouse - c++

I am doing a maya simulator application with openGL, everything is progressing pretty well. Just have only one trouble, i can not calculate 100% accuracy of moving object by mouse.
I'm using the scaling of arrows in 3D coordinates and arrows in the screen coordinates and multiplying it with some scaling to calculate the degree of motion in the x and y direction of the screen coordinates. .
But these calculations do not give high accuracy, the mouse is deviated from the arrow. I need it always on the moving arrows, meaning that the accuracy is almost 100%
Anyone who has ideas or comments on this issue will always be appreciated
This is some code i try:
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenVertex, &yScreenVertex, &zScreenVertex);
if (axis == 0)
{
gluProject((GLdouble)x3DVertex + 1, (GLdouble)y3DVertex, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else if (axis == 1)
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex + 1, (GLdouble)z3DVertex, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
else
{
gluProject((GLdouble)x3DVertex, (GLdouble)y3DVertex, (GLdouble)z3DVertex + 1, modelMatrix, projectMatrix, viewport, &xScreenArrow, &yScreenArrow, &zScreenArrow);
}
float totalScaleXY = abs(xScreenArrow - xScreenVertex) + abs(yScreenArrow - yScreenVertex);
scaleX = abs(xScreenArrow - xScreenVertex) / totalScaleXY;
scaleY = abs(yScreenArrow - yScreenVertex) / totalScaleXY;
float lengthArrowOnScreen = sqrt(pow((xScreenArrow - xScreenVertex), 2) + pow((yScreenArrow - yScreenVertex), 2));
scale3dAndScreen = 1 / lengthArrowOnScreen;
This is the code I'm testing, it's not very accurate

Here small ugly but very simple C++ example of what I had in mind for this:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double mview[16],mmodel[16],mproj[16]; // OpenGL matrices
//---------------------------------------------------------------------------
// vector/matrix math
//---------------------------------------------------------------------------
double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); } // = |vector[3]|
void vector_sub(double *c,double *a,double *b) { for (int i=0;i<3;i++) c[i]=a[i]-b[i]; } // c[3] = a[3] - b[3]
void matrix_mul(double *c,double *a,double *b) // c[4x4] = a[4x4] * b [4x4]
{
double q[16];
q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
for(int i=0;i<16;i++) c[i]=q[i];
}
void matrix_mul_vector(double *c,double *a,double *b) // c[3] = a[4x4] * (b[3],1)
{
double q[3];
q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
for(int i=0;i<3;i++) c[i]=q[i];
}
//---------------------------------------------------------------------------
class DragDrop3D // arrow translation controls (you need one for each objet)
{
public:
double *mm; // model matrix
double *mv; // view matrix
double *mp; // projection matrix
double o[3]; // start point
double l[3]; // length of the arrows
double a,b; // size of the arrow head a length, b width/2
int sel; // selected axis
DragDrop3D()
{
mm=NULL; mv=NULL; mp=NULL;
o[0]=-1.0; l[0]=3.5; a=0.5;
o[1]=-1.0; l[1]=3.5; b=0.25;
o[2]=-1.0; l[2]=3.5; sel=-1;
}
DragDrop3D(DragDrop3D& a) { *this=a; }
~DragDrop3D() {}
DragDrop3D* operator = (const DragDrop3D *a) { *this=*a; return this; }
//DragDrop3D* operator = (const DragDrop3D &a) { ...copy... return this; }
void project(double *scr,double *mmvp,double *obj) // obj -> scr
{
matrix_mul_vector(scr,mmvp,obj);
if (fabs(scr[2])>1e-10) scr[2]=1.0/scr[2]; else scr[2]=0.0;
scr[0]*=scr[2];
scr[1]*=scr[2];
}
void draw() // render arrows
{
int e;
double ang,dang=2.0*M_PI/36.0,u0,u1,v0,v1,q=1.0/sqrt((a*a)+(b*b));
// axis lines
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); glVertex3dv(o); glVertex3d(o[0]+l[0],o[1],o[2]);
glColor3f(0.0,1.0,0.0); glVertex3dv(o); glVertex3d(o[0],o[1]+l[1],o[2]);
glColor3f(0.0,0.0,1.0); glVertex3dv(o); glVertex3d(o[0],o[1],o[2]+l[2]);
glEnd();
// cones
glBegin(GL_TRIANGLES);
u1=b*cos(-dang);
v1=b*sin(-dang);
for (e=1,ang=0.0;e;ang+=dang)
{
if (ang>=2.0*M_PI) { ang=2.0*M_PI; e=0; }
u0=u1; u1=b*cos(ang);
v0=v1; v1=b*sin(ang);
// X
if (sel==0) glColor3f(1.0,0.0,0.0); else glColor3f(0.5,0.1,0.1);
glNormal3f(a*q,(u0+u1)*0.5*q,(v0+v1)*0.5*q);
glVertex3d(o[0]+l[0] ,o[1] ,o[2] );
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glNormal3f(-1.0,0.0,0.0);
glVertex3d(o[0]+l[0]-a,o[1],o[2]);
glVertex3d(o[0]+l[0]-a,o[1]+u1,o[2]+v1);
glVertex3d(o[0]+l[0]-a,o[1]+u0,o[2]+v0);
// Y
if (sel==1) glColor3f(0.0,1.0,0.0); else glColor3f(0.1,0.5,0.1);
glNormal3f((u0+u1)*0.5*q,a*q,(v0+v1)*0.5*q);
glVertex3d(o[0] ,o[1]+l[1] ,o[2] );
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glNormal3f(0.0,-1.0,0.0);
glVertex3d(o[0] ,o[1]+l[1]-a,o[2] );
glVertex3d(o[0]+u0,o[1]+l[1]-a,o[2]+v0);
glVertex3d(o[0]+u1,o[1]+l[1]-a,o[2]+v1);
// Z
if (sel==2) glColor3f(0.0,0.0,1.0); else glColor3f(0.1,0.1,0.5);
glNormal3f((v0+v1)*0.5*q,(u0+u1)*0.5*q,a*q);
glVertex3d(o[0] ,o[1] ,o[2]+l[2] );
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glNormal3f(0.0,0.0,-1.0);
glVertex3d(o[0] ,o[1] ,o[2]+l[2]-a);
glVertex3d(o[0]+v0,o[1]+u0,o[2]+l[2]-a);
glVertex3d(o[0]+v1,o[1]+u1,o[2]+l[2]-a);
}
glEnd();
}
bool mouse(double mx,double my,TShiftState sh) // handle mouse events return if redraw is needed
{
if ((mm==NULL)||(mv==NULL)||(mp==NULL)) return false;
int ql; double p[3],mmvp[16]; bool _redraw=false;
// MVP = M*V*P
matrix_mul(mmvp,mm,mv);
matrix_mul(mmvp,mmvp,mp);
// convert screen coords to <-1,+1> GL NDC
mx= (2.0*mx/double(xs))-1.0;
my=1.0-(2.0*my/double(ys)) ;
// mouse left button state (last,actual)
ql=sh.Contains(ssLeft);
// select (no mouse button)
if (!ql)
{
int sel0=sel; sel=-1;
// arrowhead center screen x,y distance to mouse select if close
p[0]=o[0]+l[0]-0.5*a; p[1]=o[1]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=0; // X
p[1]=o[1]+l[1]-0.5*a; p[0]=o[0]; p[2]=o[2]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=1; // Y
p[2]=o[2]+l[2]-0.5*a; p[1]=o[1]; p[0]=o[0]; project(p,mmvp,p); p[0]-=mx; p[1]-=my; p[2]=0.0; if (vector_len(p)<=0.5*b) sel=2; // Z
_redraw=(sel0!=sel);
}
// drag arrowhead center into mouse position (active button)
if ((ql)&&(sel>=0)&&(sel<3))
{
int i;
double len0,len;
double p0[3],dp[3]={0.0,0.0,0.0},t,dt,q[3]={mx,my,0.0};
// selected arrowhead position,direction
for (i=0;i<3;i++) p0[i]=o[i];
p0[sel]+=l[sel]-0.5*a;
dp[sel]=1.0;
// "closest" intersection between axis/mouse_ray
for (len0=-1.0,t=0.0,dt=1.0;fabs(dt)>1e-5;t+=dt)
{
// position on axis p(t) = p0 + t*dp
for (i=0;i<3;i++) p[i]=p0[i]+(t*dp[i]);
// len = distance to mouse
project(p,mmvp,p);
vector_sub(p,p,q); p[2]=0.0;
len=vector_len(p);
// handle iteration step
if (len0<-0.5) len0=len;
if (len>len0) dt=-0.1*dt;
len0=len;
}
// translate by t
double m[16]=
{
1.0,0.0,0.0,0.0,
0.0,1.0,0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0,
};
m[12+sel]=t;
matrix_mul(mm,m,mm);
_redraw=true;
}
return _redraw;
}
};
//---------------------------------------------------------------------------
DragDrop3D ctrl; // I got single cube so single arrow drag drop control suffice
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
// init
static bool _init=true;
if (_init)
{
_init=false;
// M,V init matrices
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslated(0.0,0.0,-25.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glLoadIdentity();
glTranslated(-2.0,-1.0,-1.0);
glRotated(-35.0,0.0,1.0,0.0);
glGetDoublev(GL_MODELVIEW_MATRIX,mmodel);
glPopMatrix();
// matrices -> ctrl
glGetDoublev(GL_PROJECTION_MATRIX,mproj);
ctrl.mp=mproj;
ctrl.mv=mview;
ctrl.mm=mmodel;
}
// matrices -> OpenGL
glMatrixMode(GL_MODELVIEW);
glLoadMatrixd(mview);
glMultMatrixd(mmodel);
// draw VAO cube
ctrl.draw();
vao_draw(); // here render your object instead
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
vao_init();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
vao_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// window repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheel(TObject *Sender, TShiftState Shift, int WheelDelta, TPoint &MousePos, bool &Handled)
{
// mouse wheel translates camera (like zoom)
GLfloat dz=2.0;
if (WheelDelta<0) dz=-dz;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixd(mview);
glTranslatef(0,0,dz);
glGetDoublev(GL_MODELVIEW_MATRIX,mview);
glPopMatrix();
gl_draw();
}
//---------------------------------------------------------------------------
// mouse events
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button,TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { if (ctrl.mouse(X,Y,Shift)) gl_draw(); }
//---------------------------------------------------------------------------
It is based on this: simple complete GL+VAO/VBO+GLSL+shaders example in C++ where you will find more info and the gl_simple.h I am using for this. The code is VCL based so ignore the VCL stuff and port the events to your kind of programming.
The program is using just OpenGL 1.0 API (appart of the VAO/VBO cube representing my object) so it does not need anything apart math.h for sin,cos,fabs functions.
The idea is to have one control DragDrop3D object per each controlable object in the world/scene. Init each first and then call mouse for each on every mouse event and redraw if needed.
Here small preview:
Sadly my GIF capture SW does not catch mouse cursor but it matches the movement exactly.
I was too lazy to compute the intersection between axises directly instead I iterate to find closest match (that for loop with dt). That part could be replaced by intersection equation.
The axises are bounded to the x,y,z axises of the object coordinate system (mm,mmodel).
For more objects you should also add locks so only one object can be selected ...
PS. see related:
- OpenGL ray OBB intersection

Related

Unknown type name "Shape1" in C++ Builder

I'm trying to make a Tetris clone in C++ Builder. I started by making 2 classes. I have wrote them in a .h file and included it in my .cpp file:
class Stage
{
protected:
int Width;
int Height;
public:
Stage()
{
Width=300;
Height=733;
}
Stage(int Width0,int Height0)
{
Width=Width0;
Height=Height0;
}
};
This is my first class, Stage. I don't know for sure what this class should contain, so if you have any suggestions on what I should include in it, I'm glad to hear them.
class Tetromino : public Stage
{
protected:
Tshape *Shape1;
public:
Tetromino(Tshape *Shape1)
{
TetrisGame->Shape1->Width=33;
TetrisGame->Shape1->Height=33;
TetrisGame->Shape1->Brush->Color=clBlack;
TetrisGame->Shape1->Left=10;
TetrisGame->Shape1->Top=200;
}
void mLeft()
{
if(TetrisGame->Shape1->Left<=Widht)
TetrisGame->Shape1->Left=TetrisGame->Shape1->Left+33;
}
void mRight()
{
if(TetrisGame->Shape1->Left>=Width)
TetrisGame->Shape1->Left=TetrisGame->Shape1->Left-33;
}
void free_fall()
{
TetrisGame->PieceFallTime->Enabled=true;
}
};
That's my second class, the class for pieces.
I know that that's not how it should work. I have to make a matrix for example let's say:
piece[4][4]=
{(0,0,0,0),
(0,0,0,0),
(0,1,0,0),
(1,1,1,0)}
but I still have no clue how I should make use of it. I think that I should put a shape where there is 1 and it should create my piece, but I don't know how to do that.
Right now I'm trying to figure out how to make a shape fall down and move it right or left with the keyboard. I did this before, but with no classes. It's a bit more complicated using classes and I don't understand.
I tried to initialize an object Tetromino x(Shape1) (the code is in form.cpp) having a Shape1 in my form. I'm thinking that this should force the Shape1 in my Form to use the methods that I wrote in my class, right?
But I'm getting this error:
[bcc32c Error] StartGame.cpp(110): unknown type name 'Shape1'
You forgot to use new for the bitmap in the constructor so you are loading bmp file into bitmap objects that is not yet created hence the access violation. You can use transparency for the tetrominos bitmaps ... you just need to activate it on target canvas (like your Form1 ..within its properties). However I would render my tetrominos directly into bitmap instead of using files... You can use bitmap as a screen buffer ...
Here simple BDS2006 C++/VCL Tetris example I coded with my students during one lecture years ago as they wanted to code a game and voted for tetris ... (its ugly but works ...)
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
// window
TForm1 *Form1;
// keyboard map
enum{
_key_left =0,
_key_right,
_key_rot ,
_key_drop ,
_keys
};
int keys [_keys]; // is key pressed ?
WORD keys_code[_keys]; // scan code
// back/double buffer
Graphics::TBitmap *bmp = new Graphics::TBitmap;
// scoore, position of falling tetromino,next shape
int score=0,px=0,py=0,novy_dielik=0;
// actual tetromino map
BYTE pm[4][4]=
{
0,0,1,0,
0,1,1,0,
0,0,1,0,
0,0,1,0
};
// game board size
const int map_xs=10;
const int map_ys=20;
BYTE map[map_xs][map_ys];
// tetrominos
BYTE shp[7*16]=
{
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,1,0,
0,0,0,0,
0,0,1,0,
0,0,1,0,
0,1,1,0,
0,0,0,0,
0,1,0,0,
0,1,1,0,
0,0,1,0,
0,0,0,0,
0,0,1,0,
0,1,1,0,
0,1,0,0,
0,0,0,0,
0,0,0,0,
0,0,1,0,
0,1,1,1,
0,0,0,0,
0,0,0,0,
0,1,1,0,
0,1,1,0,
0,0,0,0
};
//---------------------------------------------------------------------------
// tetromino colision test
//---------------------------------------------------------------------------
int step()
{
int i,j,x,y;
for (i=0;i<4;i++)
for (j=0;j<4;j++)
if (pm[i][j]!=0)
{
x=px+j;
y=py+i;
if (x<0) return 1;
if (x>=map_xs) return 1;
if (y<0) return 1;
if (y>=map_ys) return 1;
if (map[x][y]!=0) return 1;
}
return 0;
}
//---------------------------------------------------------------------------
// render game
//---------------------------------------------------------------------------
void draw()
{
// clear screen
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,bmp->Width,bmp->Height));
// [game board]
int x,y,a,i,j,adr;
x=bmp->Width/(map_xs+4+3);
y=bmp->Height/(map_ys+2);
if (x<y) a=x; else a=y;
// border
x=a; y=a;
bmp->Canvas->Pen->Color=clBlue;
bmp->Canvas->Pen->Width=a>>1;
bmp->Canvas->Rectangle(x,y,x+(map_xs*a),y+(map_ys*a));
bmp->Canvas->Pen->Width=1;
// game board
for (j=0;j<map_ys;j++)
{
for (i=0;i<map_xs;i++)
{
// set color from map
if (map[i][j]==0) bmp->Canvas->Brush->Color=clBlack;
else bmp->Canvas->Brush->Color=clAqua;
// grid cell
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
x+=a; // posun sa o policko vpravo
}
x-=map_xs*a; // vrat sa vlavo
y+=a; // posun sa o policko dole
}
y-=map_ys*a; // vrat sa hore
// falling tetromino
x+=px*a;
y+=py*a;
for (j=0;j<4;j++)
{
for (i=0;i<4;i++)
{
// cell
if (pm[j][i]!=0)
{
bmp->Canvas->Brush->Color=clGreen;
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
}
x+=a; // posun sa o policko vpravo
}
x-=4*a; // vrat sa vlavo
y+=a; // posun sa o policko dole
}
y-=4*a; // vrat sa hore
x-=px*a;
y-=py*a;
x+=(map_xs+1)*a; // posun sa na pravy stlpec
// score:
bmp->Canvas->Font->Color=clWhite;
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->TextOutA(x,y,AnsiString().sprintf("Score: %i",score));
y+=a+a;
// border
bmp->Canvas->Pen->Color=clBlue;
bmp->Canvas->Pen->Width=a>>1;
bmp->Canvas->Rectangle(x,y,x+(4*a),y+(4*a));
bmp->Canvas->Pen->Width=1;
adr=16*novy_dielik;
for (j=0;j<4;j++)
{
for (i=0;i<4;i++)
{
// set color from map
if (shp[adr]==0) bmp->Canvas->Brush->Color=clBlack;
else bmp->Canvas->Brush->Color=clAqua;
// nakresli stvorcek
bmp->Canvas->FillRect(TRect(x,y,x+a,y+a));
x+=a; // go right
adr++;
}
x-=4*a; // return left
y+=a; // go down
}
y-=4*a; // return up
Form1->Canvas->Draw(0,0,bmp);
}
//--- konstruktor okna ------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
keys_code[_key_left ]='O';
keys_code[_key_right]='P';
keys_code[_key_rot ]='Q';
keys_code[_key_drop ]='A';
// new game
novy_dielik=rand()%7; // new tetromino
px=(map_xs>>1)-2; // position top/middle
py=0;
int adr=16*novy_dielik;
for (int j=0;j<4;j++) // copy it to pm
for (int i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
for (int j=0;j<map_ys;j++) // clear map
for (int i=0;i<map_xs;i++)
map[i][j]=0;
novy_dielik=rand()%7;
}
//--- resize event ----------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
bmp->Width=ClientWidth;
bmp->Height=ClientHeight;
draw(); // force redraw
}
//--- redraw event ----------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
draw();
}
//--- falling timer ---------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
py++;
if (step()) // tetromino hit the ground?
{
py--;
int i,j,x,y,adr,e; // copy it to map
for (j=0;j<4;j++)
for (i=0;i<4;i++)
if (pm[j][i])
{
x=px+i;
y=py+j;
map[x][y]=1;
}
px=(map_xs>>1)-2; // new tetromino top/middle position
py=0;
adr=16*novy_dielik;
for (j=0;j<4;j++) // copy it to pm
for (i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
// map full line test
int dscore=0;
for (j=map_ys-1;j>=0;j--)
{
e=1;
for (i=0;i<map_xs;i++)
if (map[i][j]==0)
{
e=0;
break;
}
if (e)
{
dscore<<=1;
dscore+=100;
e=j;
for (j=e-1;j>=0;j--)
for (i=0;i<map_xs;i++)
map[i][j+1]=map[i][j];
j=e+1;
draw();
Sleep(200);
}
}
score+=dscore;
if (step()) // end game? (no new tetromino)
{
int q;
q=bmp->Canvas->Font->Height;
bmp->Canvas->Font->Height=40;
bmp->Canvas->Font->Color=clRed;
bmp->Canvas->Brush->Style=bsClear;
AnsiString s="Game over";
bmp->Canvas->TextOutA(
(bmp->Width-bmp->Canvas->TextWidth(s))>>1,
(bmp->Height-bmp->Canvas->TextHeight(s))>>1,s);
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->Font->Height=q;
Form1->Canvas->Draw(0,0,bmp);
Sleep(1000);
// new game
novy_dielik=rand()%7; // new tetromino
px=(map_xs>>1)-2; // top/middle
py=0;
int adr=16*novy_dielik;
for (int j=0;j<4;j++) // copy it to pm
for (int i=0;i<4;i++)
{
pm[j][i]=shp[adr];
adr++;
}
for (int j=0;j<map_ys;j++) // clear board
for (int i=0;i<map_xs;i++)
map[i][j]=0;
novy_dielik=rand()%7;
}
novy_dielik=rand()%7; // random next tetromino 0-6
}
draw();
}
//--- keyboard timer --------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
int e=0;
if (keys[_key_left ]!=0)
{
px--; if (step()) px++; else e=1;
}
if (keys[_key_right]!=0)
{
px++; if (step()) px--; else e=1;
}
if (keys[_key_rot ]!=0)
{
int i,j;
BYTE ori[4][4];
for (j=0;j<4;j++) for (i=0;i<4;i++) ori[i][j]=pm[i][j];
for (j=0;j<4;j++) for (i=0;i<4;i++) pm[3-j][i]=ori[i][j];
if (step()) for (j=0;j<4;j++) for (i=0;i<4;i++) pm[i][j]=ori[i][j];
else e=1;
}
if (keys[_key_drop ]!=0)
{
py++; if (step()) py--; else e=1;
}
if (e) draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
Caption=Key;
for (int i=0;i<_keys;i++) // test all keys
if (Key==keys_code[i]) // if match
keys[i]=1; // set it as pressed
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
{
for (int i=0;i<_keys;i++) // test all keys
if (Key==keys_code[i]) // if match
keys[i]=0; // set it as released
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormActivate(TObject *Sender)
{
for (int i=0;i<_keys;i++) // set all keys
keys[i]=0; // as released
}
//---------------------------------------------------------------------------
---------------------------------------
Its a single Form App with 2 timers on it
Timer1 is 250ms
Timer2 is 80ms
If you want to use mine you have to:
create empty form App
place 2 timers on it and set the intervals
create all events my code is using
copy the event and game codes to correct places
But you should use this more like as hint how the game architecture could look like. I know its ugly and without classes (those students where not yet OOP capable and we coded the game together so some consensus on all parts where made with the aim to keep this simple...)
So the bmp is like screen back buffer (to avoid flickering) controlling is done by O P Q A keys. One timer control the falling animation/simulation and the other handles keyboard. Rendering is done by using VCL encapsulated GDI and the bmp ...
for more info see:
Graphics rendering

SFML: Projectile stops

I am very new to SFML and C++. My problem is that I successfully fire projectiles towards the mouse. However as I move the mouse the projectiles also move.
EDIT:
Okay I solved my previous problem. However the projectile stops while it reaches the mouse position. How can the bullets continue to go further than its destination?
Code:
if (isFiring == true) {
sf::Vector2f startPos = sf::Vector2f(myPipe.getX(), myPipe.getY());
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
sf::Vector2f endPos = sf::Vector2f(mousePos.x, mousePos.y);
double projSpeed{0.3};
//float projSpeed = projSpeed * (myPipe.getY() - mousePos.y) / 100;
Projectile newProjectile(20, startPos, endPos, projSpeed);
projVec.push_back(newProjectile);
isFiring = false;
}
for (int i = 0; i < projVec.size(); i++) {
projVec[i].fire(delta.asMilliseconds());
projVec[i].draw(window);
clock.restart();
}
fire function:
void fire( float delta) {
sf::Vector2f pos = projectile.getPosition();
float veloX = (_end.x - pos.x);
float veloY = (_end.y - pos.y);
float distance = sqrt((veloX*veloX + veloY * veloY));
float dx = veloX / distance;
float dy = veloY / distance;
projectile.move(dx * delta, dy * delta);
}
One more question, am I doing it correct with multiplying with delta? The bullets are really laggy and weird.
(Original reply:)
I don't have enough reputation to comment so I'll have to put it here...
So if I understand correctly you're saying you want to shoot the projectiles towards the mouse's position but don't want them to follow its position once fired? In this case:
I see you have the code to draw the projectiles inside that for-loop, which leads me to believe you are calling the loop continuously. But the same loop also contains the code used to shoot the projectiles, which means that you are also continuously calling this "fire" code. So you are steering the projectiles towards the current mouse position in every loop iteration which means they will always follow the current mouse position.
What you should be doing is move the code to shoot the projectiles and only call it once (you can only shoot a projectile once, right?) so a projectile's directional vector is not constantly altered, resulting in the problem you stated.
And make sure to always separate logic and rendering. Ideally you should be updating the physics (for example the projectile updating) at a fixed interval so that the game will run the same on all machines, and draw all your stuff separately.
If you do want the projectiles to always follow the mouse, you can use this (semi pseudo) code (I'm using this code in one of my games):
sf::Vector2f normalizedVector = normalize(mousePos - projectilePos);
normalizedVector.x *= speed * deltaTime;
normalizedVector.y *= speed * deltaTime;
projectile.move(normalizedVector);
(Edit:)
I made a quick example project that works. This doesn't use all of your code but hopefully gives you an idea how it can be done.
Here's a short video that shows what the code below does: https://webmshare.com/play/jQqvd
main.cpp
#include <SFML/Graphics.hpp>
#include "projectile.h"
int main() {
const sf::FloatRect viewRect(0.0f, 0.0f, 800.0f, 600.0f);
sf::RenderWindow window(sf::VideoMode(viewRect.width, viewRect.height), "Test");
window.setFramerateLimit(120);
window.setVerticalSyncEnabled(false);
window.setKeyRepeatEnabled(false);
sf::Clock deltaClock;
const sf::Time timePerFrame = sf::seconds(1.0f / 60.0f);
sf::Time timeSinceLastUpdate = sf::Time::Zero;
std::vector<Projectile> projectiles;
while (window.isOpen()) {
timeSinceLastUpdate += deltaClock.restart();
// process events
{
sf::Event evt;
while (window.pollEvent(evt)) {
switch (evt.type) {
case sf::Event::Closed: { window.close(); } break;
// shoot with left mouse button
case sf::Event::MouseButtonPressed: { switch (evt.mouseButton.button) { case sf::Mouse::Button::Left: {
const sf::Vector2f center(viewRect.left + viewRect.width / 2, viewRect.top + viewRect.height / 2);
const sf::Vector2f mousePos(window.mapPixelToCoords(sf::Mouse::getPosition(window)));
const float angle = atan2(mousePos.y - center.y, mousePos.x - center.x);
projectiles.push_back(Projectile(center, angle));
} break; default: {} break; } } break;
default: {} break;
}
}
}
// update
{
while (timeSinceLastUpdate > timePerFrame) {
timeSinceLastUpdate -= timePerFrame;
// update projectiles
{
for (std::size_t i = 0; i < projectiles.size(); ++i) {
Projectile &proj = projectiles[i];
proj.update(timePerFrame);
if (!viewRect.intersects(proj.getBoundingBox())) { proj.destroy(); }
}
projectiles.erase(std::remove_if(projectiles.begin(), projectiles.end(), [](Projectile const &p) { return p.getCanBeRemoved(); }), projectiles.end());
}
}
}
// render
{
window.clear();
for (std::size_t i = 0; i < projectiles.size(); ++i) {
window.draw(projectiles[i]);
}
window.display();
}
}
return EXIT_SUCCESS;
}
projectile.h:
#ifndef PROJECTILE_H_INCLUDED
#define PROJECTILE_H_INCLUDED
#include <SFML/Graphics.hpp>
class Projectile : public sf::Drawable {
public:
Projectile();
Projectile(const sf::Vector2f pos, const float angle);
virtual ~Projectile();
const bool &getCanBeRemoved() const;
const sf::FloatRect &getBoundingBox() const;
void destroy();
void update(const sf::Time dt);
private:
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
bool canBeRemoved_;
sf::FloatRect boundingBox_;
float angle_;
float speed_;
sf::RectangleShape shape_;
};
#endif
projectile.cpp:
#include "projectile.h"
Projectile::Projectile() :
canBeRemoved_(true),
boundingBox_(sf::FloatRect()),
angle_(0.0f),
speed_(0.0f)
{
}
Projectile::Projectile(const sf::Vector2f pos, const float angle) {
canBeRemoved_ = false;
boundingBox_ = sf::FloatRect(pos, sf::Vector2f(10.0f, 10.0f));
angle_ = angle;
speed_ = 0.5f;
shape_.setPosition(sf::Vector2f(boundingBox_.left, boundingBox_.top));
shape_.setSize(sf::Vector2f(boundingBox_.width, boundingBox_.height));
shape_.setFillColor(sf::Color(255, 255, 255));
}
Projectile::~Projectile() {
}
const bool &Projectile::getCanBeRemoved() const {
return canBeRemoved_;
}
const sf::FloatRect &Projectile::getBoundingBox() const {
return boundingBox_;
}
void Projectile::destroy() {
canBeRemoved_ = true;
}
void Projectile::update(const sf::Time dt) {
boundingBox_.left += static_cast<float>(std::cos(angle_) * speed_ * dt.asMilliseconds());
boundingBox_.top += static_cast<float>(std::sin(angle_) * speed_ * dt.asMilliseconds());
shape_.setPosition(boundingBox_.left, boundingBox_.top);
}
void Projectile::draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const {
renderTarget.draw(shape_);
}

Drawing Multiple Lines with Mouse in OpenGL

I'm trying to draw multiple line segments with my mouse in my OpenGL and C++ program. Right now I can draw one, and once I start drawing another one, the previous one disappears.
Below is my code that is relevant to the mouse drawing. Any suggestions on how I can draw multiple lines?
LineSegment seg;
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) { // if left button is clicked, that is the start point
seg.x1 = seg.x2 = x;
seg.y1 = seg.y2 = y;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) { // once they lift the mouse, thats the finish point
seg.x2 = x;
seg.y2 = y;
}
}
void motion(int x, int y) {
seg.x2 = x;
seg.y2 = y;
glutPostRedisplay(); // refresh the screen showing the motion of the line
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glBegin(GL_LINES); // draw lines
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
glEnd();
glutSwapBuffers();
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT); // clear the screen
glBegin(GL_LINES); // draw lines
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
glEnd();
glutSwapBuffers();
}
You need to save in a data structure the previous line segments and add to it whenever you click with the mouse. Then the drawloop needs to iterate through that data structure and draw each of the saved line segments.
std::vector<LineSegment> segmentvector;
//Each time you release the mouse button, add the current line segment to this vector
/*...*/
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
for(const LineSegment & seg : segmentvector) {
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
}
glVertex2f(currseg.x1, currseg.y1);
glVertex2f(currseg.x2, currseg.y2);
glEnd();
I would also STRONGLY ADVISE that you not use Fixed-Function-Pipeline functions when learning OpenGL. There are lots of tutorials online for learning modern OpenGL.
You need to set up some logic to save the state of the lines you have already drawn. Currently, you never start drawing another line, you just reset the start position of the current line.
Here is a possible solution for what you're looking for:
std::vector<LineSegment> segs;
LineSegment currentSeg;
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
LineSegment newSeg; // Make a new segment
newSeg.x1 = newSeg.x2 = x;
newSeg.y1 = newSeg.y2 = y;
segs.insert(newSeg); // Insert segment into segment vector
currentSeg = newSeg;
}
if (button == GLUT_LEFT_BUTTON && state == GLUT_UP) {
currentSeg.x2 = x;
currentSeg.y2 = y;
}
}
void motion(int x, int y) {
currentSeg.x2 = x;
currentSeg.y2 = y;
glutPostRedisplay();
}
void display(void) {
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_LINES);
// Iterate through segments in your vector and draw each
for(const auto& seg : segs) {
glVertex2f(seg.x1, seg.y1);
glVertex2f(seg.x2, seg.y2);
}
glEnd();
glutSwapBuffers();
}

3D / FPS Camera Issues - SDL2

I got a problem with my code, I'm trying to make a First Person 3D Camera.
I use SDL_GetKeyboardState(NULL) to get the pressed keys.
When I press one of the defined keys nothing happens, why?
Camera (Controll):
void Control(float movevel, float mousevel, bool mi, SDL_Window* window)
{
if (mi) //if the mouse is in the screen
{
int MidX = 640 / 2; //middle of the screen
int MidY = 480 / 2;
SDL_ShowCursor(SDL_DISABLE); //we don't show the cursor
int tmpx, tmpy;
SDL_GetMouseState(&tmpx, &tmpy); //get the current position of the cursor
camYaw += mousevel*(MidX - tmpx); //get the rotation, for example, if the mouse current position is 315, than 5*0.2, this is for Y
camPitch += mousevel*(MidY - tmpy); //this is for X
lockCamera();
//SDL_WarpMouse(MidX, MidY); //move back the cursor to the center of the screen
SDL_WarpMouseInWindow(window, MidX, MidY);
const Uint8* kstate = SDL_GetKeyboardState(NULL);
if (kstate[SDLK_w])
{
if (camPitch != 90 && camPitch != -90)
{ //if we are facing directly up or down, we don't go forward, it will be commented out, when there will be gravity
moveCamera(movevel, 0.0); //move forward
}
moveCameraUp(movevel, 0.0); //move up/down
}
if (kstate[SDLK_s])
{
//same, just we use 180 degrees, so we move at the different direction (move back)
if (camPitch != 90 && camPitch != -90)
{
moveCamera(movevel, 180.0);
}
moveCameraUp(movevel, 180.0);
}
if (kstate[SDLK_a])
{ //move left
moveCamera(movevel, 90.0);
}
if (kstate[SDLK_d])
{ //move right
moveCamera(movevel, 270);
}
}
glRotatef(-camPitch, 1.0, 0.0, 0.0);
glRotatef(-camYaw, 0.0, 1.0, 0.0);
}
Main (Loop)
while (running)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
SDL_Event ev;
while (SDL_PollEvent(&ev))
{
switch (ev.type)
{
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
int x, y;
SDL_GetMouseState(&x, &y);
Main::handleKeys(ev.key.keysym.scancode, x, y);
break;
}
}
Main::update(window);
Main::render(window);
SDL_GL_SwapWindow(window);
}
Main (Update):
void Main::update(SDL_Window* window)
{
Control(0.2, 0.2, mousein, window);
UpdateCamera(0.2); //move the camera to the new location
}
you should call the glRotatef functions before you translate the camera, otherwise it will be rotated about the origin and not its position
Use SDL_PumpEvents() to update the state array. (From the SDL_GetKeyboardState() wiki).
I believe that if you do:
SDL_PumpEvents();
SDL_GetKeyboardState(NULL);
you should get the results you are looking for. (You need to SDL_PumpEvents every loop as well as SDL_GetKeyboardState obviously)
(EDIT)
Another option:
case SDL_KEYDOWN:
switch(event.key.keysym.sym){
case SDLK_w
(... code)
break;
case SDLK_s:
(... code)
break;
case SDLK_a:
(... code)
break;
case SDLK_d:
(... code)
break;
You can also send the event.key.keysym.sym to a function and use it their ofcourse.

Box2d SDL cannot apply world setting on a dynamic body

My dynamic body is not falling down after setting the world. It is not following the gravity. Even I apply force on the body it isn't work.
b2Vec2 gravity(0.0f, -9.8f);
b2World world(gravity);
//PineCone
class PineCone
{
private:
//Surface box
SDL_Rect box;
//Real position
float rX, rY;
//Velocity
float xVel, yVel;
//Physics world
b2World* world;
//Body
b2Body* pBodyPineCone;
public:
//Specify
void specify(int w, int h, SDL_Surface* source, SDL_Surface* dest, b2World* world);
//Input
void handle_input();
//Motion
void updatepos();
//void checkStanding(SDL_Rect env[], int N_SOLIDS);
//Render
void show();
};
void PineCone::specify(int w, int h, SDL_Surface* source, SDL_Surface* dest, b2World* world)
{
//Source
pPineCone = source;
//Destination
pScreen = dest;
// Define the dynamic pinecone. We set its position and call the body factory.
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
// Define a ball shape for our dynamic body.
b2CircleShape dynamicPineCone;
dynamicPineCone.m_p.Set(0, 0); //position, relative to body position
dynamicPineCone.m_radius = 0.5; //radius
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicPineCone;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
// Add the shape to the body.
bodyDef.position.Set(0, 20);
pBodyPineCone = world->CreateBody(&bodyDef);
pBodyPineCone->CreateFixture(&fixtureDef);
}
void PineCone::show()
{
int blit = SDL_BlitSurface( pPineCone, NULL, pScreen, &box );
if( blit < 0 )
{
exit(1);
}
}
I am not sure if I missed something in Main.
int main(int argc, char *argv[])
{
B2_NOT_USED(argc);
B2_NOT_USED(argv);
// Prepare for simulation. Typically we use a time step of 1/60 of a
// second (60Hz) and 10 iterations. This provides a high quality simulation
// in most game scenarios.
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 6;
int32 positionIterations = 2;
// Start the timer
g_dwStartTicks = SDL_GetTicks();
init();
load_files();
//PineCone
PineCone myPineCone;
myPineCone.specify(g_iPineConeSize, g_iPineConeSize, pPineCone, pScreen, &world);
//
// The Main Game Loop
//
do
{
// Record the current time
g_dwStartTime = SDL_GetTicks();
//
// Handle they keyboard events here
//
while (SDL_PollEvent(&event) > 0)
{
if (event.type == SDL_QUIT)
{
// Quit event! (Window close, kill signal, etc.)
g_iLoopDone = TRUE;
}
}
world.Step(timeStep, velocityIterations, positionIterations);
myPineCone.handle_input();
if (g_bPlaying || g_bEnd)
{
//Show pinecone
myPineCone.show();
}
// Update the display
SDL_Flip(pScreen);
g_dwEndTime = SDL_GetTicks();
if (g_dwEndTime < g_dwStartTime + (1000 / 60))
{
SDL_Delay(g_dwStartTime + (1000 / 60) - g_dwEndTime);
}
}while (!g_iLoopDone);
//Clean Up
clean_up();
return 0;
}
I can think for example that the physical self is updating're not visualizing
what the Sprite (Image) not update the corresponding value of its physical body.
Try to give it a Box2D body position if the image does not move you do not
have to be the update of physics.
Try this:
void setL1b2SpritePosition(Point position)
{
pBodyPineCone->SetAwake (true);
pBodyPineCone->setPosition(position);
//update box2d
pBodyPineCone->SetTransform(
b2Vec2(position.x / PTM_RATIO, position.y/ PTM_RATIO),
_body->GetAngle());
}
void updatePhysics()
{
_world->Step(.1, 10, 10);
for (b2Body *b = _world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
Sprite *ballData = dynamic_cast<Sprite*> ((Sprite *) b->GetUserData());
ballData->setPosition(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
ballData->setRotation(-1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
}
}
}