1. Hey! Guest! The 35th GMC Jam will take place between November 28th, 12:00 UTC - December 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice
  2. Hello Guest! It's with a heavy heart that we must announce the removal of the Legacy GMC Archive. If you wish to save anything from it, now's the time! Please see this topic for more information.
    Dismiss Notice

Perfect Twin Stick Top Down and WASD Keyboard + Mouse Controller

Discussion in 'Tutorials' started by tagwolf, Aug 6, 2016.

  1. tagwolf

    tagwolf Member

    Joined:
    Aug 6, 2016
    Posts:
    59
    GM Version: Studio latest version
    Target Platform: Windows
    Summary: keyboard and other controllers, and debugging stats display

    I posted this on the old forums. It has a great reception there. So I'm reposting it here in the hopes it will help people again.

    It contains a TON of debugging stats displayed on the GUI. Things like controller input levels, direction, speed, etc. Debugging is togglable via a single variable as well.

    ALL logic, is contained in a single object called obj_controller.

    It should be simple enough for most to adjust and integrate into their projects.

    You can switch between XBOX 360 controller / Gamepad and Mouse / WASD by switching a single variable in the step event called controller_using_gamepad = 1;

    To make the entire thing work, you should only need a player sprite for standing and walking, obj_player, and a room. But I'll see if I can link the project file itself in a bit.

    I'm posting the code below as I don't see a way to upload attachments on the forum (I'll keep looking):

    Code:
    Information about object: obj_controller
    Sprite:
    Solid: false
    Visible: true
    Depth: 0
    Persistent: false
    Parent:
    Children:
    Mask:
    
    No Physics Object
    Create Event:
    
    execute code:
    
    ///Debug Variables
    
    //display_debug = false;
    display_debug = true;
    
    execute code:
    
    /// Controller Initial Variables
    // (Prevents out of order initialization issues)
    
    // Device ID
    controller_id = 0; // Default ID
    
    // Deadzones
    controller_deadzone_stick = 0.2;
    controller_deadzone_trigger = 0.1;
    
    // Mouse
    controller_mouse_active = 1;
    controller_mousevx_last = 0;
    controller_mousevy_last = 0;
    //controller_mousevx = 0;
    //controller_mousevy = 0;
    
    execute code:
    
    /// Player Variables
    player_speed = 2;
    player_turn_speed = 10;
    player_moving = false;
    player_turning = false;
    player_angle_penalty_factor = .5;
    player_angle_difference = 0;
    player_angle_walking_penalty = 0;
    player_angle_walking_difference = 0;
    player_animation_speed = 1;
    //player_direction = 0;
    //player_hspeed = 0;
    //player_vspeed = 0;
    //obj_player.hspeed = 0;
    //obj_player.vspeed = 0;
    
    execute code:
    
    /// Camera Variables
    
    camera_angle = 0;
    camera_zoom = 0;
    
    // absolute
    camera_x = 0;
    camera_y = 0;
    
    // relation to player
    camera_player_x = 0;
    camera_player_y = 0;
    
    Alarm Event for alarm 0:
    
    execute code:
    
    /// Player Delayed Standing Animation
    obj_player.image_speed = 0;
    obj_player.image_index = 0;
    obj_player.sprite_index = spr_player_standing;
    
    Alarm Event for alarm 1:
    
    execute code:
    
    /// Mouse timeout
    controller_mouse_active = 0;
    
    Step Event:
    
    execute code:
    
    /// Player Movement
    
    // todo: update to smoother method?
    // todo: auto roomsize calculations for speed?
    // todo: change to a directional based movement system?
    // todo: add rotation (DONE)
    // todo: Slower movement based on direction facing (USE angle difference between axis l dir and (abs)player dir!) (DONE!)
    
    // todo: accelleration and decelleration in motion
    
    // todo: running (controller button)
    // todo: running (keyboard)
    
    // todo: automatic controller / keyboard timeout based on usage
    //controller_using_gamepad = 0;
    controller_using_gamepad = 1;
    
    // Controller Movement
    if controller_using_gamepad == 1 {
       // Still Player
       if (controller_axislh == 0) && (controller_axislv == 0) {
          //obj_player.hspeed = 0;
          //obj_player.vspeed = 0;
          obj_player.speed = 0;
          player_moving = false;
       }
       // Still Right Stick
       if (controller_axisrh == 0 && controller_axisrv == 0) || (player_angle_difference == 0) {
          player_turning = false;
       }
    
        // Move Player
        if (controller_axislh != 0) || (controller_axislv != 0) {
           player_moving = true;
           // calculate facing direction vs walking direction for movement speed
           player_angle_walking_difference = abs(angle_difference(obj_player.image_angle, controller_axisl_direction));
           player_angle_walking_penalty = (player_angle_walking_difference / 180);
        
           // Switching to DIRECTIONAL movement!
           obj_player.direction = controller_axisl_direction;
           obj_player.speed = (controller_axisl_max_speed * player_speed) - (player_angle_walking_penalty / 2);
        }
    
        // Turn Player
        // R.stick directional controls
        if (controller_axisrh != 0 || controller_axisrv != 0) {
           player_angle_difference = angle_difference(controller_axisr_direction, obj_player.image_angle);
           if (player_angle_difference != 0) {
              player_turning = true;
              player_turn_rate = player_turn_speed * controller_axisr_speed;
              obj_player.image_angle += median(-player_turn_rate, player_angle_difference, player_turn_rate);
              // stop angle from growing forever
              if (abs(obj_player.image_angle) >= 360) {
                 obj_player.image_angle = 0;
              }
           }
        }
    
        // If not using r.stick to point, player should turn towards angle of travel.
        // todo: make l.stick slowly turn player in direction of walking if not touching r.stick (DONE)
        if (player_moving = true) && (controller_axisrh == 0 && controller_axisrv == 0) { //&& (player_angle_difference != 0) {
           player_angle_difference = angle_difference(controller_axisl_direction, obj_player.image_angle);
           if (player_angle_difference != 0) {
              player_turning = true;
              player_turn_rate = player_turn_speed * controller_axisl_max_speed;
              obj_player.image_angle += median(-player_turn_rate, player_angle_difference, player_turn_rate);
              // stop angle from growing forever
              if (abs(obj_player.image_angle) > 360) {
                 obj_player.image_angle = 0;
              }
           }
        }
    }
    
    // Keyboard Movement
    if controller_using_gamepad == 0 {
       // Still Player
       if (controller_keyboardh == 0) || (controller_keyboardv == 0) {
          obj_player.speed = 0;
          player_moving = false;
       }
       // Move Player
       if (controller_keyboardh != 0) || (controller_keyboardv != 0) {
          player_angle_walking_difference = abs(angle_difference(obj_player.image_angle, controller_keyboard_direction));
          player_angle_walking_penalty = (player_angle_walking_difference / 180);
          player_direction = controller_keyboard_direction;
          player_moving = true;
          obj_player.direction = player_direction;
          obj_player.speed = player_speed - (player_angle_walking_penalty / 2);
       }
       // Turning
       // check if player image is @ mouse pointer angle, if not, turn player
       // todo: add mouse timeout, turn player towards direction of travel until pointer moves. Maybe 5-10 seconds?
    
       // test
       controller_mousevx = mouse_x - view_xview[0];
       controller_mousevy = mouse_y - view_yview[0];
    
       // check if mouse is active
       if (controller_mousevx_last == controller_mousevx) && (controller_mousevy_last == controller_mousevy) {
          // start timeout for mouse inactive
          if alarm[1] = -1 { alarm[1] = 120; } // 2 seconds
       } else {
          controller_mouse_active = 1;
       }
       // store last known mouse relative coordinates
       controller_mousevx_last = controller_mousevx;
       controller_mousevy_last = controller_mousevy;
    
       if controller_mouse_active = 1 {
          // calculate player facing vs mouse direction
          player_angle_difference = angle_difference(controller_mouse_direction, obj_player.image_angle);
      
          if (player_angle_difference != 0) {
             player_turning = true;
             player_turn_rate = player_turn_speed;
             obj_player.image_angle += median(-player_turn_rate, player_angle_difference, player_turn_rate);
             // stop angle from growing forever
             if (abs(obj_player.image_angle) >= 360) {
                obj_player.image_angle = 0;
             }
          }
       }
    }
    
    
    execute code:
    
    /// Player Animation
    
    // Still Player
    if (player_moving == false) && (player_turning == false) {
       // Slow Final Frames
       obj_player.image_speed = 0.06;
       // Delay before spr_player_standing
       if alarm[0] = -1 {
          alarm[0] = 30; // 60 (roomspeed) / 30 (steps) = 0.5 seconds
       }
    }
    
    // Animate Player
    // Gamepad
    if (controller_using_gamepad == 1) {
       if (player_moving == true) && (player_turning == true) && (controller_axisl_max_speed > .10) {
          obj_player.sprite_index = spr_player_walking;
          obj_player.image_speed = (player_animation_speed * controller_axisl_max_speed) - (player_angle_walking_penalty / 2);
       } else if (player_moving == true) && (player_turning == false) && (controller_axisl_speed > .10) {
          obj_player.sprite_index = spr_player_walking;
          obj_player.image_speed = (player_animation_speed * controller_axisl_max_speed) - (player_angle_walking_penalty / 2);
       } else if (player_moving == false) && (player_turning == true) && (abs(player_angle_difference) > 10) { // fixed animation jitter
          obj_player.sprite_index = spr_player_walking;
          obj_player.image_speed = (player_animation_speed * controller_axisr_max_speed) / 2; // half speed for turning
       }
    }
    // Keyboard and Mouse
    if (controller_using_gamepad == 0) {
       if (player_moving == true) && (player_turning == true) {
          obj_player.sprite_index = spr_player_walking;
          // todo, add turn speed based on mouse angle difference / speed over time?
          obj_player.image_speed = player_animation_speed - (player_angle_walking_penalty / 2);
       } else if (player_moving == true) && (player_turning == false) {
          obj_player.sprite_index = spr_player_walking;
          obj_player.image_speed = player_animation_speed - (player_angle_walking_penalty / 2);
       } else if (player_moving == false) && (player_turning == true) && (abs(player_angle_difference) > 10) { // fixed animation jitter
          obj_player.sprite_index = spr_player_walking;
          obj_player.image_speed = player_animation_speed / 2; // half speed for turning
       }
    }
    
    execute code:
    
    /// Camera Control
    //view_angle[0] = camera_angle;
    
    //camera_angle++;
    
    //if (camera_angle >= 360) { camera_angle = 0; }
    
    Begin Step Event:
    
    execute code:
    
    /// Get Controller Values
    
    // todo: Check for controller (at interval in alarm)
    // todo: device initialization
    
    // Left Stick
    // todo: improve total movement (maybe some func like point_direction?)
    controller_axislh = gamepad_axis_value(0, gp_axislh);
    controller_axislv = gamepad_axis_value(0, gp_axislv);
    controller_axisl_direction = point_direction(0, 0, controller_axislh, controller_axislv);
    controller_axisl_total = controller_axislh + controller_axislv;
    controller_axisl_speed = mean(abs(controller_axislh),abs(controller_axislv));
    controller_axisl_max_speed = max(abs(controller_axislh),abs(controller_axislv));
    
    // Right Stick (direction of player)
    // todo: add angle_difference and median to move towards point
    controller_axisrh = gamepad_axis_value(0, gp_axisrh);
    controller_axisrv = gamepad_axis_value(0, gp_axisrv);
    controller_axisr_direction = point_direction(0, 0, controller_axisrh, controller_axisrv);
    controller_axisr_total = controller_axisrh + controller_axisrv;
    controller_axisr_speed = mean(abs(controller_axisrh),abs(controller_axisrv));
    controller_axisr_max_speed = max(abs(controller_axisrh),abs(controller_axisrv));
    
    // Keyboard
    controller_keyboardh = keyboard_check(ord("D"))-keyboard_check(ord("A"));
    controller_keyboardv = keyboard_check(ord("S"))-keyboard_check(ord("W"));
    controller_keyboard_direction =  point_direction(0,0,controller_keyboardh,controller_keyboardv);
    
    // Mouse
    controller_mousevx = mouse_x - view_xview[0];
    controller_mousevy = mouse_y - view_yview[0];
    controller_mouse_direction = point_direction(obj_player.x, obj_player.y, mouse_x, mouse_y);
    
    Async Event: System:
    
    execute code:
    
    /// Controller Event Handler
    show_debug_message("[Event] " + async_load[? "event_type"]);
    show_debug_message("[Gamepad] " + string(async_load[? "pad_index"]));
    
    switch(async_load[? "event_type"]) {
    case "gamepad discovered":
         var controller_id = async_load[? "pad_index"];
         var controller_connected = true;
         gamepad_set_axis_deadzone(controller_id, controller_deadzone_stick);
         gamepad_set_button_threshold(controller_id, controller_deadzone_trigger);
         break;
    case "gamepad lost":
         var controller_id = async_load[? "pad_index"];
         var controller_connected = false;
         break;
    }
    
    
    Draw Event:
    
    execute code:
    
    /// Controller Debug
    
    if display_debug = true {
    // device settings
    draw_text(0,0,"id: " + string(controller_id));
    draw_text(0,20,"desc: " + gamepad_get_description(controller_id));
    draw_text(0,40,"stick deadzone: " + string(controller_deadzone_stick));
    draw_text(0,60,"trigger deadzone: " + string(controller_deadzone_trigger));
    
    // keyboard and mouse
    draw_text(0,100,"keyboard h: " + string(controller_keyboardh));
    draw_text(0,120,"keyboard v: " + string(controller_keyboardv));
    draw_text(0,140,"keyboard dir: " + string(controller_keyboard_direction));
    draw_text(0,160,"mouse x: " + string(mouse_x));
    draw_text(0,180,"mouse y: " + string(mouse_y));
    draw_text(0,200,"mouse rel x: " + string(controller_mousevx));
    draw_text(0,220,"mouse rel y: " + string(controller_mousevy));
    draw_text(0,240,"mouse dir: " + string(controller_mouse_direction));
    draw_text(0,260,"mouse rel x stored: " + string(controller_mousevx_last));
    draw_text(0,280,"mouse rel y stored: " + string(controller_mousevy_last));
    draw_text(0,300,"mouse active: " + string(controller_mouse_active));
    
    // left Axis
    draw_text(500,0,"axislh: " + string(controller_axislh));
    draw_text(500,20,"axislv: " + string(controller_axislv));
    draw_text(500,40,"axis l dir: " + string(controller_axisl_direction));
    draw_text(500,60,"axis l total: " + string(controller_axisl_total));
    draw_text(500,80,"axis l speed: " + string(controller_axisl_speed));
    draw_text(500,100,"axis l max speed: " + string(controller_axisl_max_speed));
    // right axis
    draw_text(700,0,"axisrh: " + string(controller_axisrh));
    draw_text(700,20,"axisrv: " + string(controller_axisrv));
    draw_text(700,40,"axis r dir: " + string(controller_axisr_direction));
    draw_text(700,60,"axis r total: " + string(controller_axisr_total));
    draw_text(700,80,"axis r speed: " + string(controller_axisr_speed));
    draw_text(700,100,"axis r max speed: " + string(controller_axisr_max_speed));
    
    // player info
    draw_text(900,0,"player x: " + string(obj_player.x));
    draw_text(900,20,"player y: " + string(obj_player.y));
    draw_text(900,40,"player moving: " + string(player_moving));
    draw_text(900,60,"player turning: " + string(player_turning));
    draw_text(900,100,"player dir diff: " + string(player_angle_difference));
    draw_text(900,120,"player dir: " + string(obj_player.image_angle));
    draw_text(900,140,"player walk angle diff: " + string(player_angle_walking_difference));
    draw_text(900,160,"player walk penalty: " + string(player_angle_walking_penalty));
    draw_text(900,200,"player x input spd [" + string(player_speed) + "] * axislh [" + string(controller_axislh) + "]: " + string((player_speed * controller_axislh)));
    draw_text(900,220,"player y input spd [" + string(player_speed) + "] * axislv [" + string(controller_axislv) + "]: " + string((player_speed * controller_axislv)));
    draw_text(900,240,"player x speed: " + string(obj_player.hspeed));
    draw_text(900,260,"player y speed: " + string(obj_player.vspeed));
    }
    
    
     
    Last edited by a moderator: Aug 6, 2016
  2. I'd love to see this as a project file if you ever do get around to it!
     
  3. datiel12

    datiel12 Guest

    I Can't Seem to Get it to work. i made the obj_controller and Pasted the Script in each of their seperate actions. and Set the controller_using_gamepad = 1 to equal 0.
    and i am unable to walk around using wasd. Help? Also, You can't attach things to posts. (I Dont Think) just upload it to dropbox and then link it into your post.
     
  4. jazzzar

    jazzzar Member

    Joined:
    Jun 29, 2016
    Posts:
    514
    Just make a project out of it, compress it and upload to dropbox or any other site and post the link here
     
    foxtails and csanyk like this.
  5. ISOREX

    ISOREX Member

    Joined:
    Feb 10, 2017
    Posts:
    3
    I succesfully added this to my game, but since I'm fairly new to GameMaker I was wondering how easy it is for this to implement multiplayer? Like multiple controllers.
    Also, if my game ever gets released, do you want me to mention your name in the credits?
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice