Pygame accurate shooting in game - math

I have simplified my code to the bare necessities to make it easier to understand. All that happens is when a certain area is clicked a bullet fires to around that area originating from a white square. The problem is that I don't want the bullet to hit around the area clicked, but exactly where it was clicked.
Here is my code:
import pygame, sys, time, random, math
from pygame.locals import *
background = (0, 0, 0)
entity_color = (255, 255, 255,255)
listLaser = pygame.sprite.Group()
player_x, player_y = 0, 0
move_player_x, move_player_y = 0, 0
all_sprites_list = pygame.sprite.Group()
class Entity(pygame.sprite.Sprite):
"""Inherited by any object in the game."""
def __init__(self, x, y, width, height):
pygame.sprite.Sprite.__init__(self)
self.x = x
self.y = y
self.width = width
self.height = height
# This makes a rectangle around the entity, used for anything
# from collision to moving around.
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
class User(Entity):
"""
Player controlled or AI controlled, main interaction with
the game
"""
def __init__(self, x, y, width, height):
super(User, self).__init__(x, y, width, height)
self.image = pygame.Surface([20, 37])
self.image.fill(entity_color)
self.image.blit(self.image, (0, 0))
class Player(User):
"""The player controlled Character"""
def __init__(self, x, y, width, height):
super(Player, self).__init__(x, y, width, height)
pass
class Bullet(pygame.sprite.Sprite):
def __init__(self, mouse, player):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([4, 10])
self.image.fill(entity_color)
self.mouse_x, self.mouse_y = mouse[0], mouse[1]
self.player = player
self.rect = self.image.get_rect()
def update(self):
'''
Gets vector from the two points and gets a direction and sends the bullet that way using player and clicked points
'''
speed = -4.
range = 200
distance = [self.mouse_x - self.player[0], self.mouse_y - self.player[1]]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
direction = [distance[0] / norm, distance[1 ] / norm]
bullet_vector = [direction[0] * speed, direction[1] * speed]
self.rect.x -= bullet_vector[0]
self.rect.y -= bullet_vector[1]
pygame.init()
pygame.display.set_caption('Race')
window_width = 800
window_height = 700
screen = pygame.display.set_mode((window_width, window_height))
player = Player(20, window_height / 2, 40, 37)
all_sprites_list.add(player)
while True: # the main game loop
for event in pygame.event.get():
if event.type == MOUSEBUTTONDOWN:
bullet = Bullet(pygame.mouse.get_pos(), [player.rect.x, player.rect.y])
bullet.rect.x = player.rect.x
bullet.rect.y = player.rect.y
all_sprites_list.add(bullet)
listLaser.add(bullet)
for ent in all_sprites_list:
ent.update()
screen.fill(background)
all_sprites_list.draw(screen)
pygame.display.flip()
pygame.display.update()
I really need help modifying my code to get the bullet to hit exactly at the clicked area

pygame.Rects can't have floating point numbers as their x, y attributes/coordinates and pygame just truncates the floats that you assign to the rect, so the direction becomes inaccurate.
You need to store the actual position in separate attributes (for example self.posx) and then update these attributes first and afterwards the self.rect.
class Bullet(pygame.sprite.Sprite):
def __init__(self, mouse, player):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([4, 10])
self.image.fill(entity_color)
self.mouse_x, self.mouse_y = mouse[0], mouse[1]
self.player = player
self.posx = self.player[0]
self.posy = self.player[1]
self.rect = self.image.get_rect()
speed = -4.
distance = [self.mouse_x - self.player[0], self.mouse_y - self.player[1]]
norm = math.sqrt(distance[0] ** 2 + distance[1] ** 2)
direction = [distance[0] / norm, distance[1 ] / norm]
self.bullet_vector = [direction[0] * speed, direction[1] * speed]
def update(self):
self.posx -= self.bullet_vector[0]
self.posy -= self.bullet_vector[1]
self.rect.topleft = self.posx, self.posy
You also don't need to calculate the bullet_vector every frame unless the bullets should be able to change their direction (like a homing missile).

Related

When using an instanced scene, the light source used in it does not work

I have an asteroid scene and a planet scene. When I run the asteroid scene separately, the child lights work, and when I generate them in the planet scene, the glow disappears. Moreover, I checked if this source is in the asteroid when it is already instanced as a variable, but has not yet been added to the scene. At this moment, the asteroid's child light source is absent.Eventually asteroid changes illumination (to no illumination lol) but ofc it shouldn't
Here are ready and process functions:
func _ready():
#THERE ARE ONLY MOVEMENT AND COLOR SETTINGS
random_color()
scale = Vector2(0.2, 0.2)
var go = true
rand_generate.randomize()
var delta_speed = rand_generate.randf_range(-0.5, 0.5)
angle_speed = 3 + delta_speed
angle_speed *= speed_scale
life_time = 2 * PI / angle_speed
rand_generate.randomize()
radius = rand_generate.randf_range(min_rad, max_rad)
position = Vector2(0, radius) + rotate_point
radius = Vector2(0, -radius)
func _physics_process(delta):
if PLAY:
#FUNCTION ONLY FOR DEBUG
position = get_global_mouse_position()
if not go:
#CONTROLS SHOULD IT MOVE
return
#THIS THREE IF'S ARE USED THAT THE ASTEROID
#FIRST SWIM OUT SMOOTHLY, THEN MOVE UNIFORMALLY
#THEN GENTLY REMOVE
if time < life_time * 0.3:
time += delta
var count_scale = lerp(0.01, 3, time / life_time)
scale = Vector2(count_scale,count_scale)
elif time > life_time * 0.7:
time += delta
var count_scale = lerp(3, 0.01,time / life_time)
scale = Vector2(count_scale,count_scale)
else:
time += delta
if time > life_time:
queue_free()
#DATS A CIRCULAR MOVEMENT AROUND A PLANET
position = rotate_point + radius.rotated(angle_speed*time + PI)
I solved my problem. In the Light2D settings, I set the Layer Min and Layer Max properties to -1 and 1, respectively. Everything works now

Uniformly distribute x points inside a circle

I would like to uniformly distribute a predetermined set of points within a circle. By uniform distribution, I mean they should all be equally distanced from each other (hence a random approach won't work). I tried a hexagonal approach, but I had problems consistently reaching the outermost radius.
My current approach is a nested for loop where each outer iteration reduces the radius & number of points, and each inner loop evenly drops points on the new radius. Essentially, it's a bunch of nested circles. Unfortunately, it's far from even. Any tips on how to do this correctly?
The goals of having a uniform distribution within the area and a uniform distribution on the boundary conflict; any solution will be a compromise between the two. I augmented the sunflower seed arrangement with an additional parameter alpha that indicates how much one cares about the evenness of boundary.
alpha=0 gives the typical sunflower arrangement, with jagged boundary:
With alpha=2 the boundary is smoother:
(Increasing alpha further is problematic: Too many points end up on the boundary).
The algorithm places n points, of which the kth point is put at distance sqrt(k-1/2) from the boundary (index begins with k=1), and with polar angle 2*pi*k/phi^2 where phi is the golden ratio. Exception: the last alpha*sqrt(n) points are placed on the outer boundary of the circle, and the polar radius of other points is scaled to account for that. This computation of the polar radius is done in the function radius.
It is coded in MATLAB.
function sunflower(n, alpha) % example: n=500, alpha=2
clf
hold on
b = round(alpha*sqrt(n)); % number of boundary points
phi = (sqrt(5)+1)/2; % golden ratio
for k=1:n
r = radius(k,n,b);
theta = 2*pi*k/phi^2;
plot(r*cos(theta), r*sin(theta), 'r*');
end
end
function r = radius(k,n,b)
if k>n-b
r = 1; % put on the boundary
else
r = sqrt(k-1/2)/sqrt(n-(b+1)/2); % apply square root
end
end
Might as well tag on my Python translation.
from math import sqrt, sin, cos, pi
phi = (1 + sqrt(5)) / 2 # golden ratio
def sunflower(n, alpha=0, geodesic=False):
points = []
angle_stride = 360 * phi if geodesic else 2 * pi / phi ** 2
b = round(alpha * sqrt(n)) # number of boundary points
for k in range(1, n + 1):
r = radius(k, n, b)
theta = k * angle_stride
points.append((r * cos(theta), r * sin(theta)))
return points
def radius(k, n, b):
if k > n - b:
return 1.0
else:
return sqrt(k - 0.5) / sqrt(n - (b + 1) / 2)
# example
if __name__ == '__main__':
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
points = sunflower(500, alpha=2, geodesic=False)
xs = [point[0] for point in points]
ys = [point[1] for point in points]
ax.scatter(xs, ys)
ax.set_aspect('equal') # display as square plot with equal axes
plt.show()
Stumbled across this question and the answer above (so all cred to user3717023 & Matt).
Just adding my translation into R here, in case someone else needed that :)
library(tibble)
library(dplyr)
library(ggplot2)
sunflower <- function(n, alpha = 2, geometry = c('planar','geodesic')) {
b <- round(alpha*sqrt(n)) # number of boundary points
phi <- (sqrt(5)+1)/2 # golden ratio
r <- radius(1:n,n,b)
theta <- 1:n * ifelse(geometry[1] == 'geodesic', 360*phi, 2*pi/phi^2)
tibble(
x = r*cos(theta),
y = r*sin(theta)
)
}
radius <- function(k,n,b) {
ifelse(
k > n-b,
1,
sqrt(k-1/2)/sqrt(n-(b+1)/2)
)
}
# example:
sunflower(500, 2, 'planar') %>%
ggplot(aes(x,y)) +
geom_point()
Building on top of #OlivelsAWord , here is a Python implementation using numpy:
import numpy as np
import matplotlib.pyplot as plt
def sunflower(n: int, alpha: float) -> np.ndarray:
# Number of points respectively on the boundary and inside the cirlce.
n_exterior = np.round(alpha * np.sqrt(n)).astype(int)
n_interior = n - n_exterior
# Ensure there are still some points in the inside...
if n_interior < 1:
raise RuntimeError(f"Parameter 'alpha' is too large ({alpha}), all "
f"points would end-up on the boundary.")
# Generate the angles. The factor k_theta corresponds to 2*pi/phi^2.
k_theta = np.pi * (3 - np.sqrt(5))
angles = np.linspace(k_theta, k_theta * n, n)
# Generate the radii.
r_interior = np.sqrt(np.linspace(0, 1, n_interior))
r_exterior = np.ones((n_exterior,))
r = np.concatenate((r_interior, r_exterior))
# Return Cartesian coordinates from polar ones.
return r * np.stack((np.cos(angles), np.sin(angles)))
# NOTE: say the returned array is called s. The layout is such that s[0,:]
# contains X values and s[1,:] contains Y values. Change the above to
# return r.reshape(n, 1) * np.stack((np.cos(angles), np.sin(angles)), axis=1)
# if you want s[:,0] and s[:,1] to contain X and Y values instead.
if __name__ == '__main__':
fig, ax = plt.subplots()
# Let's plot three sunflowers with different values of alpha!
for alpha in (0, 1, 2):
s = sunflower(500, alpha)
# NOTE: the 'alpha=0.5' parameter is to control transparency, it does
# not have anything to do with the alpha used in 'sunflower' ;)
ax.scatter(s[0], s[1], alpha=0.5, label=f"alpha={alpha}")
# Display as square plot with equal axes and add a legend. Then show the result :)
ax.set_aspect('equal')
ax.legend()
plt.show()
Adding my Java implementation of previous answers with an example (Processing).
int n = 2000; // count of nodes
Float alpha = 2.; // constant that can be adjusted to vary the geometry of points at the boundary
ArrayList<PVector> vertices = new ArrayList<PVector>();
Float scaleFactor = 200.; // scale points beyond their 0.0-1.0 range for visualisation;
void setup() {
size(500, 500);
// Test
vertices = sunflower(n, alpha);
displayTest(vertices, scaleFactor);
}
ArrayList<PVector> sunflower(int n, Float alpha) {
Double phi = (1 + Math.sqrt(5)) / 2; // golden ratio
Double angle = 2 * PI / Math.pow(phi, 2); // value used to calculate theta for each point
ArrayList<PVector> points = new ArrayList<PVector>();
Long b = Math.round(alpha*Math.sqrt(n)); // number of boundary points
Float theta, r, x, y;
for (int i = 1; i < n + 1; i++) {
r = radius(i, n, b.floatValue());
theta = i * angle.floatValue();
x = r * cos(theta);
y = r * sin(theta);
PVector p = new PVector(x, y);
points.add(p);
}
return points;
}
Float radius(int k, int n, Float b) {
if (k > n - b) {
return 1.0;
} else {
Double r = Math.sqrt(k - 0.5) / Math.sqrt(n - (b+1) / 2);
return r.floatValue();
}
}
void displayTest(ArrayList<PVector> points, Float size) {
for (int i = 0; i < points.size(); i++) {
Float x = size * points.get(i).x;
Float y = size * points.get(i).y;
pushMatrix();
translate(width / 2, height / 2);
ellipse(x, y, 5, 5);
popMatrix();
}
}
Here's my Unity implementation.
Vector2[] Sunflower(int n, float alpha = 0, bool geodesic = false){
float phi = (1 + Mathf.Sqrt(5)) / 2;//golden ratio
float angle_stride = 360 * phi;
float radius(float k, float n, float b)
{
return k > n - b ? 1 : Mathf.Sqrt(k - 0.5f) / Mathf.Sqrt(n - (b + 1) / 2);
}
int b = (int)(alpha * Mathf.Sqrt(n)); //# number of boundary points
List<Vector2>points = new List<Vector2>();
for (int k = 0; k < n; k++)
{
float r = radius(k, n, b);
float theta = geodesic ? k * 360 * phi : k * angle_stride;
float x = !float.IsNaN(r * Mathf.Cos(theta)) ? r * Mathf.Cos(theta) : 0;
float y = !float.IsNaN(r * Mathf.Sin(theta)) ? r * Mathf.Sin(theta) : 0;
points.Add(new Vector2(x, y));
}
return points.ToArray();
}

Using atan2 to find angle between two vectors

I understand that:
atan2(vector.y, vector.x) = the angle between the vector and the X axis.
But I wanted to know how to get the angle between two vectors using atan2. So I came across this solution:
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
My question is very simple:
Will the two following formulas produce the same number?
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
atan2(vector2.y - vector1.y, vector2.x - vector1.x)
If not: How do I know what vector comes first in the subtractions?
atan2(vector1.y - vector2.y, vector1.x - vector2.x)
is the angle between the difference vector (connecting vector2 and vector1) and the x-axis,
which is problably not what you meant.
The (directed) angle from vector1 to vector2 can be computed as
angle = atan2(vector2.y, vector2.x) - atan2(vector1.y, vector1.x);
and you may want to normalize it to the range [0, 2 π):
if (angle < 0) { angle += 2 * M_PI; }
or to the range (-π, π]:
if (angle > M_PI) { angle -= 2 * M_PI; }
else if (angle <= -M_PI) { angle += 2 * M_PI; }
A robust way to do it is by finding the sine of the angle using the cross product, and the cosine of the angle using the dot product and combining the two with the Atan2() function.
In C# this is:
public struct Vector2
{
public double X, Y;
/// <summary>
/// Returns the angle between two vectos
/// </summary>
public static double GetAngle(Vector2 A, Vector2 B)
{
// |A·B| = |A| |B| COS(θ)
// |A×B| = |A| |B| SIN(θ)
return Math.Atan2(Cross(A,B), Dot(A,B));
}
public double Magnitude { get { return Math.Sqrt(Dot(this,this)); } }
public static double Dot(Vector2 A, Vector2 B)
{
return A.X*B.X+A.Y*B.Y;
}
public static double Cross(Vector2 A, Vector2 B)
{
return A.X*B.Y-A.Y*B.X;
}
}
class Program
{
static void Main(string[] args)
{
Vector2 A=new Vector2() { X=5.45, Y=1.12};
Vector2 B=new Vector2() { X=-3.86, Y=4.32 };
double angle=Vector2.GetAngle(A, B) * 180/Math.PI;
// angle = 120.16850967865749
}
}
See the test case above in GeoGebra.
I think a better formula was posted here:
http://www.mathworks.com/matlabcentral/answers/16243-angle-between-two-vectors-in-3d
angle = atan2(norm(cross(a,b)), dot(a,b))
So this formula works in 2 or 3 dimensions.
For 2 dimensions this formula simplifies to the one stated above.
Nobody pointed out that if you have a single vector, and want to find the angle of the vector from the X axis, you can take advantage of the fact that the argument to atan2() is actually the slope of the line, or (delta Y / delta X). So if you know the slope, you can do the following:
given:
A = angle of the vector/line you wish to determine (from the X axis).
m = signed slope of the vector/line.
then:
A = atan2(m, 1)
Very useful!
If you care about accuracy for small angles, you want to use this:
angle = 2*atan2(|| ||b||a - ||a||b ||, || ||b||a + ||a||b ||)
Where "||" means absolute value, AKA "length of the vector". See https://math.stackexchange.com/questions/1143354/numerically-stable-method-for-angle-between-3d-vectors/1782769
However, that has the downside that in two dimensions, it loses the sign of the angle.
As a complement to the answer of #martin-r one should note that it is possible to use the sum/difference formula for arcus tangens.
angle = atan2(vec2.y, vec2.x) - atan2(vec1.y, vec1.x);
angle = -atan2(vec1.x * vec2.y - vec1.y * vec2.x, dot(vec1, vec2))
where dot = vec1.x * vec2.x + vec1.y * vec2.y
Caveat 1: make sure the angle remains within -pi ... +pi
Caveat 2: beware when the vectors are getting very similar, you might get extinction in the first argument, leading to numerical inaccuracies
You don't have to use atan2 to calculate the angle between two vectors. If you just want the quickest way, you can use dot(v1, v2)=|v1|*|v2|*cos A
to get
A = Math.acos( dot(v1, v2)/(v1.length()*v2.length()) );
angle(vector.b,vector.a)=pi/2*((1+sgn(xa))*(1-sgn(ya^2))-(1+sgn(xb))*(1-sgn(yb^2)))
+pi/4*((2+sgn(xa))*sgn(ya)-(2+sgn(xb))*sgn(yb))
+sgn(xa*ya)*atan((abs(xa)-abs(ya))/(abs(xa)+abs(ya)))
-sgn(xb*yb)*atan((abs(xb)-abs(yb))/(abs(xb)+abs(yb)))
xb,yb and xa,ya are the coordinates of the two vectors
The formula, angle(vector.b,vector.a), that I sent, give results
in the four quadrants and for any coordinates xa,ya and xb,yb.
For coordinates xa=ya=0 and or xb=yb=0 is undefined.
The angle can be bigger or smaller than pi, and can be positive
or negative.
Here a little program in Python that uses the angle between vectors to determine if a point is inside or outside a certain polygon
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from shapely.geometry import Point, Polygon
from pprint import pprint
# Plot variables
x_min, x_max = -6, 12
y_min, y_max = -3, 8
tick_interval = 1
FIG_SIZE = (10, 10)
DELTA_ERROR = 0.00001
IN_BOX_COLOR = 'yellow'
OUT_BOX_COLOR = 'black'
def angle_between(v1, v2):
""" Returns the angle in radians between vectors 'v1' and 'v2'
The sign of the angle is dependent on the order of v1 and v2
so acos(norm(dot(v1, v2))) does not work and atan2 has to be used, see:
https://stackoverflow.com/questions/21483999/using-atan2-to-find-angle-between-two-vectors
"""
arg1 = np.cross(v1, v2)
arg2 = np.dot(v1, v2)
angle = np.arctan2(arg1, arg2)
return angle
def point_inside(point, border):
""" Returns True if point is inside border polygon and False if not
Arguments:
:point: x, y in shapely.geometry.Point type
:border: [x1 y1, x2 y2, ... , xn yn] in shapely.geomettry.Polygon type
"""
assert len(border.exterior.coords) > 2,\
'number of points in the polygon must be > 2'
point = np.array(point)
side1 = np.array(border.exterior.coords[0]) - point
sum_angles = 0
for border_point in border.exterior.coords[1:]:
side2 = np.array(border_point) - point
angle = angle_between(side1, side2)
sum_angles += angle
side1 = side2
# if wn is 1 then the point is inside
wn = sum_angles / 2 / np.pi
if abs(wn - 1) < DELTA_ERROR:
return True
else:
return False
class MainMap():
#classmethod
def settings(cls, fig_size):
# set the plot outline, including axes going through the origin
cls.fig, cls.ax = plt.subplots(figsize=fig_size)
cls.ax.set_xlim(-x_min, x_max)
cls.ax.set_ylim(-y_min, y_max)
cls.ax.set_aspect(1)
tick_range_x = np.arange(round(x_min + (10*(x_max - x_min) % tick_interval)/10, 1),
x_max + 0.1, step=tick_interval)
tick_range_y = np.arange(round(y_min + (10*(y_max - y_min) % tick_interval)/10, 1),
y_max + 0.1, step=tick_interval)
cls.ax.set_xticks(tick_range_x)
cls.ax.set_yticks(tick_range_y)
cls.ax.tick_params(axis='both', which='major', labelsize=6)
cls.ax.spines['left'].set_position('zero')
cls.ax.spines['right'].set_color('none')
cls.ax.spines['bottom'].set_position('zero')
cls.ax.spines['top'].set_color('none')
#classmethod
def get_ax(cls):
return cls.ax
#staticmethod
def plot():
plt.tight_layout()
plt.show()
class PlotPointandRectangle(MainMap):
def __init__(self, start_point, rectangle_polygon, tolerance=0):
self.current_object = None
self.currently_dragging = False
self.fig.canvas.mpl_connect('key_press_event', self.on_key)
self.plot_types = ['o', 'o-']
self.plot_type = 1
self.rectangle = rectangle_polygon
# define a point that can be moved around
self.point = patches.Circle((start_point.x, start_point.y), 0.10,
alpha=1)
if point_inside(start_point, self.rectangle):
_color = IN_BOX_COLOR
else:
_color = OUT_BOX_COLOR
self.point.set_color(_color)
self.ax.add_patch(self.point)
self.point.set_picker(tolerance)
cv_point = self.point.figure.canvas
cv_point.mpl_connect('button_release_event', self.on_release)
cv_point.mpl_connect('pick_event', self.on_pick)
cv_point.mpl_connect('motion_notify_event', self.on_motion)
self.plot_rectangle()
def plot_rectangle(self):
x = [point[0] for point in self.rectangle.exterior.coords]
y = [point[1] for point in self.rectangle.exterior.coords]
# y = self.rectangle.y
self.rectangle_plot, = self.ax.plot(x, y,
self.plot_types[self.plot_type], color='r', lw=0.4, markersize=2)
def on_release(self, event):
self.current_object = None
self.currently_dragging = False
def on_pick(self, event):
self.currently_dragging = True
self.current_object = event.artist
def on_motion(self, event):
if not self.currently_dragging:
return
if self.current_object == None:
return
point = Point(event.xdata, event.ydata)
self.current_object.center = point.x, point.y
if point_inside(point, self.rectangle):
_color = IN_BOX_COLOR
else:
_color = OUT_BOX_COLOR
self.current_object.set_color(_color)
self.point.figure.canvas.draw()
def remove_rectangle_from_plot(self):
try:
self.rectangle_plot.remove()
except ValueError:
pass
def on_key(self, event):
# with 'space' toggle between just points or points connected with
# lines
if event.key == ' ':
self.plot_type = (self.plot_type + 1) % 2
self.remove_rectangle_from_plot()
self.plot_rectangle()
self.point.figure.canvas.draw()
def main(start_point, rectangle):
MainMap.settings(FIG_SIZE)
plt_me = PlotPointandRectangle(start_point, rectangle) #pylint: disable=unused-variable
MainMap.plot()
if __name__ == "__main__":
try:
start_point = Point([float(val) for val in sys.argv[1].split()])
except IndexError:
start_point= Point(0, 0)
border_points = [(-2, -2),
(1, 1),
(3, -1),
(3, 3.5),
(4, 1),
(5, 1),
(4, 3.5),
(5, 6),
(3, 4),
(3, 5),
(-0.5, 1),
(-3, 1),
(-1, -0.5),
]
border_points_polygon = Polygon(border_points)
main(start_point, border_points_polygon)

Why is my ball vector retaining its components after reinit?

I'm writing a Pong game with Pygame and this is the code for my Ball class so far:
class Ball(pygame.sprite.Sprite):
def __init__(self, game, vector=Vec2D.Vec2D()):
super(Ball, self).__init__()
self.image = pygame.Surface((BALL_RADIUS*2, BALL_RADIUS*2))
self.rect = self.image.get_rect()
self.__draw_ball()
screen = pygame.display.get_surface()
self.area = screen.get_rect().inflate(-GAP*2, 0)
self.vector = vector
self.game = game
self.reinit()
def __draw_ball(self):
self.image.fill(BLACK)
self.image.set_colorkey(BLACK, RLEACCEL)
pygame.draw.circle(self.image, WHITE, (self.rect.centerx, self.rect.centery), BALL_RADIUS)
def reinit(self):
self.rect.centerx = self.area.centerx
self.rect.centery = self.area.centery
self.vector = Vec2D.Vec2D.from_magn_and_angle(BALL_SPEED, 0)
def update(self):
self.rect = self.calcnewpos()
self.handle_collision()
def calcnewpos(self):
(dx, dy) = self.vector.get_xy()
return self.rect.move(dx, dy)
def handle_collision(self):
(dx, dy) = self.vector.get_xy()
if not self.area.contains(self.rect):
if self.__hit_topbottom():
dy = -dy
elif self.__hit_leftright():
self.game.increase_score()
self.reinit()
else:
for paddle in self.hit_paddle(dx):
if dx < 0: self.rect.left = GAP + PADDLE_WIDTH
elif dx > 0: self.rect.right = SCREEN_WIDTH - (GAP + PADDLE_WIDTH)
dx = -dx
dy = paddle.hitpos / 4
paddle.collided = True
self.vector = Vec2D.Vec2D(dx, dy)
def _hit_topbottom(self):
return self.rect.top < 0 or self.rect.bottom > SCREEN_HEIGHT
def _hit_leftright(self):
return self.rect.left < self.area.left or self.rect.right > self.area.right
def hit_paddle(self, dx):
if dx < 0: paddle = self.game.paddles['left']
elif dx > 0: paddle = self.game.paddles['right']
if self.rect.colliderect(paddle.rect): return [paddle]
else: return []
Well, after the player (or the AI) scores, the ball calls its reinit method that places the ball in the middle of the screen and resets the vector:
def reinit(self):
self.rect.centerx = self.area.centerx
self.rect.centery = self.area.centery
self.vector = Vec2D.Vec2D.from_magn_and_angle(BALL_SPEED, 0)
But, somehow, the ball still maintains the vector it had before reinit was called. So when the ball gets through the left side with a vector like (-5.0 -2.0), it changes quickly in the reinit call and then change back to (-5.0, -2.0). Can someone please tell me why this is happening?
The problem, is in your handle_collision method.
The short story:
The first thing the function does, is set dx and dy to the current vector. Then it calls reinit(). Then, it sets them back to what the first were at the end of the function.
To fix:
Change
def handle_collision(self):
(dx, dy) = self.vector.get_xy() # <-- It first sets (dx, dy) to the old vector
if not self.area.contains(self.rect):
if self.__hit_topbottom():
...
elif self.__hit_leftright():
... # <-- here is where the reinit gets called, which changes the vector to new values.
else:
for paddle in self.hit_paddle(dx):
...
self.vector = Vec2D.Vec2D(dx, dy) # <-- Then the vector gets changed again, to the OLD vector saved above in (dx, dy)
To
def handle_collision(self):
(dx, dy) = self.vector.get_xy()
if not self.area.contains(self.rect):
if self.__hit_topbottom():
...
elif self.__hit_leftright():
...
return # <-- With the return here, the parsing never gets to change it back
else:
for paddle in self.hit_paddle(dx):
...
self.vector = Vec2D.Vec2D(dx, dy)
This will prevent the vector getting set back to what it was, while still setting it when it needs to be.

Is it possible to fill upper left cell in wx.grid.Grid?

How this cell (up to row labels and left to column labels)
can be filled with text or image?
There's an example in the wxPython demo that uses the GridLabelRenderer. Here's the relevant code from the demo:
class MyCornerLabelRenderer(glr.GridLabelRenderer):
def __init__(self):
import images
self._bmp = images.Smiles.getBitmap()
def Draw(self, grid, dc, rect, rc):
x = rect.left + (rect.width - self._bmp.GetWidth()) / 2
y = rect.top + (rect.height - self._bmp.GetHeight()) / 2
dc.DrawBitmap(self._bmp, x, y, True)
Then you call it by doing something like this:
myGrid.SetCornerLabelRenderer(MyCornerLabelRenderer())

Resources