(solved) How to use both arrow keys and wasd for movement? (sierra)

Started by skooperstooper, Sun 07/08/2022 15:57:05

Previous topic - Next topic

skooperstooper

The default controls player movement with the arrow keys and wasd does nothing. I want to also be able to use wasd. The keyboard movement module it comes with has the arrow keys defined as default here:

Code: ags
 keymap.KeyDown = eKeyDownArrow;
  keymap.KeyLeft = eKeyLeftArrow;
  keymap.KeyRight = eKeyRightArrow;
  keymap.KeyUp = eKeyUpArrow;


I thought I could just add the wasd keys using OR but it doesn't change anything if I try...

Code: ags
keymap.KeyDown = eKeyDownArrow || eKeyS;


Is that because operators don't work when defining, only for checks (like in if/else statements) or is my syntax just wrong? The global script has its own section that defines wasd for movement...

Code: ags
  //KeyboardMovement.KeyUp = eKeyW;
  //KeyboardMovement.KeyDown = eKeyS;
  //KeyboardMovement.KeyLeft = eKeyA;
  //KeyboardMovement.KeyRight = eKeyD;


If I remove the comments to actually enable those lines, it overrides the module and the player controls with wasd instead of arrows. How would I enable both? Thanks!
Yes, I did the tutorial, went through the manual, and searched the forums and web. If I'm asking, it's because I missed it, forgot, or still don't get it.

Khris

Looks like the module doesn't support multiple keys.

Code: ags
keymap.KeyDown = eKeyDownArrow || eKeyS;

won't work; that line doesn't extend into the future. If evaluates the expression on the right and stores the result in the variable on the left. Since eKeyDownArrow is truthy, it is equivalent to
Code: ags
keymap.KeyDown = true;

CaptainD

I've never used the keyboard movement module, but you can achieve what you're trying to do easily enough by using standard AGS commands:

Code: ags
 if ((IsKeyPressed(eKeyUpArrow) == true) || (IsKeyPressed(eKeyW) == true)) 
 

skooperstooper

Thanks, Khris/Captain! I figured the OR couldn't be used in that kind of statement but took a wonky stab lol

I know I can write "if either the arrows or wasd are pressed, do something". The problem is scripting the "do something" part in this case because I don't know how to natively control character movement.

All of my searches led back to the module where the movement is already scripted and you just define which keys you want to use, so I was trying to work with that.

I deleted the module and the reference to it in the global script to learn from the ground up. Logic tells me that to move the player with the keyboard, I should change the XY when a key is pressed. Moving up would be decreasing Y, right increasing X, etc.

Character functions include move/walk which unless I'm wrong, do the same thing, only move does it without the walking animation, so I picked walk and started with this.

Code: ags
  if (IsKeyPressed(eKeyUpArrow) || IsKeyPressed(eKeyW))
  player.Walk(player.x, player.y-1);
  
  if (IsKeyPressed(eKeyDownArrow) || IsKeyPressed(eKeyS))
  player.Walk(player.x, player.y+1);
  
  if (IsKeyPressed(eKeyLeftArrow) || IsKeyPressed(eKeyA))
  player.Walk(player.x-1, player.y);
  
  if (IsKeyPressed(eKeyRightArrow) || IsKeyPressed(eKeyD))
  player.Walk(player.x+1, player.y);


I'm not trying to do shortcuts/best practices, just get the basics down. I'm using player instead of the character name because the player will always be the same character anyway. Good: The player moves using either arrows or wasd.

Bad: The player moves after the key is released, not when it's pressed, and he doesn't continue to move while holding the key down. He moves once and stops. Only the animation continues while the key is held.

I placed it inside the repeatedly_execute function thinking it would repeatedly check if the key is pressed and perform the action of changing XY again as long as the game isn't blocked but it doesn't. I assume I need some kind of loop to tell it to incrementally change the XY by however much but I'm not sure. Also it doesn't have to be by 1 pixel, it's just to have a number there. What would be the next step from here?

Bookmarked
https://adventuregamestudio.github.io/ags-manual/TemplateSierraStyle.html
https://adventuregamestudio.github.io/ags-manual/Character.html
Yes, I did the tutorial, went through the manual, and searched the forums and web. If I'm asking, it's because I missed it, forgot, or still don't get it.

CaptainD

I'm not sure exactly what type of game mechanics you're using so I don't know if what I'm going to say will be helpful... BUT...

When I make arcade games and similar with AGS, I don't use characters (apart from cCharacter to change rooms), I keep the player X and Y position in variables, and have a routine called in each game loop which clears the screen and directly draws the character sprite in the current x,y position. The keyboard commands don't do anything apart from update x and y. Depending on exactly what you're trying to achieve this may be a good option for you. If you do this then any animation has to be handheld by updating a separate variable to keep track of which image to draw.

You can see the type of effect achievable with this in one of my games - https://www.adventuregamestudio.co.uk/site/games/game/2508-shoaly-you-can-t-be-serious-mags-version-/

I'm far from being the best coder around so my methodology probably isn't the best, but I've found it works for me.
 

Crimson Wizard

#5
Quote from: CaptainD on Tue 09/08/2022 12:42:07
When I make arcade games and similar with AGS, I don't use characters (apart from cCharacter to change rooms), I keep the player X and Y position in variables, and have a routine called in each game loop which clears the screen and directly draws the character sprite in the current x,y position. The keyboard commands don't do anything apart from update x and y. Depending on exactly what you're trying to achieve this may be a good option for you. If you do this then any animation has to be handheld by updating a separate variable to keep track of which image to draw.

I realize this is bit offtopic, but would like to mention:
There's nothing bad in using AGS characters and objects for arcade games per se. In fact that makes perfomance better, as the real objects in the engine are utilizing textures, so they are drawn by a graphics card; while drawing on a DrawingSurface adds to a CPU load. This difference may not be significant for a low-res games (at least on modern computers), but becomes a real problem the higher resolution is, and the more objects are on screen. It also increases with additional effects, such as scaling.
Additionally, objects already have coordinates, scaling and animation support, and so on, so you don't have to track and implement these yourself.

There are two issues with these objects. First is disabling unwanted behaviors. For instance, keeping character's view locked all the time, to avoid "walking view" behavior when it animates or switches directions without your control.
The second is the fact that they are created only at design-time and cannot be created or deleted at runtime. That may be worked around by coding an "object pool", where you track a list of objects, mark them as used and unused as the game progresses. (see this module for a quick example)

As of AGS 3.6.0 Overlays are unlimited in numbers and may be positioned inside the room, and as they may be created and deleted at will, they make a good alternative to characters and objects. Their primary downside is the lack of Animate command, so you'll have to animate them yourself. But otherwise they are well suited for a role of a custom graphical object.

Khris

If you want held down keys, you need to change the approach completely.

The basic idea is not to control the player directly but indirectly (since AGS does the walking for you).
Which means we need to react to changes of held down keys, not currently held down keys.

First we need suitable variables to store the player's movement.
Code: ags
int mx = 0, my = 0; // above repeatedly_execute

One int for each axis is enough since the player can walk up and to the right at the same time but not to the left and to the right.
These variables are going to be -1, 0 or 1. This gives us 9 (3x3) different states: 8 directions and standing still.

To track changes we need to translate the held down keys into a new state and compare it to the current state:
(all inside repeatedly_execute):

Code: ags
  int nmx = (IsKeyPressed(eKeyRightArrow) || IsKeyPressed(eKeyD)) - (IsKeyPressed(eKeyLeftArrow) || IsKeyPressed(eKeyA));
  int nmy = (IsKeyPressed(eKeyDownArrow) || IsKeyPressed(eKeyS)) - (IsKeyPressed(eKeyUpArrow) || IsKeyPressed(eKeyW));


Forcing bools to act like numbers turns true into 1 and false into 0, which gives us exactly what we want: two values in the range of -1 to 1.
(If both the key for left and right is pressed, they cancel each other out and nmx ends up being 0)

The next step is to compare this to the previous state:
Code: ags
  if (nmx != mx || nmy != my) {
    // one or more keys were pressed or released
  }


Inside this block we now 1. change the players direction / stop them 2. update mx and my

Code: ags
    player.StopWalking(); // every change requires this
    player.WalkStraight(player.X + nmx * 10000, player.Y + nmy * 10000); // the second 10000 can be reduced according to the perspective
    mx = nmx;
    my = nmy;

skooperstooper

Okay, thanks all! I wanted to store coordinates in a variable but didn't know where to go from there. I'll look the code over! The game is just meant to be a side scrolling point and click, so nothing complicated with the mechanics besides things I've already figured out how to do (like have stats that I track). It's just the keyboard movement that had me stuck.

Thanks Crimson too for bringing up overlays. I bookmarked that. My game's main pull besides the story will be the direction and animations in cutscenes since artwork and visual storytelling is my strength, not scripting. There will be plenty of times when I'll want to show something on screen that doesn't need to be an actual object or character so learning about overlays will help.

Yes, I did the tutorial, went through the manual, and searched the forums and web. If I'm asking, it's because I missed it, forgot, or still don't get it.

skooperstooper

#8
I forgot that boolean values can be expressed as the numbers true/false represent. That's neat! Not that I would've thought to do it here, it's just a good reminder lol Here's what I took from what you wrote. I used strut so I remember that it's custom stuff for walking...

1) I create zero variables for each axis.

2) I define key press variables that represent right-left and up-down since the player can't go in opposing directions at the same time, and I subtract them because going more in one direction means going less in the other.

3) I compare the result of a key press to the zero variable. If the result doesn't equal it (isn't false), then I need to make the player move in whichever direction the key press dictates using walkstraight instead of walk.

I originally used walkstraight in my attempt, but it made the animation stop even though I was still holding down the keys. The fact that the animation continued with walk made it seem closer to what I was trying to replicate so I switched it.

I saw that the module used stopmoving but couldn't figure out why so I didn't try to factor it in. I guess you need it to stop the movement so it has a new starting XY to calculate with. You lost me at the 1000 so I removed it to see what happens. The character barely moved so is that just to multiply the distance? The code works, by the way!

Code: ags
int strutX = 0, strutY = 0;

function repeatedly_execute()
{
  int gostrutX = (IsKeyPressed(eKeyRightArrow) || IsKeyPressed(eKeyD)) - (IsKeyPressed(eKeyLeftArrow) || IsKeyPressed(eKeyA));
  int gostrutY = (IsKeyPressed(eKeyDownArrow) || IsKeyPressed(eKeyS)) - (IsKeyPressed(eKeyUpArrow) || IsKeyPressed(eKeyW));
  
  if (gostrutX != strutX || gostrutY != strutY) 
  {
    player.StopMoving();
    player.WalkStraight(player.x   gostrutX * 1000, player.y   gostrutY * 1000);
    strutX = gostrutX;
    strutY = gostrutY;
  }
}

Yes, I did the tutorial, went through the manual, and searched the forums and web. If I'm asking, it's because I missed it, forgot, or still don't get it.

Khris

The 10000 is to make sure the player definitely doesn't stop prematurely while a key is held down (because holding down a key (combination) triggers the WalkStraight command exactly once, so the character has to walk at least till the end of the walkable area. 10000 ensures that, unless the room is even larger)

This was also the main takeaway from my post btw: making the character walk a single pixel each frame while a key is held is precisely not how to solve this. The character is sent on her way (or stopped) if the held down keys change, and since AGS takes care of the walking, no character command is used while a key is held down.

Having said that, my AltKeyboardMovement module animates the character manually so it uses a similar approach as your original attempt. The only difference is that the character has internal float coordinates and I also have to manually animate the walk cycle.

SMF spam blocked by CleanTalk