Pygame - first person shooter "look" with mouse - python

I am not writing a game but a scientific renderer using Pygame. I'd like the controls to work just like in a first-person shooter, so that a user can navigate using a familiar set of controls.
I have tried to write the code to have the same properties as the 'look' feature in e.g. Skyrim or Half-Life but the mouse doesn't move the cursor - it lets you look around and around, in infinite circles. Clicking should have no effect.
The first attempt for the controls:
(code inside a game loop)
delta_y, delta_x = pygame.mouse.get_rel()
rotation_direction.x = float(delta_x)
rotation_direction.y = float(delta_y)
(don't ask me why, but y and x need to be reversed like this to get the expected look directions; must be something to do with the camera transform implementation, which isn't mine.)
This, however, leads to a cursor sitting on top of the window, and when the cursor gets to the edge of the screen the window stops rotating; i.e. the code is reporting the actual position on the screen.
I tried to 'reset' the mouse position every game loop (and, incidentally, hide the mouse):
pygame.mouse.set_pos([150, 150])
pygame.mouse.set_visible(False)
But this generates a symmetrical 'move back to start' delta in the next loop, meaning that you couldn't 'look' anywhere.
To summarize, I want to:
detect actual mouse motion reported from the device
not move/show any OS cursor
not clip at the 'edge of a screen'
What is the best way to do this, using Pygame or other Python hacks?

http://www.pygame.org/docs/ref/mouse.html :
If the mouse cursor is hidden, and input is grabbed to the current display the mouse will enter a virtual input mode, where the relative movements of the mouse will never be stopped by the borders of the screen. See the functions pygame.mouse.set_visible - hide or show the mouse cursor and pygame.event.set_grab (docs) - control the sharing of input devices with other applications to get this configured.

Try calling pygame.mouse.get_rel() once more immediately after the set_pos call to 'throw away' whatever relative movement the set_pos call has performed.

Since you are using pyOpenGL, try gluLookAt() example: How do I use gluLookAt properly?

Related

How to control the mouse in Minecraft using Python?

All in all, I'm trying to programmatically -and externally- control the Minecraft player's orientation.
No APIs, no Java mods to the game environment
Typically this requires the movement of the mouse, but every single mouse movement simulating python3 library that I've tried doesn't move the player's head in-game. Each library does something different, too.
For example, pyautogui doesn't do anything until you move the mouse manually after the script has finished. Doing this will jerk the player's view to where the program supposedly moved it to, before continuing to follow your current mouse movements. This happens for both mouse commands.
import pyautogui
pyautogui.moveTo(500, 500)
pyautogui.moveRel(100, 100)
The pynput library had the same weird result as pyautogui:
from pynput.mouse import Controller
mouse = Controller()
mouse.position = (100, 200)
mouse.move(200, -100)
Quartz doesn't do anything at all:
import Quartz
class Mouse():
down = [Quartz.kCGEventLeftMouseDown, Quartz.kCGEventRightMouseDown, Quartz.kCGEventOtherMouseDown]
up = [Quartz.kCGEventLeftMouseUp, Quartz.kCGEventRightMouseUp, Quartz.kCGEventOtherMouseUp]
[LEFT, RIGHT, OTHER] = [0, 1, 2]
def click_pos(self, x, y, button=LEFT):
self.move(x, y)
self.click(button)
def to_relative(self, x, y):
curr_pos = Quartz.CGEventGetLocation( Quartz.CGEventCreate(None) )
x += curr_pos.x;
y += curr_pos.y;
mouse = Mouse()
mouse.to_relative(200, 200)
And the python mouse library is outdated: the error showed that it will only run on Darwin (I'm on macOS High Sierra). I was sad on learning this because of the description on the Github page. It says "Global event hook on all mice devices (captures events regardless of focus)". I then thought that, somehow, Minecraft was sucking up all the simulated mouse movements on it's own. Either way, I'm not using the right interface for this game, and I need something that can bypass Minecraft's interesting mouse controls to get the movement that I want.
I even tried using mouse keys (mac's mouse-moving accessibility feature that lets you control the mouse with only keys) along with pyautogui.
import pyautogui # with mouse keys on
import time
# mouse keys is an accessibility feature on mac that controls the mouse with the keyboard
print("[ALERT]: Make sure mouse keys is on! (press option 5 times if shortcut is enabled)")
pyautogui.keyDown('8') # up in mouse keys
time.sleep(5)
pyautogui.keyUp('8')
I wasn't particularly surprised that the last one didn't work, but I think I'm running out of ways to try and bypass whatever is making Minecraft not take my python-mouse input. At this point, I'm pretty sure that there must be some distinction in the kind of input that I'm giving the computer. Minecraft as a program doesn't use the mouse like other programs do, and python mice don't control the mouse like other sources do, so there is a disconnect.
I'm on my macOS High Sierra running Minecraft in both fullscreen and windowed mode, trying everything I can to get this to function properly. I'll start the test script (python 3.6) in PyCharm, change windows (or window focus) to Minecraft (with adequate delay time in-program), and then witness what happens. Mouse clicking, keyboard presses, and even hotkeys that involve the command and escape keys all work fine in Minecraft with pyautogui, so I'm not worried about those at all. It's literally just the mouse movement that's not doing anything.
First of all, is this the right place to ask this question? Is there anything else to try, or is there something crucial that I'm missing, that would allow my mouse input to be responded to correctly?
I am trying to do the same thing, and I got mine to move the view in Minecraft (Java edition).
What worked for me was to use pynput with relative mouse commands. It also needed 'Raw Input' to be off in the Minecraft settings. [Esc -> Options... -> Controls... -> Mouse Settings... -> Raw input: OFF]
import pynput
mouse = pynput.mouse.Controller()
mouse.move(10, 10)
Also, here's the beginnings to a smooth movement of the mouse if anyone wants it:
def move_smooth(xm, ym, t):
for i in range(t):
if i < t/2:
h = i
else:
h = t - i
mouse.move(h*xm, h*ym)
time.sleep(1/60)
move_smooth(2, 2, 40)
Now, onto trying to make the keyboard work :P
I managed to make it work with the mouse library. Instead of using mouse.move(x,y,absolute,duration) I used mouse._os_mouse.move_to(x,y) and mouse._os_mouse.move_relative(x,y). Take into account that if you want a smooth effect you'll have to implement it yourself using something like time.sleep(s).
This question has also bothered me for a while and I finally found a solution to this question.(macOS Monterey 12.2, M1 chip 2020)
I have tried multiple python library and macro apps which none worked and rawinput also didn't help. However, pointer control in system preferences works with Minecraft mouse movement. The mouse keys option allows you to control mouse movement with keystrokes,
Where to find Mouse Keys in system preferences
System Preferences -> Accessibility -> Pointer control(under Motor) -> Enable Mouse Keys
so I tried to simulate the keyboard to move the mouse. For some reason, none of the python library or macro apps will work with mouse keys except for apple script. So I wrote an apple script and embedded it in python, which worked moving the mouse in Minecraft when the mouse key option is on.
The mouse keys option will allow you to use the numpad keys to control you mouse.
https://eastmanreference.com/complete-list-of-applescript-key-codes
This link will tell you the key code for the keystroke you want to press.
import os
cmd=""" osascript -e '
repeat 500 times
tell application "System Events" to key code 88 --right
end repeat'
"""
os.system(cmd)
In the script key code 88 matches numpad key 6, which will move your cursor right, change the key code to customize where the cursor will move.
I am in a similar situation to you. I was also unable to find a way to register my mouse movements in games such as minecraft.
However, I learned that you can use Java and the built-in robot library to achieve the mouse movement as desired. I don't know if you are set on python but if not it's worth checking out.
In the Minecraft Options... go to Controls... go to Mouse Settings... and there turn off Raw Input.
That should do it (you're on mac tho and I don't know if this setting is on
the mac version of minecraft)
I was struggling with this too and I found the following findings (tested on Ubuntu 20.04):
If you activate RawInput, you can use mouse.position=(dx, dy) which (although it is intended to be absolute) causes relative motion of the head! And it also works with negative values for dx and dy.
This is much faster and more accurate. You can for instance chain
mouse.position = (0, -100000) # turn head all the way up
mouse.position = (0, 600) # turn head exactly to horizon level
The horizon is 600 "pitch-pixels" down from upright, when mouse sensitivity is set to 100%. You don't need any delay between the calls and they happen so fast that in the vast majority of cases, the player does not seem to look up in between, but looks to the horizon right away. When I used the relative motion with raw input off, I needed sleeps in between and the speed was also capped at some value.
The only issue is, that you need to switch between this hacky way of controlling the mouse and the normal way using mouse.move(dx, dy) or with actually absolute screen coordinates mouse.position=(x, y) when you are on the crafting screen / in a menu. But I found that if you also create a pynput mouse listener, you will see that Minecraft sets the mouse position to the center of its window everytime you enter a menu and it does it another two times when you go back to the game. So I use these as triggers.
Try running python as admin and run the game in windowed mode. Pyautogui should work then.

How to focus on a window with pygame?

I made a Python game using Pygame. I try to make it so when it starts, it loads the shell, and entering a raw input would display the pygame window and start the game so that I can make the game playable without having to close the shell. It works, however, the window starts minimized. The game is a simple "dodge the object" and has no pause what so ever. The game still runs in the background, possibly having the player hit multiple projectiles before the user realizes it. Is there a way to focus on the window?
For anyone in the future who stumbles across this question:
As of 2022, pygame v2.1.2 has an experimental module ._sdl2 where: Window.focus() is used to make a window to be moved at the top and set focus. Windows.focus() supports optional input_only bool parameter for whenever the window should be moved at the top (False), or just collect input (True).
As I understood you, you don't want the game to start, before the window is in fullscreen-mode, right?
When I try you attempt (starting through rawinput), my display is fullscreen from the start. Have you set everything correctly?
I suggest that you stop the game until there is an actual key-input (whatever controls you have set). Like this the player has the time to arrange everything to his liking before starting the game. Because, even if you figure out how to analyse the focus-issue: When the game starts, the window HAS focus, therefore this approach wouldn't work anyway.

Pygame help needed

I am new to PyGame and need some help!
Let me start of by saying that I have a basic understanding of the Python language, I recently started learning how to use PyGame as well as it was something new to me. However I do get stuck quite a lot when trying to code for PyGame. Just so that you know I have read and watched a lot of tutorials but none of them helps me in the way I need it to.
My problem is that I am trying to get a box to appear on the screen that has a word written on it, such as: "Hello". Once that button is clicked then hello should appear.
Here is my plan:
I make a class named pane
Properties of that class would be things like: xpos, ypos, text, width height
Methods of that class would be things like: add(textToDisplay), delete(textToDisplay) - (The delete function is currently not all that important), displayPane()
If possible could you please tell me if this is actually doable using python and pygame/sort of hint to me how I would do it (Like with some useful links because Google has not been my friend for when it has come down to the searching. XD )? and if it isn't what language would you recommend I do it in?
Take a look at:
http://pygame.org/docs/ref/mouse.html#pygame.mouse.get_pos
http://pygame.org/docs/ref/rect.html#pygame.Rect.collidepoint
http://pygame.org/docs/ref/draw.html#pygame.draw.rect
http://pygame.org/docs/ref/font.html#pygame.font.SysFont
My suggestion is a procedural approach if you're dealing with only one shape:
Use pygame.draw.rect to draw a rectangle on a point.
Use a function to capture mouse clicks and their positions.
Collide mouse clicks
with you rectangle position (use a list of objects if you have
several).
Create a function to draw text on the screen and associate
it with the function which collides mouse clicks to rectangles.
Otherwise I'd take an object-oriented approach and use the pygame.sprite.Sprite class for each shape, or custom classes if you have special needs. The click capture-and-colide part would still be procedural, of course.
I adapted a project I'm working on to do what I think is what you want:
You can't actually see the cursor but I'm clicking with the middle mouse button to create the circles and with the main button to display the text.
Source: http://pastebin.com/jycEFAtX
Note: It's a circle and not a rectangle because I was working with circles, but that should be easy for you to change. Everything is a mess because I just adapted what I had and put it all together on a single file (it's probably better to separate it into different files). Use it just to get an idea of how to do what you want.
You can delete the text by setting obj.text as None, for example when event.button == SECONDARY_BUTTON, that way you can delete the text by clicking with the secondary mouse button on the circles.
P.S.: Notice I have a function that collides objects with mouse position (because it's needed for what I'm working with), but you don't need that. You can use pygame.Rect and pygame.Rect.collidepoint, as suggested above.

Ways to use relative (as opposed to absolute) mouse movement in OS X?

I'm working on a python application that controls mouse movement.
I have absolute mouse position working perfectly, using the Quartz.CoreGraphics library, which exposes some CGEvent commands for mouse control (like "CGEventCreateMouseEvent" and "CGEventPost").
However, I can't find anything in the docs about relative mouse movement. I would really like to simulate actual mouse movements (i.e. "x sideways, y up" instead of "x,y"), because some of the people using my application have multiple monitors, and I imagine it would be a lot easier just to inform the OS that there was a mouse movement rather than setting the position myself.
The nature of my interface also lends itself to relative mouse movement.
In Windows, there is a function in the win32 API that allows for "raw" mouse commands that can do exactly what I am looking for. Is there any way to achieve this in OS X?
I do not think that it's not possible with the way that events are managed. You need to capture the old (X,Y) and calculate the delta yourself. This means that you'll have a problem when you hit the end of the screen.
The good news is that you can move the mouse. So if the mouse hits the edge, you can reposition it to the center. You can also make the mouse pointer invisible. Finally, you can catch the mouse movements with a tracking rectangle that covers the entire screen. So the good news is that you can simulate precisely what you want to do , but the bad news is that it will take some work.
Useful APIs for this include:
CGEventTap (See Quartz Event Services Reference
CGPostEvent
CGDisplayMoveCursorToPoint (See Quartz Display Services Reference)
Other SO references include:
Limiting mouse to one display on Mac (Potentially using Cocoa)
Cocoa: Limit mouse to screen
Cocoa Move Mouse

python+win32: detect window drag

Is there a way to detect when a window that doesn't belong to my application is being dragged in windows using python/pywin32? I want to set it up so that when I drag a window whose title matches a pattern near the desktop edge, it snaps to the edge when the mouse is let go. I could write code to snap all windows with that title to the desktop whenever the mouse is released, but I want to only move the particular window that was being dragged.
So far the only possible solution I see is to use SetWindowsHookEx. Pywin32 doesn't interface this, so I think I'll have to do something like this:
Write a C extension module. It has a function like setCallback which takes a python function to be called when the drag event happens.
Write a C DLL that contains the actual hook into windows. This DLL will somehow have to call the python function that is currently set.
I'm not sure how to do these, or if it's correct, though..
pyHook seems to have done some of the work necessary, as it's hooked keyboard and mouse events. What I will probably do is keep a constant record of all the windows I care about, along with their positions. Then, on mouse up, I'll detect if any of the windows moved, and if so, and it's near where the mouse was let go, on the title-bar, I'll assume it was dragged there and snap it. Code to hook follows.
import pyHook
def mouseUp(event):
if event.Injected: return True
print "Mouse went up"
return True
hookManager = pyHook.HookManager()
hookManager.MouseLeftUp = mouseUp
hookManager.HookMouse()
You also need a main loop, which I have since I'm using gtk already, or you can do:
import pythoncom
pythoncom.PumpMessages()

Categories

Resources