I am creating a LOGO Turtle-like object in C++ using the CImg library. When attempting to draw a circle, defined as the commands repeat 360[fd 1 rt1] which loops 360 times, moving forward 1 pixel and turning right 1 degree each iteration. With my code though I am getting a more octagonal shape rather than an actual circle.
Code for moving forward:
void turtle::fd(int distance)
{
int endx, endy;
endx = posx-(int)round((distance*sin(heading * PI / 180)));
endy = posy-(int)round((distance*cos(heading * PI / 180)));
if(pen)
window->draw_line(posx, posy, endx, endy, color1, 1);
posx=endx;
posy=endy;
}
In CImg, draw_line() will draw a line from position posx posy to endx endy, the rest is color and opacity.
And for turning right:
void turtle::rt(int degree)
{
heading -= degree;
if(heading < 0)
heading = (360-abs(heading));
}
Related
I'm trying to move a bullet object towards a player position. I found this angle function online, and it seems to convert it to those coordinates in the comments. But i can't make the bullet follow in the direction.
float Angle(int p1x, int p1y, int p2x,int p2y)
{
//Make point1 the origin, make point2 relative to the origin so we do point1 - point1, and point2-point1,
//since we dont need point1 for the equation to work, the equation works correctly with the origin 0,0.
int deltaY = p2y - p1y;
int deltaX = p2x - p1x; //Vector 2 is now relative to origin, the angle is the same, we have just transformed it to use the origin.
float angleInDegrees = atan2(deltaY, deltaX) * 180 / PI;
//float angleInRadians = atan2(deltaY, deltaX);
angleInDegrees *= -1; // Y axis is inverted in computer windows, Y goes down, so invert the angle.
//Angle returned as:
// 90
// 135 45
//
// 180 Origin 0
//
// -135 -45
//
// -90
return angleInDegrees;
}
if (bulletsData[3] == STDEACTIVE){ //bulletData: 0 = x, 1 = y, 2 = dir, 3 = state
bulletsData[2] = Angle(bulletsData[0],bulletsData[1], plData[0], plData[1]);
bulletsData[3] = STACTIVE;
}
if (bulletsData[3] == STACTIVE){
if (ardu.everyXFrames(1)){
bulletsData[0] += cos(bulletsData[2]) * 1; //My attempt to move it towards the direction.
bulletsData[1] += sin(bulletsData[2]) * 1;
}
}
You are explicitly calculating angles in degrees, yet sin and cos expect radians, not degrees.
Without analyzing whether or not the Angle function is correct, your code will make more sense if you change the function to return a value in radians. You even have the required line there commented out.
So, something like this perhaps:
float angleInRadians = atan2(deltaY, deltaX);
return -angleInRadians; // Return negative angle to compensate for Y-down
This code draws a sine wave with function. In the following panning/zooming code, I am trying to understand how fWorldPerScreenWidthPixel is being used to draw the line segments.
WorldToScreen(fWorldLeft - fWorldPerScreenWidthPixel, -function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f, opx, opy);
It is setting opx and opy, but why is it subtracted from: fWorldLeft
It seems strange to want to start left of fWorldLeft in the for loop where it draws the line. fWorldLeft starts at -25.
I have included the necessary code to explain:
// Draw Chart
float fWorldPerScreenWidthPixel = (fWorldRight - fWorldLeft) / ScreenWidth();
float fWorldPerScreenHeightPixel = (fWorldBottom - fWorldTop) / ScreenHeight();
int px, py, opx = 0, opy = 0;
WorldToScreen(fWorldLeft - fWorldPerScreenWidthPixel, -function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f, opx, opy);
for (float x = fWorldLeft; x < fWorldRight; x += fWorldPerScreenWidthPixel)
{
float y = -function(x - 5.0f) + 5.0f;
WorldToScreen(x, y, px, py);
DrawLine(opx, opy, px, py, PIXEL_SOLID, FG_GREEN);
opx = px;
opy = py;
}
Call to set fWorldLeft:
// Clip
float fWorldLeft, fWorldTop, fWorldRight, fWorldBottom;
ScreenToWorld(0, 0, fWorldLeft, fWorldTop);
Sets fWorldleft:
// Convert coordinates from Screen Space --> World Space
void ScreenToWorld(int nScreenX, int nScreenY, float &fWorldX, float &fWorldY)
{
fWorldX = ((float)nScreenX / fScaleX) + fOffsetX;
fWorldY = ((float)nScreenY / fScaleY) + fOffsetY;
}
and while I'm at it, World to Screen:
// Convert coordinates from World Space --> Screen Space
void WorldToScreen(float fWorldX, float fWorldY, int &nScreenX, int &nScreenY)
{
nScreenX = (int)((fWorldX - fOffsetX) * fScaleX);
nScreenY = (int)((fWorldY - fOffsetY) * fScaleY);
}
Thank you!
Josh
Let's break it down
WorldToScreen(
fWorldLeft - fWorldPerScreenWidthPixel,
-function((fWorldLeft - fWorldPerScreenWidthPixel) - 5.0f) + 5.0f,
opx, opy);
A clearer way to write that would be
x = fWorldLeft - fWorldPerScreenWidthPixel;
WorldToScreen(
x,
-function((x) - 5.0f) + 5.0f,
opx, opy);
This transforms the position (x, f(x)) from world space to screen space and stores the result in (opx, opy). Let's see how these two variables are used:
for(...)
{
...
DrawLine(opx, opy, px, py, PIXEL_SOLID, FG_GREEN);
...
}
This draws a line from (opx, opy) to (px, py) (which is the current point on the function. (opx, opy) is the old point on the function. And this is exactly what you are doing with the initialization from above. You set (opx, opy) to a point that is one pixel outside of the screen to ensure that there are no gaps at the border.
I would like to receive some insight as to how I can make an AI, that can walk smoothly around the map(between window size). Like, if the AI reached that defined spot, then it will walk to another spot.
Here is what I have tried,
First, I get a random float number from 0.0f to 608.0f because my window size is 640,640.
void AIntelligence::GenRandom()
{
MapX = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / 608.0f));
MapY = MapX;
}
Then,I pass in the current position of my sprite to this function
void AIntelligence::RandomMove(float PosX, float PosY)
{
this->PosX = PosX;
this->PosY = PosY;
if (PosX == MapX || PosY == MapY) //If the current is the same as the generated random, then
{ generate it again.
GenRandom();
}
else
{
if (PosX < MapX || PosY < MapY) //If not then I see if the position less than the
{ generated and translate it.
this->PosX += 8.0f;
this->PosY += 8.0f;
}
else if (PosX > MapX || PosY > MapY)
{
this->PosX -= 8.0f;
this->PosY -= 8.0f;
}
else
this->PosX += 0.0f;
this->PosY += 0.0f;
}
}
In my message loop, here is how I call the method
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
Inputs->GetInput(); //Not related
Moving->RandomMove(PosX,PosY);
D3DXVECTOR2 SpritePos = D3DXVECTOR2(Moving->getPosX(), Moving->getPosY());
PosX = Moving->getPosX();
PosY = Moving->getPosY();
Graphic->ClearBegin(); //Begin the direct3d scene
Sprite->Begin(D3DXSPRITE_ALPHABLEND);
float Radian = D3DXToRadian(Rotation);
D3DXMatrixTransformation2D(&Mat, NULL, 0.0f, &SpriteScaling, &SpriteCenter, Radian, &SpritePos); // This is where the transformation is set.
Sprite->SetTransform(&Mat);
Sprite->Draw(Texture, NULL, NULL, NULL, D3DCOLOR_XRGB(255, 255, 255));
Sprite->End();
Graphic->EndPresent();
}
The sprite did move but only moving downward right. And once it reached the same certain spot, it will only stay and vibrate there.... Sorry if my explanation is not clear enough or did not provide enough information needed.
Here are a few things that should help you:
1) In RandomMove, your last else doesn't have braces, since you're performing two operations, you should wrap both of them in braces like you did elsewhere
2) float comparison is tricky. It's very unlikely that your PosX == MapX || PosY == MapY will ever trigger. A better idea would be to calculate the distance between your current position and the random position and then execute the code if the distance is less than an epsilon (small value). Here is a pretty detailed post about float comparison (link)
3) GenRandom always assigns the same value to MapX and MapY. You should try to execute two random calls instead (and probably use a const float to define your max value or make it configurable instead of hardcoding that width
4) Your RandomMove method is a bit misleading. It's not performing random movement, it's going towards MapX and MapY. You should separate the calls to GenRandom from your movement code.
5) Your movement code is meant to work only in diagonals since you always increment or decrement your position in both axes at the same time, in the same direction.
Here is a suggestion (not tested) of what your code could look like:
void AIntelligence::GenRandom(const float in_MaxValueX, const float in_MaxValueY)
{
MapX = in_MaxValueX * (float)rand() / (float)RAND_MAX;
MapY = in_MaxValueY * (float)rand() / (float)RAND_MAX;
}
bool AIntelligence::MoveTowards(const float in_PosX, const float in_PosY)
{
// how far are we from our objective
const float distX = in_PosX - PosX; // by calculating the distance from the target position, it makes our speed calculations easier later on
const float distY = in_PosY - PosY;
// tolerance in pixels
const float tolerance = 1.0f;
const float absDistX = abs(distX);
const float absDistY = abs(distY);
if(absDistX <= tolerance && absDistY <= tolerance) // destination reached
return true;
else
{
// here, normally, you would use a desired speed AND a delta time (your message loop is not really good for that though) to compute how much movement you can execute in a given frame
const float movement = min(10.f, absDistX + absDistY); // by using min, we're making sure not to overshoot our destination
// compute how this movement is spread on each axis
const float movementX = movement * distX / (absDistX + absDistY);
const float movementY = movement * distY / (absDistX + absDistY);
PosX += movementX;
PosY += movementY;
}
return false;
}
// in your loop
if(Moving->MoveTowards(MapX, MapY))
{
Moving->GenRandom(608.f, 608.f); // you should definitely not hardcode these values
}
Feel free to comment if there's parts you don't quite understand
Question rescinded by original poster
Hey so when running the following code my square is supposed to travel around in a circle, but there is some kind of issue with the function that calculates the x,y movement that should happen based on the velocity and angle of travel.
It succesfully travels around and around, but not in the right way. the 2nd and 4th quadrant are kind of inversed, and curving inwards towards the center of the circle rather than outward.
I can't figure out what the problem is... anyone wanna help?
#include<SFML/Graphics.hpp>
#include<SFML/System.hpp>
#include<cmath>
#include<vector>
# define M_PI 3.14159265358979323846
sf::RenderWindow Window;
template<typename T>
void CalculateMove(T Time, T Speed, T Angle, T& buffX, T& buffY)
{ //Make the degrees positive
if(Angle<0) Angle= 360-Angle;
//determine what quadrant of circle we're in
unsigned int Quadrant= 1;
if(Angle>90) Quadrant= 2;
if(Angle>180) Quadrant= 3;
if(Angle>270) Quadrant= 4;
//anything above 90 would be impossible triangle
Angle= (float)(Angle-(int)Angle)+(float)((int)Angle%90);
// calculates x and y based on angle and Hypotenuse.02433
if((int)Angle!=0){
buffX= sin(Angle / 180 * M_PI)/ (1.f/(Speed*Time));
buffY= sin((180-Angle-90)/ 180 * M_PI)/ (1.f/(Speed*Time));}
else{// Movement is a straight line on X or Y axis
if(Quadrant==0 || Quadrant==2) buffX= Speed*Time;
if(Quadrant==1 || Quadrant==4) buffY= Speed*Time;}
//Quadrant Factor (positive or negative movement on the axis)
switch(Quadrant){
case 1: break;
case 2: buffX=-buffX; break;
case 3: buffX=-buffX; buffY=-buffY; break;
case 4: buffY=-buffY; break;}
};
///////////////////////////////////////// Mysprite ////////////////////////////////
class mySprite : public sf::Sprite
{
private:
float velocity;
float angle;
public:
// all the values needed by the base class sprite();
mySprite(
const sf::Image& Img,
const sf::Vector2f& Position = sf::Vector2f(0, 0),
const sf::Vector2f& Scale = sf::Vector2f(1, 1),
float Rotation = 0.f,
const float Angle= 0.f,
const float Velocity= 0.f,
const sf::Color& Col = sf::Color(255, 255, 255, 255)):
Sprite(Img, Position, Scale, Rotation, Col){
angle= Angle;
velocity= Velocity;};
float Velocity(){return velocity;};
void SetVelocity(float newVelocity){velocity=newVelocity;};
float Angle(){return angle;};
void SetAngle(float newAngle){angle=(float)(newAngle-(int)newAngle)+(float)((int)newAngle%360);};
void Update(){
float frameTime= Window.GetFrameTime();
float X=0,Y=0;
CalculateMove(frameTime,velocity,angle,X,Y);
Move(X,-Y);
};
void Accelerate(float PPS){velocity+=PPS;};
void Turn(float degrees){
float test= (float)((angle+degrees)- (int)(angle+degrees)); //TODO: Get rid of these test
float test2=(float)((int)(angle+degrees)%360);
float test3=test+test2;
angle=(float)((angle+degrees)-(int)(angle+degrees))+(float)((int)(angle+degrees)%360);};
void Reflect(float CollAngle){
SetRotation(-GetRotation());
angle=-angle;
//TODO: factor in the collision angle
};
};
int main()
{
Window.Create(sf::VideoMode(800, 600), "Pong! by Griffin Howlett");
sf::Image img;
img.Create(30,50,sf::Color(255,0,0));
mySprite box(img, sf::Vector2f(400,200), sf::Vector2f(1,1), 0, 180, 200);
Window.Display();
for(;;){
Window.Clear();
box.Update();
box.Turn(45.0*Window.GetFrameTime());
Window.Draw(box);
Window.Display();
}
}
Your first mistake:
if(Angle<0) Angle= 360-Angle;
should be:
if(Angle<0) Angle= 360+Angle;
I'm not quite sure why you're going to the trouble of dividing the angle into quadrants. Do you think that the sin function is only defined for the range of 0 to 90 degrees?
Not sure all of the problems, but this line of code is wrong:
if(Angle<0) Angle= 360-Angle;
If Angle < 0 then 360 - Angle will be > 360
you can also clean up the quadrant setting code, otherwise when the Angle is > 270, you do the assignment 4 times.
int Quadrant = 1;
if (Angle > 270)
{
Qadrant = 4;
}
else if (Angle > 180)
{
Quadrant = 3;
}
else if (Angle > 90)
{
Quadrant = 2;
}
It seems I was wrong in assuming the triangle formed and used to calculate the movement required to get to the x, y coordinates would always automatically use the Y axis as the side opposite of the 'Angle', and istead the coordinates were backwards for Quadrant 2 and 4, Thanks for the other feedback though!
Here's the updated code:
if((int)Angle!=0){
if(Quadrant==2 || Quadrant==4) Angle=90-Angle; //The unit circle triangle is flipped otherwise, causing x and y to be switched
buffY= sin(Angle / 180 * M_PI)/ (1.f/(Speed*Time));
buffX= sin((180-Angle-90)/ 180 * M_PI)/ (1.f/(Speed*Time));}
by doing 90-Angle i'm switching the angles used to find the X, and Y side of the imaginary triangle....
Here is what I'm trying to do. I'm trying to make a bullet out of the center of the screen. I have an x and y rotation angle. The problem is the Y (which is modified by rotation on the x) is really not working as intended. Here is what I have.
float yrotrad, xrotrad;
yrotrad = (Camera.roty / 180.0f * 3.141592654f);
xrotrad = (Camera.rotx / 180.0f * 3.141592654f);
Vertex3f Pos;
// get camera position
pls.x = Camera.x;
pls.y = Camera.y;
pls.z = Camera.z;
for(float i = 0; i < 60; i++)
{
//add the rotation vector
pls.x += float(sin(yrotrad)) ;
pls.z -= float(cos(yrotrad)) ;
pls.y += float(sin(twopi - xrotrad));
//translate camera coords to cube coords
Pos.x = ceil(pls.x / 3);
Pos.y = ceil((pls.y) / 3);
Pos.z = ceil(pls.z / 3);
if(!CubeIsEmpty(Pos.x,Pos.y,Pos.z)) //remove first cube that made contact
{
delete GetCube(Pos.x,Pos.y,Pos.z);
SetCube(0,Pos.x,Pos.y,Pos.z);
return;
}
}
This is almost identical to how I move the player, I add the directional vector to the camera then find which cube the player is on. If I remove the pls.y += float(sin(twopi - xrotrad)); then I clearly see that on the X and Z, everything is pointing as it should. When I add pls.y += float(sin(twopi - xrotrad)); then it almost works, but not quite, what I observed from rendering out spheres of the trajector is that the furthur up or down I look, the more offset it becomes rather than stay alligned to the camera's center. What am I doing wrong?
Thanks
What basically happens is very difficult to explain, I'd expect the bullet at time 0 to always be at the center of the screen, but it behaves oddly. If i'm looking straight at the horizon to +- 20 degrees upward its fine but then it starts not following any more.
I set up my matrix like this:
void CCubeGame::SetCameraMatrix()
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(Camera.rotx,1,0,0);
glRotatef(Camera.roty,0,1,0);
glRotatef(Camera.rotz,0,0,1);
glTranslatef(-Camera.x , -Camera.y,-Camera.z );
}
and change the angle like this:
void CCubeGame::MouseMove(int x, int y)
{
if(!isTrapped)
return;
int diffx = x-lastMouse.x;
int diffy = y-lastMouse.y;
lastMouse.x = x;
lastMouse.y = y;
Camera.rotx += (float) diffy * 0.2;
Camera.roty += (float) diffx * 0.2;
if(Camera.rotx > 90)
{
Camera.rotx = 90;
}
if(Camera.rotx < -90)
{
Camera.rotx = -90;
}
if(isTrapped)
if (fabs(ScreenDimensions.x/2 - x) > 1 || fabs(ScreenDimensions.y/2 - y) > 1) {
resetPointer();
}
}
You need to scale X and Z by cos(xradrot). (In other words, multiply by cos(xradrot)).
Imagine you're pointing straight down the Z axis but looking straight up. You don't want the bullet to shoot down the Z axis at all, this is why you need to scale it. (It's basically the same thing that you're doing between X and Z, but now doing it on the XZ vector and Y.)
pls.x += float(sin(yrotrad)*cos(xrotrad)) ;
pls.z -= float(cos(yrotrad)*cos(xrotrad)) ;
pls.y += float(sin(twopi - xrotrad));