[Solved] Differentiating between mouse movement left and mouse movement right?

I have a character that I want to move around a hexagonal grid. I need the character to rotate exactly 60 degrees either left or right depending on which way the mouse is moved. I’ve tried a few things that sort of do what I want to do, but it involves a lot of empties and separate objects. It’s gotten complicated, and it still doesn’t work 100%.

Is there a simple way to do this? And also possibly have a threshold the motion needs to overcome in order to trigger the left or right rotation?

I can probably handle the rotation myself, my problem is just being able to separate mouse left vs mouse right.



import bge


<b>def</b> mousePos():
   x = (bge.render.getWindowWidth() / 2 - mouse.position[0])
   y = (bge.render.getWindowHeight() / 2 - mouse.position[1])
   <b>return</b> (x, y)mouseposition = mousePos()

if 'init' not in own:
    own['init']=True
    #Center mouse on first run
    
    bge.render.setMousePosition(int(bge.render.getWindowWidth() / 2), int(bge.render.getWindowHeight() / 2))

diff = abs(mouseposition[0])
sensitivity = .05

if diff &gt; sensitivity:
    if mousposition.x&gt;0:
        doRightStuff()
    else:
        doLeftStuff()

#center mouse every run after calculation
bge.render.setMousePosition(int(bge.render.getWindowWidth() / 2), int(bge.render.getWindowHeight() / 2)) 

untested*

There is no build-in sensor on that.

That means you have determine this by yourself.

The direct way would be a Python sensor, as that does not exist you can create a Python controller to act like a sensor (and an AND controller):

mouse.py


from bge.logic import getCurrentController as getController


PROPERTY_X_MOTION = "xmotion"
'Mouse cursor horizontal motion &lt;0: left; &gt;0 right'


INTERNAL_PROPERTY_POSITION = "_last.position"


# trigger this exactly once per frame on mouse movement
def determineMotion():   
    getController().owner[PROPERTY_X_MOTION] = determineMouseXMotion()
    
# trigger this to directly activate actuators    
def isLeftMotion():
    if allSensorsPositive() and getMouseXMotion()&lt;0:
        activateAll()
    else:
        deactivateAll()


def isRightMotion():
    if allSensorsPositive() and getMouseXMotion()&gt;0:
        activateAll()
    else:
        deactivateAll()


def isNoMotion():
    if getMouseXMotion()==0:
        activateAll()
    else:
        deactivateAll()


# internal functions
def determineMouseXMotion():
    position = getMousePosition()
    storage = getController().owner
    lastPosition = storage.get(INTERNAL_PROPERTY_POSITION, position)   
    storage[INTERNAL_PROPERTY_POSITION] = position
    xMotion =  position[0] - lastPosition[0]
    return xMotion
    
def getMousePosition():
    for sensor in getController().sensors:
        try:
            return  sensor.position
        except AttributeError:
            pass   


def allSensorsPositive():
    return all(sensor.positive for sensor in getController().sensors)


def activateAll():
    controller = getController()
    for actuator in controller.actuators:
        controller.activate(actuator)
        
def deactivateAll():
    controller = getController()
    for actuator in controller.actuators:
        controller.deactivate(actuator)


def getMouseXMotion():
    return getController().owner[PROPERTY_X_MOTION]




There are several ways to use this code:

A) direct activation:



First calculate the x-motion then evaluate the results and activate the according actuators all in one frame. Be aware the mouse.determineMouseXMotion needs to be triggered first.

B) One frame behind evaluation:



First calculate the x-motion. At the next frame the property sensors will evaluate the result and trigger the according actuators. Due to the timing it means the motion will take effect one frame later.

C) Custom code
You can use the above code to perform whatever custom code you want. Either you manipulate the function determineMouseXMotion() or (recommended )you create and setup a new function similar to isLeftMotion()


def onLeftDoSomething():
    if allSensorsPositive() and getMouseXMotion()&lt;0:
        doSomeThing()

How the code works:

determineMouseXMotion():

  • grabs the current mouse position
  • grabs the last mouse position
  • stores the current mouse position (to be used next time)
  • calculates the x-motion (positional difference between last and current mouse position)

determineMotion():

  • stores the calculated x-motion in property “xmotion” which can be checked at the next frame

is???Motion():

  • checks the sensors to act like an AND controller
  • checks if the property “xmotion” is below, above or equal to 0
  • activates all connected actuators dependent on the evaluation result.

Remarks:

Be aware determineMouseXMotion() should be called ONCE per frame ONLY! Otherwise some results will be 0.

I hope it helps you

Attachments

MouseMotionSensor.blend (412 KB)

This is how i would do it:


import bge

cont = bge.logic.getCurrentController()
scene = bge.logic.getCurrentScene()

player = scene.objects["Player"]

mouse = bge.logic.mouse

xdiff = mouse.position[0] - 0.5 #mouse.position = (0.5,0.5) = tuple, values from 0 to 1
ydiff = mouse.position[1] - 0.5

deadzone = 0.001*player.get("deadzone",1.0) #a property in player called "deadzone"

rad = 57.295779513082320876798154814105 # = 180 / ( 2 * pi)

diffx = False
diffy = False

def signum(x):if x &gt; 0:
[INDENT=2]return 1[/INDENT]
elif x == 0:

[INDENT=2]return 0
[/INDENT]
return -1 #dont need an else statement because "return" stops the function


mousepos = []
if abs(xdiff) &gt; deadzone:rotval = signum(xdiff)*60/rad

player.applyRotation((0,0,rotval),False)


if abs(ydiff) &gt; deadzone:#you dont need they y value i think, but it would be bad if you could move the mouse up
diffy = True

#now reset mouse position
if diffx and diffy:
    mouse.position = (.5,.5)
elif diffx:
    mouse.position = (.5,mouse.position[1])
elif diffy:
    mouse.position = (mouse.position[0],.5)

#the round brackets are neccessary because mouse.position must be a tuple


Wow, thanks for the fast replies!

I’m using Monster’s setup right now because it was easy for me to understand and add actuators.

I’ve almost gotten the result I want with animation actions, but I can’t get the clockwise rotation for turning right to work properly. It seems like the 60 degree rotation is being added first and then rotating back. I’ve already reset the rotation at frame 15 to 0 degrees. What else can I do to avoid this? I would prefer to use animations for the rotation so I can add quirks to the characters motion later on.

Blend MouseMotionSensor1.blend (500 KB)

*** edit: solved it by deleting all of the keyframes and ensuring the objects rotation went from 0 to 60 degrees and from 0 to -60. ***

I think everything is working perfectly now. Thanks Monster! And thanks BPR and Leralasss :slight_smile: For now this will work great for everything I need.

https://youtu.be/Rj5WWTKX3z8

Added a location action for exacting the distance and used a frame property to make the player unable to move when the action is not on the first or last frame.

How could I reset the mouse position to the center of the screen without the script thinking it is mouse motion? The mouse sometimes gets stuck to one side of the screen when moving the character.

The script I offered to you does not manipulate the mouse cursors’s position. This is good for mouse input like menus and so on (where you see the mouse cursor). I call that “absolute mouse”.

The other scripts reset the mouse position to the middle of the screen. This is good when acting like a joystick. This is often used on FPV. I call it “relative mouse”.

To get a “relative mouse x motion” the a different architecture is needed.

You do not need the internal storage as by resetting the mouse cursor you already know the “last position” as it is reset.
You can determine the motion just once per frame (rather than once per object). Due to the mouse position manipulation any subsequent motion calculation will result 0.

I suggest to have a single object that determines the motion (both vertical and horizontal) ->MouseMotionCalculator. mouse.determineMotion should run first (enable the priority flag at the controller). The resulting motion will be stored at the script. This way any other object can get it from there.


Motion-dependent objects can call mouse.isLeftMotion, mouse.isRightMotion, mouse.isNoMotion as before -> DirectActivation.

The other option needs a small change. Replace mouse.determineMotion with mouse.motionToProperties.



from bge.logic import getCurrentController as getController
import bge


PROPERTY_X_MOTION = "xmotion"
'Mouse cursor horizontal motion &lt;0: left; &gt;0 right'


PROPERTY_Y_MOTION = "ymotion"
'Mouse cursor vertical motion &lt;0: up; &gt;0 down'


# Module variables
lastMotion = None


# Trigger this exactly once per frame on a single object 
# (install it at a single object only)
def determineMotion():   
    global lastMotion
    centerX = bge.render.getWindowWidth() // 2
    centerY = bge.render.getWindowHeight() // 2
    positionX, positionY = getMousePosition()
    if not lastMotion:
        lastMotion = [0, 0]
    else:    
        lastMotion = [positionX-centerX, positionY-centerY]
    
    bge.render.setMousePosition(centerX, centerY)
    
# Trigger this once per object if you want ot  setup output properties    
def motionToProperties():
    owner = getController().owner    
    owner[PROPERTY_X_MOTION] = lastMotion[0]
    owner[PROPERTY_Y_MOTION] = lastMotion[1]
    
# Trigger this to directly activate actuators    
def isLeftMotion():
    if allSensorsPositive() and lastMotion[0]&lt;0:
        activateAll()
    else:
        deactivateAll()


def isRightMotion():
    if allSensorsPositive() and lastMotion[0]&gt;0:
        activateAll()
    else:
        deactivateAll()


def isNoMotion():
    if lastMotion[0]==0:
        activateAll()
    else:
        deactivateAll()


def getMousePosition():
    for sensor in getController().sensors:
        try:
            return  sensor.position
        except AttributeError:
            pass   


def allSensorsPositive():
    return all(sensor.positive for sensor in getController().sensors)


def activateAll():
    controller = getController()
    for actuator in controller.actuators:
        controller.activate(actuator)
        
def deactivateAll():
    controller = getController()
    for actuator in controller.actuators:
        controller.deactivate(actuator)

Attachments

MouseMotionSensor_relative.blend (416 KB)

if you only want to reset the mouse position:


import bge

mouse = bge.logic.mouse

mouse.position = (0.5,0.5)


you could just add this to the end of the script you are using now (dont need the “import bge” because thats probably at the start of the script anyway), or connect this to an always sensor