Dual Keyboards/Joysticks in pygame [duplicate] - python

I am trying to create a project that I have made before but apart from this time I am going to be using a play-station one remote I was given for free from my school. The problem that when I move the joystick upwards and downwards it shows the same coordinates.(If you do not understand what I mean then look at the picture below). Then also I am not sure what I wold need to put into the if statement so that it checks if the joystick is upwards or downwards. I am also having trouble thinking on how you would check if the joystick is going in no direction.
I have already tried using an if statement where if the joystick is more than one number and less than another one (the first number being in the top half of the joystick and the other number meaning that it is in the bottom half of the joystick it will move downwards. The current if statement does not give off any errors but does not work. I have tried an if statement to check if it is in the middle but I am not too sure about it.
joystick_count = pygame.joystick.get_count()
if joystick_count == 0:
# No joysticks!
print("Error, I didn't find any joysticks.")
else:
# Use joystick #0 and initialize it
joystick = pygame.joystick.Joystick(0)
joystick.init()
if pygame.joystick.Joystick(0).get_axis(0) >= -0.0 and pygame.joystick.Joystick(0).get_axis(0) <= 0.0:
player_one.speed_y = 5
elif pygame.joystick.Joystick(0).get_axis(0) > -0.1 and pygame.joystick.Joystick(0).get_axis(0) < -0.9:
player_one.speed_y = -5
elif pygame.joystick(0).get_axis(0) == 0.0:
player_one.speed_y = -5
#The first if statement checks if the joystick is up and the second one
#checks if the joystick is downwards
# the middle one checks if the if statement is in the middle (not too sure)
#player one and two speed is what gets added on each time
The actual results that are that the sprite does not move when the joystick is moved downwards.
Joystick axis

First ensure that you have a joystick, by getting the number of joysticks by pygame.joystick.get_count(). Initialize the joystick by pygame.joystick.Joystick.init:
joystick = None
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
Once a joystick is initialized, ist axis value can be get by pygame.joystick.Joystick.get_axis. The value returned by this function is in range [-1, 1]. -1 for the maximum negative tilt and +1 for the maximum positive tilt.
Note, each analog stick of a gamepad or joystick has 2 axis, one for the horizontal direction and 1 for the vertical direction. Since the amount of the value returned by get_axis() depends on the tilting of the analog stick you should multiply the speed by the value.
Further you should ignore values near 0 for the dead center, because of the inaccuracy of the hardware. This can be don by a simple check using the built in function abs(x) e.g. abs(axisval) > 0.1:
if joystick:
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
player_one.speed_x = 5 * axis_x
if abs(axis_y) > 0.1:
player_one.speed_y = 5 * axis_y
See the following simple demo app:
import pygame
pygame.init()
size = (800,600)
screen = pygame.display.set_mode(size)
clock = pygame.time.Clock()
pos = [size[0]/2, size[1]/2]
speed = 5
joystick = None
done = False
while not done:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key==pygame.K_RETURN:
done = True
if joystick:
axis_x, axis_y = (joystick.get_axis(0), joystick.get_axis(1))
if abs(axis_x) > 0.1:
pos[0] += speed * axis_x
if abs(axis_y) > 0.1:
pos[1] += speed * axis_y
else:
if pygame.joystick.get_count() > 0:
joystick = pygame.joystick.Joystick(0)
joystick.init()
print("joystick initialized")
screen.fill((0, 0, 255))
pygame.draw.rect(screen, (255,255,255), (*pos, 10, 10))
pygame.display.flip()

Related

How to make a cue ball follow an equation in a pool game

I am writing a python project for a pool game where rather than using a cue stick to hit a ball straight, the user can input a math equation (This will allow the use of exponential absolute and trigonometry etc. For example: y = x, y = exp(x), y = sin(x) etc) which the cue ball will follow. However, I am not sure how to move the cue ball object along the equation of a line.
The cue ball will have a fixed velocity and friction. I have thought about using a graph and centering the origins of the graph to the balls x and y coordinates when the velocity of the cue ball is equal to 0.
The game is written mostly in Pygame, apart form the equation input box where a new window has been created in Tkinter.
If anyone has any knowledge of useful modules for using equations (Rather than just representing data or equations on a graph), that will help.
import pygame
from config import *
class Cueball:
def __init__(self, x, y):
self.x = x
self.y = y
self.velocity = pygame.math.Vector2(0,0)
self.image = pygame.image.load(path.join(CUEBALL_FOLDER, "Cueball.png"))
Here is an example where you can type in your equation and the ball position is updated. It uses the Python built-in eval() function. This function should only be used when you can trust your input. There is some very rudimentary filtering but it should not be relied upon to prevent malicious activity:
import pygame
from math import sin, tan, cos
def move(pos, eq, width, height):
"""Eval() the equation to return a new position"""
x = pos[0] + 1
if x >= width:
x = 0 # reset X position
try:
y = eval(eq[3:]) # skip "y ="
if type(y) not in (int, float):
raise Exception("Unexpected eval() return")
except:
x, y = pos # don't move if equation is invalid
if abs(y) > (height // 2): # reset position when off the screen
return 0, 0
return x, y
pygame.init()
# grab the first installed font
sys_font = pygame.font.SysFont(None, 37)
clock = pygame.time.Clock()
width, height = 320, 240
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Movement Equation")
x, y = 0, 0
eq = "y = x" # initial equation
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.TEXTINPUT:
# very limited input filtering
if event.text in "0123456789 +-/*x().sintaco":
eq += event.text
elif event.type == pygame.KEYUP:
if event.key == pygame.K_BACKSPACE:
eq = eq[:-1]
elif event.key == pygame.K_ESCAPE:
x, y = 0, 0 # reset position
# move the ball position
x, y = move((x, y), eq, width, height)
# Update text
text = sys_font.render(eq, True, pygame.Color("turquoise"))
# Graphics
screen.fill(pygame.Color("black"))
# Draw Text in the center
screen.blit(text, text.get_rect(center=screen.get_rect().center))
# Draw the ball (shift y axis so zero is in the center)
pygame.draw.circle(screen, "red", (x, height // 2 - y), 10)
# Update Screen
pygame.display.update()
clock.tick(30)
pygame.quit()
Your question states that the ball will have a fixed velocity and friction, so I don't understand how trigonometric functions apply. It sounds like you should be using Vector2 as you indicate, but you also want to follow an equation, so that's what I've done. Perhaps it'll help you develop your own approach. If there's any take away, it could be to remove your Tkinter dependency.
You can type to add to the equation, Backspace removes the last character and Esc will reset the ball position to the origin. Once the ball moves outside of the screen, it will reset its position.

Why is the math of my stacking game not working?

This is my first pygame project, and it's a stacking game. I made the game with the idea of any piece not touching the base is chopped off, and it goes on until your piece gets too small or if you reach the top of the screen. The problem with my game here, is that it works if you only stack pieces to the right. However, it falls apart if you start stacking to the left. I checked the math many times already, but it doesn't work still. It calculates the part that is chopped off by taking the base x position of the piece on the bottom, and then subtracting or adding the x position of the piece on top.
import pygame
import time
pygame.init()
win=pygame.display.set_mode((800,800))
pygame.display.set_caption("Stacking Game")
length=200 #The length of you CURRENT piece, the one that is moving
base=0 #X pos of the PREVIOUS stacked piece
x=0 #X pos of current piece
y=750 #y pos of current piece
blocks=0 #How many blocks have been stacked
difference=0
speed=5
direction=1
running=1
lengthbase=0 #Length of the PREVIOUS stacked piece
while (running==1):
pygame.time.delay(10)
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
if direction==1:
if x>800-length:
direction=0
else:
x+=speed
else:
if direction==0:
if x<0:
direction=1
else:
x-=speed
pygame.draw.rect(win,(0),(0,0,800,y+50)) #Blacks out the screen from the current piece to the top, leaving previously stacked pieces
pygame.draw.rect(win,(255,0,0),(x,y,length,50))
if blocks==16: #16 is the maximum number of blocks on the screen
pygame.quit()
print("You win!")
if event.type==pygame.KEYDOWN and event.key==pygame.K_SPACE:
if blocks==0: #Since the math is all based off the previous piece's length and position, this inserts the information of the first piece. We now know the position and length of the first stacked piece
base=x
lengthbase=200
else:
if x>base and x<(base+lengthbase): #The current piece should always be the same size and the previous piece, so it can either be stacked perfectly, to the right, or to the left. This calulates data when it is stacked to the right.
difference=(x+length)-(base+lengthbase)
length=length-difference
pygame.draw.rect(win,(0),(0,0,800,y+50))
pygame.draw.rect(win,(255,0,0),(x,y,length,50)) #Draws the new piece at the same position as the current piece, but with the new length
else:
if (x+length)>base and (x+length)<(base+lengthbase): #Calculates if the piece is stacked to the left.
difference=base-x
length=length-difference
pygame.draw.rect(win,(0),(0,0,800,y+50))
pygame.draw.rect(win,(255,0,0),(x+difference,y,length,50)) #If it was drawn on x, the stacked piece and the one below it would not be aligned. It has to move a distance of difference
else:
pygame.quit()
print("You lost.")
base=x #The stacked piece then becomes the bases for the next piece
lengthbase=length
speed+=1
blocks+=1
y-=50
time.sleep(0.5)
pygame.display.update()
You have to evaluate if the ranges [x1, x1+w1] and [x2, x2+w2] are overlapping.
Not overlapping:
x1 x1+w1
+----+
+----+
x2 x2+w2
x1 x1+w1
+----+
+----+
x2 x2+w2
Overlapping
x1 x1+w1
+--------------+
+----+
x2 x2+w2
x1 x1+w1
+----+
+---------------+
x2 x2+w2
x1 x1+w1
+---------+
+----------+
x2 x2+w2
x1 x1+w1
+----------+
+----------+
x2 x2+w2
This mean, that the ranges are overlapping if
x1 < x2+w2 and x2 < x1+w1
You don't need 2 separate cases for left and right. Just evaluate whether the areas overlap. Calculate the beginning and the end of the common area with min and max:
if x < base + lengthbase and base < x + length:
start = max(x, base)
end = min(x+length, base+lengthbase)
x = start
length = end - start
pygame.draw.rect(win,(0),(0,0,800,y+50))
pygame.draw.rect(win,(255,0,0),(x,y,length,50))
else:
pygame.quit()
print("You lost.")
This can be further simplified:
start = max(x, base)
end = min(x+length, base+lengthbase)
if start < end:
x, length = start, end - start
pygame.draw.rect(win,(0),(0,0,800,y+50))
pygame.draw.rect(win,(255,0,0),(x,y,length,50))
else:
# [...]
Side note: You've to handle the events in the event loop, rather than after the event loop:
while running==1:
pygame.time.delay(10)
for event in pygame.event.get():
if event.type==pygame.QUIT:
pygame.quit()
if event.type==pygame.KEYDOWN and event.key==pygame.K_SPACE:
if blocks==0:
base=x
lengthbase=200
else:
start = max(x, base)
end = min(x+length, base+lengthbase)
if start < end:
x, length = start, end - start
pygame.draw.rect(win,(0),(0,0,800,y+50))
pygame.draw.rect(win,(255,0,0),(x,y,length,50))
else:
pygame.quit()
print("You lost.")
base, lengthbase = x, length
speed += 1
blocks += 1
y -= 50
time.sleep(0.5)

Problems with creating a UI and glRotatef()

I am making a minimal Doom-style FPS game engine using Python, PyGame and Legacy PyOpenGL. I hope that the player will be able to look around in four directions - forward, backwards, left and right - using glRotatef() by pressing the left and right arrow keys.
A few problems have arisen:
A gun (a cube with a texture applied that changes the texture coordinates depending on the direction the player is facing) that should always appear 0.5 units ahead of the camera in the corresponding x and z position depending on the angle glRotatef() sets it to face towards, is moving to a strange position if I move on the x axis and then look left unless I stand dead centre in the room. The cube also appears to be static when I move left and right even though I am supplying it the x value I obtained from glGetDoublev(), and when I move forward the gun appears to be scaling even though I never implemented such functionality.
When I call
if event.type == pygame.KEYDOWN: # key pressed events
if event.key == pygame.K_LEFT:
glRotatef(-90,0,1,0)
if direction == 0:
direction = 3
else:
direction -= 1
to look to the left of the room, I occasionally get moved inside the wall and this sometimes affects the gun's position further.
I've tried adding fixed x and z variables (x_steps and z_steps) that are incremented by 0.1 every time the player moves. I'm not particularly sure why that removes the "static gun" problem but it did. However, when I rotated the camera, the same problem (of the gun moving to a strange position) still occurred.
## pygame/opengl initialisation code
def main():
pygame.init()
display = (800,600)
global displaySurface
displaySurface = pygame.display.set_mode(display, DOUBLEBUF|OPENGL)
pygame.display.set_caption("Wolfenstein 4D")
glEnable(GL_TEXTURE_2D)
glEnable(GL_DEPTH_TEST)
gluPerspective(45, (display[0]/display[1]),0.1,50.0)
## game loop, obtaining x,y,z positions and looking around the room
def room1():
direction = 3 ## 3 = forward, 2 = left, 1 = backward, 0 = right
while True:
pos = glGetDoublev(GL_MODELVIEW_MATRIX)
x = pos[3][0]
y = pos[3][1]
z = pos[3][2]
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN: # key pressed events
if event.key == pygame.K_LEFT:
glRotatef(-90,0,1,0)
if direction == 0:
direction = 3
else:
direction -= 1
x_steps = 0
z_steps = 0
if event.key == pygame.K_RIGHT:
glRotatef(90,0,1,0)
if direction == 3:
direction = 0
else:
direction += 1
x_steps = 0
z_steps = 0
## movement code
spd = 0.1
keys = pygame.key.get_pressed()
if direction == 3:
if keys[pygame.K_a]:
glTranslatef(spd,0,0)
x_steps -= spd
if keys[pygame.K_d]:
glTranslatef(-spd,0,0)
x_steps += spd
if keys[pygame.K_w]:
glTranslatef(0,0,spd)
z_steps -= spd
if keys[pygame.K_s]:
glTranslatef(0,0,-spd)
z_steps += spd
if direction == 2:
if keys[pygame.K_a]:
glTranslatef(0,0,-spd)
x_steps += spd
if keys[pygame.K_d]:
glTranslatef(0,0,spd)
x_steps -= spd
if keys[pygame.K_w]:
glTranslatef(spd,0,0)
z_steps -= spd
if keys[pygame.K_s]:
glTranslatef(-spd,0,0)
z_steps += spd
## gun drawing code in game loop
if direction == 3:
loadTexture("gun1.png")
drawHUDGun(x,-0.1,z-0.5,3,0.1,0.1,0.1)
if direction == 2:
loadTexture("gun.png")
drawHUDGun(z-0.5,-0.1,x+0.5,2,0.1,0.1,0.1)
## gun drawing function
def drawHUDGun(x,y,z,angle,width,height,depth=0.5,color = ((1,1,1))):
vertices = (
(width+x,-height+y,-depth+z),
(width+x,height+y,-depth+z),
(-width+x,height+y,-depth+z),
(-width+x,-height+y,-depth+z),
(width+x,-height+y,depth+z),
(width+x,height+y,depth+z),
(-width+x,-height+y,depth+z),
(-width+x,height+y,depth+z)
)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glBegin(GL_QUADS)
if angle == 3:
j = 0
if angle == 2:
j = 8
i = 0
for surface in surfaces:
i += 1
for vertex in surface:
glColor4f(1,1,1,1)
setTexCoord(0, texCoords, j)
if angle == 3:
if i >= 0 and i < 4:
if j < 4:
j += 1
if angle == 2:
if i == 2:
if j < 12:
j += 1
glVertex3fv(vertices[vertex])
glEnd()
glDisable(GL_BLEND)
glBegin(GL_LINES)
for edge in edges:
glColor3fv((0,1,0))
for vertex in edge:
glVertex3fv(vertices[vertex])
glEnd()
## implementation of the functions
main()
room1()
I expect the gun to appear 0.5 units ahead of the player in any direction regardless of where they are in the room, but the gun is often out of view due to being assigned incorrect x or z co-ordinates.
In legacy OpenGL there exists different current matrices. The current matrix which is affected by matrix operations can be chosen by glMatrixMode. Each matrix is organized on a stack. Matrices can be pushed and popped by glPushMatrix/glPopMatrix.
The projection matrix should be placed set to the projection matrix stack, the view and model transformations to the modelview matrix stack:
// choose projection matrix stack
glMatrixMode(GL_PROJECTION)
gluPerspective(45, (display[0]/display[1]),0.1,50.0)
// choose modelview matrix stack, for the following matrix operations
glMatrixMode(GL_MODELVIEW)
The gun should be placed in a first person view ("in front of you"). The esiest wy to achieve this is to draw the gun in viewspace, this means you've to cancel all the previous transformations to the modelview matrix. The matrix can be replace with the identity matrix by glLoadIdentity.
Save the modelview matrix onto the stack, set the identity matrix, draw the gun and finally restore the modelview matrix.e.g:
glPushMatrix()
glLoadIdentity()
drawHUDGun(x,-0.1,z-0.5,3,0.1,0.1,0.1)
glPopMatrix()

Smooth Jump over different FPS

I've created a game, but I have problems controlling the jump of the player. This code is a simplified version only showing the jump problem. Keep in mind that in the game itself the FPS may vary in mid jump. I've separated the jump code so it's the easier to identify, and also with the UP/DOWN arrows you can change the FPS to make tests.
Problem
The higher the FPS the smaller the jumps are, and the lower the FPS are the higher the jumps.
Expected Result
The jump reach the same height over many different FPS. Example: 30 and 120 FPS
Code
import pygame
Screen = pygame.display.set_mode((250,300))
Clock = pygame.time.Clock()
X = 50; Y = 250
FPS = 60; Current_FPS = FPS
Move = 480 / Current_FPS; Dir = "Up"
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if pygame.key.get_pressed()[pygame.K_UP]: FPS += 10 / Current_FPS
elif pygame.key.get_pressed()[pygame.K_DOWN] and FPS > 2: FPS -= 10 / Current_FPS
pygame.display.set_caption("FPS: "+str(int(FPS)))
Screen.fill((255,255,255))
X += 120 / Current_FPS
#---------------- JUMP CODE ---------------------#
if Dir == "Up":
if Move <= 0.0: Dir = "Down"
else:
Move -= 10 / Current_FPS
Y -= Move
else:
#RESET \/
if Y >= 250:
Dir = "Up"
X = 50; Y = 250
Move = 480 / Current_FPS; Dir = "Up"
#RESET /\
else:
Move += 120 / Current_FPS
Y += Move
#--------------------------------------------------#
pygame.draw.circle(Screen,(0,0,0),(int(X),int(Y)),5)
pygame.display.update()
Current_FPS = 1000.0 / Clock.tick_busy_loop(FPS)
You should set your initial jump velocity to be independent of frame rate, i.e:
Move = 480
Note that when you update the velocity (or in your case, it looks, speed) you do need to divide by the frame rate, since you are essentially multiplying by the time interval: v ~ u + a*dt. The same applies when updating the position, so this should be Y += Move / Current_FPS.
There are a couple of other things worth mentioning. Why do you track the direction variable? Why not just have your variable Move be the velocity. That way you would just need: Y += Move / Current_FPS and have positive/negative values indicate the direction. Also, your gravity is currently a lot stronger on the way down than on the way up, but this could be entirely deliberate!

Pygame making an object chase the cursor

Been at this for the past few hours, trying to make a small program where an image chases the cursor around. So far I've managed to make it so that the image is directly on top of the cursor and follows it around that way. However what I need is for the image to actually "chase" the cursor, so it would need to initially be away from it then run after it until it's then on top of the mouse.
Basically hit a wall with whats going wrong and what to fix up, here's what I've gotten so far:
from __future__ import division
import pygame
import sys
import math
from pygame.locals import *
class Cat(object):
def __init__(self):
self.image = pygame.image.load('ball.png')
self.x = 1
self.y = 1
def draw(self, surface):
mosx = 0
mosy = 0
x,y = pygame.mouse.get_pos()
mosx = (x - self.x)
mosy = (y - self.y)
self.x = 0.9*self.x + mosx
self.y = 0.9*self.y + mosy
surface.blit(self.image, (self.x, self.y))
pygame.display.update()
pygame.init()
screen = pygame.display.set_mode((800,600))
cat = Cat()
Clock = pygame.time.Clock()
running = True
while running:
screen.fill((255,255,255))
cat.draw(screen)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
Clock.tick(40)
Probably not in the best shape of coding, been messing with this for just over 5 hours now. Any help is much appreciated! Thanks :)
Assuming you want the cat to move at a fixed speed, like X pixels per tick, you need to pick a new position X pixels toward the mouse cursor. (If you instead want the cat to move slower the closer it gets, you'd instead pick a position a certain % of the way between the current position and the mouse cursor. If you want it to move faster the closer it gets, you need to divide instead of multiply. And so on. But let's stick with the simple one first.)
Now, how do you move X pixels toward the mouse cursor? The usual way of describing this is: You find the unit vector in the direction from the current position to the cursor, then multiply it by X, and that gives you the steps to add. And you can reduce that to nothing fancier than a square root:
# Vector from me to cursor
dx = cursor_x - me_x
dy = cursor_y - me_y
# Unit vector in the same direction
distance = math.sqrt(dx*dx + dy*dy)
dx /= distance
dy /= distance
# speed-pixel vector in the same direction
dx *= speed
dy *= speed
# And now we move:
me_x += dx
me_y += dy
Note that me_x and me_y are going to be floating-point numbers, not integers. That's a good thing; when you move 2 pixels northeast per step, that's 1.414 pixels north and 1.414 pixels east. If you round that down to 1 pixel each step, you're going to end up moving 41% slower when going diagonally than when going vertically, which would look kind of silly.

Categories

Resources