Attribute scaling anomaly - python

I'm making a 2D game that uses 2D transformations to get an objects position in its environment to the relative position it will be drawn at. As if viewed through a moving camera.
In the zoom function I take the position vector (self.pos) and scale it with a value (z).
Assigning this value to a different attribute (self.zoom_pos)
However the line:
self.zoom_pos.x=self.pos.x*z
changes the original position vector which I don't want to do.
Any explanations?
def zoom(self,z):
print(self.pos.x)
self.zoom_pos.x=self.pos.x*z
self.zoom_pos.y=self.pos.y*z
print(self.pos.x,z)
INPUT
self.pos.x = 100
z = 2
OUPUT
self.zoom_pos.x = 200
self.pos.x = 200
DESIRED OUTPUT
self.zoom_pos.x = 200
self.pos.x = 200
edit: print statements were just for testing

while it is not clear from your question, it seems like you have self.zoom_pos = self.pos somewhere in your code, so these two variables are now pointing to the same object, and any change to one will change the other
an easy fix is to change that line to:
import copy # somewhere at the top
self.zoom_pos = copy.copy(self.pos)
this will only make self.zoom_pos a copy of the object in self.pos, and not the same object.
you should also check Facts and myths about Python names and values

Related

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

How to dynamically instantiate objects from a certain class [duplicate]

This question already has answers here:
How do I create variable variables?
(17 answers)
Closed 2 years ago.
I'm making a simple ball game using a 2D map. The amount of balls on the map is variable. When starting the game I load the map and look for balls (The balls are unique letters). I append these ball names to a list.
I also have a Ball class. __init__ contains the coordinates of the ball and the class contains a couple of methods.
Here comes the problem:
I want to create objects of each ball using the Ball class, with the object name corresponding to the letter belonging to the ball.
I tried this:
(The map is a txt file looking like this:)
0 0 0 0
0 0 X 0
0 A 0 B
Where the zero's represent empty tiles.
class Ball:
def __init__( self, x, y):
self.x = x
self.y = y
map_data = np.genfromtxt('map.txt', dtype=str)
ball_names = []
for row in variables.map_data:
for i in row:
if i.isalpha():
ball_names.append(i)
for ball in ball_names:
coordinates = np.where(map_data == ball)
ball = Ball(coordinates[0], coordinates[1])
But then I want the object to be named after the string contained by the variable ball, not 'ball'.
I know this can be done using exec(), but I've been told to avoid that function.
Generating variables dynamically with exec is a bad idea. As in fact variables will be totally hidden from developer. Supporting such code will be a nightmare and it's going to be a giant source of problems.
Instead, why don't you create a dict of balls?
my_balls = dict()
for ball in ball_names:
coordinates = np.where(map_data == ball)
my_balls[ball] = Ball(coordinates[0], coordinates[1])
But if you really want to create a variable, then:
exec(f'{ball} = Ball({coordinates[0]}, {coordinates[1]})')

Python: How do I detect visible shape detection in Tkinter

I am new to Python and I have a program that has 2 classes, one is essentially a rectangle and the other is essentially a circle. I am drawing them using the Canvas in Tkinter in the way below:
def draw(self):
self.canvas.delete("all")
self.rect.draw(self.canvas)
self.ball.draw(self.canvas)
The Ball class has its location variables and diameter variables and the Rect class has its location and dimension variables.
I am wondering how I detect the collision between these two "shapes". I know that one was is to treat the Ball as a square and do basic rectangle collision but I would like to know how to be precise.
I was also wondering if there was anything similar in Python to the way shape collision can be done in Java. In my Java game I use the following code to detect collision between any 2 Shapes:
public boolean collisionCheck(Shape a, Shape b) {
Area aA = new Area(a);
Area aB = new Area(b);
aA.intersect(aB);
return !aA.isEmpty();
}
Is there anything similar to this simple solution in Python?
And if not how would I go about circle-rectangle collision in Python?
Thank you for any help
I managed to figure this out using a method that Tkinter's Canvas object has. Every time something is drawn using Canvas it is given an ID. So as long as you record that ID somewhere you can use the find_overlapping method that the Canvas object has.
Say you have an object, in my case a custom Platform object, that keeps the ID stored in a variable. I did so like this:
def draw_platform(self, canvas): #Located in the Platform Class
self.ID = canvas.create_rectangle(self.x, self.y, self.x+self.w, self.y+self.h)
Now I can use this ID to determine if there are any object overlapping it.
def check_collision(self, plat, other_plat):
result = self.canvas.find_overlapping(plat.x, plat.y, plat.x + plat.w, plat.y + plat.h)
for i in result:
if i == other_plat.ID:
return True
return False
result returns a tuple of the ID's that are located within the rectangle bounds entered into find_overlapping. You can than loop through the tuple to see if any of the ID's match your other shape.
This works great for Rectangle-Rectangle collision and Circle-Rectangle collision.

Create a lot of objects and delete them

so i'm kinda new to programming, but for now with python and pygame i'm trying to create a small game. It is quite simple, the player will move around dodging small projectiles. However i'm having trouble creating a lot of objects, for example i need to randomly generate a position and a speed for a new projectile, and how do i create many objects like that, plus when they actually go 'out of the screen' they should disapear.
So I also need to delete them. My first idea was to create a list of objects, I and a loop that would move them one by one before updating the screen, but how do i create new objects with different names while I don't know how many projectiles will be on the screen, it should be random.
class Projectiles:
ProjectilesCount = 0
def __init__(self, x, y, speed):
self.pos = (x,y)
self.speed = speed
Projectiles.ProjectilesCount += 1
def moveProj(self):
x, y = self.pos
x -= self.speed
self.pos = (x,y)
pygame.draw.line(DISPLAY, WHITE, self.pos, (x,y+self.SIZE), self.SIZE)
bullet = Projectiles(500,200,5)
bullet.SIZE = SIZE
while true:
# some stuff
bullet.moveProj()
pygame.display.update()
fpsClock.tick(FPS)
This is the class i use for now (it just goes left) and it works for just one projectile.
You want a list:
list_of_bullets = []
for i in range(100):
list_of_bullets.append(Projectiles(...))
Use dictionaries. A list will work, but when using objects where the order is not important use dictionaries. Dictionaries also perform better when iterating through lots of data within them. Use some code similar to below...
bulletdict = {}
#This is a list because we can change the value within a function without returning it
#You could also use a global variable instead of a list
bulletcount = [0]
def SpawnBullet(bulletdict,bulletcount):
bulletdict[bulletcount] = Projectiles
bulletcount[0] += 1
The key will be a number and the value will be the bullet itself. To iterate through the dictionary to update stuff do...
for item in bulletdict:
bulletdict[item].moveproj(...)
If you need to remove stuff use code similar to this. I usually have 'checkDelete' functions inside my objects that I can call to check to see if we should remove them. This is used for things like deleting objects if they get off the screen or collide with something.
def deleteObjects(objectdict):
deletelist = []
for item in objectdict:
if objectdict[item].checkdelete() == True:
deletelist.append(item)
for item in deletelist:
del objectdict[item]
This allows for easy use and iteration of your objects. You can always tell the last spawned object because it will have the highest number as the key. This key number will also tell you the total number of spawned objects if you so need it.
I hope this helps you and good luck making your game.

Fast Dictionary Comparison by key, but value within a range of primary Dicationarys Value

Just wondering if anyone can provide me some insight.
I have the following code that generates random positions on my screen, (basically it is positioning around 200 images)
right now the images can have the same co-ord's or very close, my goal is to check the current position generated to previously generated positions(to be stored in the _positioncheck dictionary) and if they overlap or are within the 50% range then skip and regenerate again.
apologies if I have not made this very clear :/, if any other info is needed I can re-edit.
def generateposhint(self):
global _positioncheck
xf =0
yf = 0
self.position = None
xf = random.uniform(0.0,0.9)
yf = random.uniform(0.0,0.9)
self.position = {'x':xf,'y':yf}
for items in _positioncheck:
code to be completed here
#print position
return self.position

Categories

Resources