I want to simulate mouse movement through Python, so that the perspective of the Unity game rotates accordingly.
I used pydirectinput.moveTo() to move mouse. Used pydirectinput.keyDown() and pydirectinput.keyUp() to input key.
It can work in 《Borderlands 2》. I can move forward and backward and turn the camera.
But it can't work in 《Aim Hero》, which is a unity game. I can also move forward and backward. The characters in the game move with my control. But the character's perspective doesn't move and shoot.
I execute the command to move the mouse, and the mouse actually does move. However, the mouse will move out of the game's window, and the in-game perspective does not move with it.
Initial suspicion is the difference between Unity games and DirectX games.
This is my code:
import pydirectinput
import pyautogui
import time
def func1():
# ------------this can work in borderlands 2 and Aim Hero(unity)
time.sleep(4)
pydirectinput.keyDown('w')
time.sleep(1)
pydirectinput.keyUp('w')
time.sleep(1)
pydirectinput.keyDown('d')
time.sleep(0.5)
pydirectinput.keyUp('d')
time.sleep(1)
# ------------
# ------------this all can't work in Aim Hero(unity)
pos = pyautogui.position()
pydirectinput.moveTo(pos.x + 100, pos.y) # can't work in borderlands 2
# pos = pyautogui.position()
pydirectinput.moveTo(pos.x + 200, pos.y) # can work in borderlands 2
time.sleep(0.5)
pydirectinput.click()
# -------------
def func2():
time.sleep(4)
# in borderlands 2:
# If the command to move the mouse is executed once, the in-game camera does not move.
# If the command to move the mouse is executed n times, the in-game camera will move n-1 times
# in Aim Hero(Unity Game):
# The mouse keeps moving and eventually moves out of the game window.
# But the in-game perspective has never changed.
for i in range(2):
pos = pyautogui.position()
pydirectinput.moveTo(pos.x + 10, pos.y)
# pydirectinput.click()
print(pos, '\n')
time.sleep(0.1)
if __name__ == '__main__':
print("Start!\n")
func1()
# func2()
print("Done.")
Can you try
# ------------this all can't work in Aim Hero(unity)
pos = pyautogui.position()
pydirectinput.moveTo(900, 600) # estimated center position of screen
time.sleep(1.5) #'cause this code working slow
pydirectinput.click()
pydirectinput.click()
pydirectinput.click()
# -------------
instead of
# ------------this all can't work in Aim Hero(unity)
pos = pyautogui.position()
pydirectinput.moveTo(pos.x + 100, pos.y) # can't work in borderlands 2
# pos = pyautogui.position()
pydirectinput.moveTo(pos.x + 200, pos.y) # can work in borderlands 2
time.sleep(0.5)
pydirectinput.click()
# -------------
EDIT
Okay I found the problem! It's not about Python, it's about Unity. Let me show you a video from my computer.
As far as I understand, if you want to use the pydirectinput.moveTo() method in Unity game, you should not lock your cursor. Be careful about your cursor state and use CursorLockMode.None instead of CursorLockMode.Locked. But it wont be effective for real game :( And I don't know any alternative for now
Related
I can't figure out how to move sideways and jump at the same time, only one or the other.
I have tried asyncio and multithreading/multiprocessing and couldn't get it to work. I am all out of ideas and can't find anymore online. I also have another issue where I can jump and if I reach the apex of the jump and hold a or d I can move side to side floating.
class Player():
def __init__(self,health,boosts,height,width):
self.health = health
self.booosts = boosts
self.height = height
self.width = width
def jump():
global gravtest, press
press.remove("'w'")
gravtest = False
y = player[0].ycor()
for i in range (1, 10):
player[0].sety(y+(i*5))
time.sleep(0.05)
#while player[0]
gravtest = True
# def powers(self, boosts):
import turtle as t
import time, random
from pynput.keyboard import Key, Listener
t.ht()
press = []
gravtest = True
wn = t.Screen()
wn.title("Jump Man")
wn.bgcolor("white")
wn.screensize(250, 250)
wn.setup(width=1.0, height=1.0)
player = [t.Turtle(), Player(100, [], 25, 25)]
player[0].speed(0)
player[0].shapesize(0.5)
player[0].shape("square")
player[0].color("black")
player[0].up()
player[0].goto(0, 0)
floor = t.Turtle()
floor.speed(0)
floor.shape("square")
floor.shapesize(100)
floor.color("black")
floor.up()
floor.goto(0, -1150)
def gravity():
global gravtest
if gravtest == True:
grav = 0
while player[0].distance(floor) > 1007:
y = player[0].ycor()
player[0].sety(y + grav)
if grav > -5:
grav -= 1
player[0].sety(y + grav)
gravtest = False
if player[0].distance(floor) < 1045:
player[0].sety(-145)
def show(key):
global press
if not(format(key) in press):
press.append(format(key))
print(key)
def rem(key):
global press
if format(key) in press:
press.remove(format(key))
def move():
global press
while "'a'" in press:
player[0].setx(player[0].xcor()-2)
while "'d'" in press:
player[0].setx(player[0].xcor()+2)
if press == '\'s\'':
print()
gravity()
if "'w'" in press:
jump()
with Listener(on_press = show, on_release = rem) as listener:
while 1:
move()
Your problem with moving and jumping is that you have separate loops for each that try to handle one kind of movement of the movement in one place. That won't work properly if other stuff (like jumping while moving, or moving while falling under gravity) are supposed to be possible.
Instead, you need to have just one main loop. On each run of that loop, you should do one frame worth of movement of whatever kinds is appropriate (e.g. moving horizontally, falling or jumping). This may require some bookkeeping to keep track of how long the vertical acceleration from a jump lasts.
I'd make the main loop something like this:
on_the_ground = True
jumping = False
while True:
horizontal_movement() # this is always possible if the buttons are pressed
if on_the_ground or jumping:
jump() # can start or continue a jump if a button is pressed
else:
gravity() # fall, if you're not grounded or accelerating in a jump
handle_collisions_etc()
time.sleep(1/FRAMERATE)
Note, I've made some assumptions about the game logic you want. If you don't want to be able to move horizontally while in the air (as you can in many platformer games), you'll need to change this a little bit.
The jump function will need to keep track of how long you've been jumping for, since you probably want the player to be limited in how high they can go. I'm not exactly sure I understand the logic of your current code, so I'll leave it up to you to figure out exactly what to track.
A final suggestion is to move some of the global variables into the Player class. Even the turtle object you're using to draw things could be stored as an attribute on the instance you create.
I am trying to make a code where you can press the spacebar and an object will move forwards constantly. I am hoping to be able to have multiple of these objects moving at once without having to code hundreds of them separately.
This is my current code:
Bullet:
bullet = turtle.Turtle()
bullet.speed(0)
bullet.shape("circle")
bullet.color("red")
bullet.shapesize(stretch_wid=0.5, stretch_len=0.5)
bullet.penup()
bullet.goto(-200, -200)
bullet.hideturtle()
Movement:
def shoot_bullet():
stop = False
bullet2 = bullet.clone()
bullet2.showturtle()
while stop == False:
y = bullet2.ycor()
bullet2.sety(y + 20)
wn.update()
time.sleep(0.5)
...
onkeypress(shoot_bullet, "space")
This works until I press space again and the bullet just stops as 'bullet2' has been redefined as the new bullet I create when I press space. Is there a way to create multiple clones which can run on top of each other?
Your while stop == False: loop and time.sleep(0.5) have no place in an event-driven environment like turtle. Instead, as we fire each bullet, the below code attaches a timer event that moves it along until it disappears. At which point the bullet is recycled.
This simplified example just shoots bullets in random directions from the center of the screen. You can keep hitting the space bar to generate simultaneous bullets that all move in their own direction until they get far enough away:
from turtle import Screen, Turtle
from random import randrange
def move_bullet(bullet):
bullet.forward(15)
if bullet.distance((0, 0)) > 400:
bullet.hideturtle()
bullets.append(bullet)
else:
screen.ontimer(lambda b=bullet: move_bullet(b), 50)
screen.update()
def shoot_bullet():
screen.onkey(None, 'space') # disable handler inside hander
bullet = bullets.pop() if bullets else bullet_prototype.clone()
bullet.home()
bullet.setheading(randrange(0, 360))
bullet.showturtle()
move_bullet(bullet)
screen.onkey(shoot_bullet, 'space') # reenable handler on exit
bullet_prototype = Turtle('circle')
bullet_prototype.hideturtle()
bullet_prototype.dot(10) # just for this example, not for actual code
bullet_prototype.shapesize(0.5)
bullet_prototype.color('red')
bullet_prototype.penup()
bullets = []
screen = Screen()
screen.tracer(False)
screen.onkey(shoot_bullet, 'space')
screen.listen()
screen.mainloop()
I am new to Python and using Zelle's graphics to create a game. I need the two while loops below to run at the same time, however I am running into difficulty. I tried nesting the while loops but then the horses and civilians would only move if the mouse was clicked, which I don't want. What I want is for the horses and civilians to always be moving, and the princess to move only when the mouse is clicked, and stop the game with a "game over" when she has saved 10 civilians.
# animation loop.
while True==True:
for horse in horseList:
if horse.leg.getX() > -187:
horse.move( -1, 20 )
else:
horse.move( 5, 28 )
for civilian in civiliansList:
if civilian.getX() < 800:
civilian.move( 20, 0 )
else:
civilian.move( -100, 0 )
while civiliansSaved != 10:
mouse = win.getMouse()
princess.move( mouse, civilianCounter)
civilianCounter = princess.move( mouse, civilianCounter)
# move is a method that will return an updated civilianCounter ( it is initially 0 and defined outside of the while loop ), depending on whether princess runs into civilians
else:
print( "Game over" )
win.getMouse()
win.close()
Just use checkMouse() instead of getMouse() inside your animation loop.
Simple as that, I think.
while civiliansSaved < 11:
for horse in horseList:
if horse.leg.getX() > -187
horse.move( -1, 20 )
else:
horse.move( 5, 28 )
for civilian in civiliansList:
if civilian.getX() < 800:
civilian.move( 20, 0 )
else:
civilian.move( -100, 0 )
mouse = win.checkMouse()
if mouse:
princess.move( mouse, civilianCounter)
civilianCounter = princess.move( mouse, civilianCounter)
print( "Game over" )
win.getMouse()
win.close()
Doco:
checkMouse() Similar to getMouse, but does not pause for a user click.
Returns the latest point where the mouse was clicked or None if the
window as not been clicked since the previous call to checkMouse or
getMouse. This is particularly useful for controlling simple animation loops.
Here is an example that should do what you want without the need for parallel processing (which is tricky in python):
while True: # you don't need to write True==True
for horse in horseList:
if horse.leg.getX() > -187:
horse.move( -1, 20 )
else:
horse.move( 5, 28 )
for civilian in civiliansList:
if civilian.getX() < 800:
civilian.move( 20, 0 )
else:
civilian.move( -100, 0 )
mouse = win.getMouse()
princess.move( mouse, civilianCounter)
civilianCounter = princess.move( mouse, civilianCounter)
if civiliansSaved >= 10: # check to see if 10 or more have been saved
break
print( "Game over" )
win.getMouse()
win.close()
What you want to do is to keep the game running until the civilianSaved counter is at least 10. But you can't do that in a separate loop from your main game loop, so it makes more sense to not stop the game until the count is at least 10. This if statement can be included in your main game loop.
Zelle's graphics is not an event driven graphics tool, which I think is what you are looking for. Maybe consider switching to tkinter which is part of the Python standard library. You can use a callback function to handle mouse events so that your app is free to do other things while it is waiting to process a mouse event.
tkinter has an after() feature which allows you to call a function after a specified period of time. You can create separate functions for civilian movement and horse movement and call these functions after a specified period of time (say 50 ms). This effectively simulates running the functions in parallel and allows horses and civilians to move at the same time, at least from the user's perspective. You would need another call to after() within the movement functions so that they are continuously called.
self.root.after(50, self.process_horse_movement)
self.root.after(50, self.process_civilian_movement)
Then at the end of each movement function:
def process_horse_movement():
...
self.root.after(50, self.process_horse_movement)
def process_civilian_movement():
...
self.root.after(50, self.process_civilian_movement)
Also, a minor point. Use while True: instead of while True == True:.
This has been edited, the original post was about blitting the splash screens
I'm working on a simple game using pygame, the game is almost complete however i'm having some issues with the splash screen. The current issue is that when the game is run, the splash screens blit like they are supposed to, however if the user clicks anywhere on the screen or tries to exit the game, it crashes. Does anybody know a way around this issue, what i am hoping for is that the user would even be able to exit the game or even skip the splash screen whilst they are being displayed.
def main():
'''Here the game is being initialised'''
pygame.init() #Initialising Pygame
pygame.key.set_repeat(1, 20) #Registers event every 20MS if a key is held down
countFont = pygame.font.Font(None,18) #Font being set
statusFont = pygame.font.Font(None,18) #Font being set
screen = pygame.display.set_mode([WINDOW_WIDTH, WINDOW_HEIGHT])
pygame.display.set_caption('Do Not Get Capped')
#Drawable surface
background = pygame.Surface(screen.get_size())
#Used for converting color maps
background = background.convert()
#Splashscreen
#image fades in
for i in range (225):
background.fill((0,0,0))
image = pygame.image.load("splash_screen1.png")
image.set_alpha(i)
logoimage = screen.blit(image,(0,0))
pygame.display.flip()
pygame.time.delay(2000)
for i in range (225):
background.fill((0,0,0))
image = pygame.image.load("splash_screen2.png")
image.set_alpha(i)
logoimage = screen.blit(image,(0,0))
pygame.display.flip()
pygame.time.delay(4000)
'''The main function which is called at the end of this code'''
class Game():
'''This class's purpose is to keep track of the current score'''
def __init__(self):
self.score=0
self.goalNumbers=0
class Gun(pygame.sprite.Sprite):
'''This is the gun that the user controls'''
def __init__(self):
'''This is the class contructor'''
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("turret.png") #Loading the gun's image
self.rect = self.image.get_rect() #Getting the image's Rect
self.rect.x = 240 #Setting the rect's X position
self.rect.y = 630 #Setting the rect's Y position
def moveGun(self,orientation):
'''This function allows the gun to move on the screen.
If the orientation is facing left and the gun is 5 pixels
away from the wall, the gun is moved left & vice-a-versa'''
if orientation=="left" and self.rect.x>5:
self.rect.x-=5
if orientation=="right" and self.rect.x<(480-self.rect.width):
self.rect.x+=5
class Projectile(pygame.sprite.Sprite):
'''This class sets up the projectile/bullets that are controlled
by the user'''
def __init__(self,gun):
pygame.sprite.Sprite.__init__(self)
self.image=pygame.image.load("bullet.png")
self.rect=self.image.get_rect()
'''The code below places the projectile on top of the gun'''
self.rect.x=gun.rect.x+(gun.rect.width/2)-(self.rect.width/2)
self.rect.y=gun.rect.y-gun.rect.height
def updateProjectile(self):
'''This checks if the projectile has reached the top of the screen
if it hasn't it will continue to move up. If it has it will be deleted'''
if self.rect.y>0-self.rect.height:
self.rect.y-=5
else:
self.kill()
class Objects(pygame.sprite.Sprite):
'''This class creates the objects, they are loaded from the computer
and assigned to variables'''
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.obj=random.randint(1,3)
if self.obj==1: imagefile="capped"
if self.obj==2: imagefile="notcapped1"
if self.obj==3: imagefile="notcapped2"
self.image=pygame.image.load(imagefile+".png")
self.rect=self.image.get_rect()
self.rect.y=-0-self.rect.height
self.rect.x=(random.randint(2,44)*10)
def updateProjectile(self,game):
'''This function allows for the objects to move down the screen'''
if self.rect.y<640:
self.rect.y+=7 #This controls the amount of pixels the objects move down thus contrlling the speed
else:
if self.obj==1:
'''Here the code is tracking the users score'''
game.score+=10 #This adds 10 to the game's score
game.goalNumbers+=1
else:
game.score-=50
self.kill()
def shot(self,game):
'''This function updates the score as well as removing the objects when they are hit by a projectile'''
if self.obj == 1:
game.score-=50
else:
game.score+=10
self.kill()
# Create initial object instances
'''Here i am creating objects based on the classes i created'''
game=Game()
gun=Gun()
sprites=pygame.sprite.Group()
sprites.add(gun)
obstacles=pygame.sprite.Group()
projectiles=pygame.sprite.Group()
'''This variable will be used to control when to come out of the loop, i will state when this happens belows'''
finish=False
clock=pygame.time.Clock() #Initialising the clock
tick=0
'''This is the start of the main while loop, this loop will continue
until the variable 'finish' becomes false'''
while finish == False:
clock.tick(30) #Loop will run 30 times a second
tick+=1
screen.fill(bColour)
'''Here the main events are being run'''
for event in pygame.event.get():
if event.type==pygame.QUIT:
'''If the user clicks the exit button, the finish variable is made True,
this means that rather than exiting the game, the user's score is displayed after
which the game closes'''
finish = True
if event.type==pygame.KEYDOWN:
'''Here the script is checking for KEYDOWN events, these are events triggered
when the user presses on a keyboard key. In this case events are triggered when the left, right
and space keys are pressed.'''
if event.key==pygame.K_LEFT:
gun.moveGun("left") #If this is activated the 'orientation' changes to 'left' which shunts the gun 5 pixels to the left
if event.key==pygame.K_RIGHT:
gun.moveGun("right") #'Orientation' changes to 'right' which shunts the gun 5 pixels to the right
if event.key==pygame.K_SPACE:
'''This triggers the projectiles function'''
projectile=Projectile(gun)
projectiles.add(projectile)
'''This controls the projectiles and objects moving around the window'''
for projectile in projectiles:
projectile.updateProjectile()
for obstacle in obstacles:
obstacle.updateProjectile(game)
if tick>60:
'''This controls at what rate the objects fall which is now once every two
seconds, this is because the loop runs in 30 second intervals and the
clock is ticking at 60 seconds'''
if len(obstacles)<10:
obstacle=Objects()
obstacles.add(obstacle)
tick=0
collisions=pygame.sprite.groupcollide(obstacles,projectiles,False,True)
'''Here the script is checking whether the objects are hit by a projectile
if they are, the 'shot' function is triggered'''
if collisions:
for obstacle in collisions:
obstacle.shot(game)
'''This block of code constantly updates the player's scores'''
scoreText=countFont.render('Your current score is:'+str(game.score),True,(255,255,255),bColour)
screen.blit(scoreText,(0,620))
statusText=statusFont.render('You have '+str(10-game.goalNumbers)+' more tries',True,(255,255,255),bColour)
screen.blit(statusText,(0,10))
'''This code below updates and blits the objects to the screen'''
sprites.draw(screen)
projectiles.draw(screen)
obstacles.draw(screen)
pygame.display.flip()
if game.goalNumbers>=10:
'''This if statement is checking whether 'obj1' has touched the floor 10 times
and if it did, the finish variable is made true thus ending the game'''
finish=True
'''This is the last piece of code visible to the user,
what happens here is that the final message showing the final score is shown'''
scoreCountHolder=pygame.image.load("scoreframe.png")
scoreCountHolder.convert_alpha()
left=90
top=250
screen.blit(scoreCountHolder,(left,top))
countFont=pygame.font.Font(None,52)
statusText=countFont.render('Your Score:'+str(game.score),True,bColour,(255,255,255))
screen.blit(statusText,(105,300))
pygame.display.flip()
while True:
'''This waits for the user to quit the game'''
for event in pygame.event.get():
if event.type==pygame.QUIT:
sys.exit()
if __name__ == '__main__':
'''The main function being called'''
main()
When you have a pygame application, you take all events from the input queue.
The operating system, adds new events onto the queue, while your application removes them.
Since you do not remove the events, the operating system thinks that your application froze.
To fix this, you should call pygame.event.pump()
From the pygame docs:
For each frame of your game, you will need to make some sort of call
to the event queue. This ensures your program can internally interact
with the rest of the operating system. If you are not using other
event functions in your game, you should call pygame.event.pump() to
allow pygame to handle internal actions.
This function is not necessary if your program is consistently
processing events on the queue through the other pygame.eventpygame
module for interacting with events and queues functions.
There are important things that must be dealt with internally in the
event queue. The main window may need to be repainted or respond to
the system. If you fail to make a call to the event queue for too
long, the system may decide your program has locked up.
What happens is that you put a frame, Pygame then expects you to take the events that happened in the mean time out of its event queue. If you don't the OS thinks, that application got stuck!
You need to handle the events while you show the splash screen, if you want to allow the user to skip the flash screen.
Here's a way around that problem, it allows you to add specific event handling for splash screen:
class SplashScreen(object):
def __init__(self):
super().__init__()
# Use numbers for ordering, make them continuous.
self.image_dict = {
1: "splash_screen1.png",
2: "splash_screen2.png",
}
# Current image we are on!
self.current_index = min(self.image_dict)
# Last image
self.end_index = max(self.image_dict)
# Minimum alpha value
self.alpha_min = 0
# Maximum alpha value
self.alpha_max = 225
# Current alpha value
self.alpha = self.alpha_min
self.show = True
# I leave it to you to figure out this function.
def get_image(self):
image_name = self.image_dict[self.current_index]
image = pygame.image.load(image_name)
image.set_alpha(self.alpha)
self.alpha += 1
if self.alpha > self.alpha_max:
self.alpha = self.alpha_min
self.current_index += 1
if self.current_index == self.end_index:
self.show = False
return image
splash_screen = SplashScreen()
while True:
# The ever-green event handling loop!
for event in pygame.event.get():
...
if splash_screen.show:
# Splash screen stuff, one frame at a time
background.fill((0, 0, 0))
img = splash_screen.get_image()
screen.blit(img, (0, 0))
else:
# Game stuff, no need to call display.flip here, called at the end.
...
# Called at the end after frame is ready!
pygame.display.flip()
Left as an exercise
You should load and add the loaded images in the image_dict in __init__. It's not a good idea to keep reloading images from disk each time you blit them.
I have Turtle commands in my code as you see below. I would like to put a timer on these commands inside an if statement. My current code just works like so:
'# WHEN USER PRESSES SPACE, LIGHT TURNS GREEN
'# WHEN LIGHT IS GREEN ARROW MOVES 50 PIXELS
player1.pendown()
player1.forward(50)
player2.pendown()
player2.forward(50)
The arrow really only moves once every time I press the space key.
I would like to turn that into a timer so the arrow would move every 60 milliseconds until the user presses the space key again.
I tried using an wn.ontimer but I keep on messing up something. Below is how the code looks now:
def advance_state_machine():
global state_num
if state_num == 0:
tess.forward(70)
tess.fillcolor("red")
state_num = 1
else:
tess.back(70)
tess.fillcolor("green")
state_num = 0
player1.pendown()
player1.forward(50)
player2.pendown()
player2.forward(50)
wn.onkey(advance_state_machine, "space")
wn.listen()
wn.mainloop()
Your description of your problem is inconsistent and your code is out of sync with your description. Here's what I believe you stated you wanted your code to do:
The turtle moves forward 1 step every 60 milliseconds and alternates between red/forward and green/backward whenever the space bar is pressed.
I believe this code implements that, a key event is used to detect the space bar and a timer event is used to keep the turtle in motion:
from turtle import Turtle, Screen, mainloop
def advance_state_machine():
global state_num
wn.onkey(None, 'space') # avoid overlapping events
if state_num > 0:
tess.fillcolor('green')
else:
tess.fillcolor('red')
state_num = -state_num
wn.onkey(advance_state_machine, 'space')
def advance_tess():
tess.forward(state_num)
wn.ontimer(advance_tess, 60)
wn = Screen()
tess = Turtle()
tess.fillcolor('red')
state_num = 1
wn.onkey(advance_state_machine, 'space')
wn.ontimer(advance_tess, 60)
wn.listen()
mainloop()
Make sure to click on the window to make it active before trying the space bar. Left unattended, the turtle will eventually go off screen.