Translating Mouse Movement into code

Started by TheManInBoots, Thu 20/02/2020 19:44:35

Previous topic - Next topic

TheManInBoots

Hi everyone.

I would like to let specific things happen depending on the mouse movement.

For example if I move the mouse cursor to the right, a character animates and moves in a certain way, if I move the mouse up, the character animates differently and maybe moves differently.

It happened multiple times that I would have liked to be able to do that, but all my attempts failed.

Does anyone have an idea how this can be done?

dayowlron

IF I Understand what you are looking for:
You would start by saving the mouse x coordinate and the mouse y coordinate then you would need to have some amount to which the mouse would have to move before it would even register that the mouse was moved. The reason for the latter is if someone has their hand on the mouse and moves it only slightly you probably would not want the animation to take place.
Create 2 global ints (SaveMouseX and SaveMouseY) and initialize them to -1. lets use 10 pixels for the movement to be detected so the player has to move at least 10 pixels before anything happens.
When you want the activity to begin.
Code: ags

  SaveMouseX = mouse.x;
  SaveMouseY = mouse.y;

Then in one of the repeatedly execute
Code: ags

  if (SaveMouseX > -1) {
    if (abs(mouse.x - SaveMouseX) > abs(mouse.y - SaveMouseY)) { //Mouse was moved horizontally more so check to see if left, right or not far enough
      if (abs(mouse.x - SaveMouseX) > 10) {
        if (mouse.x > SaveMouseX) {
          //Animation code for mouse moving right goes here
        } else {
          //Animation code for mouse moving left goes here
        }
      }
    } else { //Mouse was moved vertically more so check to see if up, down or not enough
      if (abs(mouse.y - SaveMouseY) > 10) {
        if (mouse.y > SaveMouseY) {
          //Animation code for mouse moving down goes here
        } else {
          //Animation code for mouse moving up goes here
        }
      }
    }
  }


I typed all of this in freehand so not sure if there are any syntax errors in it.
Pro is the opposite of Con                       Kids of today are so much different
This fact can clearly be seen,                  Don't you know?
If progress means to move forward         Just ask them where they are from
Then what does congress mean?             And they tell you where you can go.  --Nipsey Russell

TheManInBoots

Hi dayowlron. Just letting you know that I'm super busy for the moment. It looks interesting what you wrote. So don't worry, I'll come back to you when things loosen up a bit for me time wise ;)

TheManInBoots

#3
Okay, I like your approach. When I tried it I worked with timers, so when the mouse changed far enough after a period of time the animation code would be triggered.
But that did not work at ALL reliably lol. So I really like how you only work with space, and not time. I had to change a few things in your code to make it work.

Here is the changed code:

Code: ags
 
//I actually would like the horizontal AND vertical mouse movement to trigger something at the same time so I removed one 'if' condition of yours

//Mouse moves horizontally more than 10 pixels
      if (mouse.x - savemousex > 10||mouse.x - savemousex < -10) //I also had to add when the difference is smaller than -10 (for left movements)
      {
        
        if (mouse.x > savemousex)
        {
          //mouse moving right
          if(cJim.Frame!=18)
          cJim.Frame=cJim.Frame 1;
          else cJim.Frame=1;
        } 
        
        else 
        {
          //mouse moving left
          if(cJim.Frame!=0)
          cJim.Frame=cJim.Frame-1;
          else cJim.Frame=17;
        }
        savemousex=mouse.x;//And then after a movement of ten pixels and the animation has been triggered, I had to reset the savemouse ints, obviously
        savemousey=mouse.y;

      } 
    

    //Mouse moves vertically more than 10 pixels
      if (mouse.y - savemousey > 10||mouse.y - savemousey < -10) 
      {
        
        if (mouse.y > savemousey) 
        {
          cJim.y=cJim.y 40;
          //Animation code for mouse moving down goes here
        } 
        else 
        {
          cJim.y=cJim.y-40;
          //Animation code for mouse moving up goes here
        }
        savemousex=mouse.x;
        savemousey=mouse.y;
      }



So what I changed:

  • I removed one 'if' condition
  • I added the condition for mouse position difference<-10
  • I added the reset of the savemouse ints after each 10 pixel interval

(I tried using a pointer instead of two ints, but that makes things complicated because of null pointer references.)
Now, first off it works, the character animates with horizontal mouse movement, and moves with vertical movement. So thanks for the basic idea. But there are two problems I still face with that:
Up and down movements are not triggered the same way. So the character travels faster up than he does down. Even though it's set to change the y-position to  40/-40, so in theory it should be the same. Does anyone have an idea how that could be fixed?

And then I have another problem: horizontal and vertical movements are never triggering at the same time. I would like that to change. I would like them to be triggering at the same time. So that you can move mouse only right, or right-up, or left-down etc. (causing animation, or animation AND movement...)
However that does not work, even though I removed the if bracket that was meant for that. And I don't want an if condition for EACH direction combination to be honest...

I tried placing up movement and side movement, one in repeatedly always, and one in late repeatedly always, that did not work.

So if anyone has an idea how it can be done, let me know :)


On a side note: AGS does not work with absolute value functions so I had to remove them anyways. But even on a different platform, why would you use an absolute value function for something as simple as a substraction? Is there something I'm not seeing?


Thanks

Crimson Wizard

#4
Quote from: TheManInBoots on Tue 25/02/2020 21:04:38
On a side note: AGS does not work with absolute value functions so I had to remove them anyways. But even on a different platform, why would you use an absolute value function for something as simple as a substraction? Is there something I'm not seeing?

If you are referring to "abs" in dayowlron's code, they are used to get a always positive difference between two coordinates, i.e. in this case instead of writing two conditions you write only one, making the code concise.

AGS does not have it, but you may write your custom one.
Code: ags

int abs(int a)
{
    if (a < 0) return -a;
    return a;
}

TheManInBoots

#5
Yes I was refering to the abs in the code.
Got it, thanks! That's why he didn't write 10 and -10 what I added.

TheManInBoots

#6
How do you call this way of creating the abs function btw. if I want to learn more about it?
It's not really an array, but it's not really a custom function...
Is it AGS code or part of C language?

morganw

It is just a regular AGS script function. You could also write it as:
Code: ags

function abs(int a)
{
    if (a < 0) return -a;
    return a;
}


(default return type for function is int, but you can specify the type instead of using the keyword 'function')

TheManInBoots

That's interesting!

For one, I see I can declare an int using "function".

Code: ags
function number=3;


And then I can use "number" as an int.

I will experiment more with this when I have the time.

Thanks

Crimson Wizard

Quote from: TheManInBoots on Wed 26/02/2020 18:11:27
For one, I see I can declare an int using "function".

Code: ags
function number=3;


And then I can use "number" as an int.

This is a coincidental and unsupported behavior, it's possible simply because "function" (secretly) is declared as an alias to "int" keyword in ags script, for compatibility reasons.

Khris

Just to clarify, C type languages don't really have a  function  keyword. You're rather supposed to state which type is returned by the function.

Some examples:

Code: ags
void Alert() {
  Display("Uh-oh!");
  // nothing is returned
}

float Cuberoot(float x) {
  return Math.RaiseToPower(x, 1.0/3.0);
}

String Greeting(String name) {
  return String.Format("Hello, %s!", name);
}


void functions are usually called on their own:  Alert();  (does something, returns nothing)
While functions that return something are usually part of expressions or assignments:  float side = Cuberoot(volume);  (does nothing but returns something)

Getting how custom functions work is hard enough if you've never programmed before, so I guess CJ opted for the inclusion of a function keyword to make things easier. Maybe for people who've dabbled in Javascript.
It's just a substitute for int, like CW said.

TheManInBoots

#11
Crimson Wizard,

Since, originally in C language, one has to assign the return value for each function, the key word "function" is simply a substitute for the int value, and is that not enough reason for that to work to declare an int with it?
Khris mentioned that the expression
Code: ags
float side = Cuberoot(volume); 

is basically a function that returns something but does not do something.
In that case
Code: ags
int number=3;

Can be considered a function that only returns a value and does nothing, and since int defined functions can be replaced with the key word "function", it should be obvious that you can also write
Code: ags
function number=3;

instead.
When you say for "compatibility reasons" you are not talking about the afore-mentioned,are you?

Khris,

Yes, morganw explained that very clearly that "function" substitutes "int".
But I really appreciate all the examples you give. Context is extremely helpful, and the next thing I want to try when I got time is experiment with functions that return a different value than int, so your examples are really useful!

I also think that's why CJ did it.
When you say void functions are called on their own, what do you mean with "on their own"?


Thanks

Crimson Wizard

#12
Quote from: TheManInBoots on Thu 27/02/2020 16:24:36
Khris mentioned that the expression
Code: ags
float side = Cuberoot(volume); 

is basically a function that returns something but does not do something.

To be precise, this expression in whole is not a function. It's calling a function Cuberoot, passing "volume" as parameter, and saves returned value in a "float side".
Whether "Cuberoot" is doing something or not is not known from this code only.

Hmm, thinking this further, I believe you misinterpret what Khris said, or maybe he was not clear enough. Function that "is not doing anything" could be an obscure wording. Every function does something, otherwise there's no point to have one. I am in doubts how to explain this better. Guess, functions may be divided in the three kinds:
* ones that tell you about something - these just return some existing value, but don't do anything else. Random example: GetGameSpeed().
* ones that calculate a value out of provided parameters, but again don't change anything. They work as a value "converter". You pass A, and get B in return. Random example: Maths.Sin().
* ones that actually do some action. They may also return something, like a success/error code, or result of a work, but not necessarily. Random example: Character.Walk().

Tha above is merely a categorization, but not a programming rule.



Quote from: TheManInBoots on Thu 27/02/2020 16:24:36
In that case
Code: ags
int number=3;

Can be considered a function that only returns a value and does nothing, and since int defined functions can be replaced with the key word "function", it should be obvious that you can also write
Code: ags
function number=3;

instead.

Well, in proper terms this code creates a variable, not a function. Of course, you may consider a variable a variant of a function, and, perhaps, philosophically that would make sense.

Thing is, function/int substitution is not relevant at all, and should not be minded too much. In the modern AGS script "function" is declared as a "macro" (alias) which is blindly replaced with "int" by a pre-compiler anywhere you wrote it in code. This was done by CJ for ease of transition from old version of scripts into new ones. This way a person who got used to old scripts could keep writing "function" everywhere, or copy old scripts to the new game. That is - "for compatibility reasons".
This is a simple, yet not perfect solution, as declaring a "function a = 0;" makes no sense syntactically, yet works.

Even further, following also works, while making even less sense:
Code: ags

function MyFunc(function a, function b, function c);

you'd think that passes 3 functions into MyFunc... (but it does not)

Khris

Yeah, when I said "does nothing" I meant that nothing is happening "outside the function". Calling it a "converter" is a much better description, since the function's only concern is the input.

By saying that (void) functions are called "on their own" is was referring to the fact that there's usually nothing else in that line except for the function call itself. Other examples for this are Display(), or Character.Walk().

Code: ags
  int a = 3;


That's a declaration and assignment rolled into one. There's no function involved here, none is called let alone declared.
A line like  function a = 3;  is not only weird, you should unlearn that as soon as possible. It happens to work in AGS for legacy reasons explained by CW but that's a definite syntax error in any other environment.

TheManInBoots

#14
Yeah makes sense, and of course Khris was talking about the function being the second part of the expression (namely Cubervoot()). not the entire expression being a function, now that you say it Crimson Wizard.

Haha, Khris, this stuff with function is very interesting to me, even if I don't use that in game scripting.
I don't have to unlearn it at all, because I know what is useful and works, even when I know more stuff like this on the side.
Besides it teaches me about the nature of script transitioning to newer versions.
Exploratory mind doesn't stop with what's rule bound.
Also, I think you should relax a bit before telling me that I should unlearn that immediately. That's too krass.
Some people just do things differently and you don't need to wrap your head around it and can just accept it.

Crimson Wizard, a philosopher might start hypothesizing if philosophy is a better approach to programming than maths haha. But let's not go there.

I really appreciate your explanation. I don't have the time to reply to it for now.


I'll just leave the solution to the problem I stated above:

How to make it so that both up and side mouse movements trigger different action at the same time:


X and y savemouse coordinates are reset every ten pixels. The problem was that I reset the savemousey in the mouse x-movement bracket and vice versa.
When I calculate the x-movement, I only need to reset savemousex, and the same for y-movement.
This way the two of them are independant and can run at the same time.
Otherwise one direction will never be triggered because the direction in which the mouse moves faster resets the coordinates before the second direction triggered.

So, renewed script, just remove two lines and it works (I removed lines 23 and 42):

Code: ags
 

//Mouse moves horizontally more than 10 pixels
      if (mouse.x - savemousex > 10||mouse.x - savemousex < -10) //I also had to add when the difference is smaller than -10 (for left movements)
      {
        
        if (mouse.x > savemousex)
        {
          //mouse moving right
          if(cJim.Frame!=18)
          cJim.Frame=cJim.Frame 1;
          else cJim.Frame=1;
        } 
        
        else 
        {
          //mouse moving left
          if(cJim.Frame!=0)
          cJim.Frame=cJim.Frame-1;
          else cJim.Frame=17;
        }
        savemousex=mouse.x;//Reset of saved mouse coordinates
        //removed: savemousey=mouse.y;

      } 
    

    //Mouse moves vertically more than 10 pixels
      if (mouse.y - savemousey > 10||mouse.y - savemousey < -10) 
      {
        
        if (mouse.y > savemousey) 
        {
          cJim.y=cJim.y 40;
          //Animation code for mouse moving down goes here
        } 
        else 
        {
          cJim.y=cJim.y-40;
          //Animation code for mouse moving up goes here
        }
        //removed: savemousex=mouse.x;
        savemousey=mouse.y;
      }

Khris

#15
If you see somebody putting dogshit on their pizza because they have never smelled it yet but think it *looks* kind of tasty, don't you warn them about it?

I'm not telling you to unlearn it because I like to order people around, I'm trying to convey how objectively nonsensical and bad it is.

TheManInBoots

#16
Yeah, I just mean, explaining it is enough.

The way you said it initially, it sounded more like an order than an advice, even if it was intended as an advice.
I think you improved it and it sounds better now.
The right communication is important as well, to convey the proper message.

It is just by coincidence that "function" can be used to declare ints in AGS.
It is not the proper way to code your script.

I know this. And even Crimson Wizard's explanation was already enough to convey it.

Since I know this, playing around with it wasn't like eating dog shit on a pizza.
It was more like noticing the bread on the pizza tastes like vanilla, and pretending that it's vanilla ice cream.

It's just playing around and having fun.
There's nothing wrong with playing and fooling around.
It stimulates creativity, instigates new ideas and it is the most natural way to create a deeper understanding of the world, or in this case, the AGS engine.

That's why I said, you can relax a little. It's just playing around after all.

Khris

Sure, experiment and play around and have your creativity stimulated, I'm all for that.

Just goddamn don't declare ints using the function keyword ;)

SMF spam blocked by CleanTalk