A little help with 3D into 2D convertment?

Started by beomoud, Fri 06/02/2009 06:56:14

Previous topic - Next topic

beomoud

I know this must have been brought up before but i couldn't find any post that really helped at what i am trying. I want to calculate the paths of projectiles, leaving from the center of the screen and see where they hit. I have trouble converting the 3d coordinate system into 2d. At this point i am calculating as the projectile is moving at a fixed rate on the y-axis (the depth) the angle on the y-z level and y-x level. I understood that in order to figure out these angles (let's say tan1 and tan2) i am going to need two fixed points. One is 0,0 (the starting point) and the other would be on another level parallel to x-z (width, height) on a specific y where we are using the coordinates of the mouse to calculate the projectiles direction. When i convert them to 2D coordinates using x as width z as height and y as depth everything goes wrong.... Has anyone done this before? I could really use some help. At this point the space i have created is perspective decreasing the coordinate values as the y (depth) value increases, leading to a point on the middle of the screen. Sorry if i wasn't very clear... any suggestion? Also someone suggested i use a matrix but i don't fully understand them and i feel it would be confusing without a real reason...

Thanks

Ghost

A very (really, VERY) simple approach would be to increase the size of objects as they move to the edges of the screen. That way, a tree at (160/120) would be smaller than one at (10, 120) or (310,120). This is the base of bird's eye view calculations (Think GTA1), and can look at least decently convincing.

Not too sure how you would actually calculate movement though, this is just for looks.

Khris

You shouldn't use angles but vectors. I assume the player can set the angle of the gun using the mouse? In this case you can calculate the vector without ever having to resort to angles.
Thus the path of the bullet is e.g. (0,0,0) + t*(-1.2, -9.5, 2.9)

Code: ags
int 2dx(int 3dx, 3dy) {
  return FloatToInt((3dx*100.0)/(3dy+100.0))+160;   // screen width = 320
}

int 2dy(int 3dz, 3dy) {
  return (FloatToInt((3dz*100.0)/(3dy+100.0))+120)*(-1);  // screen height = 240
}


At y=0, x,z translate 1:1 to 2d coords.
At y=100, x,z translate 1:2 to 2d coords.

Not very realistic, but should do fine for a flying projectile.

(Btw, it's very confusing to work with y as depth; it's sort of an unwritten law that one always uses the z-coord as depth. Ground is always X-Z, height is Y)

beomoud

#3
Ok, this is what i have been doing
Code: ags

function shoot(){
  if (go == true){
  float middle_virtual_x;
  float middle_virtual_z;
  middle_virtual_x = (IntToFloat(stat_mouse_x) - 200.0)/75.0;
  middle_virtual_z = (150.0 - IntToFloat(stat_mouse_y))/75.0;
  if (middle_virtual_x == 0.0) middle_virtual_x = 0.1;
  if (middle_virtual_z == 0.0) middle_virtual_z = 0.1;
  float tan1 = 50.0 /middle_virtual_x;
  if (tan1 <= -0.75) {
    if (person[cBall.ID].virtual_x < -200.0){
      go = false;
      return;
    }
  } 
  if (tan1 >= 0.75) {
    if (person[cBall.ID].virtual_x > 200.0){
      go = false;
      return;
    }
  }
  float tan2 = 50.0 /middle_virtual_z;
  if (tan2 <= -1.0) {
    if (person[cBall.ID].virtual_z < -150.0){
      go = false;
      return;
    }
  } 
  if (tan2 >= 1.0) {
    if (person[cBall.ID].virtual_z > 150.0){
      go = false;
      return;
    }
  }
  if (person[cBall.ID].virtual_y > 150.0){
    go = false;
    return;
  }
  person[cBall.ID].virtual_y++;
  person[cBall.ID].virtual_x = person[cBall.ID].virtual_y /tan1;
  person[cBall.ID].virtual_z = person[cBall.ID].virtual_y /tan2;
  cBall.y = 150 - FloatToInt(person[cBall.ID].virtual_y);
  cBall.x = 200 + (FloatToInt(person[cBall.ID].virtual_x) *GetScalingAt(200, FloatToInt(person[cBall.ID].virtual_y)) /100);
  cBall.z = 150 + (FloatToInt(person[cBall.ID].virtual_z) *GetScalingAt(200, FloatToInt(person[cBall.ID].virtual_y)) /100);
}
}

...

else if (mouse.Mode == eModeAim) {
  person[cBall.ID].virtual_x = 0.0;
  person[cBall.ID].virtual_y = 0.0;
  person[cBall.ID].virtual_z = 0.0;
  stat_mouse_x = mouse.x;
  stat_mouse_y = mouse.y;
  go = true;
}

.. in room script

function room_RepExec(){

shoot();
}

Now this should work but the virtual x and z are always much lower than they should be and when i call the function the ball (projectile) disappears... I just can't seem to get my mind around these things... If anyone understand anything ... i was pretty sure i made no mistake...

beomoud

#4
I find it more confusing to use z as depth as it is not used in that way by the game. How do you figure the path of the bullet using vectors, i was never too good with vectors. If you could explain that would be great. The mouse only determines the angle but the projectile doesn't stop there. I see you are not using z at all, are you suggesting that i should find the hit point and use the walk function? The way i am trying to get this to move it simple disappears for some reason.

Khris

Ah ok, you're using a character, didn't realize that.

I'd use global variables to store the ball's 3D position, then add the movement vector to them every game loop and redraw it.

Code: ags
float bx,by,bz;  // ball position
float mx,my,mz;  // movement direction
# define speed 2.0;     // movement speed, 2 -> ~ 75 loops/2 seconds to reach y = 150
bool go;

int 2dx(int 3dx, 3dy) {
  return FloatToInt((3dx*100.0)/(3dy+100.0))+200;   // screen width = 400
}

int 2dy(int 3dz, 3dy) {
  return (FloatToInt((3dz*100.0)/(3dy+100.0))+150)*(-1);  // screen height = 300
}

// on_mouse_click
  // calculate target xyz using mouse position, y = 150 -> factor = 2.5
  mx = IntToFloat(mouse.x-200)*2.5;
  my = 150.0;
  mz = IntToFloat(150-mouse.y)*2.5;
  // normalize movement vector (change length to 1.0), multiply by speed
  float len = Maths.Sqrt(mx*mx+my*my+mz*mz);  // len = |m|
  mx = (mx*speed)/len;
  my = (my*speed)/len;
  mz = (mz*speed)/len;
  bx = 0.0; by = 0.0; bz = 0.0; // reset ball position
  go = true;

// rep_exe
  // move ball
  bx+=mx;
  by+=my;
  bz+=mz;

  if (by >= 150.0) {
    go = false;
    // etc
  }

  // calculate size of 100 pixels to determine scaling factor
  int f = 2dx(50.0, by) - 2dx(-50.0, by);
  // update character
  cBall.Scaling = f;
  cBall.x = 2dx(bx, by);
  cBall.y = 2dy(bz, by);
  cBall.z = (SIZE*f)/(-200)  // center ball at 2d x,y, replace SIZE with pixel height of sprite

beomoud

 :D Thank you man... for going into the trouble to be so analytic, i probably wouldn't have realized it any other way, i find it difficult to read other peoples script. Thanks. I don't know if you noticed but we have a few common points, you use len which is cos(f) where i use tan, which is tan(f). For speed i was using the y coordinate but your way is much better. You have great insight, i will try it out but i am sure it will work...

beomoud

#7
A little fix: cBall.y = 2dy(bz, by); actually is 300 + cBall.y = 2dy(bz, by);

SMF spam blocked by CleanTalk