GameMaker: Studio

GameMaker: Studio

View Stats:
Overhead RPG movement and more forviging "wall hugging"
I've posted this in a couple areas, but I'm still struggling with it.

You know when you play Chrono Trigger, and Crono runs past the edge of a wall or table? He kind of slides to the nearest available path and keeps on going. This will also happen if he trys to run along walls that are oddly shapped in general. Running along or past walls and objects is pretty forgiving and you never really get "stuck" on anything.

Currently, I feel like I've been able to get my character in Game Maker to walk and run into walls or objects. I can hold two directions and my character will slide up a wall or object. It's very cool to be able to do that.

However, if I'm running by some block, and I'm just a pixel off, my character will stop dead in his tracks. I'd love to have something more forgiving. Like I said: If I could have my character slide to the nearest avaible path (if I'm close to the edge of something, of course).

Does anyone know of a way to accomplish this? Is it simple? My current code in my character's step event looks like this:

if key_left
{
if !place_meeting(x-speedup,y,par_wall) x -= speedup;

};

Am I going about this all the wrong way? Should I use a place_free command instead, and figure out some way to say "if the distance from X to the nearest available path is free, move the character along X until the path is free"?

< >
Showing 1-15 of 16 comments
Sera Jan 29, 2014 @ 7:15pm 
You're thinking along the right lines, certainly.

In the event of a collision at the point you're moving to, check to see if there's a free point in either perpendicular direction within a margin of error. The most accurate way to check for this would probably be a for loop, but the quickest way to just check if the concept works would be a simple "if collision at x, try x+3 and then x-3, move to whichever's open." This falls short if you have a wall too close or a thin enough object that the player could pheasibly wrap to either side of it hitting it from the middle, but for general situations it should work fine.
PogueSquadron Jan 30, 2014 @ 9:49am 
I'm wondering if something should actually be written in the Step Event of the wall itself? Like, "if there's a place free at x-3, and there's a collision with the player at (x,y-characterspeed), move the player to the left." I'm going to play around with it.
Sera Jan 30, 2014 @ 10:55am 
That's not a terrible notion, but it creates a lot of overhead; every active instance of the object is going to run that code every step. Also, it would be checking for free space with the wall's own collisions instead of the player's, which could be problematic very easily.

That said, if the instance id of the wall you've bumped into would be beneficial, you could try using instance_nearest() or some similar method, which returns the id of whatever it finds.
PogueSquadron Jan 30, 2014 @ 12:57pm 
Well I played around with things a bit more and feel like I almost have something that's about what I'm looking for.

Here's what I have for my left key input

"wall_buffer=20 //The number of pixels my character is to the edge of an object.

key_left
{
if !place_meeting(x-speedup,y,par_wall) x-=speedup; //If there's not a wall to the left, move left.
if place_meeting(x-speedup,y,par_wall) && place_free(x-12,y-wall_buffer) y-=speedup;//If there's a wall to the left AND there's a space free above and to the left, move up.
if place_meeting(x-speedup,y,par_wall) && place_free(x-12,y+wall_buffer) y+=speedup;//If there's a wall to the left AND there's a space free below and to the left, move down.

};"

It's actually kind of working the way I want it to, though I'm sure this isn't the most efficient way of doing this. My character moves unless there's a wall in front of him. If there IS a wall in front of him, AND if there's a place free at a steep diagonal (the, the character moves along the wall until the path is clear.

The trouble now seems to be coming in with:

a.) Skirting around circles (the behavior for whatever reason seems to work best on square objects or objects with square sprite/collision masks).
b.) The character doubles in speed if the player is holding a diagonal at an object edge, because it's taking speed from both the direction behind held AND the wall hugging code.
c.) Right now it's having trouble for some reason with objects that are too small.

I suppose the obvious workaround would just be that objects would have to be of a certain size for these commands to work. It's also been a bit tricky to figure out

What I suppose would further complicate things is that this is all just code as it pertains to my par_wall object. This is just a parent object that all of my 'walls' inherit (which at some point I suppose would include invisible tiles behind things like couches, tables, etc). Since I'm just kind of doing testing, I have this as a parent to a couple of different objects. If this was a real game, I suppose I would want to have different "wall hugging" behaviors on other objects - people for instance (where maybe I don't want such forgiving wall hugging behavior).

Edit: I've toyed with this for the past hour but still can't figure out how to resolve some issues.
1.) My guy keeps getting stuck on the corner of things. My current code is all for when you're going down, up, right, or left INTO the edge of something...but when you go in at a diagonal in the opposite direction (hard to articulate...may have to illustrate), things get funky.
2.) My guy still whips around corners. Right now, if he hits the edge, he'll start moving along an axis at his speed. However, if I'm holding two directions, the game takes THAT speed, PLUS the speed of the wall hugging, so he kind of whips around the edge for a moment. I want to be able to say "When you're holding Left and Right and you hug the corner, speed = speed/2." Maybe I'll have to make the wall hugging code a variable, and then say "If wall_hug_diagonal, speed = speed/2."

For the most part, it works great. if you run towards an edge, it behaves exactly the way I want it to. He even slides along large circles once he reaches a certain point on the circle. It's when you start running at diagonals that things get bizarre.
Last edited by PogueSquadron; Jan 30, 2014 @ 2:34pm
Sera Jan 30, 2014 @ 2:39pm 
Aye, but even if it's not working 100% you've got a mostly functional proof of concept to build on, which is a great start.

Purely for the sake of code efficiency, you might want to consider changing some of your ifs to else ifs and utilizing some curly braces, and I'll explain why. Presently your code is basically doing this:

{ if there's not a wall, move forward. if there is a wall and a place is free to the left, slide left. if there is a wall and a place is free to the right, slide right }

(My directions don't match yours completely here, because I guess I just find it easier to think in terms of moving north than anything else.)

There's a couple problems here, but the biggest one is that you'll always be going through all three checks; what may well be causing your problems with thin objects is that a thin enough object would return true on both checks on seeing if there's a place free to either side. The quickest solution there would be to convert the conditions into one logic tree:

{ if there is a wall { if a place is free to the left { slide left, move forward } else if there is a wall and a place is free to the right{ slide right, move forward } } else { move forward. } }

In this case, the two free position checks are only run if a wall is detected in the first place, and if the first returns true, the other is ignored because a solution has already been found. If no wall was detected to begin with, you simply move forward like normal. On top of avoiding conflicts, the only logical bit the program needs to process in this case for most steps will be "is there a wall?", which helps keep things running pretty smoothly.

Another thing you might want to try to help with the diagnal movement might be checking if the player's moving diagonally at all before running the slide checks, possibly opting to ignore them, say, if a perpendicular key is being held down. So, assuming we're working with the up key here:

{ if there is a wall { if neither the left or right keys are being held { if a place is free to the left { slide left, move forward } else if there is a wall and a place is free to the right{ slide right, move forward } } } else { move forward. } }

This still won't be perfect, but it'll stop the speed doubling. The problem is that if a wall is hit by either key's direction check, the player won't move at all in that direction unless you do *seperate* checks for diagonal movement entirely, at which point you might be better off moving this all into the Step event and working out the details there lest this become a logical nightmare, though an alternative might be to do different checks for up and down than left and right; in left and right's logic, you could put the entire thing in a conditional that it only runs if neither up or down is pressed, and then keep the diagonals restricted to the keys assigned to vertical movement so you don't get any weird doubling or conflicts in your collision checks.

EDIT: you updated your post before I finished mine, but hopefully this still helps a little or gives you some decent ideas.
Last edited by Sera; Jan 30, 2014 @ 2:40pm
PogueSquadron Jan 30, 2014 @ 3:08pm 
Definitely gives me a lot to think about! I was playing with "else" commands earlier but couldn't quite figure it out. "Else if" commands seem to maybe help out with what I'm trying to do.

Are you saying I shouldn't be doing this in the Step event? That is currently where I have this code. Where else would I be putting it? Aren't these all checks that need to be done every frame?
Sera Jan 30, 2014 @ 4:03pm 
Based on how you'd posted things before I'd assumed you had in in the keydown event for the left arrow key; the Step event is almost always the better way to do just about anything, imo, so no, you're fine if you're in there. That actually makes managing your logic a lot easier, because you don't have to worry about events negating each other so much.

if and else if are both super useful things, and else can be as well when used properly. Basically, if you cast an if condition and it returns true, everything after that statement in the brackets (or just for the rest of the line, if you're only performing one statement) gets executed, whereas anything after "else" occurs in the opposite scenario. It's a bit hard to pull off on the single-line instructions, especially if you want to come back to your code later and still be able to read it, but last I knew you could still do something like

if place_meeting(x, y, It) then TouchedIt+=1 else TheThing = true;

You'll probably want to look into switch statements, as well. I can't see getting far with an RPG project without at least knowing about those.
PogueSquadron Jan 30, 2014 @ 8:47pm 
Well thank you so much. This is helping a lot. My stuff feels more organized as of now. I'm still getting that speed doubling though, and I'm not sure why. Maybe I don't have the right commands for saying "if not holding the left or right keys"? There's just so much to learn in terms of how Game Maker wants you to write things. I guess that's learning any language though.

Looking at the Up key again, I have the following:

if key_up{ if place_meeting(x,y-speedup,par_wall){ if (not key_left) or (not key_right){ if place_free(x-wall_buffer, y-12) x-=speedup; else if place_free(x+wall_buffer, y-12) x+=speedup; }} else y-=speedup
Last edited by PogueSquadron; Jan 30, 2014 @ 9:14pm
Sera Jan 30, 2014 @ 9:23pm 
I think you've an or where an and should be when checking your horizontal keys. Because of how the logic works, you're making it so that the only way you'll get a total statement of false is if the player is holding both left and right.

Analyzing it, you get something like this:

if (not left) or (not right) ...

So that if the player is pressing left, you'll get

if (FALSE) or (TRUE)

Because the right key isn't being held in this scenario, the second statement returns true, so the or statement will return true as a whole. Meanwhile with and, you'd get

if (FALSE) and (TRUE)

and requires both statements to be true to advance; in this case, that's not what we're getting, but if we let go of both horizontal keys...

if (not left) and (not right)
if (TRUE) and (TRUE)

In less words, with the or statement, if any one thing is true, then the entire statement confirms as true. With the and statement, you're getting the opposite: if anything is false, the entire statement comes back as false.

As a further example, you could check for an idle animation like this:

if (not left) and (not right) and (not up) and (not down) then idle

and the second any key is pressed, say our present favorite to pick on, the up key:

if (TRUE) and (TRUE) and (FALSE) and (TRUE)

the entire thing comes out to false, simply because one condition returned as such.

As an aside, if the pseudo-code starts driving you nuts just let me know. At this point most of what we've been looking at is logic more than any specific function, so I've been keeping to it simply because I find it a bit clearer to communicate with. That, and it keeps things a little more hands-on and retainable. ;)
PogueSquadron Jan 31, 2014 @ 7:24am 
Ah, I guess I was getting mixed up (and the psuedo code is fine - at this point I think I'm getting better translating things to GML). The way my mind was working, I was saying "If this is true OR if this is true, the following is true." I see now though that OR just means that one of those has to be true in order for the whole statement to return true.

And lo and behold, it worked like a charm. There is no speed doubling anymore, whatsoever. The movement feels more forgiving and fluid. I'll see if I can paste a link to my progress on the Workshop.

Thank you so so so much! I feel like I can finally move on to some other things now. I suppose what I'll really have to get used to is the logic trees. If I ever have something that has a lot of conditions, I'll have to maybe work backwards (if I want someone to run....maybe start with that....but then what if there's a wall....what if there's a person blocking the way....what if there's this, what if there's that,etc etc)

I feel like I owe you already! Here's a link to my progress on Steam Workshop (forgive the awful art...I'm a professional illustrator but I'm just getting the basics down, haha) - http://steamcommunity.com/sharedfiles/filedetails/?id=222634895
Last edited by PogueSquadron; Jan 31, 2014 @ 7:53am
Sera Jan 31, 2014 @ 9:32am 
Ain't a thing wrong with placeholder graphics. It's actually a really smart way to go, and with the transparency in a lot of indie projects these days, it's also apparently a really common route to take, possibly because it makes so much sense. I think Shantae and Cryamore are both doing things this way, though they're using rough sketches more than purely temp things. I'm kiiind of trying that myself lately.
PogueSquadron Jan 31, 2014 @ 12:29pm 
Seriously thank you so much. I do have a lot to learn but I'm slowly figuring things out. I basically want to be able to move my character around like Chrono Trigger (or to use other examples, Secret of Mana, Illusion of Gaia). What I'm going to do is make a small contained RPG of sorts that kind of outlines all the things I'd want to do in a "real" game. Walking around, talking with NPCs, and maybe culminating in some sort of battle. I feel pretty confident in my ability to make things look like an SNES game, but I really want to push this small project to include almost anything I'd want to try to do in a bigger game. Menu systems, full screen options, etc. Time to learn more about room changes :) I'm having so much fun.
Sera Jan 31, 2014 @ 4:09pm 
Battle systems are definitely the hardest part of that process, especially with maintaining any sort of turn order or timing (even with an ATB like FF or CT you're going to have the occassional tie to manage); my latest attempts rely on a timeline somewhat akin to something like Grandia 2, and that seems to work really well. I've seen a lot of people pull off an ATB to some capacity in GM without much trouble, as well. A lot of it is figuring out the underlying systems that keep everything flowing the way you intend.

I will advise that you do your best to avoid persistent rooms, as one of the only GM RPGs I know of that reached completion used those (and I believe unique objects for each and every NPC and item box in the game) and wound up having pretty exhorbant save files by the point you got near the end, to the extent that at the time many people couldn't finish the game because the time it took GM to write the file resulted in Windows flagging the app as not responding. I used a method to save that was outdated with GM:S, so I think we'd both be well advised to learn to use .ini files for saving; it doesn't seem particularly difficult, and it's easier to test sooner rather than later.

I've done a lot of RPG dabbling in GM (and consequently have learned arguably more don'ts than dos), so if there's anything you need help with down the line feel free to ask. :)
PogueSquadron Feb 3, 2014 @ 10:54am 
Again, thanks so much Zaron. Moving around my little prototype feels much smoother now. I've updated it with my latest small victory, a "fade to black" effect upon room change. I have the fade effect in the Draw effect of my "room change" objects (adapting it from a tutorial regarding NPC interaction and another tip regarding image fading). I even made sure in my character inputs to freeze my character's movement when they enter a room change (so that the player can't, say, run around before the room change occurs). Small victories!

I did have one question real quickly though...is it bad form to have objects exist outside of a room? I ask because I have some "Room Change" objects for when my character leaves certain edges of the screen (or eventually, a door in front of a house). Since I want the room change to occur when the character gets to the edge of the screen, I have obj_roomchange right outside my room (with a target room/position in obj_roomchange's creation code). Is this sloppy Game Maker usage, or could it possibly cause any problems with memory or resource management? It sounded way easier for me to do it this way, rather than to place my obj_roomchange's inside the room, and then have to figure out how to not initiate the room change until a certain point.

If it's not a problem to have these obj_roomchange objects outside the room, then disregard :)
Sera Feb 3, 2014 @ 11:08am 
I think it's fine. GM at the very least used to ask if you wanted to remove objects outside of the room when you closed the subwindow, but that was mostly because people are slobs and ending up with a couple hundred extra collision boxes placed outside your bounds can slow things down quite a bit and not remotely matter for gameplay stability. I'm not sure if that check still occurs, but be mindful if it does, lest a stray reflex mouse click result in your room changers going AWOL. Barring that, I don't think there's really any problem with putting things outside your room boundaries, so go for it.
< >
Showing 1-15 of 16 comments
Per page: 1530 50

Date Posted: Jan 29, 2014 @ 2:51pm
Posts: 16