Hello Guest, please login or register.
Did you miss your activation email?
Login with username, password and session length.

Pages: [1]   Go Down

Author Topic: Link Movement  (Read 3180 times)

0 Members and 1 Guest are viewing this topic.
Link Movement
« on: May 18, 2009, 09:46:01 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Yeah, I am still trying to figure out how to do corner-cutting and seeing if I can make Goodnight's engine more effective. The current step event code looks like this:
Code: [Select]
/***************************************************************
 PRIORITIZED MOVEMENT SCRIPT
 Code Source: www.zfgc.com
 Contributors:
 - 4Sword: Switch statement based movement checking/correction,
     collision based on bounding boxes
 - Goodnight: Original movement system concept
 IF USED, DO NOT REMOVE THIS CREDIT INFORMATION AND MAKE SURE TO
 CREDIT ZFGC (aka. ZELDA FAN GAME CENTRAL) PUBLICALLY SO THAT
 THIS CODE IS NOT MISTAKEN FOR YOUR OWN OR CONSIDERED STOLEN.
***************************************************************/

var hold_u, hold_d, hold_l, hold_r, move_f, coll_obj;

hold_u = keyboard_check(vk_up);
hold_d = keyboard_check(vk_down);
hold_l = keyboard_check(vk_left);
hold_r = keyboard_check(vk_right);

if (hold_u && hold_d){
  hold_u = 0; hold_d = 0;
}

if (hold_l && hold_r){
  hold_l = 0; hold_r = 0;
}

switch (hold_u + hold_d + hold_l + hold_r){
  case 0:
    is_moving = false; move_f = 0; move_count = 0;
    break;
  case 1:
    is_moving = true; move_f = 1; move_count = 0;
    if (hold_u)
      direction = 90;
    else if (hold_d)
      direction = 270;
    else if (hold_l)
      direction = 180;
    else
      direction = 0;
    break;
  case 2:
    is_moving = true; move_f = sqrt(2);
    switch (direction){
      case 90:
        if (hold_d)
          direction =  270;
        break;
      case 270:
        if (hold_u)
          direction = 90;
        break;
      case 180:
        if (hold_r)
          direction = 0;
        break;
      case 0:
        if (hold_l)
          direction = 180;
        break; 
    }   
    break;
}

if (move_f != 0){
  move_count += move_speed;
  while (move_count >= 1){
    if (place_free(x + hold_r - hold_l, y))
      x += hold_r - hold_l;
    else if (hold_u + hold_d == 0){
      //coll_obj = instance_position(x + hold_r - hold_l,y,objSolid);
     
    }

    if (place_free(x, y + hold_d - hold_u))
      y += hold_d - hold_u;
    else if (hold_l + hold_r == 0){
      //coll_obj = instance_position(x,y + hold_d - hold_u,objSolid);

    }
   
    move_count -= move_f;
  }
}

The coll_obj local variable was something I was testing and just left in so I could return to it later and pick up on my train of thought from where I left off. I tried using the "motion planning" as mentioned in the GML Help file and instance_nearest, but those gave odd results - so does instance_position - you have to align just right or else you'll get -4 for the function return value which I guess indicates noone or no instance found at that position.

Beyond that though, I think I made a slight change to the non-diagonal movement code. If, for example, you are moving non-diagonally and you are not pressing up/down/left, you must be pressing right so you do not need to evaluate the condition that right is pressed in that situation.

Another thing I was thinking about is that Game Maker old seems to calculate the square root of 2 to be a certain floating decimal value, so would it be possible to just use that as a constant in the Global Game Settings? This would remove the calls to the square root function which might seem a little minor but it would possibly improve efficiency just a bit.

Logged
Re: Link Movement
« Reply #1 on: May 19, 2009, 07:01:31 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Not really an update but a different thought. The way the collision system works is that it is preventative, meaning that it checks to see what would happen at a position if Link were to be there and there is a collision. Another method of doing this is the opposite, where Link is there and there is a collision and it corrects. I am currently trying to see if I can use the second method to see if it gives me more to work with.
Logged
Re: Link Movement
« Reply #2 on: May 21, 2009, 07:05:49 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Seems like I am the only one posting, but I guess that other people are a little busy. Anyway, I have added more to my movement code so that I can more easily determine the object that is getting ran into. However, most of my work was done in a new GML file with just the basics so that I would have less to sift through and so I could focus on the collision corner cutting at hand. I also decided to going back to having the origin be set at 0,0 for the masks/shadows so that calculations would be or would appear easier.

First off, I had to declare two new variables in the Create Event, that are:
Code: [Select]
width = sprite_get_width(mask_index);
height = sprite_get_height(mask_index);
These are for calculations later used in the collision code.

The collision code which is not done but it does find the object you are running into quite effectively:
Code: [Select]
if (move_f != 0){
  move_c += move_s;
  while (move_c >= 1){
    if (place_free(x + hold_r - hold_l, y))
      x += hold_r - hold_l;
    else if (hold_u + hold_d == 0){
      coll_obj = instance_position(x + hold_r - hold_l + width * hold_r,y,objSolid);
      
      if (coll_obj == noone){
        coll_obj = instance_position(x + hold_r - hold_l + width * hold_r,y + height,objSolid);
      }
    }      

    if (place_free(x, y + hold_d - hold_u))
      y += hold_d - hold_u;
    else if (hold_l + hold_r == 0){
      coll_obj = instance_position(x,y + hold_d - hold_u + height * hold_d,objSolid);
      
      if (coll_obj == noone){
        coll_obj = instance_position(x + width,y + hold_d - hold_u + height * hold_d,objSolid);
      }
    }
    
    move_c -= move_f;
  }
}

It works on the following premises. If you are to the right of something you run into, your x is on it. If you are to the left of something you run into, you x is as far as your width away from it. instance_position depends on calculating it like that. Analyzing part of it, at:

coll_obj = instance_position(x + hold_r - hold_l + width * hold_r,y,objSolid);

The bolded part is kind of interesting to me. Basically, if you are going right, it will apply the "correction" so that it can do the instance_position correctly. If you are going left, the correction is not done because hold_r is 0 - and cannot be 1 if going left due to an earlier correction in the step event that cancels hold_l and hold_r if both pressed.

Additionally:

Code: [Select]
if (coll_obj == noone){
  coll_obj = instance_position(x + hold_r - hold_l + width * hold_r,y + height,objSolid);
}

If you are colliding with an object, there is a collision. Thus, if your coll_obj (the collision object, what you are running into) is noone (a built-in variable that indicates nothing at that position is getting run into), then another "correction" is made. The error will only happen if you are above the coll_obj. Thus it checks the collision at the bottom.

I also made it so there was an object called objSolid which is the parent to all collision objects.


Edit:

There actually is a problem with the code I just figured out but it is minor. Basically, if you have a spherical collision on the "tips" of that spherical object, it will say no object is getting ran into. This is fixable by a "correction" but I haven't added it in yet.
« Last Edit: May 21, 2009, 07:10:24 pm by 4Sword »
Logged
Re: Link Movement
« Reply #3 on: May 21, 2009, 08:03:33 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Wait, hold up, I'm being an idiot again. Turns out that what I was trying to do is alright if instance_position is to be used. However, if I use the instance_place function, it does what I need. In essence, the width, height, etc. calculations are unneeded.
Code: [Select]
if (move_f != 0){
  move_c += move_s;
  while (move_c >= 1){
    if (place_free(x + hold_r - hold_l, y))
      x += hold_r - hold_l;
    else if (hold_u + hold_d == 0){
      coll_obj = instance_place(x + hold_r - hold_l,y,objSolid);
    }      

    if (place_free(x, y + hold_d - hold_u))
      y += hold_d - hold_u;
    else if (hold_l + hold_r == 0){
      coll_obj = instance_place(x,y + hold_d - hold_u,objSolid);
    }
    
    move_c -= move_f;
  }
}

Well the width, height stuff might help out on the checking to see if there is just a difference of zero between the player and the wall, but then again, this might be stupid.
« Last Edit: May 21, 2009, 08:05:33 pm by 4Sword »
Logged
Re: Link Movement
« Reply #4 on: June 03, 2009, 02:20:55 am »
  • *
  • Reputation: +9/-0
  • Offline Offline
  • Posts: 728
I'll be on board again soon, been busy. I guess summer has that effect on people. Plus I made it a point to finish GMare before I put time into other things. Seems like you got things together though. Heh, I did something similar with Adventures of Lolo 3, was using instance_position, when instance_place was easier.
Logged
  • Pyxosoft
Re: Link Movement
« Reply #5 on: June 03, 2009, 02:59:39 am »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Nah, it's cool don't worry about it. My progress on this is a little stunted as well due to lack of discipline/focus - it seems that I mostly learn through constant failure, exhausting possibilities until all I can do after is succeed to some degree.

Besides trying many different ways to do it, I've been looking over Goodnight's stuff and so far all that has been able to be significantly changed for the better has been representing the square root of 2 as a floating number and not a function call, using the sum of the key states to determine a move factor, possibly fixing a small movement delay from diagonal to non-diagonal movement, and only correcting the sprites when the possibility arises for them to !@#$% up. The corner-cutting is a !@#$%, lol.

But yeah, everyone gets busy.
Logged
Re: Link Movement
« Reply #6 on: June 04, 2009, 11:14:56 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
Update:

I think I have come up with an ideal solution finally. I eliminated the need for the for loop sensitivity which might be possible to the overlap check. I was also able to make it so the code for the collision can be reactive and based on all objects which are objSolid or are children of objSolid - it doesn't have to check all the solid objects for collision and because it is reactive, the code can be put into "chunks". That might be a little vague, but like if you walked over something that made you slower, you'd be able to put that code in there without alteration to the corner cutting code too much.

Anyway, I probably !@#$% something up as I tend to do so I was wondering if you could any of you could look through it and try to get any bugs or errors. At the end I was considering some concave objects but coding for them might not be necessary. Also, I'll probably be working on a sub-function for the collision stuff.

File is attached.

Edit: lol, I just realized that in some of my place_meeting function calls, I could have also used the collobj variable. More efficiency.
« Last Edit: June 04, 2009, 11:48:32 pm by 4Sword »
Logged
Re: Link Movement
« Reply #7 on: June 17, 2009, 09:34:15 pm »
  • *
  • Reputation: +8/-0
  • Offline Offline
  • Gender: Male
  • Posts: 6604
I rewrote some of the code to be even more efficient. I had a dumb idea thinking that something like hold_u + hold_d == 0 was comparison-wise faster than !hold_u && !hold_d - the former has to add and then do a boolean comparison, in the latter if the first condition is false, then it's done. So the former always does two things while the latter does either one thing or two.

I was also able to do that collobj replacement for the objSolid in the corner-cutting. The overlap variable was unnecessary as well since the evaluation of it was only used once in the code chunk. The direction changing code for non-diagonal movement is also more efficient. Additionally, I added an "able to move" and "able to change direction" variable which is useful for later.

However, I realized that the movement engine would be better off if it was broken up to work on the x and y separately as there can be times like when dashing when an x-speed is different from a y-speed. There are also times when you want to limit either x or y movement, so breaking it up improves that. I haven't finished all of this yet, but here is the code for some of the above changes:
Code: [Select]
var hold_u, hold_d, hold_l, hold_r, collobj;
if (able_m){
  hold_u = keyboard_check(vk_up);
  hold_d = keyboard_check(vk_down);
  hold_l = keyboard_check(vk_left);
  hold_r = keyboard_check(vk_right);
  if (hold_u && hold_d){
    hold_u = 0; hold_d = 0;
  }
  if (hold_l && hold_r){
    hold_l = 0; hold_r = 0;
  }
  switch (hold_u + hold_d + hold_l + hold_r){
    case 0:
      move_f = 0;
      break;
    case 1:
      move_f = 1; move_c = 0;
      if (able_d){
        direction = hold_u * 90 + hold_d * 270 + hold_l * 180;
      }
      break;
    case 2:
      move_f = 1.4142135620;
      if (able_d){
        switch (direction){
          case 90:
            if (hold_d)
              direction =  270;
            break;
          case 270:
            if (hold_u)
              direction = 90;
            break;
          case 180:
            if (hold_r)
              direction = 0;
            break;
          case 0:
            if (hold_l)
              direction = 180;
            break;
        }  
      }
      break;
  }
  if (move_f > 0){
    move_c += move_s; blink = 0;
    while (move_c >= 1){
      x += hold_r - hold_l;
      if (place_meeting(x,y,objSolid)){
        collobj = instance_place(x,y,objSolid);
        if (!hold_u && !hold_d){
          if (bbox_top > collobj.bbox_bottom || bbox_bottom < collobj.bbox_top || bbox_left + hold_l > collobj.bbox_right || bbox_right - hold_r < collobj.bbox_left){
            x -= hold_r - hold_l;
          }
          else{
            move_f = 1.4142135620;
            if (!place_meeting(x,y - 1,collobj)){
              y -= 1;
            }
            else if (!place_meeting(x,y + 1,collobj)){
              y += 1;
            }
            else if (!place_meeting(x - hold_r + hold_l,y - height div 2,collobj)){
              x -= hold_r - hold_l; y -= 1;
            }
            else if (!place_meeting(x - hold_r + hold_l,y + height div 2,collobj)){
              x -= hold_r - hold_l; y += 1;
            }
          }
        }
        else{
          x -= hold_r - hold_l;  
        }
      }
      y += hold_d - hold_u;
      if (place_meeting(x,y,objSolid)){
        collobj = instance_place(x,y,objSolid);
        if (!hold_l && !hold_r){
          if (bbox_top + hold_u > collobj.bbox_bottom || bbox_bottom - hold_d < collobj.bbox_top || bbox_left > collobj.bbox_right || bbox_right < collobj.bbox_left){
            y -= hold_d - hold_u;
          }
          else{
            move_f = 1.4142135620;
            if (!place_meeting(x - 1,y,collobj)){
              x -= 1;
            }
            else if (!place_meeting(x + 1,y,collobj)){
              x += 1;
            }
            else if (!place_meeting(x - width div 2,y - hold_d + hold_u,collobj)){
              x -= 1; y -= hold_d - hold_u;
            }
            else if (!place_meeting(x + width div 2,y - hold_d + hold_u ,collobj)){
              x += 1; y -= hold_d - hold_u;
            }
          }
        }
        else{
          y -= hold_d - hold_u;  
        }    
      }
      move_c -= move_f;
    }
  }
}


Logged
Pages: [1]   Go Up

 


Contact Us | Legal | Advertise Here
2013 © ZFGC, All Rights Reserved



Page created in 0.018 seconds with 50 queries.