Legacy GM Need a little help with trig

Dackers

Member
Hi. I'm playing around with the physics engine and creating a small top down space shooter. I'm working on the enemy states and now have the enemy detect the player, slowly rotate towards player, pursue player and fire whenever the player is within the small window of attack (few degrees left and right of head-on).

My problem is I'd like the enemy to avoid the player whenever it gets within a certain distance by rotating left or right, flying off and coming back around (i.e., not crash into player). I'm okay on the "flying off" and "coming back around" part, but I cannot for the life of me figure out how to make the enemy aim its phy_rotation left or right of the player (say 30-45 degrees or so).

Here's my code for rotating to orient with the player, as an example:
Code:
// Turn towards player
var dir_to_player = -point_direction(phy_position_x,phy_position_y,obj_player.phy_position_x,obj_player.phy_position_y);
var angle_dif = angle_difference(dir_to_player, phy_rotation);

phy_rotation += median(-turn_speed, turn_speed, (ease_turn_rate) * angle_dif); // Rotate slowly towards player
physics_apply_local_force(com_x, com_y, accel/6, 0); // Accelerate slowly while rotating towards player
physics_apply_local_force(com_x, com_y, 0, accel/8 * sign(angle_dif)); // Slight lateral force in direction out from turn
I haven't taken trig in a VERY long time and I just don't know what the formula/function is to aim left and right of player (and hopefully, if i'm not asking too much, choosing left or right opposite of the direction the player may be turning or moving).

I hope I've made sense. Thanks for any help you can provide
 

geky

Member
Have not used the physics bit yet in Game Maker, so I am afraid I can't give you any input there. However, under the presumption I understood your query, maybe this can help you get started (unless, of course, you have already managed to solve it). Just copy and paste the whole bunch into the draw event of an empty object in a new project and run. Just be aware that it could probably be done in a more efficient way -- let's leave that to the more talented than I.

Code:
// note: uses image_index to make globalvars for this demo so you can simply
// paste everything into a draw event of an object in a new project for test
    if (image_index != -1)
    {
        image_index = -1;            // so it won't overwrite @next step
        image_speed =  0;

        globalvar enemy_x, enemy_y;
        globalvar enemy_a, enemy_v;
        globalvar enemy_flee_dist;
        globalvar enemy_target_a;
        globalvar enemy_angular_v;
        globalvar enemy_ai_stage;
        globalvar enemy_ai_timer;
            enemy_x = room_width/2;  // start position
            enemy_y = room_height/2;
            enemy_a = 90;            // start angle
            enemy_v = 5;            // velocity
            enemy_angular_v = 5;
            enemy_flee_dist = 100;   // distance from player when enemy flee
            enemy_ai_stage = 0;      // 0: move to player
                                     // 1: flee from player
                                     // 2: wait for orders
            enemy_ai_timer = 0;      // amount of steps spent waiting
            
        globalvar player_x, player_y;
        globalvar player_old_x;
        globalvar player_old_y;
        globalvar player_old_d;
        globalvar player_move_d;
            player_x = mouse_x;
            player_y = mouse_y;
            player_old_d = 0;
            player_old_x = 0;
            player_old_y = 0;
            player_move_d = 0;
    }
// -------------------------------------------------------------------------


// simple player movement
    var player_move_d_old = player_move_d;
    player_old_x = player_x; player_old_y = player_y;
    player_x = mouse_x; player_y = mouse_y;
    var player_move_x = player_x - player_old_x;
    var player_move_y = player_y - player_old_y;
    var player_move = (player_move_x<>0) || (player_move_y<>0);
    if (player_move)
    {
        player_move_d = point_direction( player_old_x, player_old_y, player_x, player_y );
    }else{
        player_move_d = player_move_d_old;
    }

// simple enemy movement
    var enemy_hunt = 0; // ai stages
    var enemy_flee = 1;
    var enemy_wait = 2;
    
    // towards the player
    if (enemy_ai_stage == enemy_hunt)
    {
        enemy_target_a = point_direction( enemy_x, enemy_y, player_x, player_y );
        if (point_distance( enemy_x, enemy_y, player_x, player_y ) <= enemy_flee_dist)
            enemy_ai_stage = enemy_flee;
    }
    
    // away from player
    if (enemy_ai_stage == enemy_flee)
    {
        // player is moving
        if (!player_move == 0)
        {
            // where is the safest route - behind, right, or left of player?
            var behind_x = player_x-lengthdir_x( enemy_flee_dist, player_move_d ); // get behind info
            var behind_y = player_y-lengthdir_y( enemy_flee_dist, player_move_d );
            var check_behind = point_distance( enemy_x, enemy_y, behind_x, behind_y );
            var left_x = player_x-lengthdir_x( enemy_flee_dist, player_move_d-90 ); // get left info
            var left_y = player_y-lengthdir_y( enemy_flee_dist, player_move_d-90 );
            var check_left = point_distance( enemy_x, enemy_y, left_x, left_y );
            var right_x = player_x-lengthdir_x( enemy_flee_dist, player_move_d+90 ); // get right info
            var right_y = player_y-lengthdir_y( enemy_flee_dist, player_move_d+90 );
            var check_right = point_distance( enemy_x, enemy_y, right_x, right_y );
            
            // try to avoid collision with player using shortest route
            if (check_behind < check_left) && (check_behind < check_right) // target is behind player
            {
                enemy_target_a = point_direction( enemy_x, enemy_y, behind_x, behind_y );
            }
            if (check_left < check_behind) && (check_left < check_right) // target os tp tje ;eft
            {
                enemy_target_a = point_direction( enemy_x, enemy_y, left_x, left_y );
            }
            if (check_right < check_behind) && (check_right < check_left) // target is to the right
            {
                enemy_target_a = point_direction( enemy_x, enemy_y, right_x, right_y );
            }

        // player is not moving
        }else{
            // choose random side of player (at 45 degree interval)
            var player_direction = point_direction( enemy_x, enemy_y, player_x, player_y );
            enemy_target_a = player_direction + choose( -45, 45 );
        }
        enemy_ai_stage = enemy_wait;
    }
    
    // flee for 30 steps
    if (enemy_ai_stage == enemy_wait)
    {
        if (enemy_ai_timer == 1) enemy_ai_stage = enemy_hunt;
        enemy_ai_timer = (enemy_ai_timer+1)%31
    }
    
    // update enemy
    var aDiff = angle_difference( enemy_a, enemy_target_a );
    enemy_a -= min( abs(aDiff), enemy_angular_v ) * sign(aDiff);
    enemy_x += (lengthdir_x( enemy_v, enemy_a ));
    enemy_y += (lengthdir_y( enemy_v, enemy_a ));

    
    
// Draw..
    
// draw backdrop
    draw_set_colour( c_black );
        draw_rectangle( 0, 0, room_width, room_height, 0 );

// draw enemy
    var enemy_size = 20;
    var eX1 = enemy_x - (lengthdir_x( enemy_size/2, enemy_a+90 ));
    var eY1 = enemy_y - (lengthdir_y( enemy_size/2, enemy_a+90 ));
    var eX2 = enemy_x + (lengthdir_x( enemy_size/2, enemy_a+90 ));
    var eY2 = enemy_y + (lengthdir_y( enemy_size/2, enemy_a+90 ));
    var eX3 = enemy_x + (lengthdir_x( enemy_size, enemy_a ));
    var eY3 = enemy_y + (lengthdir_y( enemy_size, enemy_a ));
    if( enemy_ai_stage == enemy_hunt ) draw_set_colour( c_red );
    else draw_set_colour( c_yellow );
    draw_triangle( eX1, eY1, eX2, eY2, eX3, eY3, 0 );
// debug enemy
    var temp_xlen = lengthdir_x( 50, enemy_target_a );
    var temp_ylen = lengthdir_y( 50, enemy_target_a );
    var eX4 = eX3+temp_xlen;
    var eY4 = eY3+temp_ylen;
    draw_set_colour( c_teal );
        draw_line( eX3, eY3, eX4, eY4 );
        draw_circle( eX4, eY4, 2, 0 );


// draw player
    var player_size = 20;
    var pX1 = player_x - (lengthdir_x( player_size/2, player_move_d+90 ));
    var pY1 = player_y - (lengthdir_y( player_size/2, player_move_d+90 ));
    var pX2 = player_x + (lengthdir_x( player_size/2, player_move_d+90 ));
    var pY2 = player_y + (lengthdir_y( player_size/2, player_move_d+90 ));
    var pX3 = player_x + (lengthdir_x( player_size, player_move_d ));
    var pY3 = player_y + (lengthdir_y( player_size, player_move_d ));
    draw_set_colour( c_white );
        draw_triangle( pX1, pY1, pX2, pY2, pX3, pY3, 0 );
// debug player
    if (player_move)
    {
        var temp_xlen = lengthdir_x( 50, player_move_d );
        var temp_ylen = lengthdir_y( 50, player_move_d );
        var pX4 = pX3+temp_xlen;
        var pY4 = pY3+temp_ylen;
        draw_set_colour( c_teal );
            draw_line( pX3, pY3, pX4, pY4 );
            draw_circle( pX4, pY4, 2, 0 );
    }
 

Dackers

Member
This was quite helpful. I was able to modify some of your code to use as a starting point to have my enemy veer away from the player before colliding. Thank you.
 
Top