1. Hey! Guest! The 36th GMC Jam will take place between February 27th, 12:00 UTC - March 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

GM:S 1.4 Targeting UI in 3D

Discussion in 'Programming' started by hijong park, Jan 20, 2020.

  1. hijong park

    hijong park Member

    Joined:
    Dec 29, 2016
    Posts:
    143
    [​IMG]

    I'm making a 3D game that has the targeting system, So I want to display a target UI on the target.

    The targeting UI should look like the picture adove, qualifying these conditions :

    1. It should be a 2D picture displayed on the 3D object.

    2. It should never be covered by other 3D objects.

    3. the size of the UI should always be same, ignoring the distance between the camera and target.

    I have no idea how to make this UI. It should always be visible and keep the same size ignoring the distance of the target, so obviously I shouldn't use d3d_draw_wall (Like when making Doom-style Bill boarding sprites) but draw it in draw_GUI event instead. But If I draw the UI in draw_GUI event, There's no way to get the correct location of it to draw on the screen as the target is in different dimension.
     
    Fredrik likes this.
  2. kraifpatrik

    kraifpatrik Member

    Joined:
    Jun 23, 2016
    Posts:
    148
    Hey there, actually there is a way! When you draw a vertex in 3D space, it goes through series of matrix multiplications to calculate its position on screen, and this can be replicated!

    The formula is simple:

    Multiply your matrix_world, matrix_view and matrix_projection matrices (= MVP matrix):
    Code:
    var _world = matrix_get(matrix_world);
    var _view = matrix_get(matrix_view);
    var _projection = matrix_get(matrix_projection);
    var _mvp = matrix_multiply(matrix_multiply(_world, _view), _projection);
    
    Multiply MVP with vector [x, y, z, w], where x,y,z is position of the enemy in 3D space and w is 1:
    Code:
    var _enemyX = ...;
    var _enemyY = ...;
    var _enemyZ = ...;
    var _enemyW = 1;
    
    var _x = _mvp[ 1]*_enemyX + _mvp[ 5]*_enemyY + _mvp[ 9]*_enemyZ + _mvp[13]*_enemyW;
    var _y = _mvp[ 2]*_enemyX + _mvp[ 6]*_enemyY + _mvp[10]*_enemyZ + _mvp[14]*_enemyW;
    var _z = _mvp[ 3]*_enemyX + _mvp[ 7]*_enemyY + _mvp[11]*_enemyZ + _mvp[15]*_enemyW;
    var _w = _mvp[ 0]*_enemyX + _mvp[ 4]*_enemyY + _mvp[ 8]*_enemyZ + _mvp[12]*_enemyW;
    
    Divide x, y (and z) of the resulting vector by w of the resulting vector - this is called perspective division:
    Code:
    _x /= _w;
    _y /= _w;
    // _z =/ _w; // Not necessary for our purposes
    
    X and Y are now values in range [-1,1], you want to get them into [0, 1]:
    Code:
    _x = _x * 0.5 + 0.5;
    _y = _y * 0.5 + 0.5;
    
    Also the coordinates are flipped vertically, fix that with:
    Code:
    _y = 1 - _y;
    
    Now you can scale the x coordinate from [0,1] range to [0,screenWidth] and y from [0,1] range to [0, screenHeight]:
    Code:
    _x *= window_get_width();
    _y *= window_get_height();
    
    And now x and y are the enemy's position on screen! Seems like a lot of code, but can be squished into two scripts really (transform, project). I don't remember any good learning resources on this topic, but you can try googling "world view projection matrix", "perspective projection", "clip space", "NDC space" and "linear algebra" in general. Good luck!

    EDIT: Wrote this without any tests, if there are some errors, please let me know!
     
    Last edited: Jan 20, 2020
  3. hijong park

    hijong park Member

    Joined:
    Dec 29, 2016
    Posts:
    143
    Where am I suppossed to put these code ?

    I tried it in both draw and draw_GUI event but none of them worked.

    When I tried to draw the value of _x and _y, They displayed insane amount of value (202003,452168) when the target's location is (0,0,0).
     
  4. kraifpatrik

    kraifpatrik Member

    Joined:
    Jun 23, 2016
    Posts:
    148
    You can calculate the MVP matrix in the draw event, right after you use d3d_set_projection_ext, and store it into a variable. Then use that variable in the draw GUI event in the rest of the code. There's also a chance that I've put the matrix multiplications in an opposite order and it should be matrix_multiply(matrix_multiply(_projection, _view), _world), but I'm not sure about that, so please try both variants.
     

Share This Page