Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Solved: Trowing projectiles on 2d isometric map  (Read 1989 times)

0 Members and 1 Guest are viewing this topic.

Switchboy

  • Newbie
  • *
  • Posts: 19
    • View Profile
Solved: Trowing projectiles on 2d isometric map
« on: May 26, 2020, 09:39:44 pm »
I am creating an isometric RTS with SFML I have most of the unit AI and basic melee combat in place.

For a little demo see my devlog:
https://www.youtube.com/watch?v=XsZtY1JZatI

Now comes the hard part. It would be fun to add ranged units. In order to acomadate for this I've created a projectile class and started to work to try and render a paraboloic 3d trajectory in my 2d isometric space.

My projectiles move along x,y,z axis and I then translate their coördinates to my isometric projection:

Code: [Select]
screenX = x*2 //since a tile is 64 pixels wide and 32 tall
screenY = y+z //since any upward movement is only visable in the Y axis

around this concept I made the following class:

Code: [Select]
struct mouseWorldCord
{
    int x;
    int y;
};

class projectile
{
public:
projectile(int projectileStartX, int projectileStartY, int projectileTargetX, int projectileTargetY, int projectileType, int damageOnImpact, int splashDamageOnImpact);
void updatePosition();
void drawProjectile();
void doDamage();
void doSplashDamage();

private:
mouseWorldCord projectilePosition;
mouseWorldCord projectileTarget;
float timeFired;
int projectileType;
int damageOnImpact;
int splashDamageOnImpact;
float X;
float Y;
float Z;
float deltaX;
float deltaY;
float deltaZ;
bool reachedTarget;
};


std::vector<projectile> listOfProjectiles;

projectile::projectile(int projectileStartX, int projectileStartY, int projectileTargetX, int projectileTargetY, int projectileType, int damageOnImpact, int splashDamageOnImpact)
{
this->X = worldSpace(projectileStartX, projectileStartY, true)/2;
this->Y = worldSpace(projectileStartX, projectileStartY, false);
this->Z = 0;
this->projectileType = projectileType;
this->damageOnImpact = damageOnImpact;
this->projectileTarget = { projectileTargetX, projectileTargetY };
this->projectilePosition = {projectileStartX, projectileStartY};
this->splashDamageOnImpact = splashDamageOnImpact;
float travelTimeInSeconds = distEuclidean(projectileStartX * 32.f, projectileStartY * 32.f, projectileTargetX * 32.f, projectileTargetY * 32.f) / 32.f; //32 pixel/s
this->deltaX = ((projectileStartX * 32.f) - (projectileTargetX * 32.f)) / travelTimeInSeconds;
this->deltaY = ((projectileStartY * 32.f) - (projectileTargetY * 32.f)) / travelTimeInSeconds;
this->deltaZ = travelTimeInSeconds / 2;
this->timeFired = currentGame.getTime();
this->reachedTarget = false;
}

void projectile::updatePosition()
{
if (!reachedTarget) {
//speed is in pixels per second
if (this->timeFired + 0.016 < currentGame.getTime()) {
this->timeFired = currentGame.getTime();
this->X += this->deltaX/60.f;
this->Y += this->deltaY/60.f;
this->Z += this->deltaZ/60.f;
this->deltaZ -= 0.016f;
if (this->Z <= 0.0f) {
doDamage();
reachedTarget = true;
}
}
}
}

void projectile::drawProjectile()
{
int xScreenCord = this->X * int(2);
int yScreenCord = this->Y + this->Z;
currentGame.spriteArrow.setPosition(xScreenCord, yScreenCord);
window.draw(currentGame.spriteArrow);
}

void projectile::doDamage()
{
if (currentGame.occupiedByActorList[this->projectileTarget.x][this->projectileTarget.y] != -1) {
listOfActors[currentGame.occupiedByActorList[this->projectileTarget.x][this->projectileTarget.y]].takeDamage(this->damageOnImpact);
}
else if (currentGame.occupiedByBuildingList[this->projectileTarget.x][this->projectileTarget.y] != -1)
{
listOfBuildings[currentGame.occupiedByBuildingList[this->projectileTarget.x][this->projectileTarget.y]].takeDamage(this->damageOnImpact);
}
}


What I want to achieve is have a fixed horizontal velocity. I broke this down in an x-vector and y-vector. I vary the z velocity. My aim is to have the velocity decrease over time and be at 0 in the middle of the path and then have a negative velocity the second half which increases each frame. Just like gravity is pulling on the projectile. In this way I get a nice parabolic trajectory for my arrow without complicated math. Or so I thought.

I must have made a mistake somewhere because my arrow is not moving as intended. It just follows a straight line of the screen. There is probably something amiss in my initial deltaZ velocity.

Maybe a fresh set of eyes looking at the code will help.
« Last Edit: May 27, 2020, 10:09:15 pm by Switchboy »

Switchboy

  • Newbie
  • *
  • Posts: 19
    • View Profile
Re: Trowing projectiles on 2d isometric map
« Reply #1 on: May 27, 2020, 10:08:37 pm »
Well getting a good night sleep helped me solve it!

The answer was in hindsight pretty obvious:
I was doing my movement calculations in world space instead of screenspace! And going "up" ment going to a lower Y value. This is working code now:

Code: [Select]
projectile::projectile(int projectileStartX, int projectileStartY, int projectileTargetX, int projectileTargetY, int projectileType, int damageOnImpact, int splashDamageOnImpact)
{
this->X = worldSpace(projectileStartX, projectileStartY, true);
this->Y = worldSpace(projectileStartX, projectileStartY, false);
this->Z = 0;
this->projectileType = projectileType;
this->damageOnImpact = damageOnImpact;
this->projectileTarget = { projectileTargetX, projectileTargetY };
this->projectilePosition = {projectileStartX, projectileStartY};
this->splashDamageOnImpact = splashDamageOnImpact;
float travelTimeInSeconds = distEuclidean(projectileStartX * 32.f, projectileStartY * 32.f, projectileTargetX * 32.f, projectileTargetY * 32.f) / 128.f; //32 pixel/s
this->deltaX = (this->X - float(worldSpace(this->projectileTarget.x, this->projectileTarget.y, true))) / travelTimeInSeconds;
this->deltaY = (this->Y - float(worldSpace(this->projectileTarget.x, this->projectileTarget.y, false))) / travelTimeInSeconds;
this->deltaZ = travelTimeInSeconds*3;
this->timeFired = currentGame.getTime();
this->reachedTarget = false;
this->X += 32;
}

void projectile::updatePosition()
{
if (!reachedTarget) {
//speed is in pixels per second
if (this->timeFired + 0.016 < currentGame.getTime()) {
this->timeFired = currentGame.getTime();
this->X -= this->deltaX/60.f;
this->Y -= this->deltaY/60.f;
this->Z -= this->deltaZ;
this->deltaZ -= 0.096f;
if (this->Z >= 0.0f) {
doDamage();
reachedTarget = true;
}
}
}
}

Near the end of this video shows the script in action!

https://www.youtube.com/watch?v=7h9-RLGY7QM&t=189

« Last Edit: May 27, 2020, 10:13:39 pm by Switchboy »