Billboarding 3D how change enemy sprite based on player position

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Marirari89

Hello everyone! I am unsure where I would post a question like this, so I posted it in the Godot forums as well.

I am trying to make a Doom/Wolfenstein clone with billboarding 3D so the enemy sprite is always facing the player. I know how to do the billboarding effect, but I also want to change the enemy sprite based on whether or not the enemy is facing the player. I want to see the enemy’s back if the player is behind the enemy, the front left if viewing the enemy from a front left angle, and right side of enemy if viewing from that angle. Hence, I would need to change the enemy’s sprites accordingly.

Does anyone here know how one would go about programming such a feature?

:bust_in_silhouette: Reply From: Calinou

To determine which sprite should be drawn, using the dot product between the player’s position and the enemy’s position will be what you need. You then need to use a series of if statements to assign a sprite depending on the value of the dot product.

Edit: This can also be done with a shader: Directional 3D Sprite · godotengine/godot-proposals · Discussion #5082 · GitHub

Copying the shader from that link for reference. Note that it’s written for 4.0, not 3.x. To use the shader in 3.x, some changes will be needed:

void vertex() {
    // billboard mode from the default material
    MODELVIEW_MATRIX = VIEW_MATRIX * mat4(vec4(normalize(cross(vec3(0.0, 1.0, 0.0), INV_VIEW_MATRIX[2].xyz)), 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(normalize(cross(INV_VIEW_MATRIX[0].xyz, vec3(0.0, 1.0, 0.0))), 0.0), MODEL_MATRIX[3]);
    VERTEX += grow * NORMAL;

void fragment() {
    float d = dot(NORMAL.zx, vec2(-1, 0));
    float angle = -acos(NORMAL.z) + TAU/2.0;
    float current_view = NORMAL.x > 0.0 ? angle/TAU : 1.0 - angle/TAU;
    float x = 0.0; // UV horizontal start for the view in the atlas
    x = round(current_view * view_count) / view_count;
    float view_width = 1.0 / view_count; // UV width of the view in the atlas
    vec4 tex = texture(views, vec2(x + view_width * UV.x, UV.y));
    ALBEDO = tex.rgb;
    ALPHA = round(tex.a);