How to make a new arrowkey press trump an old one...?

Ask and answer questions about making games and related topics. Unrelated topics go in that other forum.

Moderators: marionline, SDHawk

User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

How to make a new arrowkey press trump an old one...?

Post by sheamkennedy »

This question is specifically aimed at Bob since we were talking about something similar earlier but anyone can answer.

I have a keypress script which temporarily overrides the normal arrow key movement allowing the hero to turn to face a direction before proceeding to walk in that direction. This code seems to work fine asides from one thing. When the hero is moving in one direction, say down, and I also click the left key (which should make the hero walk left), the hero continues to walk down.

I want to know if there is any way to make the latest arrow key press trump the old arrow key press. The way my code is now certain arrow keys tend to trump others but I can't seem to understand why that's so. Here's my keypress script (Note: the timers are just for carrying out a wait so when a direction is changed the character turns to the new direction momentarily, this timer does not apply during the case in which multiple arrow keys are being pressed):

Code: Select all

### Right Arrow ###
  if(keyval(key:Right) > 1) then(
    if(hero direction == right) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Down) == 0)) then(
        suspend player
        set hero direction(0,right)
        changingDirectionR := 1
        DirectionR Timer
      )
    )
  )  
### Left Arrow ###
  if(keyval(key:Left) > 1) then(
    if(hero direction == left) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Right) == 0) && (keyval(key:Up) == 0) && (keyval(key:Down) == 0)) then(
        suspend player
        set hero direction(0,left)
        changingDirectionL := 1
        DirectionL Timer
      )
    )
  )
### Up Arrow ###
  if(keyval(key:Up) > 1) then(
    if(hero direction == up) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Right) == 0) && (keyval(key:Down) == 0)) then(
        suspend player
        set hero direction(0,up)
        changingDirectionU := 1
        DirectionU Timer
      )
    )
  )      
### Down Arrow ###
  if(keyval(key:Down) > 1) then(
    if(hero direction == down) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Right) == 0)) then(
        suspend player
        set hero direction(0,down)
        changingDirectionD := 1
        DirectionD Timer
      )  
    )
  ) 
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

I don't understand the question. If the hero is already in the middle of a movement, why would you want to interrupt that movement and start moving in another direction, which would cause misalignment with the grid?

I also don't see what the "if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Down) == 0)) then( ", etc, checks are for. It should usually not be necessary to check for the case where the player is pressing multiple keys at once. Instead, check if the hero is already moving.

BTW, instead of (X == 0) && (Y == 0) you can write just X == 0 && Y == 0.
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Give me a bit, I think your suggestion about not needing to override the normal arrow key control may fix this. If not I'll explain the problem in better detail.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Okay so I tried a bunch of stuff and still can't get things working how I'd like. For the sake of clarity I'm going to re-state my question in more detail.

By default the arrow keys seem to work like this in a normal game:
-when you hold the arrow in a given direction your character moves in that direction.
-when you hold a direction (let's say right) and then hold a perpendicular direction while still holding the original direction (let's say right and up are now being held together), then the character will move upward (this happens when the character is aligned with the next available tile, I don't mean that the direction change occurs midstep). This is what I mean by one keypress trumps another.

However in my code which currently overrides the default arrow keys the "trumping" aspect does not work in the same way. In some directions it works similar to how I've described above. While traveling in other directions though, pressing a new direction simultaneously doesn't do anything (as if the original direction being travelled trumps the new one somehow).

Ideally I'd like my movement system to be exactly like the default system but also allow for two additional features:
(1) Strafing (which I think I've implemented properly).
(2) When a new direction is pressed the character will turn and face their new direction without taking a step in that direction (for a few ticks), then if the new direction key is further held the character will proceed in that direction (I have this working too).

The problem is that my code for carrying out feature (2) uses the function "suspend player" and "resume player" in order to temporarily cause my hero to wait a few ticks when facing a new direction before proceeding in the new direction. Since the "suspend player" is used, the default keyboard functionality is override by my coded keyboard functionality which means that the trumping problem occurs.

This means there are two ways to solve my problem:
(i) To somehow perform feature (2) without suspending the player, thus allowing the default keyboard functionality to carry out.
(ii) To write my override code in such a way that the trumping works the same way as in the default keyboard functionality.

Here is my current code:

Code: Select all

if(isStrafing == false) then(
### Right Arrow ###
  if(keyval(key:Right) > 1) then(
    if(hero direction == right) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Down) == 0)) then(
        suspend player
        set hero direction(0,right)
        changingDirectionR := 1
        DirectionR Timer
      )
    )
  )  
### Left Arrow ###
  if(keyval(key:Left) > 1) then(
    if(hero direction == left) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Up) == 0) && (keyval(key:Down) == 0) && (keyval(key:Right) == 0)) then(
        suspend player
        set hero direction(0,left)
        changingDirectionL := 1
        DirectionL Timer
      )
    )
  )
### Up Arrow ###
  if(keyval(key:Up) > 1) then(
    if(hero direction == up) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Right) == 0) && (keyval(key:Down) == 0)) then(
        suspend player
        set hero direction(0,up)
        changingDirectionU := 1
        DirectionU Timer
      )
    )
  )      
### Down Arrow ###
  if(keyval(key:Down) > 1) then(
    if(hero direction == down) then(
     #Do nothing, default movement resumes
    ) else (
      if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Right) == 0)) then(
        suspend player
        set hero direction(0,down)
        changingDirectionD := 1
        DirectionD Timer
      )  
    )
  )  
)  
Here are the timers that are called by that code:

Code: Select all

script, DirectionR Timer, begin
  If (changingDirectionR == 1) then(
    changingDirectionR := 0
    set timer(27, 0, 2, @DirectionR Timer)    
  ) else (
    resume player
    changingDirectionR := 1 
  )
end 
script, DirectionL Timer, begin
  If (changingDirectionL == 1) then(
    changingDirectionL := 0
    set timer(26, 0, 2, @DirectionL Timer)    
  ) else (
    resume player
    changingDirectionL := 1 
  )
end
script, DirectionU Timer, begin
  If (changingDirectionU == 1) then(
    changingDirectionU := 0
    set timer(25, 0, 2, @DirectionU Timer)    
  ) else (
    resume player
    changingDirectionU := 1 
  )
end
script, DirectionD Timer, begin
  If (changingDirectionD == 1) then(
    changingDirectionD := 0
    set timer(24, 0, 2, @DirectionD Timer)    
  ) else (
    resume player
    changingDirectionD := 1 
  )
end
@TMC: You had questioned why my code says:
"if((keyval(key:Left) == 0) && (keyval(key:Up) == 0) && (keyval(key:Right) == 0))"
If I don't have this what happens is when two directions are pressed at the same time then the hero will move in a jerky pattern. This is because the codes for the other directions are also being called and resulting in "suspend player" to occur. By having this conditional statement I eliminate that problem.


I think that's all that needs to be said. Please let me know if you need more clarification of anything. I hope I explained what I mean by trumping clearly enough.
Last edited by sheamkennedy on Tue Nov 10, 2015 9:19 pm, edited 2 times in total.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
Bob the Hamster
Lord of the Slimes
Posts: 7660
Joined: Tue Oct 16, 2007 2:34 pm
Location: Hamster Republic (Ontario Enclave)
Contact:

Post by Bob the Hamster »

I think I understand what you are saying.

You want the most recently pressed arrow key to take priority, but right now it is giving priority to arrow keys based on the order that your if(keyval(... commands appear in the script, right?
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Bob the Hamster wrote:I think I understand what you are saying.

You want the most recently pressed arrow key to take priority, but right now it is giving priority to arrow keys based on the order that your if(keyval(... commands appear in the script, right?
Yes what I want is the most recently pressed arrow key to take priority. I can't be certain that the current priority is a result of the if(keyval(... commands, but that could be the case. If it is the case I don't know how to solve the issue still.
Last edited by sheamkennedy on Tue Nov 10, 2015 9:22 pm, edited 1 time in total.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
Bob the Hamster
Lord of the Slimes
Posts: 7660
Joined: Tue Oct 16, 2007 2:34 pm
Location: Hamster Republic (Ontario Enclave)
Contact:

Post by Bob the Hamster »

A quick way to test would be to change the order of your if(keyval tests, and see if that changes which directions get priority

Looking at the script you just posted, it looks like the priority would be given to the check that appears last, so Down should currently be trumping Up, which is trumping Left, which is trumping Right.
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Bob the Hamster wrote:A quick way to test would be to change the order of your if(keyval tests, and see if that changes which directions get priority

Looking at the script you just posted, it looks like the priority would be given to the check that appears last, so Down should currently be trumping Up, which is trumping Left, which is trumping Right.
Okay I'm trying it now. This is what's happening:

-While im travelling right, if I press any other key they all take priority over right.

-While im travelling left, only up and down take priority over left.

-While im travelling up, nothing is able to take priority over up.

-While im travelling down, only up can take priority over down.

After switching the conditions around in the if statement there seems to be no effect on priority.

This led me to further rearrange the if(keyval(key:Right) > 1) statements to see if they were what's determining priority, yet after doing so the priority remained the same. Thus it seems that the priority is somehow being determined elsewhere...?
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
Bob the Hamster
Lord of the Slimes
Posts: 7660
Joined: Tue Oct 16, 2007 2:34 pm
Location: Hamster Republic (Ontario Enclave)
Contact:

Post by Bob the Hamster »

Oh, I see what is going on here.

That arrow key priority is actually hard-coded into the default movement, so you would see it even in a game with no scripts at all
ohrrpgce source code game.bas wrote:

Code: Select all

    IF carray(ccUp) > 0 THEN herow(0).ygo = 20: catd(0) = 0: EXIT DO
    IF carray(ccDown) > 0 THEN herow(0).ygo = -20: catd(0) = 2: EXIT DO
    IF carray(ccLeft) > 0 THEN herow(0).xgo = 20: catd(0) = 3: EXIT DO
    IF carray(ccRight) > 0 THEN herow(0).xgo = -20: catd(0) = 1: EXIT DO
It is just more noticeable in your game because of the other stuff you are doing.
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Okay, so this means that I can only fix the problem by completely writing my own keyboard functionality and suspending player permanently? Or is there a simpler way?
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
TMC
Metal King Slime
Posts: 4308
Joined: Sun Apr 10, 2011 9:19 am

Post by TMC »

You only need to override the movement code, and leave the use and menu keys alone, unless you want to be able to press the use and movement keys at the same time.

You're overriding built-in movement in most cases, and it seems simpler to just do it always. Beware that you probably want to take into account the same things that blocks the normal movement code: a textbox is displayed, the screen is faded out*, player is suspended, a vehicle is animating/mounting/dismounting*, or a menu has paused the game.

* Not actually possible to check for.

Normally to override a key you would suspend player, wait for a tick, and then resume if the key has been released, otherwise keep waiting. However that would be a problem if other scripts get triggered and interrupt the script, which I guess is why you're using a timer. Still, there's no reason for the timer to trigger after two ticks instead of one.

On the other hand, if a cutscene script gets triggered that calls 'suspendplayer' when it starts, then your timer will resume player. You can't win!

It's been plainly obvious for a long time that we need a more direct way to override keys than to use the suspendplayer hack. I guess that a "Movement keys: Ignore/Run script/Default movement" option is needed, but I think it's better to run the script at the same point that the movement code normally would, otherwise replacing the builtin movement with a script that does the same thing will behave *very* differently. However the script interpreter needs some more work before that's possible. But a "Ignore/Default" option would be very simple and I'm in favour of adding it for Callipygous.

Oh damn, I just noticed that the option to call a script instead of opening the menu when you press Esc/Alt (a new feature in nightlies) does NOT act like the normal menu key, just as I worried :(. I guess I'll be adding a back-compat bit to fix that one. Actually there are so many script triggers that are 'wrong' in a similar way that it makes sense to fix them all at once.
Last edited by TMC on Wed Nov 11, 2015 12:36 pm, edited 1 time in total.
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Hmm I see. Well I could either just deal with the current result (which there's nothing too wrong with it, it's just a little inconsistent) or I could do over the entire thing and add in those conditionals you mentioned then also add the same conditional checks for the resume command so that resume can't occur say during a menu pause.

Perhaps I'll just leave it though since it may be easier to accomplish in the future Callipygous release.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Or maybe I could even somehow create my code so there is no trumping happening whatsoever. This would also be fine. I think my main issue is that I like things to look and feel consistent so doing this would result in consistent behaviour too. Might run in to the same issue with this though anyways.
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
User avatar
Gizmog
Metal King Slime
Posts: 2622
Joined: Tue Feb 19, 2008 5:41 am

Post by Gizmog »

sheamkennedy wrote:Or maybe I could even somehow create my code so there is no trumping happening whatsoever. This would also be fine. I think my main issue is that I like things to look and feel consistent so doing this would result in consistent behaviour too. Might run in to the same issue with this though anyways.
I don't think that's possible? No matter how you write your code, something has to happen first. One thing I've read about doing, but never tried, is to seperate the pushing from the doing. Instead of

Code: Select all

### Left Arrow ### 
  if(keyval(key:Left) > 1) then( 
    if(hero direction == left) then( 
     #Do nothing, default movement resumes 
    ) else ( 
      if((keyval(key:Up) == 0) && (keyval(key:Down) == 0) && (keyval(key:Right) == 0)) then( 
        suspend player 
        set hero direction(0,left) 
        changingDirectionL := 1 
        DirectionL Timer 
      ) 
    ) 
  )
You'd do...

Code: Select all

### Left Arrow ### 
  if(keyval(key:Left) > 1) then( 
  ReceivedLeftInput := 1
    ) 
And then sort out what that means later, after all the pushing's been done. How exactly you do that is open to interpretation. You could use variables, you could use tags, or you could make a queue of slices and sort 'em by priority, so that.. up always goes first or however you wanted to do. But the important part of the idea is that you're getting all the pushing done before you start doing stuff.. though obviously, at some point, something has to get done first.
User avatar
sheamkennedy
Liquid Metal Slime
Posts: 1110
Joined: Mon Sep 16, 2013 9:29 pm
Location: Tama-shi, Tokyo, Japan
Contact:

Post by sheamkennedy »

Hmm, that's an interesting technique though I'm not sure whether I understand. Doesn't doing so just shift the code to somewhere else and thus just shifts the problem? If I were to set a variable as you have shown in your example and then have another script work things out elsewhere (say in the while loop of my map autorun) then I still find it hard to see the problem being solved. Ultimately won't the default built-in stuff always carry out first? Or can I maybe suspend the default code and then if the variable has been set to one I can carry out the map autorun script and resume the code there on the condition that the variable equals one. (I can see how this might work, I just want to confirm that I know what you meant).
⊕ P E R S O N A L M U S I C: https://open.spotify.com/album/6fEo3fCm5C3XhtFRflfANr
� C O L L A B M U S I C: https://dustpuppets.bandcamp.com/releases
Post Reply