how to restrict rotation to a set amount of degrees in pygame? - python-2.7

My question is related to the answer of the question below.
rotating a rectangle.
How do i restrict the rotation to the area marked in red as in this picture? I tried various ways but was unsuccesfull. The code always keeps the poiter in the other area.
my modified code below.
import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))
surf = pygame.image.load("D:\\PYTHON\\SoftwareDG\\Games\\Platform_Suvivor\\assets\\rg.png").convert_alpha()
def rot_center(image, angle):
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
current_angle = 0
while True:
if pygame.event.get(pygame.QUIT): break
pygame.event.get()
mouseX, mouseY = pygame.mouse.get_pos()
rect = surf.get_rect(center=(92, 92))
target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))
if target_angle < -120:
target_angle = -120
if target_angle > 120:
target_angle = 120
print target_angle
if current_angle > target_angle:
current_angle -= 0.03
if current_angle < target_angle:
current_angle += 0.03
screen.fill((40, 140, 40))
screen.blit(rot_center(surf, -current_angle), rect)
pygame.display.update()

Are you looking for a clamp function like this?
def clamp(value, min_, max_):
"""Clamp value to a range between min_ and max_."""
return max(min_, min(value, max_))
In your case you also have to check if the current_angle is greater or less than 0.
if current_angle <= 0:
current_angle = clamp(current_angle, -180, -120)
elif current_angle > 0:
current_angle = clamp(current_angle, 120, 180)
Update: Here's the example with vectors. I use the vectors to figure out in which direction the sprite needs to be rotated. Note that right is now 0 degrees, left is 180° and it goes from 0° to 360°.
And here are some interesting links that helped me to learn how to do this:
http://docs.godotengine.org/en/stable/tutorials/matrices_and_transforms.html
http://www.wildbunny.co.uk/blog/vector-maths-a-primer-for-games-programmers/
import math
import pygame
pygame.init()
screen = pygame.display.set_mode((300, 300))
font = pygame.font.Font(None, 24)
GRAY = pygame.Color('gray90')
def clamp(value, min_value, max_value):
"""Clamp value to a range between min_value and max_value."""
return max(min_value, min(value, max_value))
def main():
current_angle = 0
clock = pygame.time.Clock()
surf = pygame.Surface((80, 50), pygame.SRCALPHA)
pygame.draw.polygon(surf, (40, 100, 200), ((0, 0), (80, 25), (0, 50)))
orig_surf = surf
rect = surf.get_rect(center=(150, 150))
orig_direction = pygame.math.Vector2(0, 1)
playing = True
while playing:
for event in pygame.event.get():
if event.type == pygame.QUIT:
playing = False
# Here I figure out if the target is closer in clock-
# or counterclockwise direction. `orientation` is positive
# if the target is closer in clockwise and negative
# if it's in counterclockwise direction.
vec_to_target = pygame.math.Vector2(pygame.mouse.get_pos()) - rect.center
direction = orig_direction.rotate(current_angle)
orientation = vec_to_target.dot(direction)
# I use orientation > 3 and < -3 instead of 0 to
# avoid jittering when the target angle is reached.
if orientation > 3:
current_angle += 3
elif orientation < -3:
current_angle -= 3
# You can use this modulo operation to keep the angle between
# 0° and 360°, but this is not needed because of the clamp.
# current_angle %= 360
# Clamp the value to the desired range.
current_angle = clamp(current_angle, 120, 240)
surf = pygame.transform.rotate(orig_surf, -current_angle)
rect = surf.get_rect(center=rect.center)
# Draw
screen.fill((40, 40, 40))
screen.blit(surf, rect)
txt = font.render('angle {:.1f}'.format(current_angle), True, GRAY)
screen.blit(txt, (10, 10))
txt = font.render('orientation {:.1f}'.format(orientation), True, GRAY)
screen.blit(txt, (10, 25))
pygame.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pygame.quit()

I managed to restrict the rotation of the gun to a limited area by just adding a one line check just before rotation. The code is far from perfect. When crossing from the + quadrant to the - quadrant and vice versa, the gun rotates in the opposite direction to get to the other side. More checks before incrementing the angle will correct the direction of rotation but for now the problem is solved in a basic way. The code is given below.
import pygame, math, base64
pygame.init()
screen = pygame.display.set_mode((200, 200))
surf = pygame.image.load("put your image here").convert_alpha()
def rot_center(image, angle):
orig_rect = image.get_rect()
rot_image = pygame.transform.rotate(image, angle)
rot_rect = orig_rect.copy()
rot_rect.center = rot_image.get_rect().center
rot_image = rot_image.subsurface(rot_rect).copy()
return rot_image
current_angle = -180
clock = pygame.time.Clock()
while True:
if pygame.event.get(pygame.QUIT): break
pygame.event.get()
mouseX, mouseY = pygame.mouse.get_pos()
rect = surf.get_rect(center=(92, 92))
target_angle = math.degrees(math.atan2(mouseY - rect.centery, mouseX - rect.centerx))
# the new line is just below
if 180 > target_angle > 120 or - 120 > target_angle > -179:
print "ok", target_angle
if current_angle > target_angle:
current_angle -= 2
if current_angle < target_angle:
current_angle += 2
screen.fill((40, 140, 40))
screen.blit(rot_center(surf, -current_angle), rect)
pygame.display.update()
clock.tick(60)

Related

pyHook TypeError: an integer is required [duplicate]

I am making a game in pygame 1.9.2.
It's a faily simple game in which a ship moves between five columns of bad guys who attack by moving slowly downward. I am attempting to make it so that the ship moves left and right with the left and right arrow keys. Here is my code:
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
location-=1
if location==-1:
location=0
if keys[K_RIGHT]:
location+=1
if location==5:
location=4
It works too well. The ship moves too fast. It is near impossible to have it move only one location, left or right. How can i make it so the ship only moves once every time the key is pressed?
You can get the events from pygame and then watch out for the KEYDOWN event, instead of looking at the keys returned by get_pressed()(which gives you keys that are currently pressed down, whereas the KEYDOWN event shows you which keys were pressed down on that frame).
What's happening with your code right now is that if your game is rendering at 30fps, and you hold down the left arrow key for half a second, you're updating the location 15 times.
events = pygame.event.get()
for event in events:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
location -= 1
if event.key == pygame.K_RIGHT:
location += 1
To support continuous movement while a key is being held down, you would have to establish some sort of limitation, either based on a forced maximum frame rate of the game loop or by a counter which only allows you to move every so many ticks of the loop.
move_ticker = 0
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
if move_ticker == 0:
move_ticker = 10
location -= 1
if location == -1:
location = 0
if keys[K_RIGHT]:
if move_ticker == 0:
move_ticker = 10
location+=1
if location == 5:
location = 4
Then somewhere during the game loop you would do something like this:
if move_ticker > 0:
move_ticker -= 1
This would only let you move once every 10 frames (so if you move, the ticker gets set to 10, and after 10 frames it will allow you to move again)
pygame.key.get_pressed() returns a list with the state of each key. If a key is held down, the state for the key is 1, otherwise 0. Use pygame.key.get_pressed() to evaluate the current state of a button and get continuous movement:
while True:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
x -= speed
if keys[pygame.K_RIGHT]:
x += speed
if keys[pygame.K_UP]:
y -= speed
if keys[pygame.K_DOWN]:
y += speed
This code can be simplified by subtracting "left" from "right" and "up" from "down":
while True:
keys = pygame.key.get_pressed()
x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * speed
y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed
The keyboard events (see pygame.event module) occur only once when the state of a key changes. The KEYDOWN event occurs once every time a key is pressed. KEYUP occurs once every time a key is released. Use the keyboard events for a single action or movement:
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x -= speed
if event.key == pygame.K_RIGHT:
x += speed
if event.key == pygame.K_UP:
y -= speed
if event.key == pygame.K_DOWN:
y += speed
See also Key and Keyboard event
Minimal example of continuous movement: replit.com/#Rabbid76/PyGame-ContinuousMovement
import pygame
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
rect = pygame.Rect(0, 0, 20, 20)
rect.center = window.get_rect().center
vel = 5
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
print(pygame.key.name(event.key))
keys = pygame.key.get_pressed()
rect.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * vel
rect.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * vel
rect.centerx = rect.centerx % window.get_width()
rect.centery = rect.centery % window.get_height()
window.fill(0)
pygame.draw.rect(window, (255, 0, 0), rect)
pygame.display.flip()
pygame.quit()
exit()
Minimal example for a single action: replit.com/#Rabbid76/PyGame-ShootBullet
import pygame
pygame.init()
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
tank_surf = pygame.Surface((60, 40), pygame.SRCALPHA)
pygame.draw.rect(tank_surf, (0, 96, 0), (0, 00, 50, 40))
pygame.draw.rect(tank_surf, (0, 128, 0), (10, 10, 30, 20))
pygame.draw.rect(tank_surf, (32, 32, 96), (20, 16, 40, 8))
tank_rect = tank_surf.get_rect(midleft = (20, window.get_height() // 2))
bullet_surf = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(bullet_surf, (64, 64, 62), bullet_surf.get_rect().center, bullet_surf.get_width() // 2)
bullet_list = []
run = True
while run:
clock.tick(60)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
bullet_list.insert(0, tank_rect.midright)
for i, bullet_pos in enumerate(bullet_list):
bullet_list[i] = bullet_pos[0] + 5, bullet_pos[1]
if bullet_surf.get_rect(center = bullet_pos).left > window.get_width():
del bullet_list[i:]
break
window.fill((224, 192, 160))
window.blit(tank_surf, tank_rect)
for bullet_pos in bullet_list:
window.blit(bullet_surf, bullet_surf.get_rect(center = bullet_pos))
pygame.display.flip()
pygame.quit()
exit()
import pygame
pygame.init()
pygame.display.set_mode()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit(); #sys.exit() if sys is imported
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_0:
print("Hey, you pressed the key, '0'!")
if event.key == pygame.K_1:
print("Doing whatever")
In note that K_0 and K_1 aren't the only keys, to see all of them, see pygame documentation, otherwise, hit tab after typing in
pygame.
(note the . after pygame) into an idle program. Note that the K must be capital. Also note that if you don't give pygame a display size (pass no args), then it will auto-use the size of the computer screen/monitor. Happy coding!
I think you can use:
pygame.time.delay(delayTime)
in which delayTime is in milliseconds.
Put it before events.
Try this:
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
if count == 10:
location-=1
count=0
else:
count +=1
if location==-1:
location=0
if keys[K_RIGHT]:
if count == 10:
location+=1
count=0
else:
count +=1
if location==5:
location=4
This will mean you only move 1/10 of the time. If it still moves to fast you could try increasing the value you set "count" too.
The reason behind this is that the pygame window operates at 60 fps (frames per second) and when you press the key for just like 1 sec it updates 60 frames as per the loop of the event block.
clock = pygame.time.Clock()
flag = true
while flag :
clock.tick(60)
Note that if you have animation in your project then the number of images will define the number of values in tick(). Let's say you have a character and it requires 20 sets images for walking and jumping then you have to make tick(20) to move the character the right way.
Just fyi, if you're trying to ensure the ship doesn't go off of the screen with
location-=1
if location==-1:
location=0
you can probably better use
location -= 1
location = max(0, location)
This way if it skips -1 your program doesn't break
make something like this, but based on time delay. i call my function first time immediately and then lunch timer, and while button is pressed i call it every button_press_delta seconds
from time import time
before main loop:
button_press_delta = 0.2
right_button_pressed = 0
while not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
if not right_button_pressed:
call_my_function()
right_button_pressed = 1
right_button_pressed_time_start = time()
if right_button_pressed:
right_button_pressed_time = (
time() - right_button_pressed_time_start)
if right_button_pressed_time > button_press_delta:
call_my_function()
right_button_pressed_time_start = time()
else:
right_button_pressed = 0
You should use clock.tick(10) as stated in the docs.
all of the answers above are too complexicated i would just change the variables by 0.1 instead of 1
this makes the ship 10 times slower
if that is still too fast change the variables by 0.01
this makes the ship 100 times slower
try this
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
location -= 0.1 #or 0.01
if location==-1:
location=0
if keys[K_RIGHT]:
location += 0.1 #or 0.01
if location==5:
location=4
To slow down your game, use pygame.clock.tick(10)

How to detect randomly curved non-continuous fixed colored lines from an image?

I am very new in the python openCV. I wanted to find the 7 fixed colored randomly curved lines from an image. The result should be boolean which will give me if the image contains the 7 fixed colored randomly curved lines or not. The sample input image is as below:
I also wanted to find out the non-continuous faint green colored trapezium from the image.
I have written the below code to filter out the specific color from the image but unable to detect the lines & unable to conclude if the image is containing the 7 different lines & trapezium. Below is my sample code for the same:
import cv2
import numpy as np
boundaries = [
(32, 230, 32), # 2 Green lines
(10, 230, 230), # 1 Yellow line
(230, 72, 32), # 1 Blue line
(255, 255, 255), # 2 White lines
(32, 72, 230) # 1 Red line
]
box = [(0, 100, 0), (100, 255, 100)]
image = cv2.imread('testImage5.png')
image = removeBlackBands(image)
# cv2.imshow('Cropped Image', image)
# cv2.waitKey(0)
for row in boundaries:
# create NumPy arrays from the boundaries
row = np.array(row, dtype="uint8")
mask = cv2.inRange(image, row, row)
cv2.GaussianBlur(mask, (5,5), 0)
cv2.imshow('Filtered', mask)
cv2.waitKey(0)
lines = cv2.HoughLinesP(mask, cv2.HOUGH_PROBABILISTIC, np.pi / 180, 50, 50, 100)
if lines is not None:
for x in range(0, len(lines)):
print("line ", x)
for x1, y1, x2, y2 in lines[x]:
print("x1 = {}, y1 = {}, x2 = {}, y2 = {}".format(x1, y1, x2, y2))
cv2.line(image,(x1,y1),(x2,y2),(0,0, 255),2, cv2.LINE_AA)
pts = np.array([[x1, y1], [x2, y2]], np.int32)
cv2.polylines(image, [pts], True, (0, 255, 0))
cv2.imshow('Processed.jpg', image)
cv2.waitKey(0)
# create NumPy arrays from the boundaries
lower = np.array(box[0], dtype="uint8")
upper = np.array(box[1], dtype="uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)
output = cv2.cvtColor(output, cv2.COLOR_BGR2GRAY)
output = cv2.Canny(output, 100, 150)
# show the images
# cv2.imshow("output", output)
# cv2.waitKey(0)
cv2.destroyAllWindows()
Can somebody help me? Thanks in Advance..!!!
This is the function I wrote for the same.
def detectNodes(self, image, tolerance=0):
"""
Detect the nodes in the image
Algorithm used:
1. Pre-process the image to filter out the required color
2. Convert the pre-processed image to binary image
Args:
image(np.ndarray): Numpy Nd array of image
tolerance: (int): Margin of consideration while extracting color from image. Default: 0
Returns
True upon success, False otherwise
"""
noOfNodesDetected = 0
curveWidth = 2
noOfNodeDetectThreshold = 5
cropH = self.testData["nodalROI"].get("height")
cropW = self.testData["nodalROI"].get("width")
roiImage = ppu.crop(image, cropH, cropW) # Crop node ROI
for color in self.nodalColorBoundaries.keys():
filtered = ImageProc.colorFilter(roiImage, colors=self.nodalColorBoundaries[color], tolerance=tolerance)
bgrImage = ppu.convertColorSpace(filtered, "bgr_to_gray")
thresh = ppu.threshold(bgrImage, 1, "thresh_binary")
logging.info("The shape of image is [{}]".format((thresh.shape)))
height, width = thresh.shape
pointFraction = self.testData.get("pointsToFormEquationOfCurve", None)
points = [int(fraction * height) for fraction in pointFraction]
logging.info("Point using for formulating the equation are [{}]".format(points))
pointFractionEvaluation = self.testData.get("pointsForEvaluationOfCurve", None)
pointsForEvaluation_h = [int(fraction * height) for fraction in pointFractionEvaluation]
logging.info("Point using for Evaluating the equation are [{}]".format(pointsForEvaluation_h))
curve1 = []
curve2 = []
for point in points:
prevX = 0
flag = 0
for w in range(0, width):
if thresh[point][w] == 255:
if (abs(prevX - w)) > curveWidth:
if flag == 0:
curve1.append((point, w))
flag = 1
else:
curve2.append((point, w))
prevX = w
fitter = CurveFitter1D()
if curve2:
logging.error("Second curve detected with color {} having points {}".format(color, curve2))
if curve1:
x1 = [point[0] for point in curve1]
y1 = [point[1] for point in curve1]
logging.qvsdebug("Points using to find the Polynomial with color {} are {} ".format(color, curve1))
fitter._fit_polyfit(x1, y1, 4)
logging.qvsdebug("Coefficient of the Polynomial with color {} are {} ".format(
color, fitter._fitterNamespace.coefs))
else:
logging.error("Points not found with {}".format(color))
return False
pointsForEvaluation_w = [int(round(fitter._predY_polyfit(point))) for point in pointsForEvaluation_h]
logging.qvsdebug(
"Points using for the verification of Polynomial representing curve with color {} are {} ".format(
color, zip(pointsForEvaluation_h, pointsForEvaluation_w)))
counter = 0
for i in range(len(pointsForEvaluation_h)):
if pointsForEvaluation_w[i] + 2 >= width:
continue # Continue if control is reaching to width of iamge - 2
if any(thresh[pointsForEvaluation_h[i]][pointsForEvaluation_w[i] - 2:pointsForEvaluation_w[i] + 3]):
counter += 1
logging.info(
"Out of {} points {} points are detected on the curve for color {}".format(len(pointsForEvaluation_h),
counter, color))
nodeDetectThreshold = int(len(pointsForEvaluation_h) * 0.6)
if counter >= nodeDetectThreshold:
noOfNodesDetected += 1
if noOfNodesDetected >= noOfNodeDetectThreshold:
logging.info("Nodes found in this frame are [%d], minimum expected [%d]" % (
noOfNodesDetected, noOfNodeDetectThreshold))
return True
else:
logging.error("Nodes found in this frame are [%d], minimum expected is [%d]" % (
noOfNodesDetected, noOfNodeDetectThreshold))
return False

how to make complex shapes using swarm of dots......like chair,rocket and many more using pygame and numpy

i am working on a project of swarm algorithms and i am trying to make complex shapes using the swarm consensus. However, the mathematics to achieve that seems quite difficult for me.
I have been able to make shapes like stars, circle and triangle but to make other complex shapes seems more harder. It would be very helpful if i get the idea of using numpy arrays to build these complex shapes using swarms....................................................
# general function to reset radian angle to [-pi, pi)
def reset_radian(radian):
while radian >= math.pi:
radian = radian - 2*math.pi
while radian < -math.pi:
radian = radian + 2*math.pi
return radian
# general function to calculate next position node along a heading direction
def cal_next_node(node_poses, index_curr, heading_angle, rep_times):
for _ in range(rep_times):
index_next = index_curr + 1
x = node_poses[index_curr][0] + 1.0*math.cos(heading_angle)
y = node_poses[index_curr][1] + 1.0*math.sin(heading_angle)
node_poses[index_next] = np.array([x,y])
index_curr = index_next
return index_next
##### script to generate star #####
filename = 'star'
swarm_size = 30
node_poses = np.zeros((swarm_size, 2))
outer_angle = 2*math.pi / 5.0
devia_right = outer_angle
devia_left = 2*outer_angle
# first node is at bottom left corner
heading_angle = outer_angle / 2.0 # current heading
heading_dir = 0 # current heading direction: 0 for left, 1 for right
seg_count = 0 # current segment count
for i in range(1,swarm_size):
node_poses[i] = (node_poses[i-1] +
np.array([math.cos(heading_angle), math.sin(heading_angle)]))
seg_count = seg_count + 1
if seg_count == 3:
seg_count = 0
if heading_dir == 0:
heading_angle = reset_radian(heading_angle - devia_right)
heading_dir = 1
else:
heading_angle = reset_radian(heading_angle + devia_left)
heading_dir = 0
print(node_poses)
with open(filename, 'w') as f:
pickle.dump(node_poses, f)
pygame.init()
# find the right world and screen sizes
x_max, y_max = np.max(node_poses, axis=0)
x_min, y_min = np.min(node_poses, axis=0)
pixel_per_length = 30
world_size = (x_max - x_min + 2.0, y_max - y_min + 2.0)
screen_size = (int(world_size[0])*pixel_per_length, int(world_size[1])*pixel_per_length)
# convert node poses in the world to disp poses on screen
def cal_disp_poses():
poses_temp = np.zeros((swarm_size, 2))
# shift the loop to the middle of the world
middle = np.array([(x_max+x_min)/2.0, (y_max+y_min)/2.0])
for i in range(swarm_size):
poses_temp[i] = (node_poses[i] - middle +
np.array([world_size[0]/2.0, world_size[1]/2.0]))
# convert to display coordinates
poses_temp[:,0] = poses_temp[:,0] / world_size[0]
poses_temp[:,0] = poses_temp[:,0] * screen_size[0]
poses_temp[:,1] = poses_temp[:,1] / world_size[1]
poses_temp[:,1] = 1.0 - poses_temp[:,1]
poses_temp[:,1] = poses_temp[:,1] * screen_size[1]
return poses_temp.astype(int)
disp_poses = cal_disp_poses()
# draw the loop shape on pygame window
color_white = (255,255,255)
color_black = (0,0,0)
screen = pygame.display.set_mode(screen_size)
screen.fill(color_white)
for i in range(swarm_size):
pygame.draw.circle(screen, color_black, disp_poses[i], 5, 0)
for i in range(swarm_size-1):
pygame.draw.line(screen, color_black, disp_poses[i], disp_poses[i+1],2)
pygame.draw.line(screen, color_black, disp_poses[0], disp_poses[swarm_size-1], 2)
pygame.display.update()
Your method for drawing takes huge advantage of the symmetries in the shapes you are drawing. More complex shapes will have fewer symmetries and so your method will require a lot of tedious work to get them drawn with stars. Without symmetry you may be better served writing each individual line 'command' in a list and following that list. For example, drawing the number 4 starting from the bottom (assuming 0 degrees is --> that way):
angles = [90,225,0]
distances = [20,15,12]
Then with a similar program to what you have, you can start drawing dots in a line at 90 degrees for 20 dots, then 225 degrees for 15 dots etc... Then by adding to these two lists you can build up a very complicated shape without relying on symmetry.

Firing bullets from a rotating gun

My question is connected to this question:
rotating gun with restricted movement
What calculations do you need to do to get bullets firing from the gun muzzle?
my code is given below
def gun_radar(self):
for p in self.gameobjects:
if "R" in p.name or "L" in p.name:
if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar: # if hero within radar
p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
p.direction = p.orig_direction.rotate(p.current_angle)
p.orientation = p.vec_to_target.dot(p.direction)
if p.orientation > 2:
p.current_angle += 1
elif p.orientation < -2:
p.current_angle -= 1
p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)
p.gun_rotate(-p.current_angle)
self.blt_timer -= 1 #count down the timer. when zero calculate vector and add bullet to fired_blts
if self.blt_timer<= 0:
w, h = p.rect.center
angle_in_rad = p.current_angle * (math.pi) / 180
w = w + math.cos(angle_in_rad)
h = h + math.sin(-angle_in_rad)
bullet = Bombullet(bulletimage, w, h)
bullet.xvel = bullet.speed * math.cos(angle_in_rad)
bullet.yvel = bullet.speed * math.sin(angle_in_rad)
bullet.rect.x += bullet.xvel
bullet.rect.y += bullet.yvel
self.fired_blts.add(bullet)
self.blt_timer = 100
when the hero comes within a circular area of the gun it is activated and a bullet shoots from the center of the gun.
I move the bullet with
def move(self):
self.rect.x += self.xvel
self.rect.y += self.yvel
print self.rect.x, self.rect.y, self.life
self.life -= 1
the bullet updates and shoots in the correct direction but shoots from the center of the gun. How do I move the shooting point to the muzzle?
Here's an example of a firing cannon. You just have to pass the current angle and the rect.center of the cannon to the newly created bullet and then rotate its image and velocity vector in the __init__ method.
import math
import pygame as pg
from pygame.math import Vector2
pg.init()
screen = pg.display.set_mode((640, 480))
FONT = pg.font.Font(None, 24)
BLACK = pg.Color('black')
BG_COLOR = pg.Color('darkseagreen4')
class Bullet(pg.sprite.Sprite):
def __init__(self, pos, angle):
super(Bullet, self).__init__()
self.image = pg.Surface((20, 11), pg.SRCALPHA)
pg.draw.rect(self.image, pg.Color('grey11'), [0, 0, 13, 11])
pg.draw.polygon(
self.image, pg.Color('grey11'), [(13, 0), (20, 5), (13, 10)])
self.image = pg.transform.rotate(self.image, -angle)
self.rect = self.image.get_rect(center=pos)
# To apply an offset to the start position,
# create another vector and rotate it as well.
offset = Vector2(80, 0).rotate(angle)
# Use the offset to change the starting position.
self.pos = Vector2(pos) + offset
self.velocity = Vector2(5, 0)
self.velocity.rotate_ip(angle)
def update(self):
self.pos += self.velocity
self.rect.center = self.pos
def main():
clock = pg.time.Clock()
# The cannon image and rect.
surf = pg.Surface((40, 22), pg.SRCALPHA)
surf.fill(pg.Color('grey27'))
pg.draw.rect(surf, pg.Color('grey11'), [30, 6, 10, 10])
orig_surf = surf
rect = surf.get_rect(center=(320, 240))
angle = 0 # Angle of the cannon.
# Add bullets to this group.
bullet_group = pg.sprite.Group()
playing = True
while playing:
for event in pg.event.get():
if event.type == pg.QUIT:
playing = False
if event.type == pg.MOUSEBUTTONDOWN:
if event.button == 1: # Left button fires bullet.
# Fire a bullet from cannon center with current angle.
bullet_group.add(Bullet(rect.center, angle))
bullet_group.update()
# Find angle to target (mouse pos).
x, y = Vector2(pg.mouse.get_pos()) - rect.center
angle = math.degrees(math.atan2(y, x))
# Rotate the cannon image.
surf = pg.transform.rotate(orig_surf, -angle)
rect = surf.get_rect(center=rect.center)
# Draw
screen.fill(BG_COLOR)
bullet_group.draw(screen)
screen.blit(surf, rect)
txt = FONT.render('angle {:.1f}'.format(angle), True, BLACK)
screen.blit(txt, (10, 10))
pg.draw.line(
screen, pg.Color(150, 60, 20), rect.center, pg.mouse.get_pos(), 2)
pg.display.update()
clock.tick(30)
if __name__ == '__main__':
main()
pg.quit()
You can also use math.cos and sin to calculate the offset.
run = math.cos(math.radians(angle)) * 80
rise = math.sin(math.radians(angle)) * 80
offset = run, rise
I achieved what I needed to do with thefollowing code which is the same as above with a very slight modification which is explained in the code. #skrx answer and the comment by Nick A. aided me to achieve this.
def gun_radar(self):
for p in self.gameobjects:
if "R" in p.name or "L" in p.name:
if abs(p.rect.centerx - self.hero.rect.centerx) < p.radar and abs(p.rect.centery - self.hero.rect.centery) < p.radar: # if hero within radar
p.vec_to_target = pygame.math.Vector2(self.hero.rect.center) - p.rect.center
p.direction = p.orig_direction.rotate(p.current_angle)
p.orientation = p.vec_to_target.dot(p.direction)
if p.orientation > 2:
p.current_angle += 1
elif p.orientation < -2:
p.current_angle -= 1
p.current_angle = p.clamp(p.current_angle, p.clamp_min, p.clamp_max)
p.gun_rotate(-p.current_angle)
p.timer -= 1 #count down the timer. when zero calculate vector and add bullet to fired_blts
if p.timer<= 0:
w, h = p.rect.center
# adjust for the distance fromm the gun center to the gun muzzle
w = w + math.cos(math.radians(p.current_angle)) * 28
h = h + math.sin(math.radians(p.current_angle)) * 28
bullet = Bombullet(bulletimage, w, h) # create the bullet
# calculate the velocity
bullet.xvel = bullet.speed * math.cos(math.radians(p.current_angle))
bullet.yvel = bullet.speed * math.sin(math.radians(p.current_angle))
self.fired_blts.add(bullet)
p.timer = 100

Python boids implementation [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to implement a fairly basic boids simulation in python. My goal is to have a simulation with a basic predator prey setup. I found some pseudocode (can't post more than two links but it is the first result if you google boids pseudocode) and some code here and decided to give it a go. Because I wanted to add predators, I decided to give take the code I found modify it so that the boids (that will become prey) are sprites, and then go from there. However, I have run into this problem.
After I modified the code to use pygame sprites, all of the boids move to the lower right hand corner (the original code worked correctly).
My code (just clone the repo) is here(github). Has anyone ever run into the first issue? Does anyone have any ideas to solve it? As for question 2, could someone please explain how to do that?
Thank you and any help would be greatly appreciated.
P.S.
The behavior of the boids (their movement) appears to be working fine apart from the fact that they always go to the lower right hand corner.
P.P.S.
Thanks to furas the prey behave correctly now.
P.P.P.S.
As the debugging problem has been solved, the part of my question that remains involves an explanation, and I think should be on topic.
You have to differences in your code
You use different velocity speed at start - in __init__ but this shouldn't do difference.
You updates object in different moment.
You move all preys (using Group update) at the same time - after all calculations.
Original code moves every boid after its calculations so next boid use different data to calculate its move.
I put prey.update() inside for loops and remove all_sprites_list.update()
I organize code in a little different:
#!/usr/bin/env python
# Prey implementation in Python using PyGame
from __future__ import division # required in Python 2.7
import sys
import pygame
import random
import math
# === constants === (UPPER_CASE names)
SCREEN_SIZE = SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
BLACK = (0, 0, 0)
MAX_VELOCITY = 10
NUM_BOIDS = 50
BORDER = 25
# === classes === (CamelCase names for classes / lower_case names for method)
class Prey(pygame.sprite.Sprite):
def __init__(self, x, y):
super(Prey, self).__init__()
# Load image as sprite
self.image = pygame.image.load("ressources/img/prey.png").convert()
# Fetch the rectangle object that has the dimensions of the image
self.rect = self.image.get_rect()
# Coordinates
self.rect.x = x
self.rect.y = y
# self.velocityX = random.randint(-10, 10) / 10.0
# self.velocityY = random.randint(-10, 10) / 10.0
self.velocityX = random.randint(1, 10) / 10.0
self.velocityY = random.randint(1, 10) / 10.0
def distance(self, prey):
'''Return the distance from another prey'''
distX = self.rect.x - prey.rect.x
distY = self.rect.y - prey.rect.y
return math.sqrt(distX * distX + distY * distY)
def move_closer(self, prey_list):
'''Move closer to a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average distances from the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
if prey.rect.x == self.rect.x and prey.rect.y == self.rect.y:
continue
avgX += (self.rect.x - prey.rect.x)
avgY += (self.rect.y - prey.rect.y)
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
distance = math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0
self.velocityX -= (avgX / 100)
self.velocityY -= (avgY / 100)
def move_with(self, prey_list):
'''Move with a set of prey_list'''
if len(prey_list) < 1:
return
# calculate the average velocities of the other prey_list
avgX = 0
avgY = 0
for prey in prey_list:
avgX += prey.velocityX
avgY += prey.velocityY
avgX /= len(prey_list)
avgY /= len(prey_list)
# set our velocity towards the others
self.velocityX += (avgX / 40)
self.velocityY += (avgY / 40)
def move_away(self, prey_list, minDistance):
'''Move away from a set of prey_list. This avoids crowding'''
if len(prey_list) < 1:
return
distanceX = 0
distanceY = 0
numClose = 0
for prey in prey_list:
distance = self.distance(prey)
if distance < minDistance:
numClose += 1
xdiff = (self.rect.x - prey.rect.x)
ydiff = (self.rect.y - prey.rect.y)
if xdiff >= 0:
xdiff = math.sqrt(minDistance) - xdiff
elif xdiff < 0:
xdiff = -math.sqrt(minDistance) - xdiff
if ydiff >= 0:
ydiff = math.sqrt(minDistance) - ydiff
elif ydiff < 0:
ydiff = -math.sqrt(minDistance) - ydiff
distanceX += xdiff
distanceY += ydiff
if numClose == 0:
return
self.velocityX -= distanceX / 5
self.velocityY -= distanceY / 5
def update(self):
'''Perform actual movement based on our velocity'''
if abs(self.velocityX) > MAX_VELOCITY or abs(self.velocityY) > MAX_VELOCITY:
scaleFactor = MAX_VELOCITY / max(abs(self.velocityX), abs(self.velocityY))
self.velocityX *= scaleFactor
self.velocityY *= scaleFactor
self.rect.x += self.velocityX
self.rect.y += self.velocityY
# === main === (lower_case names)
# --- init ---
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE)
#screen_rect = screen.get_rect()
# --- objects ---
# lists
# This is a list of 'sprites.' Each block in the program is
# added to this list. The list is managed by a class called 'Group.'
prey_list = pygame.sprite.Group()
# This is a list of every sprite. All blocks and the player block as well.
all_sprites_list = pygame.sprite.Group()
# create prey_list at random positions
for i in range(NUM_BOIDS):
prey = Prey(random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT))
# Add the prey to the list of objects
prey_list.add(prey)
all_sprites_list.add(prey)
# --- mainloop ---
clock = pygame.time.Clock()
running = True
while running:
# --- events ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- updates ---
for prey in prey_list:
closeBoids = []
for otherBoid in prey_list:
if otherBoid == prey:
continue
distance = prey.distance(otherBoid)
if distance < 200:
closeBoids.append(otherBoid)
prey.move_closer(closeBoids)
prey.move_with(closeBoids)
prey.move_away(closeBoids, 20)
# ensure they stay within the screen space
# if we roubound we can lose some of our velocity
if prey.rect.x < BORDER and prey.velocityX < 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.x > SCREEN_WIDTH - BORDER and prey.velocityX > 0:
prey.velocityX = -prey.velocityX * random.random()
if prey.rect.y < BORDER and prey.velocityY < 0:
prey.velocityY = -prey.velocityY * random.random()
if prey.rect.y > SCREEN_HEIGHT - BORDER and prey.velocityY > 0:
prey.velocityY = -prey.velocityY * random.random()
prey.update()
# Calls update() method on every sprite in the list
#all_sprites_list.update()
# --- draws ---
screen.fill(BLACK)
# Draw all the spites
all_sprites_list.draw(screen)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Limit to 60 frames per second
# Used to manage how fast the screen updates
clock.tick(120)
# --- the end ---
pygame.quit()
sys.exit()
EDIT: real problem was dividing of two integer numbers in Python 2 which gives result rounded to integer number
Solution:
from __future__ import division
Use this before other imports.