change of impulse direction Pymunk (python) - python

Hi i would like to create a small drone simulator. Two engines on the left and right. I was based on examples from the pymunk library. But I have no idea how to add engine power to make it hit the point, i.e. make the object rotate. This can be done easily with the help of this library? Thank you for any help
import sys,math
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
from pymunk.vec2d import Vec2d
import pymunk.pygame_util
width, height = 690,400
fps = 60
dt = 1./fps
JUMP_HEIGHT = 16.*2
def main():
### PyGame init
pygame.init()
screen = pygame.display.set_mode((width,height))
clock = pygame.time.Clock()
running = True
### Physics stuff
space = pymunk.Space()
space.gravity = 0,-1000
draw_options = pymunk.pygame_util.DrawOptions(screen)
# box walls
static = [ pymunk.Segment(space.static_body, (10, 50), (680, 50), 3)
, pymunk.Segment(space.static_body, (680, 50), (680, 370), 3)
, pymunk.Segment(space.static_body, (680, 370), (10, 370), 3)
, pymunk.Segment(space.static_body, (10, 370), (10, 50), 3)
]
for s in static:
s.friction = 1.
s.group = 1
space.add(static)
# player
body = pymunk.Body(5, pymunk.inf)
body.position = 100,100
left = pymunk.Poly(body, [(-30,25), (0, 25), (0, 0), (-30, 0)] )
right = pymunk.Poly(body, [(0,0), (0, 25), (30, 25), (30, 0)] )
left.color = 46,25,1,46
right.color = 1,25,1,25
space.add(body, left, right)
while running:
for event in pygame.event.get():
if event.type == QUIT or \
event.type == KEYDOWN and (event.key in [K_ESCAPE, K_q]):
running = False
elif event.type == KEYDOWN and event.key == K_LEFT:
# here is the problem
jump_v = math.sqrt(2.0 * JUMP_HEIGHT * abs(space.gravity.y))
power = max(min(jump_v, 1000), 10) * 1.5
impulse = power * Vec2d(0, 3)
impulse.rotate(body.angle - 45)
body.apply_impulse_at_world_point(impulse, (0,0) )
elif event.type == KEYDOWN and event.key == K_RIGHT:
# here is the problem
jump_v = math.sqrt(2.0 * JUMP_HEIGHT * abs(space.gravity.y))
power = max(min(jump_v, 1000), 10) * 1.5
impulse = power * Vec2d(0, 3)
impulse.rotate(body.angle + 45)
body.apply_impulse_at_world_point(impulse, (0,0) )
### Clear screen
screen.fill(pygame.color.THECOLORS["black"])
### Helper lines
for y in [50,100,150,200,250,300]:
color = pygame.color.THECOLORS['darkgrey']
pygame.draw.line(screen, color, (10,y), (680,y), 1)
# ### Draw stuff
space.debug_draw(draw_options)
pygame.display.flip()
### Update physics
space.step(dt)
clock.tick(fps)
if __name__ == '__main__':
sys.exit(main())

Your main issue is that your Body has infinite moment, which means that it can't rotate. Try creating the Body as something like:
body = pymunk.Body(5, 500)
You also need to apply the impulse somewhere that makes sense. Try:
body.apply_impulse_at_local_point(impulse, (5, 0))
and:
body.apply_impulse_at_local_point(impulse, (-5, 0))
for left and right.

Related

Circle not being drawn at the correct position pygame

problem in my tic tac toe project once again, the circle isnt appearing where it should when I click a square, i tried multiple different coordinates but still cant figure it out, so if any of you know what to do then let me know, thanks.
import pygame, sys
import numpy as np
pygame.init()
screen_color = (28, 170, 156)
line_color = (23, 140, 135)
line_width = 9
screen = pygame.display.set_mode((550, 450))
pygame.display.set_caption("Tic Tac Toe")
screen.fill(screen_color)
board = np.zeros((3, 3))
def draw_lines():
#1st horizontal
pygame.draw.line(screen, line_color, (0, 135), (550, 135), line_width)
#2nd horizontal
pygame.draw.line(screen, line_color, (0, 300), (550, 300), line_width)
#1st vertical
pygame.draw.line(screen, line_color, (175, 0), (175, 450), line_width)
#2nd vertical
pygame.draw.line(screen, line_color, (370, 0), (370, 450), line_width)
def draw_figures():
for row in range(3):
for col in range(3):
if board[row][col] == 1:
pygame.draw.circle(screen, 'cyan', (int(col * 550/3), int(row * 450/3)), 60, 10)
def mark_square(row, col, player):
board[row][col] = player
def available_square(row, col):
if board[row][col] == 0:
return True
else:
return False
def is_board_full():
for row in range(3):
for col in range(3):
if board[row][col] == 0:
return False
print(is_board_full())
print(board)
draw_lines()
player = 1
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
mouseX = event.pos[0] #X coordinate
mouseY = event.pos[1] #Y coordinate
clicked_row = int(mouseY * 3 // 450)
clicked_col = int(mouseX * 3 // 550)
#print(clicked_col)
#print(clicked_row)
if available_square(clicked_row, clicked_col):
if player == 1:
mark_square(clicked_row, clicked_col, 1)
player = 2
elif player == 2:
mark_square(clicked_row, clicked_col, 2)
player = 1
draw_figures()
print(board)
pygame.display.update()
(if there any other errors let me know too)
also let me know if getting the coordinates is trial and error or there is a specific way to do it, thanks!
The 3rd argument of pygame.draw.circle is the center of the circle. Draw the circle at (col + 0.5) and (row + 0.5):
pygame.draw.circle(screen, 'cyan', (int(col * 550/3), int(row * 450/3)), 60, 10)
pygame.draw.circle(screen, 'cyan', (int((col + 0.5) * 550/3), int((row+0.5) * 450/3)), 60, 10)
The problem is that the circle is centered on the grid and on on the middle of the squares.
You need to add an offset to the center like this:
pygame.draw.circle(screen, 'cyan', (int(col * 550/3 + x_offset), int(row * 450/3 + y_offset)), 60, 10)
I would also recommend you to create a variable called grid_width so that you don't need to have numbers like 550 or 450 everywhere in your programm.

Python PyMunk acts weirdly

here is the problem :
My pymunk code is from https://pymunk-tutorial.readthedocs.io/en/latest/shape/shape.html
But when i launch the program, a weird force acts on the segment.
See the result : https://youtu.be/s4a0RLb6Y6k
See the code : (without useless part about dependencies)
import pymunk
import pymunk.pygame_util
import pygame
from pygame.locals import *
pygame.init()
width, height = 1280, 720
window = pygame.display.set_mode((width, height), FULLSCREEN)
draw_options = pymunk.pygame_util.DrawOptions(window)
window.fill((0,0,0))
run = True
space = pymunk.Space()
space.gravity = 0, -1000
b0 = space.static_body
sol = pymunk.Segment(b0, (0, 30), (width, 30), 10)
sol.elasticity = 1
body = pymunk.Body(mass=1, moment=1000)
body.position = (width/2, 50)
segment = pymunk.Segment(body, (width/2-20,50), (width/2+20,50), 1)
segment.elasticity = 0.95
space.add(sol, body, segment)
while run == True:
space.step(0.01)
window.fill((0,0,0))
pygame.draw.line(window, (0, 255, 0), (0, height-30), (width, height-30), 1)
space.debug_draw(draw_options)
pygame.display.flip()
pygame.time.Clock().tick(60)
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == K_ESCAPE:
run = False
if event.type == QUIT:
run = False
mouse_x, mouse_y = pygame.mouse.get_pos()
pass
continue
I don't see where is the problem..
Thanks by advance
One common reason why strange behavior like that in the video happens is because center of gravity of the shape is not in the actual center. Try changing so that you make the Segment around the its center (0,0), where the body is.
Something like this maybe
segment = pymunk.Segment(body, (-20,0), (20,0), 1)
If you want to move it you can adjust the position of the body instead.
So the a an b values are relative from the center of mass of the body precited.
(with a and b from shape = pymunk.Segment(body, a, b, thickness) )
Thanks very much !

[Pygame]: Move surface towards position

Is there an algorithm to change a set of coordinates to make it move towards another set of coordinates?
Like, if I have ax,ay=(20,30) a
#fonctions
import pygame, sys
from pygame.locals import *
pygame.init()
fpsClock = pygame.time.Clock()
windows= pygame.display.set_mode((1200,500), 0, 32)
pygame.display.set_caption('testing')
size = width, height = (32, 32)
PNGImg = pygame.Surface(size)
sob_X=575
sob_Y=250
FPS = 15 # frames per second setting
RED= (255, 0, 0) #red color
while True:
windows.fill((54, 141, 197))
pygame.draw.circle(windows, RED, (70, 80), 20, 0)
windows.blit(PNGImg, (sob_X,sob_Y))
dis_X=sob_X-600 #cicle X_Cordinate
dis_Y=sob_X-870 #cicle Y_Cordinate
if dis_X<0:
sob_X=sob_X+ dis_X
if sob_X==70 and dis_Y<0:
sob_Y=sob_X+dis_Y
elif sob_X==70 and dis_Y>0:
sob_Y=sob_Y-dis_Y
elif dis_X>0:
sob_X=sob_X -dis_X
if sob_X==70 and dis_Y<0:
sob_Y=sob_Y+dis_Y
elif sob_X==70 and dis_Y>0:
sob_Y=sob_Y-dis_Y
windows.blit(PNGImg, (sob_X,sob_Y))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
pygame.display.update()
fpsClock.tick(FPS)
pygame.quit()
nd bx,by=(40,60)
How can I change ax and ay (coordinate of the image"surface") to equal bx and by? (Preferably an algorithm achievable in python.)
i tried to make algorithme but in the end don't work
thanks you
Replying here as I can't comment yet, but from the question itself, do you mean to do ax, ay = bx, by ? You can simply do that, or even individual ones like ax = by.
With an example from the terminal:
>>> ax,ay=(20,30)
>>> bx,by=(40,60)
>>> ax, ay = bx, by
>>> ax, ay
(40, 60)

Text not updating within loop (within function)

I'm trying to make my own version of OLDTV in Python, but I've run into a problem. I am making an effect where the text is bigger for a moment before going to a regular size. The problem that I am running up against is that the text does not display at all. I am running Anaconda 2.7 (installed in a previous project) with the Spyder editor that comes with, if that helps.
Code:
import pygame
from pygame.transform import scale
from random import randint
def make_font(fonts, size):
available = pygame.font.get_fonts()
# get_fonts() returns a list of lowercase spaceless font names
choices = map(lambda x:x.lower().replace(' ', ''), fonts)
for choice in choices:
if choice in available:
return pygame.font.SysFont(choice, size)
return pygame.font.Font(None, size)
_cached_fonts = {}
def get_font(font_preferences, size):
global _cached_fonts
key = str(font_preferences) + '|' + str(size)
font = _cached_fonts.get(key, None)
if font == None:
font = make_font(font_preferences, size)
_cached_fonts[key] = font
return font
_cached_text = {}
def create_text(text, fonts, size, color):
global _cached_text
key = '|'.join(map(str, (fonts, size, color, text)))
image = _cached_text.get(key, None)
if image == None:
font = get_font(fonts, size)
image = font.render(text, True, color)
_cached_text[key] = image
return image
pygame.init()
screen = pygame.display.set_mode((640, 480))
real = 0
guess = 0
score = 0
i = 0
gameRunning = False
runEnd = False
clock = pygame.time.Clock()
done = False
font_preferences = [
"Arial",
"Times New Roman",
"Papyrus",
"Comic Sans MS"]
def endRun(score):
global gameRunning
global runEnd
global i
screen.fill([255, 255, 255])
text = create_text(str(score), font_preferences, 72, (0, 0, 0))
screen.blit(text,
(320 - text.get_width() // 2, 240 - text.get_height() // 2))
gameRunning = False
runEnd = True
i = 0
def genNext():
screen.fill([255, 255, 255])
global real
gen = randint(1, 2)
if gen == 2:
ref = 0
else:
ref = 1
rand1 = randint(1, 6)
rand2 = randint(1, 6)
words = ["Red", "Orange", "Yellow", "Green", "Blue", "Purple"]
if ref == 1:
if rand1 == rand2:
real = 0
else:
real = 1
displayWord = words[rand1-1]
displayCol = rand2
elif ref == 0:
real = 0
displayWord = words[rand1-1]
displayCol = rand1
return ReturnValue(displayWord, displayCol)
def displayWordCol(word, col):
colVal = [(255, 0, 0), (255, 128, 0), (190, 190, 0), (0, 255, 0), (0, 0, 255), (128, 0, 255)]
for i in range(10, 5):
text = create_text(word, font_preferences, 72, colVal[col-1])
scale(text, (text.get_width() * i/5, text.get_height() * i/5))
screen.blit(text, (320 - text.get_width() // 2, 240 - text.get_height() // 2))
pygame.display.update()
clock.tick(60)
def checkNext(real, guess):
global score
if real == guess:
score = score + 1
e = genNext()
displayWordCol(e.y0, e.y1)
else:
endRun(score)
def homeScreen():
screen.fill(0)
text = create_text("OpenTV", font_preferences, 72, (100, 100, 100))
screen.blit(text,
(320 - text.get_width() // 2, 240 - text.get_height() // 2))
class ReturnValue:
def __init__(self, y0, y1):
self.y0 = y0
self.y1 = y1
homeScreen()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
if gameRunning == True:
guess = 0
checkNext(real, guess)
elif event.button == 3:
if gameRunning == True:
guess = 1
checkNext(real, guess)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_RETURN:
if gameRunning == False:
gameRunning = True
score = 0
e = genNext()
displayWordCol(e.y0, e.y1)
if event.key == pygame.K_ESCAPE:
if gameRunning == True:
endRun(score)
elif gameRunning == False:
done = True
if runEnd:
i = i + 1
if i == 120:
homeScreen()
runEnd = False
pygame.display.flip()
clock.tick(60)
The part that is causing trouble is the displayWordCol function. Is there something wrong with my code, or should I switch to IDLE and paste in my code?
I think the main problem is using scale() to scale rendered text. As far as I know, scale() is for images.
Try this as a mere example (you can adapt it to your needs afterwards).
def displayWordCol(word, col):
colVal = [(255, 0, 0), (255, 128, 0), (190, 190, 0), (0, 255, 0), (0, 0, 255), (128, 0, 255)]
for i in range(10):
text = create_text(word, font_preferences, int(72 *i/5), colVal[col-1])
#scale(text, (text.get_width() * i/5, text.get_height() * i/5))
screen.blit(text, (320 - text.get_width() // 2, 240 - text.get_height() // 2))
pygame.display.update()
clock.tick(60)
Another possible problem is that you might need to erase the screen before blitting the word in every loop cycle (not sure which effect you are looking for).
Finally, using pygame is almost always a good thing to use "dirty" rects, erasing and updating only the parts of the screen that have actually changed.
Regards!
EDIT: Try this as well. I added erasing the screen before blitting. I guess this effect is what you were looking for. I also added "dirty" rects management as an example.
def displayWordCol(word, col):
colVal = [(255, 0, 0), (255, 128, 0), (190, 190, 0), (0, 255, 0), (0, 0, 255), (128, 0, 255)]
regular_size = 72
cicles = 20
size_gap = 5
initial_size = regular_size + cicles * size_gap
text = create_text(word, font_preferences, int(initial_size), colVal[col - 1])
pos = (320, 240)
rect = pygame.Rect(pos[0] - text.get_width()/2, pos[1] - text.get_height()/2, text.get_width(), text.get_height())
screen.fill(pygame.Color("white"))
pygame.display.update()
for i in range(cicles):
text = create_text(word, font_preferences, int(initial_size - i*size_gap), colVal[col-1])
screen.fill(pygame.Color("white"), rect)
screen.blit(text, (pos[0] - text.get_width()/2, pos[1] - text.get_height()/2))
pygame.display.update(rect)
clock.tick(60))

Bouncing an image in a polygonal way with pygame

Hey guys am new to pygame .I have to make a ball image ball.jpg to bounce in polygonal way.I have also an another ball image which is running in a square way.What i need is to add an another ball image and bounce it in a polygonal way. My code is
import pygame
from itertools import cycle
pygame.init()
screen = pygame.display.set_mode((300, 300))
s_r = screen.get_rect()
ball = pygame.image.load('ball.jpg')
player = pygame.Rect((100, 100, 50, 50))
timer = pygame.time.Clock()
speed = 5
up, down, left, right = (0, -speed), (0, speed), (-speed, 0), (speed, 0)
dirs = cycle([up, right, down, left])
dir = next(dirs)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise
# move player
player.move_ip(dir)
# if it's outside the screen
if not s_r.contains(player):
# put it back inside
player.clamp_ip(s_r)
# and switch to next direction
dir = next(dirs)
screen.fill(pygame.color.Color('Black'))
screen.blit(ball,player)
pygame.display.flip()
timer.tick(25)
This code works perfectly with one image ..What i need is to add an another image and it must run in polygonal shape on pygame window..
Hope you guys can help me out ..Thanks
You'll need some vector math to do this.
Create a list of points that describe the path the object should move, then calculate a vector describing the direction from the current position to the target position. Then move the object by using that vector. Once you hit your target, aim for the next point to move.
Here's an example:
import pygame
import math
from itertools import cycle
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def add(u, v):
return [ u[i]+v[i] for i in range(len(u)) ]
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def dot(u, v):
return sum(u[i]*v[i] for i in range(len(u)))
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
class Ball(object):
def __init__(self, path):
self.x, self.y = (0, 0)
self.speed = 2.5
self.color = (200, 200, 200)
self.path = cycle(path)
self.set_target(next(self.path))
#property
def pos(self):
return self.x, self.y
# for drawing, we need the position as tuple of ints
# so lets create a helper property
#property
def int_pos(self):
return map(int, self.pos)
#property
def target(self):
return self.t_x, self.t_y
#property
def int_target(self):
return map(int, self.target)
def next_target(self):
self.set_target(self.pos)
self.set_target(next(self.path))
def set_target(self, pos):
self.t_x, self.t_y = pos
def update(self):
# if we won't move, don't calculate new vectors
if self.int_pos == self.int_target:
return self.next_target()
target_vector = sub(self.target, self.pos)
# a threshold to stop moving if the distance is to small.
# it prevents a 'flickering' between two points
if magnitude(target_vector) < 2:
return self.next_target()
# apply the balls's speed to the vector
move_vector = [c * self.speed for c in normalize(target_vector)]
# update position
self.x, self.y = add(self.pos, move_vector)
def draw(self):
pygame.draw.circle(screen, self.color, self.int_pos, 4)
pygame.init()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path2 = [(26, 43),
(105, 10),
(45, 125),
(150, 134),
(150, 26),
(107, 12)]
ball = Ball(path)
ball.speed = 1.9
ball2 = Ball(path2)
ball2.color = (200, 200, 0)
balls = [ball, ball2]
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
map(Ball.update, balls)
screen.fill((0, 0, 0))
map(Ball.draw, balls)
pygame.display.flip()
clock.tick(60)
Here's an example without a custom class:
import pygame
import math
from itertools import cycle
# some simple vector helper functions, stolen from http://stackoverflow.com/a/4114962/142637
def magnitude(v):
return math.sqrt(sum(v[i]*v[i] for i in range(len(v))))
def sub(u, v):
return [ u[i]-v[i] for i in range(len(u)) ]
def normalize(v):
vmag = magnitude(v)
return [ v[i]/vmag for i in range(len(v)) ]
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
pygame.init()
quit = False
path = [(26, 43),
(105, 110),
(45, 225),
(145, 295),
(266, 211),
(178, 134),
(250, 56),
(147, 12)]
path = cycle(path)
target = next(path)
ball = pygame.rect.Rect(target[0], target[1], 10, 10)
speed = 3.6
while not quit:
quit = pygame.event.get(pygame.QUIT)
pygame.event.poll()
if ball.topleft == target:
target = next(path)
target_vector = sub(target, ball.topleft)
if magnitude(target_vector) < 2:
target = next(path)
else:
move_vector = [c * speed for c in normalize(target_vector)]
ball.move_ip(move_vector)
screen.fill((0, 0, 0))
pygame.draw.rect(screen, pygame.color.Color('Grey'), ball)
pygame.display.flip()
clock.tick(60)

Categories