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
Related
I have been struggling with this problem for over a month, so I really need your help.
To further elaborate on the question :
The question is whether a vector called 'direction' that starts at a vertex called 'start' passes through the 'taget'.
You need to confirm the direction and distance.
I decided that using the dot product was impossible because I went through enough debugging.
The result is good when calculated directly, but why is the result different when executed in the shader?
The same thickness should be printed depending on the distance, but why does the thin line appear when the distance is far?
Do you have any good ideas even if it's not the way I use the rotation matrix?
These are three questions.
First of all, my situation is
drawing fSQ.
I want to check whether the direction of start passes through the target.
Compute in the pixel shader.
1 is one pixel
The screen size is 1920*1080
bool intersect(float2 target, float2 direction, float2 start) {
bool intersecting = false;
static const float thresholdX = 0.5 / SCREENWIDTH;
static const float thresholdY = 0.5 / SCREENHEIGHT;
if (direction.x == 0 && direction.y == 0);
else {
float2 startToTarget = target - start;
float changedTargetPositionX = startToTarget.x * direction.x + startToTarget.y * direction.y;
float changedTargetPositionY = startToTarget.x * (-direction.y) + startToTarget.y * direction.x;
float rangeOfX = (direction.x * direction.x) + (direction.y * direction.y);
if (changedTargetPositionX <= rangeOfX + thresholdX && changedTargetPositionX >= -thresholdX &&
changedTargetPositionY <= thresholdY && changedTargetPositionY >= -thresholdY) {
intersecting = true;
}
}
return intersecting;
We use a rotation matrix to rotate a vector and then check the difference between the two vectors, which works in most cases, but fails for very small pixels.
For example
start = (15,0) direction= (10,0) taget = (10,0)
In this case, the intersect function should return false, but it returns true.
But if the pixel difference is bigger then it works fine.
and
#define MAX = 5;
float2 points[MAX*MAX];
for (float fi = 1; fi < MAX; fi++)
for (float fj = 1; fj < MAX; fj++)
points[(int)(fi * MAX + fj)] = float2(fi / MAX , fj / MAX);
for(uint ni=0; ni < MAX*MAX;ni++)
for(uint nj=3; nj < MAX*MAX; nj++)
if (intersect(uv, points[nj]- points[ni], points[ni])) {
color = float4(1, 0, 0, 1);
return color;
}
return float4(0, 0, 0, 1);
When debugging like this, the line becomes thinner depending on the distance.
All the lines should have the same thickness, but I don't know why.
This is the result of running the debugging code:
We look forward to your reply.
thank you
I have to move a ball in an angle in an open SFML and keep it within the window size (Like the DVD thing), but my current function makes it to the bottom and doesn't "bounce". It slides across the bottom and stops once it reaches the other corner. The initial position is (1,1)
void Bubble::updatePosition() {
if( isTopBottom() ){
do{
_x += .1;
_y += -.2;
}while( !isTopBottom() );
}
else if( isLeftRight() ){
do{
_x += -.1;
_y += .2;
}while( !isLeftRight() );
}
else{
_x += .1;
_y += .2;
}
_bubble.setPosition(_x, _y);
}
the isLeftRight, isTopBottom are bools that check if they have reached the edges
Simple Solution
Use velocities and manipulate those on collision; then, use the velocity to update the position.
Check each edge separately and decide on one relevant velocity component based on that.
e.g. (following your values)
// Positions:
float x = 1.f;
float y = 1.f;
// Velocities:
float vx = 0.1f;
float vy = 0.2f;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (x < 0.f)
vx = 0.1f;
else if (x > 640.f)
vx = -0.1f;
if (y < 0.f)
vy = 0.2f;
else if (y > 640.f)
vy = -0.2f;
// update position (still inside loop):
x += vx;
y += vy;
Cleaner Solution
This is the same as the simple solution above but, since you tagged SFML, you can use SFML vectors to keep the two components together. Also modified variable names to be more clear. Pulled out the size of the window and the velocity amounts from being hard-coded into the logic as well:
const sf::Vector2f windowSize(640.f, 640.f);
const sf::Vector2f velocityAmount(0.1f, 0.2f);
sf::Vector2f position(1.f, 1.f);
sf::Vector2f velocity = velocityAmount;
// ... then, inside loop:
// Check collisions (and adjust velocity):
if (position.x < 0.f)
velocity.x = velocityAmount.x;
else if (position.x > windowSize.x)
velocity.x = -velocityAmount.x;
if (position.y < 0.f)
velocity.y = velocityAmount.y;
else if (position.y > windowSize.y)
velocity.y = -velocityAmount.y;
// update position (still inside loop):
position += velocity;
You should notice that the velocity is the values that are added to the position on each iteration of the loop and that velocity does not change when it is not considered colliding with an edge.
Initial Problem
The initial problem you had is it always moves in the same direction (towards the bottom-right) if it is not hitting an edge. This means that it'll never be allowed to rise above the bottom edge (or away from right edge).
I am currently trying some simple line rotation in sdl and I have found a function that rotates a point around another point. Here it is ( changed to work with sdl ).
SDL_Point rotate_point(double cx, double cy, double angle, SDL_Point p)
{
double pi = acos(-1);
double rotation_angle = (double)angle / 180.0 * pi;
double s = sin(rotation_angle);
double c = cos(rotation_angle);
// translate point back to origin:
p.x -= cx;
p.y -= cy;
// rotate point
double xnew = p.x * c - p.y * s;
double ynew = p.x * s + p.y * c;
// translate point back:
p.x = xnew + cx;
p.y = ynew + cy;
return p;
}
Now im having a problem where the line gets shorter and shorter until it stops at the center point im rotating it around. I dont know why.
Here is the way im using the function:
double angle1=0.1, angle2=0,oldangle1=0,oldangle2=0;
SDL_Point line11 = { 10,0 }, line12 = { 110,0 }, line21 = { 110,0 }, line22 = { 210,0 };
SDL_Event event;
int mousex = 0, mousey = 0;
while (SDL_PollEvent(NULL)) {
SDL_GetMouseState(&mousex, &mousey);
if (GetAsyncKeyState('W'))
angle1 += 0.1;
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
if (angle1 < oldangle1-0.1||angle1 > oldangle1 + 0.1) {
line12 = rotate_point(0, 0, (angle1-oldangle1), line12);
line21 = rotate_point(0, 0, (angle1 - oldangle1), line21);
oldangle1 = angle1;
}
if (angle2 < oldangle2 - 0.1 || angle2 > oldangle2 + 0.1) {
line22 = rotate_point(line21.x,line21.y, (angle2 - oldangle2), line22);
oldangle2 = angle2;
}
if (angle2 == 0)
angle2 = angle1;
printf("%f ", angle2);
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 0);
SDL_RenderDrawLine(renderer, line11.x, line11.y, line12.x, line12.y);
SDL_RenderDrawLine(renderer, line21.x, line21.y, line22.x, line22.y);
//solveIK(&angle1, &angle2, false, 100, 100, mousex, mousey);
//angle1 = get_degrees(angle1);
//angle2 = get_degrees(angle2);
SDL_RenderPresent(renderer);
SDL_Delay(16);
}
I know that it is incredibly messy but ive just started with sdl and c++. i will try to fix it up after ive gotten it to work.
What ive already tried:
Different functions for rotating. They all seem to be worse in terms of the line shortening. Its most likely not a problem with the rotation function.
Rewriting the code. This is version 3 and it works the best but is also the worst looking.
Do not rerotate.
Start with the initial points. Now apply a rotation.
If you have a new rotation, start with the initial points and apply a new rotation. Don't rotate the result of the first rotation.
In general, floating point operations induce error away from the theoretical result. Rounding to integers even more error (not sure if your code does that). Cascading large numbers of floating point ops will result in garbage sometimes.
Now, adding angles will also have rounding error, but not in a noticable way: it will have rotated more/less than it should have, not stretch/shrink the line. Again here you can replace repeated addition with a fixed number of multiplications to reduce this, if you care.
I'm making a 2D game with SFML in C++ and I have a problem with collision. I have a player and a map made of tiles. Thing that doesn't work is that my collision detection is not accurate. When I move player up and then down towards tiles, it ends up differently.
I am aware that source of this problem may be calculating player movement with use of delta time between frames - so it is not constant. But it smooths movement, so I don't know how to do it other way. I tried with constant speed valuses and to make collision fully accurate - speed had to be very low and I am not satisfied with that.
void Player::move() {
sf::Vector2f offsetVec;
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
offsetVec += sf::Vector2f(0, -10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
offsetVec += sf::Vector2f(0, 10);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
offsetVec += sf::Vector2f(-10, 0);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
offsetVec += sf::Vector2f(10, 0);
this->moveVec += offsetVec;
}
void Player::update(float dt, Map *map) {
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
futurePos.move(offset);
if (map->isCollideable(this->pos.x, this->pos.y, futurePos.getGlobalBounds())) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
In player position update I create future sprite object, which is object after applying movement, to get it's boundaries and pass it to collision checker. To collision checker I also pass player pos, because my map is stored in 2d array of tile pointers, so I check only these in player range.
bool Map::isCollideable(float x, float y, const sf::FloatRect &playerBounds) {
int startX = int(x) / Storage::tileSize;
int startY = int(y) / Storage::tileSize;
Tile *tile;
for (int i = startX - 10; i <= startX + 10; ++i) {
for (int j = startY - 10; j <= startY + 10; ++j) {
if (i >= 0 && j >= 0) {
tile = getTile(i, j);
if (tile != nullptr && playerBounds.intersects(tile->getGlobalBounds()))
return true;
}
}
}
return false;
}
Full project on Github
My solution
I have changed if statement in update function to while statement, which decreases my offset vector till no collision is present. I still have to make some adjustments, but general idea is:
void Player::update(float dt, Map *map) {
int repeats = 0;
sf::Vector2f offset = sf::Vector2f(this->moveVec.x * this->playerSpeed * dt,
this->moveVec.y * this->playerSpeed * dt);
sf::Sprite futurePos = this->sprite;
while (map->isCollideable(this->pos.x, this->pos.y, futurePos, offset)) {
offset = 0.7f * offset;
repeats++;
if (repeats > 5) {
this->moveVec = sf::Vector2f(0, 0);
return;
}
}
this->sprite.move(offset);
this->pos += offset;
this->moveVec = sf::Vector2f(0, 0);
return;
}
I also had to rework isCollideable method a little, so it accepts sf::Sprite and offset vector so it can calculate boundaries on it's own.
When the player collides with a tile, you should calculate the penetration, that is, the value of "how much the player went into the tile". When you have this value, nudge your player back that much.
This is just a thought but you could have some inaccuracies in your collision detection when you typecast the float x, and y to integers and then divide them. This could cause problems because some of the data in the float could be lost. If the float was 3.5 or 3.3 or 3.9 then it would become 3 which throws off your collision calculations.
I've been working on detecting collision between to object in my game. Right now everything tavels vertically, but would like to keep the option for other movement open. It's classic 2d vertical space shooter.
Right now I loop through every object, checking for collisions:
for(std::list<Object*>::iterator iter = mObjectList.begin(); iter != mObjectList.end();) {
Object *m = (*iter);
for(std::list<Object*>::iterator innerIter = ++iter; innerIter != mObjectList.end(); innerIter++ ) {
Object *s = (*innerIter);
if(m->getType() == s->getType()) {
break;
}
if(m->checkCollision(s)) {
m->onCollision(s);
s->onCollision(m);
}
}
}
Here is how I check for a collision:
bool checkCollision(Object *other) {
float radius = mDiameter / 2.f;
float theirRadius = other->getDiameter() / 2.f;
Vector<float> ourMidPoint = getAbsoluteMidPoint();
Vector<float> theirMidPoint = other->getAbsoluteMidPoint();
// If the other object is in between our path on the y axis
if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius)) {
// Get the distance between the midpoints on the x axis
float xd = abs(ourMidPoint.x - theirMidPoint.x);
// If the distance between the two midpoints
// is greater than both of their radii together
// then they are too far away to collide
if(xd > radius+theirRadius) {
return false;
} else {
return true;
}
}
return false;
}
The problem is it will randomly detect collisions correctly, but other times does not detect it at all. It's not the if statement breaking away from the object loop because the objects do have different types. The closer the object is to the top of the screen, the better chance it has of collision getting detected correctly. Closer to the bottom of the screen, the less chance it has of getting detected correctly or even at all. However, these situations don't always occur. The diameter for the objects are massive (10 and 20) to see if that was the problem, but it doesn't help much at all.
EDIT - Updated Code
bool checkCollision(Object *other) {
float radius = mDiameter / 2.f;
float theirRadius = other->getDiameter() / 2.f;
Vector<float> ourMidPoint = getAbsoluteMidPoint();
Vector<float> theirMidPoint = other->getAbsoluteMidPoint();
// Find the distance between the two points from the center of the object
float a = theirMidPoint.x - ourMidPoint.x;
float b = theirMidPoint.y - ourMidPoint.y;
// Find the hypotenues
double c = (a*a)+(b*b);
double radii = pow(radius+theirRadius, 2.f);
// If the distance between the points is less than or equal to the radius
// then the circles intersect
if(c <= radii*radii) {
return true;
} else {
return false;
}
}
Two circular objects collide when the distance between their centers is small enough. You can use the following code to check this:
double distanceSquared =
pow(ourMidPoint.x - theirMidPoint.x, 2.0) +
pow(ourMidPoint.x - theirMidPoint.x, 2.0);
bool haveCollided = (distanceSquared <= pow(radius + theirRadius, 2.0));
In order to check whether there was a collision between two points in time, you can check for collision at the start of the time interval and at the end of it; however, if the objects move very fast, the collision detection can fail (i guess you have encountered this problem for falling objects that have the fastest speed at the bottom of the screen).
The following might make the collision detection more reliable (though still not perfect). Suppose the objects move with constant speed; then, their position is a linear function of time:
our_x(t) = our_x0 + our_vx * t;
our_y(t) = our_y0 + our_vy * t;
their_x(t) = their_x0 + their_vx * t;
their_y(t) = their_y0 + their_vy * t;
Now you can define the (squared) distance between them as a quadratic function of time. Find at which time it assumes its minimum value (i.e. its derivative is 0); if this time belongs to current time interval, calculate the minimum value and check it for collision.
This must be enough to detect collisions almost perfectly; if your application works heavily with free-falling objects, you might want to refine the movement functions to be quadratic:
our_x(t) = our_x0 + our_v0x * t;
our_y(t) = our_y0 + our_v0y * t + g/2 * t^2;
This logic is wrong:
if(std::min(getAbsoluteMidPoint().y - radius, getPreviousAbsoluteMidPoint().y - radius) <= theirMidPoint.y &&
theirMidPoint.y <= std::max(getAbsoluteMidPoint().y + radius, getPreviousAbsoluteMidPoint().y + radius))
{
// then a collision is possible, check x
}
(The logic inside the braces is wrong too, but that should produce false positives, not false negatives.) Checking whether a collision has occurred during a time interval can be tricky; I'd suggest checking for a collision at the present time, and getting that to work first. When you check for a collision (now) you can't check x and y independently, you must look at the distance between the object centers.
EDIT:
The edited code is still not quite right.
// Find the hypotenues
double c = (a*a)+(b*b); // actual hypotenuse squared
double radii = pow(radius+theirRadius, 2.f); // critical hypotenuse squared
if(c <= radii*radii) { // now you compare a distance^2 to a distance^4
return true; // collision
}
It should be either this:
double c2 = (a*a)+(b*b); // actual hypotenuse squared
double r2 = pow(radius+theirRadius, 2.f); // critical hypotenuse squared
if(c2 <= r2) {
return true; // collision
}
or this:
double c2 = (a*a)+(b*b); // actual hypotenuse squared
double c = pow(c2, 0.5); // actual hypotenuse
double r = radius + theirRadius; // critical hypotenuse
if(c <= r) {
return true; // collision
}
Your inner loop needs to start at mObjectList.begin() instead of iter.
The inner loop needs to iterate over the entire list otherwise you miss collision candidates the further you progress in the outer loop.