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
Related
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
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.
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)
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.
I managed to create a function that with a given radius, starting point and a number of points. It will create a big circle and withing this circle it will create 4 small circles.
I want to add a grid on the background that will show the Y and X axis in TKinter every 100 pixels apart starting from the top left. The coordinate origin should be the top left corner.
For example if the screen is 300x300 then the window will have 3 lines (at 100, 200 and 300) on his X axis going from left to right and top up to bottom.
A grid as a coordinate system.
Example of how I create a normal line. I use a line class which contains 2 points start point and end point:
rootWindow = Tkinter.Tk()
rootFrame = Tkinter.Frame(rootWindow, width=1000, height=800, bg="white")
rootFrame.pack()
canvas = Tkinter.Canvas(rootFrame, width=1000, height=800, bg="white")
canvas.pack()
def draw_line(l):
"Draw a line with its two end points"
draw_point(l.p1)
draw_point(l.p2)
# now draw the line segment
x1 = l.p1.x
y1 = l.p1.y
x2 = l.p2.x
y2 = l.p2.y
id = canvas.create_line(x1, y1, x2, y2, width=2, fill="blue")
return id
This will create a grid on the canvas for you
import tkinter as tk
def create_grid(event=None):
w = c.winfo_width() # Get current width of canvas
h = c.winfo_height() # Get current height of canvas
c.delete('grid_line') # Will only remove the grid_line
# Creates all vertical lines at intevals of 100
for i in range(0, w, 100):
c.create_line([(i, 0), (i, h)], tag='grid_line')
# Creates all horizontal lines at intevals of 100
for i in range(0, h, 100):
c.create_line([(0, i), (w, i)], tag='grid_line')
root = tk.Tk()
c = tk.Canvas(root, height=1000, width=1000, bg='white')
c.pack(fill=tk.BOTH, expand=True)
c.bind('<Configure>', create_grid)
root.mainloop()